TIL: How to publish an Android library with github+jitpack

Key steps I was missing:

  1. Ensure that the repository root has a settings.gradle file with a line referring to the library:
    include ':customcomponents'
  2. Add apply plugin: 'com.android.library'​ at the top of the library’s build.gradle file.
  3. Add group, version and base name to library’s build.gradle file:
    group = "com.github.adityabhaskar"
    version = android.defaultConfig.versionName
    archivesBaseName = 'customcomponents'
  4. Push to Github. Preferably, give it a version tag.
  5. Open jitpack.io. Connect to Github. Select library
    • If using a free account, the github repository needs to be public/OSS.
  6. Select a commit, or a branch snapshot and click on ‘Get it’. Build process will start in the background. If all goes well, the library will be available to use in projects.

Continue reading

TIL: How to add moment.js intellisense to VSCode

npm install --save-dev @types/moment

Run that command at the root of your workspace folder, and that’s it. All the moment classes and methods are available in intellisense. To confirm, open the package.json in the workspace root folder. It should have a line like this:

"devDependencies": {
    ...
    "@types/moment": "^2.13.0",
    ...

Simples :)

Callbacks Vs JavaScript promises & Kotlin coroutines

Convert callback based asynchronous methods for more linear code, with Promises + Async/Await in JavaScript and Coroutines in Kotlin.

In JavaScript, wrap the callback based method inside a new Promise((resolve, reject) => {...}) call. Then call the resolve/reject method of the promise on callback completion. The result is an asynchronous promise that resolves with the result of the callback.

In Kotlin, wrap the callback based method inside a suspendCoroutine {continuation-> ...}. Then call continuation.resume or continuation.resumeWithException on callback completion. The result is an asynchronous/suspending Coroutine that returns the value of the callback.

A small drawback of these approaches is that they return a single value, while the callback can have multiple values. The solution to this is to wrap the values returned from the callback in an object (JS) or data class (Kotlin).

TIL: Automatic date in version name // Android+Gradle

I name my app versions as YYYY.MMDD.Major.Minor.Working. As an example, the current version of an app while I’m working on it may be 2020.0506.1.24.3 while the published version may be 2020.0501.1.23.

I’ve got a good habit of updating the versions section—the Major.Minor.Working bit. But I often forget to update the date bit, specially the DD section. So, after a bit of research (THANK YOU STACKOVERFLOW, FOR EVERYTHING), I got this bit working.

This is all it took:

// build.gradle (app module)

def versionString="0.6.1"

static def getDate() {
    return new Date().format('yyyy.MMdd')
}

android {
    ...
    defaultConfig {
    ...
    versionName "${getDate()}.${versionString}"
    ...

I manually update the versionString when I move to the next version. The build process automatically adds the date to it. Simple, and robust :)

TIL: Firebase…

A couple of weeks ago, I spent a lazy Sunday afternoon studying Firebase docs. Got interested enough to start playing with some code. Ended up writing a couple of Firebase (cloud) functions to fetch and serve data to two of my apps. In the process learnt to use Firebase functions and Firestore database. Next day, I went back, cleaned up the code a bit, and moved one of my subdomains to Firebase hosting to make the API endpoints look clean and tidy. It’s been running smooth and tidy ever since.

In half a day, I created a robust, scalable, and free (at current scale) backend to provide data to two of my apps while massively reducing load on the real data server. The requests to real server went from a few thousand to about 200 a day. Despite increasing the front-end fetch frequency to nearly 3x the previous value.

I really enjoyed learning, developing, and deploying this solution. So much so that I’m now itching to move other backend stuff to firebase, sometimes even when it doesn’t need to be :D

TIL: Retrofit2

Discovering, using and loving Retrofit in PhotoPress.

Got the starter boilerplate code from Retrofit website, and updated a couple of methods for the WordPress.com API. Got it to work, then started expanding the interface with more methods and data classes to flesh it out more.

It’s a delight to work with—simplifies things a lot by taking care of request creation, error handling, and response parsing. It has also helped make the API calls a lot more robust.

I’m already planning to convert AcceleReader for Instapaper to Retrofit as well! It’ll take more work because of the slightly wild-west nature of Instapaper API, but should be doable, and good for the code (and my learning).

Continue reading

Splashing around

I’m interviewing at one of my dream workplaces. To prepare for the first interview, I started looking at their repositories on Github. Since I enjoy learning by doing, I decided to look if they have any easy outstanding issues. They did. They even helpfully marked them as ‘good first issue.’ That’s how I landed up on the task at hand.

A user reported that even when his app was set to dark mode, when opening, it first created an all white flash screen, before opening the activity in dark mode. A team member had suggested that they probably didn’t have the correct background colour resource for dark mode. Seemed like a trivial fix, so I decided to look into it.

After a bit of digging around, it became clear that this wasn’t the cause. The theme and colours had been setup properly. The flash was occurring before the theme was even applied.

They were setting the night mode at the beginning of the activity. The recommendation is to set it in the application class. My guess was that this was causing the issue. But I couldn’t replicate the issue, so I couldn’t really test if this was really the cause. Nonetheless, I made the change and sent in a PR, stating that I didn’t know if this was really the cause.

The team member had a look and reported that the bug was still occurring. He also gave detailed instructions on creating the bug. He may have also suggested that it was a low priority bug not worth spending much time on. But, now that I had a way to reproduce the bug, I had to give it another go.

This time I got the diagnosis correct. The bug occurs not because the theme is not applied. It occurs that when an app is launched, the system always creates a splash window for it. For this splash, the system uses the windowBackground attribute in the app’s default theme. If there are day/night variants, it applies the variant according to current system night mode. Not according to the app’s specified night mode.

This difference between the system and app night mode was what created the unique condition for this issue. It only occurs when the system night mode is different from the app’s night mode. Moreover it never appears when the night mode is set to ‘Follow system’ in the app. Which is why I had been unable to replicate it.

Now that I knew the cause, there were two clear solutions:

  1. Set an attribute in the app theme to hide the splash window:
    <item name="android:windowDisablePreview">true</item>
    This approach is very simple. But it produces a visible delay between the user pressing the app button, and the app window actually appearing.
  2. Create a special splash theme with a background that appears neutral on  both night and dark theme.
    I use this in my Todo.txt app—background in primary colour with the app icon on it. I recommended this approach.

After another round of feedback, I created a new theme for their app with brand colour as background and a clear white logo centered on screen. I set it as the default application theme, and added the actual app theme to the individual activities. Works like a charm.

I’ve updated the code and am ready to send in another PR. Their team is having a discussion on size of the logo—I’d set it at 96dp, the designer wanted to check how 72dp looked. (I now prefer the 72dp logo.) Once that is confirmed, I’ll update the size and send the PR.


Side effects

Working on that introduced me to two changes I needed to make to my apps.

Todo.txt uses a green splash screen that works (mostly) in both dark and light modes. However, I use a special Splash Activity to with this theme. The activity doesn’t have any content. It’s just there to ensure the splash theme is used. It just forwards all launch intents to the Main Activity.

My first intent was to just update the manifest with splash theme as the default application theme, and the actual app theme as default theme for various activities. Sadly, the system is smart. If both the application and the launch activity have a theme, it ignores the application theme and just uses the activity theme. Which means no splash :(

I have now set the Splash theme as the default application theme in the manifest. Next, I created a base ThemedAppCompatActivity from which all my applications extend. All that this activity does is to apply the actual app theme. It uses the old default theme. A child activity can override the themeId variable with another theme’s id to use it. Simples. These changes allowed me to get rid of the SplashActivity. (Code deletion dance! Yay!)

AcceleReader for Instapaper uses night mode specific splash screen. A light or dark background with the pink app icon centered in it. Now that I know the special case of the original issue, I verified that AcceleReader suffers from it as well. Set the app theme to dark and the system theme to light. Open the app. It shows the light mode splash screen before opening the app in dark mode.

I need to change this to a single splash screen irrespective of night mode. Just need to find the right colour. I don’t think a full pink screen will work :)


tldr: System uses system night mode to create splash screen, not the app’s specified night mode. This causes an issue when app and system are in different night modes. To rectify, specify a single splash theme with a neutral windowBackground that works in both day and night mode.

TIL: Webview @JavascriptInterface methods run in a different thread

I was trying to update a LiveData in a ViewModel from a Javascript method within the Webview. The Kotlin method was being called but the LiveData was not being updated. There were no errors either.

This fails silently:

@JavascriptInterface
fun onSelection(selection: String) {
    readerViewModel.setSelectedText(text)
}

After a lot of frustrating tinkering, I got the LiveData to update when I wrapped the update statement within a CoroutineScope.launch  on the Main thread.

This works:

@JavascriptInterface
fun onSelection(selection: String) {
    setSelectedText(selection)
}
...
private fun setSelectedText(text: String) {
    CoroutineScope(Dispatchers.Main).launch {
        readerViewModel.setSelectedText(text)
    }
}

It appears that the @JavascriptInterface methods are executed on a separate non-Main thread. Usually an exception is thrown when a ViewModel is updated from a non-Main thread in Kotlin. Surprisingly (and very confusingly), this exception isn’t thrown when updating the ViewModel from @JavascriptInterface. It just fails silently :/