GSoC '23: Wrapping Up

Hello and welcome back to my blog! In this blog, I will be detailing the work I've done over the second coding period of my 2023 GSoC journey with KDE. Let's dive in!

Work done

Over the second coding period, I accomplished the following tasks:

Fix Okular Icon Rendering

Okular's Android app was not rendering the icons at all. At first, I thought that it might be an issue with the icons not being packaged. I tested out this theory, by exploring the contents of Okular's apk file. Inside the APK was a .qrc file, which I decompiled using a tool recommended to me in the KDE Android matrix room. Upon exploring the contents of the .qrc file, I found that all the icons were already packaged there. If I used the icons with their full paths in the qrc file instead of their freedesktop name (i.e. qrc:/path/to/document-open.svg instead of just document-open), the icon would show up properly in the app. I concluded that the issue was not due to faulty packaging of icons, but Kirigami not being able to find the proper icons.

Over the next week, I spent a large amount of time desperately trying to pick apart Okular and find the faulty bit of code that was causing the icons not to display. As I grew increasingly frustrated, I turned to the KDE android matrix room to ask for help.

They suggested that the KIconThemes library was causing issues just by being present, due to this line of code that executes any time the Okular app started up. What it does is, it automatically configures the app to use the breeze theme as a fallback. This was interfering with Kirigami's icon-finding mechanism, causing the icons to not show up.

Initially, I just removed KIconThemes as a dependency from Okular's CMakeLists.txt, but that didn't work. A couple of days later, several developers from the KDE Android matrix room pointed out that even though KIconThemes was removed, it was still being packaged with Okular by craft. They suggested that I wrap the offending code within a conditional compilation block so that Android would not execute it automatically.

I followed their advice, and it worked like a charm - Okular was finally displaying the icons properly.

I then created a Merge Request at the KIconThemes repository to implement the fix for the icons issue.

Peruse, another KDE application, was also having similar issues with icons as Okular. The fix also helped it to render icons properly.

Package Okular Icon

After fixing icon rendering, I added the Okular icon to the CMakeLists.txt for Okular's Android version, which would package it along with the other icons. This was needed because two places in the Okular app use icons:

  • The global drawer

  • The About Page

These places were empty when they should've been displaying the Okular logo, as can be seen here:

After adding the Okular icon to the kirigami_package_breeze_icons() array, you can now see the Okular icon in its glory in the global drawer and about page.

However, I did run into some footgun moments with craft while performing this task. This is because to test changes in Okular, I had always run the following craft commands to build and package it as an APK:

craft --compile okular
craft --package okular

However, icons are a special case. If you add a new icon, it has to be installed and qmerged as well. You do that by:

craft --compile --install --qmerge okular
craft --package okular

I spent a couple days trying to figure out what was wrong due to this small mistake. I only realized what was happening when I took a closer look at the output when freshly installing Okular using craft -i okular.

The members of the KDE Android Matrix room suggested that I edit the wiki page for Craft so that other people can avoid being plagued by this small mistake.

Moving from Kirigami.AboutPage to MobileForm.AboutPage

While adding the Okular icon to the app, I also came across MobileForm.AboutPage, which is an experimental feature from Kirigami Addons that changes the layout of the About page from Kirigami.AboutPage. I asked my mentor if we should move to MobileForm.AboutPage since it looks better and more modern, and he agreed. So I replaced Kirigami.AboutPage with MobileForm.AboutPage in this merge request.

Here's how the Kirigami.AboutPage looks in Okular:

Here's how the new MobileForm.AboutPage looks in Okular:

Porting to FormCard from MobileForm

Shortly after I had moved Okular to using MobileForm.AboutPage, Kirigami Addons renamed MobileForm to FormCard. The MobileForm QML import would still be available but it would not receive new features. The new FormCard will receive new improvements, so it is advised to switch to it. This blog post by Carl Schwan explains it in more detail. So I ported Okular to the new FormCard API in this merge request.

FormCard.AboutPage has a few subtle differences from MobileForm.AboutPage. Here's how it looks like:

Fix qtbase Crash/Hang in Okular

Sometime around the start of the first coding period, I noticed that Okular on my phone would freeze on a black screen when attempting to open PDFs in it. I initially worked around this running Okular in the Android emulator packaged with Android Studio.

However, since I had some more free time during the second coding period, my mentor and I decided to tackle this issue. The issue seemed to be device specific, and only a couple of the phones I tested Okular on had this issue.

I distinctly remembered that an older version of the Okular apk was running properly on my device, without freezing/crashing to a black screen whenever I attempted to open a PDF. Luckily I was able to find an old APK from my phone backups, and confirm this.

This older APK was v22.12.3 of Okular, with Qt 5.15.8, and KDE Frameworks 103.0. The newer APK with the black screen issue was release/23.04 of Okular, with Qt 5.15.10 and KDE Frameworks 108.0.

My mentor suggested downgrading Okular to version 22.12.3 and testing it. The app still crashed, so my mentor suggested that Qt itself might be causing issues, and so I compiled Qt 5.15.8 in Craft and testing Okular by building against it. It worked fine, and opened PDFs properly. After this I did the same with Qt 5.15.9, which displayed the black screen issue in Okular.

So far we had deduced that the issue occurred sometime between Qt version 5.15.8, and 5.15.9. To narrow down the search, my mentor then shared a list of commits which were applied between Qt 5.15.8 and 5.15.9, and were related to Android.

Immediately I noticed that when running Okular on Android, adb logcat would have some warning messages from Qt A11Y. During testing, I had also noticed that in the tombstone file generated by Okular crashing during the black screen, the stacktrace indicated that Qt was doing some threading-related stuff and also executing Qt A11Y code.

In the list of commits shared by my mentor, you will notice that commit 513fcf0d2ad3328830dbf73dc2a55ad1487393c0 deals with Qt A11Y and threading both, so this commit stood out to me. My suspicions turned out to be correct - when I checked out the commit in git, Okular was freezing/crashing as usual, but when I checkoud out the commit immediately before it, Okular was working fine.

I shared this information with my mentor, and he looked up some commits from qt6 that seemed related:

To test these patches, I backported them using git cherry-pick, rebuilt Qt, and the rebuilt Okular.

After a bit of testing, I saw that the commit ac984bd8768b3d7e6439e0ffd98fd8b53e16b922 completely solved the issue of black screening when opening a PDF on Okular. Out of the 2 devices I tested this on, both no longer displayed any issues with this commit.

My mentor suggested that I send these patches to the kde/5.15 branch of qtbase at KDE's qtbase repository by following this guide:

I followed the instructions given in the page - I cherry picked the commit to the kde/5.15 branch of qtbase, and opened a merge request on the kde/5.15 branch of KDE's qtbase repository.

Challenges faced

Getting used to Craft's system:

Craft is an open source meta build system and package manager. It manages dependencies and builds libraries and applications from source, on Windows, Mac, Linux, FreeBSD and Android.

In order to work with Okular, I had to get used to Craft. As a beginner who just learnt CMake over the course of GSoC, learning to use Craft took quite a bit of effort. The first few steps were a breeze, thanks to the KDE wiki page about Craft and also because I had worked with Craft during the first coding period.

However, Craft keeps bumping up versions of software such as KDE Frameworks, Qt, etc. every once in a while. This can make it tough to test out older versions of such software. In my personal experience, I had to mess around in Craft's version.ini files for Qt5, located at /CraftRoot/etc/blueprints/locations/craft-blueprints-kde/libs/qt5.

Building Qt in craft - binutils

While trying to build applications and libraries in craft, I frequently ran into errors about the GNU binutils not being able to recognize the file format of the compiled files. I usually bypassed these by setting the $PATH variable to point to the GNU binutils built for Android, usually located at /home/user/CraftRoot/tmp/android-24-toolchain/arm-linux-androideabi/bin. Now keep in mind that I have only encountered this issue when building for arm32 devices, it could be entirely possible that it builds just fine when building for arm64 android devices.

However, that's not all - I encountered this issue again when building Qt for arm32 Android inside the Craft environment. For example, when building qtbase, I got the following error when using the default toolchain.

/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy:/home/user/CraftRoot/build/libs/qt5/qtbase/image-MinSizeRel-5.15.9/bin/qmake: File format not recognized
Command ['/opt/android-sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy', '--only-keep-debug', '/home/user/CraftRoot/build/libs/qt5/qtbase/image-MinSizeRel-5.15.9/bin/qmake', '/home/user/CraftRoot/build/libs/qt5/qtbase/image-MinSizeRel-5.15.9-dbg/bin/qmake.debug'] failed with exit code 1
Action: install for libs/qt5/qtbase:5.15.9-1 FAILED

In this case, simply setting the $PATH variable did not work, I had to improvise and use the Xamarin android toolchain as it was more up to date, and add it to $PATH inside the Craft docker container. However, this toolchain generated much larger APK sizes, jumping from a modest 56 MB to 110 MB.

Maybe in the future I could try creating a Craft package for GNU binutils for Android, which would minimize, or at least reduce the frequency of running into problems like this. No promises though! :P

Debugging in Android

This has been mentioned in my previous blog post already, but Android's way of handling permissions made debugging a headache. Initially I rooted my Android phone and used gdbserver to try and get debugging going, but it eventually ended with me giving up and resorting to staring at ADB logcat and trying to make Okular crash so that it would produce a tombstone file. While this approach did lead me to success with solving the black screen issue, it would have been nice to have some good debugging infrastructure.

Remaining Work

While this GSoC coding period was useful to fix some of Okular's issues as well as improve it a bit, there is still some remaining work that would go a long way for Okular to be considered an attractive PDF reader on Android:

Adding a "recent documents" section

Okular on desktop has a list of documents that were recently opened by the user. This allows the user to quickly re-open documents that they frequently access, and consequently speeds up their workflow as well as being much more usable. However, Okular on Android lacks this feature, and it would go a long way to make Okular a more attractive PDF viewer.

Adding the ability to jump to a specific page number

Okular on Android has no support for jumping to a specific page number of a PDF. This can be tedious for example in large documents with hundreds of pages.

Fixing bookmarks

At the moment, bookmarked pages do not show up in the bookmarks tab, as the bookmarks tab of the side drawer cannot be accessed as it is grayed out.

What now?

This GSoC journey has been an enjoyable learning experience for me. In the future, I'd like to contribute to KDE again, no matter if its through GSoC or not.

I'll most likely try to contribute to KDE and other FOSS projects whenever I have free time, and help those projects to improve, while improving my skills at the same time.

Wrapping it up

This GSoC has been a valuable and unforgettable experience into the world of Open Source Software. Not only did I get to learn the process for contributing to open-source and hone my own skills, I was also able to contribute to KDE, one of my favorite FOSS projects.

I'm thankful to my mentor Albert Astals Cid for his patience and guidance, which undoubtedly helped me overcome many of the obstacles in my journey.

I'd also grateful for the KDE Community, especially the members of the KDE Android Matrix room - they helped me to fix the Okular icon rendering, and without their help I might have still been bashing my head trying to solve it.

That's all for now. See you next time, and have a nice day! :D