Monthly update, July 2025
These monthly updates may be a bit technical. They’re written for my future self (to remember how I spent the month and to motivate me to do at least something remotely interesting), for friends and colleagues (past and future) to give them an idea of what I’m working on, and, of course, for potential clients of my C++ freelance business and fellow software developers.
The picture shows that the grapes in my garden are soon ready to be enjoyed.
Projects
NextApp
NextApp is a GTD/productivity application for desktop and mobile.
Android and Running in the Background
When I created NextApp, I decided to use Qt because I could use the same code and program on Linux, Windows, MacOS, Windows, and iOS. Qt has worked very well on Linux, Windows, and MacOS desktop. There have been very few things I needed to specifically adapt for each platform to make it function like a completely normal application.
Android, however, is a completely different story.
One of the key features in NextApp is a calendar. When a calendar event starts, the app plays a notification sound, and when the event ends, it also plays a sound. This is incredibly useful for me because I often get into a "flow" state when working. I lose track of time and the world around me. For example, when I’m coding, I can work for ten hours without noticing the time passing. It's useful to have a sound that tells me this is finished and that I should soon start something else.
Since the start, I’ve mostly only used the desktop version of NextApp. The Android version has been very practical when, for example, I’ve been traveling or in the city with a list of things to do. I could do some tasks, then check the app to see what else needed to be done, prioritize, and organize tasks based on my physical location. It helped me avoid forgetting things. More than once, I was ready to go home but then checked NextApp and discovered I still had a task in the city that I hadn’t done. This feature has been practical worked from the start.
But the feature that plays notification sounds when calendar tasks start and end has not worked at all on Android.
The reason is that a Qt app, and probably regular Android apps as well, work fine when the app is in the foreground and the screen is on. But as soon as the screen turns off or the app goes into the background, it freezes. It’s a bit like a laptop — if you close the lid, it goes into sleep mode and the applications stop running. When you open the lid again, the apps restart, but nothing happens in between. On Android, only one app can have focus. If you switch apps, the first app goes into the background and gets no resources. That’s not practical if you’re working on something and expect your phone to give sound alerts to remind you of something. For NextApp, there are two things that must always work on the mobile app for it to be useful: it must play the alarm sounds to notify about calendar events, and it must update when to play the sounds if the event is changed on another device, for example on the laptop.
The classic solution on Android is to use services.
An Android app with a service has a part of the app running all the time, even if the app is in the background. This service can have timers, perform calculations, and run background tasks like compression or synchronization.
In NextApp, a communication module sends messages to the cloud server when the user makes changes, like adding or editing calendar entries. When I do something on my PC, updates are sent to the server, and the server sends messages to all active sessions. So the PC and phone stay synchronized.
These updates go via gRPC. When the communication module receives messages, most of them are forwarded as Qt signals. Other modules in the app catch messages they’re interested in, for example to update the UI and audio alarms.
But on Android, this only works when the app is in the foreground. A Qt app running as a service typically has two processes: the foreground app and a separate service. They share the same codebase and permissions but are two different processes. That means if an event happens in the background app, the foreground app cannot directly receive it via signals as you normally would in Qt.
I experimented with Qt and Android services for a few days. The plan was to keep the connection to the server open either in a service or preferably a “foreground service” that runs even if the app is in the background. If that wasn’t possible, I planned to use push notifications to send important messages. I found that I can have a background service that receives push notifications (data messages), but only if the foreground app is running. If Android kills the foreground app, the service no longer receives push notifications. I saw in my phones logs that push notifications arrived on Android but were discarded because no active app could receive them.
However, there is a setting in the Qt Android manifest that allows the Qt app to be active in the background:
1<meta-data android:name="android.app.background_running" android:value="true"/>
This is ideal for NextApp! It means the entire system with signals and slots continues working when the app is in the background, alarms work, and network connections remain alive. However, keeping a network connection open when the app is not in use can have a negative impact on the phone’s battery life. So I concluded it’s best to use push notifications anyway to send the most relevant updates (alarms). When the app is in the background now, it does very little and closes the network connection to the server.
On the server side, messages that can’t be sent directly to Android devices are routed to push instead. Technically, I serialize protobuf messages normally sent via gRPC, encode them in Base64, pack them in JSON, and send them to Firebase (Google). On the device, they’re unpacked and handled as if they came from the gRPC communication module.
This works perfectly, with delays under one second. The Android app now feels online all the time, and alarms go off correctly. Changes on the desktop immediately updates on Android.
What doesn’t work is if the app is stopped, for example if Android kills the app after being in the background for a long time, or if you swipe the app away from the screen. Then the alarms stop, and I haven’t found a solution to that yet.
Most apps solve this by sending regular notifications instead of data messages. I can send notifications from the backend that work like an email or Slack message — just a reminder that you have a new message. That will probably be the solution moving forward.
But right now it works as long as it’s allowed to run in the foreground or background on Android.
Google Play and Bundles
Another thing I worked on this month was getting the app ready to be published on Google Play. The entire Google Play Console is quite complex and confusing. There’s no clear logic to it. It’s very easy to shoot yourself in the foot, and I spent a significant amount of time this month doing exactly that.
When compiling Qt apps for Android, you must compile for a specific architecture. There’s no way to build all four Android architectures in one go. I created a pipeline in Jenkins that produced four builds and saved four APKs.
Then I discovered that Google has moved away from APKs. They no longer accept APK uploads in Google Play. Instead, they use something called bundles (.aab files). Qt cannot produce .aab bundles directly, but Gradle can.
After building an APK, you can cd
into the build directory where the Android build happens and then run ./gradlew bundleRelease
to generate a bundle (.aab) file. However, you only get a bundle for the specific architecture you compiled for. Google Play expects you to upload a shared bundle with all the architectures you support. Each upload must have a version, and if you upload one bundle per architecture, you must get creative with the versioning scheme.
What I did was to create a bash script that builds for Android, looping over all architectures and building for 32-bit and 64-bit ARM and Intel architectures. It copies each Android build into a separate destination folder, overwriting files as it goes. The majority of the files are the same across builds — Java compilations, manifests, etc. The platform-specific parts are mainly the shared libraries and some other files, which end up in their respective subdirectories. Once all four builds are copied and merged into a single directory, I run Gradle to build a bundle containing all four architectures. I sign the bundle using the Java signing tool with the same signature I have used for Android for the last ten years. Google Play accepts it.
The downside of this setup is that I cannot build in parallel; I have to build everything sequentially on one build machine. So instead of less than 10 minutes, it takes 20–25 minutes to build the Android bundle. But it works, and now the process is very simple. I can just go to Jenkins in my web browser, click build, go have a coffee, and when I come back, I have a bundle ready to upload to Google Play. Very nice.
Android’s Broken File System
Another discovery: I talked last month about Google Play and exporting and importing data. It turned out that this didn’t work on Android because Google has completely broken filesystem support for applications.
Android is a Linux variant, but you don’t have access to a normal Linux filesystem. The app has its own subdirectory where it can read and write files using a standard API. But if you want access to the user’s “home directory” equivalent — the Downloads, Documents, and other folders — accessing these from a Qt app or any Android app using the latest APIs is nearly impossible.
Personally, I believe Google and Apple share the blame here. They have found ways to trick or force users into storing files in their cloud services instead of locally on the device. I find it disgusting that they use dark patterns to trick users into sharing data with them. It also makes working with Android or iOS phones much harder than it should be if you just want to work with files.
Sure, it’s okay to make things easy for non-technical users, but actively sabotaging users who understand filesystems and workflows is dark. There should be antitrust actions forcing them to stop this, but I’m not a politician — I’m a developer, so I have to live with the system as it is.
My workaround is that when you export data, NextApp creates it locally in the app’s data folder. In the import/export dialog, you can click a button to export the data and another to share that file with other apps.
When you share the file, Android shows the usual apps you can share with. I use NextCloud instead of Google Drive to save the file on my own servers. I don’t think my file managers appeared in the sharing dialog, but that’s probably by design to prevent users from using local storage.
To import data into NextApp, you simply open the file on the device. You can download it to Downloads or elsewhere, then click it. This starts NextApp with an “activity” to open the file, which reads it and processes it just as it would if you imported it on the desktop version of NextApp your laptop.
One special thing about Qt apps opened with an intent on Android is that the intent is triggered before the main() function is called. When the intent triggers in NextApp, the event is received in Java. I don't think Qt itself can handle this directly in Qt 6.9, so the Java code handles the intent, copies the file to the app’s data directory, and calls a C function, which then forwards it to the NextApp core class. It checks if it has been initialized, and if it hasn’t, then it queues a lambda function to handle the import later. After initialization, it runs the queued functions so everything works as expected.
cpp-push
cpp-push is a C++ library to send push notifications.
To get push notifications working, the NextApp server needs to support sending push notifications. Instead of embedding the push notification code directly in NextApp, I decided to write a general C++ library for sending push notifications.
One reason is that I have ideas for a couple of other apps where push notifications will also be essential. Reusing code is very practical — it saves me from having multiple versions of similar code scattered across different applications. Push notifications have nothing specifically to do with NextApp; they’re a core functionality.
In July, I dreamed up and implemented this library, initially with support for sending messages to Google. I haven’t started working on the iOS app for NextApp yet, so that part is still pending.
Anyway, the library works well and can send different types of push notifications. It includes a client program called pushcli for testing.
To use it with Android: if you’re familiar with how push notifications work, you know that you must register the app with Google and obtain a configuration file that’s compiled with the app. On the server side, you must also register your server module and get a service file from Google containing URLs, certificates, and everything needed to send push notifications.
The cpp-push library takes the file you download from Google and parses it into a relevant C++ struct that you can use to send notifications. This makes it very easy to send push messages.
Apple requires HTTP/2 to send push notifications. This means I cannot use restc-cpp or Boost.Beast, as both only support HTTP 1.1. However, libcurl supports HTTP/2.
So, in this project, I use my own C++ wrapper, RESTinCurl, over libcurl for all communications. The library handles authentication in the background. Google has decided (for reasons unknown) that you must authenticate using a service account, obtain a token, and then use this token to authenticate each individual message. Apple uses a simpler approach with a single JWT token you sign with a key they provide for authentication. In both cases, the tokens must be refreshed periodically because their lifetime is about one hour. The application must create new tokens before they time out. These authentication methods are originally designed for web users. A server that acts on behalf of users is not a web client, so API keys or cryptographic certificates are the appropriate way to authenticate. However, the world is insane, and servers must now use web authentication to call APIs.
The library uses HTTP REST for communication and requires an Asio context passed in when it starts. It uses this context to run a coroutine that creates a token, then sleeps for almost an hour, creates a new token, sleeps again, and so on. The token is stored in a shared pointer wrapped in an atomic, so no mutex locking is needed. When you send a message, the function simply copies the current shared pointer and uses it to authenticate. There is some overhead here, which really shouldn’t be necessary. It’s actually ridiculous. It provides no real security benefit and only wastes resources, increases cost (development time), adds complexity (generally bad with regard to security), and makes testing harder.
RESTinCurl
RESTinCurl is a modern C++ header-only library wrapper around libcurl.
Since the decision was made to use RESTinCurl as the communication layer for the C++ push library, I also decided to make a small update to RESTinCurl so that it can use C++ coroutines. The reason for this is that in my servers, I use a lot of asynchronous coroutines with Asio.
A few years ago, I used stackful coroutines. I still like them more because they are much easier to debug and work with. But they have some overhead — at least in theory. The last time I measured this, which was a few years ago, the overhead between Asio C++ coroutines and stackful coroutines was about the same, but stackless coroutines, like those Asio implements internally with a loop, were much, much faster. I guess this might have improved in compilers and libraries since then.
What I discovered when I implemented async coroutine support in RESTinCurl was that Asio's coroutine support is not standard. If you implement C++20 coroutines with Asio, it only works with Asio. If you implement coroutine support using the C++20 standard without considering which library will use it, you can't await
on them within an Asio coroutine scope.
So, when I implemented these new async coroutines in RESTinCurl, I implemented both a function for generic coroutines that works with general C++20 coroutine libraries, and another version that works specifically with Asio. This way, we can use Boost.Asio and generic awaitable coroutines.
There are test cases for both types in the library code.
If you like C++20 coroutines and need HTTP/2 or HTTP/3 support, RESTinCurl is a good option.
How I Made This Post
Until now, I have written these updates in English using the keyboard. In the last year or so, I have asked ChatGPT to spellcheck them before they are published.
Today, I decided to try something new. I made a list of things to cover, then dictated the text in my native language to the Audacity application on my Linux workstation, using a USB headset microphone. Once recorded, I used Whisper with its medium model to transcribe the audio to text locally. Then I uploaded the text file to ChatGPT and asked it to clean it up and translate it into English. I edited that text, removed repetitions, and added clarifications as usual. Finally, I asked ChatGPT to proofread it.
So, even if the text shows some markers of being AI-generated, it is not. The full content is created by me but run through AI to translate and check the language in the final version. I can express myself quite better in Norwegian than in English, so this was an interesting experience.