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 :/

TIL: VS Code tasks and bash scripts

I use Hugo for some static pages on the website. I wanted to convert some common actions that take a couple of steps into a single command. The one I tacked now is to create a new ‘Book’ page template in Hugo and open it in the editor. It’s a simple action, but took me a bit to figure out the basic stuff.

1. Create a VSCode task to execute the commands without opening terminal.

If the workspace doesn’t have a ‘tasks.json‘ already, create one
Press 'F1' - my shortcut key for "Show all commands" Select "Run task", and follow the prompts to create a new task. Select "Other" when selecting task type. VSCode will create a 'tasks.json'  file with a single task that prints 'Hello' on the command line.
If there’s already a ‘tasks.json‘ in the workspace folder, add a new task

Simplest way is to just copy a previous task and change details – label, type, command, args, problemMatcher, etc

2. Configure task and inputs

Label is just the name for the task.

"label": "New Book"

Type is the command type. In my case, this was just ‘shell

"type": "shell"

For command, we’ll just point to the shell script. I use VSC’s in-built variable, ‘${workspaceFolder}‘ to mark the path to the script.

"command": "${workspaceFolder}/pages/makebooks.sh"

I provided two arguments to the shell script—the workspace folder path and the name of the book to create.

"args": [
    "${workspaceFolder}",
    "${input:bookName}"
]

Finally, since I was using a user input ‘input:bookName‘, I had to create an inputs section too.

"inputs": [{
    "id": "bookName",
    "type": "promptString",
    "description": "Name of the book, please:",
    "default": "A book"
}]

Here, id is the variable name of the input which we can use to reference it in the task section. type is the type of input. It can be a promptString asking user to type a string, a pickString providing user options to choose from, or a command which runs a command and returns the output as input. description is the prompt shown to the user when input is asked, and default is the default input.

That’s the task creation done. I still have to make the actual shell script to execute, but this bit is done.

3. Create a shell script to execute when the task is run

Create a new file with the name and path chosen in ‘command‘ section above. Also make the shell script executable.

Now open the shell script in VSC, and make it do the work.

First, just switch to the subfolder where hugo related content is kept.

cd "$1"/pages

$1 is the first argument to the script, which in our case is the first item in the ‘args‘ provided in the task description—the home folder of the VSC workspace.

TIL: Escape inputs with "" to send them to other commands.
It took me a bit of googling to figure out that the easiest way to take user inputs and pass them, escaped, to other commands is to just enclose them in "". Specially, like in my case when there are spaces in book names or paths.
Another alternative that didn’t work for me was using printf: printf -v bookName "%q" $2. This will escape the $2 arg and save it in variable bookName.

Next, create the new books page:

hugo new books/"$2".md

Now, check if the command succeeded (it fails if a book with that name already exists).

if [ $? -eq 0 ]; then

If it succeeded, open the book file for editing:

code content/books/"$2".md

TIL

  • Using $? to get the exit code of the last command. 0 means successfully completed, anything higher means failure.
  • How to use the shell if-then-else
  • Using test with the [...] syntax

That’s it. We’re done. Now when I need to create a new book page, all I do is press F1, select Run task, type in the book name, and it opens a new page with the template for the book.

Continue reading

Javascript class methods

My understanding of javascript object methods (pre-ES6) from Douglas Crockford’s post.

Public

Can’t access private members of the object

Anything created with this.xyz inside the constructor, or

Any methods added by MyObject.prototype.myMethod outside the constructor.

Private

Not accessible from outside the object definition (e.g. as myObj.myMethod())

Any variables or functions created inside the constructor with var or function abc(){...}.

Privileged

(I.e. publicly accessible, but can access private members)1

Methods declared with this.myMethod = function()... inside the constructor.

Continue reading