With the ongoing work on realtime data access in KDE Itinerary we need a way show notifications in case of delays or other trip changes. That’s what KF5Notifications is for, which unfortunately isn’t supported on Android yet. Since an Android specific code path in KDE Itinerary for that would be quite ugly, I did look into adding Android support for KF5Notifications. How hard can it be? ;)
How to do notifications with the official Android Java API is widely documented, and also matches the model of KF5Notifications well enough. This would however be the first KF5 framework using Android Java API for its backend, so I’ll focus on these integration bits here.
On the code level this means we need to be able to call Java code from C++ (to trigger a notification), and vice versa (to handle user interaction with the notification), and we need to be able to pass data back and forth.
The mechanism for this is the Java Native Interface (JNI), of which Qt abstract some parts via QAndroidJniObject. Qt itself contains a number of examples on how to use this, and you can look at the KF5Notifications code in D17851. The JNI signature syntax takes a bit of getting used too due to its built-in traps (like a different way of writing fully qualified class names compared to Java code), but at least you usually get helpful debug output at runtime in case of a mistake.
Building and Deployment
The build system integration turned out to be the most challenging part, as the framework consists of a Java
part and a native part now. The general idea is that the framework builds and installs a Java library next to the
native library, and provides a meta data file that tells
androiddeployqt to also integrate the Java part when
linking against the native part.
This is done in several Qt modules, but using qmake rather than CMake. Also, Qt only does this for Java code using the official Android Java API, not for using the Android support libraries, which is needed in case of notifications to simultaneously provide compatibility with older Android versions and to comply with requirements of very recent Android versions or the Google Play Store.
The first attempt was following closely what Qt does, and just implement that with CMake. You’ll find the code in D17851. There’s already basic Java support in CMake that makes this pretty straightforward, it essentially just builds a JAR library from a given set of Java source files using the Java compiler directly.
This approach works as long as you just use the basic Android API and don’t need any dependency outside of the normal Android SDK. But as that’s not enough for notifications, we needed another way.
The Android support library (and presumably other higher-level dependencies) break this approach in two places:
- The canonical way of accessing dependencies on Android is via Maven repositories. Neither CMakre nor the raw Java compiler support that easily.
- Those libraries are not regular Java libraries (JAR files) but Android libraries (AAR files). That’s essentially a ZIP file bundling the JAR file as well as resources, manifest elements, and other things you might find in an Android APK. However, this is also not directly supported by the Java compiler.
In order to consume AAR files we use the default Android way, building with Gradle
(which is also what
androiddeployqt does when building APKs).
Obtaining and running Gradle is hidden behind a small CMake macro for easy integration, but how to build the Java
side is now specified in a
build.gradle file rather than in CMake files. This means all Android development
related tools and resources become applicable for us. Besides the automatic dependency handling and inbound and
outbound support for AAR libraries also things like using Kotlin should be possible.
This approach has been implemented in D17986.
Consuming the result in the application is almost identical to the JAR case, we only needed a
minor tweak in the
androiddeployqt template for this.
Starting with KF5 5.55 we will have basic support for notifications, notification interaction and notification actions on Android. There’s probably still a number of features and flags in KF5Notifications that can be better mapped to Android’s native system, and there’s still work to be done to improve compatibility with a wider range of Android versions, but it’s a good start. But maybe even more importantly, we now have a template for integrating Android Java code in KF5 frameworks.