Awesome sauce: wireless adb debugging

tldr: Android 11 has introduced adb debugging over wifi. It is super awesome!

My test device, a Pixel 1, stopped working recently. It shutdown while connected over USB during a test, and then never restarted. Debugging with an emulator on my old (though still much loved) laptop is tiresome. So, I needed an alternative to continue development before I could buy another testing device.

Solution: A different applicationIdSuffix1 for my debug apk so I can install it alongside the production version. When combined with wireless debugging, I’m all set up to continue development on my main device, a Pixel 2 XL.

It’s awesome sauce.

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.

It crashed

I just did a dance because my app crashed!

Only developers would understand this—the joy in successfully reproducing a reported bug :)

Now that I’ve reproduced it, I can get down to fixing it. Success!

Installing RCurl on linux

Will keep failing without providing a valid reason.

The reason is lack of a system library – libcurl4-openssl-dev.

Install the library from default sources:

sudo apt-get install libcurl4-openssl-dev

Now, install package RCurl inside R again, and we’re ready to go

Note to self: Use curlies!

This small block of code – it looks clean, and easy to understand

signout: function(callback){
  dropbox.client.signOut(function(error){

    if(error)
      if(callback) callback(false, showError(error));
    
    else
      resetLocalStorage(true, function(){
        if(callback) callback(true);
      });

  });
}

Sadly, it’s also buggy.

When I started programming in javascript, I’d come from a C & Java background, so I encapsulated everything in curlies.

Then sometime later, I started dabbling in Python for work related data processing. I loved how indentation was used to identify code blocks in Python. Didn’t need curlies cluttering everything.

Soon after I discovered that single statements following a loop command (for…) or a conditional (if… else…) didn’t need to be encapsulated inside curlies either. If there was just one statement, you could write it straight after – in same line, or a following one, with any amount of indentation.

Recently, I’ve become addicted to this style of coding. I don’t like unnecessary curlies cluttering the code, and I get rid of them quite aggressively.

Too aggressively as this example shows.

Looking at the code, it appears that the else statement triggers when there isn’t an error. However, in reality, the js parser attaches that else to the if(callback) inside the error conditional.

The correct way to write this is:

signout: function(callback){
  dropbox.client.signOut(function(error){
    
    if(error){
      if(callback) callback(false, showError(error));
    }
    
    else
      resetLocalStorage(true, function(){
        if(callback) callback(true);
      });
    
  });
}

Offline App Development

… is a pain.

Mainly because once you add a file to the manifest, and thus to the browser’s app cache, you’re never sure if it’s getting updated. I spent most of last night and much of today just trying to get the browser to fetch the new version of html doc, and then the correct js file. Too bugged. It seems to be an issue primarily with Chrome & Chromium, but they are my primary development platform.

Anyway, if despite all the changes to manifest, restarting servers, clearing browser cache, the browser is still fetching old cached files, here’s a way out:

  1. Open a new tab in chrome/chromium and go to ‘chrome://appcache-internals/
  2. Clear all cached app resources
  3. Refresh the page to confirm there are no more cached app resources
  4. Refresh your test page – it should fetch everything from the server now

There’s another bit I learnt and implemented (though my implementation is pretty simplistic): Dynamic Cache Manifest.

Basically, instead of writing the application manifest, for offline app storage, by hand and changing it every time you change a file, let your server-side script do the work. What I’ve done is :

  1. Hand-wrote a cache manifest file but with a comment line that includes a django tag {{ts}}.
  2. Changed my app.yaml so that instead of serving the file directly, the request for it is sent to my server script.
  3. In the script I use the handwritten cache manifest and replace the ts tag with a random number.

What this ensures is that the browser is forced to fetch all elements in the app manifest every time because of the random number changing cache manifest on every fetch. This ensures that while I’m connected to the server and testing my js scripts, I get the latest versions every time. On the other hand, when I switch off the server and test the offline mode, the script still has resources available offline for rendering.

There is a lot more you can do here with the dynamic cache manifest rather than just plug-in a random number. I learnt this trick from this blog at Cube, and there are a couple more handy tricks being used there, so I suggest reading that resource.

Update: Seems like, as with script-initiated xhr requests, Chrome also makes multiple requests for the cache manifest from the server for every page load. When I use a randomizer function to send a ‘new’ cache manifest every time, this results in the two cache manifests being different to each other and thus Chrome doesn’t save any files. Not what I wanted, now? So, here’s a small change I made

previous randomizer: int(random.random()*10000)
new randomizer: time.strftime(‘%y%m%d%H%M’)

Now, instead of returning a new resource every time, I return a new resource every minute. This means that the two simultaneous requests that Chrome fires, will get the same cache manifest. But a page refresh/load later, will return a new cache manifest. It’s working so far :)
[Ofcourse, I know that there’s still a remote chance that the two requests will hit either side of the minute change. But the probability is small enough for me to take a chance.]