Tuesday, July 29, 2025

Escapa aka Pilot test

If you browsed the internet about a quarter of a century ago, you might remember this game. I have no idea who the original author is, but it is possibly one of the first JavaScript games that went viral. At that time, there were no social networks, so "going viral" actually meant being shared via email. Now it sounds so stone age, but it actually worked quite well.

Way back in 2010, I "ported" the game to Android and published it (it was my fourth Android app), and since then I have pretty much forgotten about it. It was yet another financial hit of mine - the banner ad earned me a total of $22.43 USD over those 15 years. You can't imagine how I spoiled myself with that money.

 

Recently, Google Play reminded me that I should update the game if I want to keep it in the store (why not?!), and it was also time for me to open-source it. (I have decided to clean up my old self-hosted source code versioning systems and eventually open-source everything I've written.)

So here is the game for you geezers to indulge your nostalgia. You can grab it from Google Play (the version with ads) or from GitHub (ad-free version, supporting old devices from 2012 running Android 4.0.4). 

Try to beat my score!

Sunday, July 13, 2025

Step-by-step: monitor Ko-fi traffic sources with Google Analytics

Tracking sources to your Ko-fi landing page using Google Analytics is straightforward. Simply append the appropriate utm_* parameters to your link. According to the documentation here, you should always use utm_source, utm_medium and utm_campaign. For example, the final URL might look like this:

https://ko-fi.com/<your_name>/?utm_source=<source>&utm_medium=<medium>&utm_campaign=<campaign>

Of course, replace <your_name> with your Ko-fi handle and <source>, <medium> and <campaign> with what you find suitable. That will be the link you distribute instead of the common one.

Once you have done that, head to your Google Analytics account. In the left side menu, under 'Reports', 'Generate leads', click on 'Traffic acquisition'. In the table shown, instead of 'Session primary channel group', select 'Session source/medium'.

That would be it!

Saturday, July 12, 2025

Android SDK 35 update: fixing edge-to-edge layout problems with minimal code interventions

As an Android developer with apps on the Google Play Store, you should have received an email from Google regarding the requirement to update your apps to target SDK 35 by the end of August. According to StatCounter, SDK 35 is currently present on 20% of Android devices.

In case you haven't decided to abandon your apps, there is no option but to update them. As is often the case with Android, Google tends to break things, so targeting SDK 35 also causes issues. In this post, I will focus on the enforced edge-to-edge app layout and the easiest way to opt out of it.

After updating the targetSDK and compileSDK to version 35 in your app's build.gradle file, you may have thought you were nearly finished with the necessary updates. However, upon running the app on an SDK 35 device, you might be surprised to discover that your app's layout is now rendered beneath the action bar and navigation buttons. Check out the image below for a visual reference.


Let's resolve this with minimal code changes and move on with our lives.

My demo app is quite simple, and it uses the Theme.Material3.DayNight theme. This theme is referenced directly in the AndroidManifest.xml file as follows:

<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Material3.DayNight"
>
    ...

You might be referencing another theme directly, or you are defining your own theme, inheriting from an existing one. We are going exactly in that direction, as we will need to define our own theme anyway. 

First, we'll add a themes.xml file to our res/values/ directory (if it isn't there already). In that file, we'll define BaseTheme, in this example inheriting from Theme.Material3.DayNight. You will be adding general theme customizations to the BaseTheme. We will then inherit our final AppTheme from the BaseTheme (you'll see in a moment why another theme inheritance is necessary). So, themes.xml will look something like the following (you can use the styles.xml file instead, I prefer separating layout element styles from themes):

<resources>
<style name="BaseTheme" parent="Theme.Material3.DayNight">
<!-- General theme customizations go here. -->
</style>
<style name="AppTheme" parent="BaseTheme" />
</resources>

Now, we'll create the res/values-v35 directory and add another themes.xml file to it. In this file, our AppTheme will inherit from the BaseTheme as well, but it will opt out of edge-to-edge enforcement for SDK 35 devices. The file will look like the following:

<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="BaseTheme">
<item
name="android:windowOptOutEdgeToEdgeEnforcement"
tools:targetApi="35">true</item>
</style>
</resources>

Finally, we need to update the AndroidManifest.xml file to reference the AppTheme:

<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
>
    ... 

And that would be it! Happy coding!

Update:

Thanks to Leon Omelan for the heads-up. The windowOptOutEdgeToEdgeEnforcement attribute, that has been added in SDK 35, has been deprecated in SDK 36, and it will not work on SDK 36 devices once you target that API level. Therefore, this solution is only temporary (just like anything else with Android).