TIL: Gradle tasks and doLast

I’ve known about custom Gradle tasks and functions for a while (Android Studio). I’ve used one, copied from SO and edited to suit my needs, in one of my projects before. This week I got to create a task from scratch to add a dev feature to the app at work. So far so good.

This TIL is not about learning about Gradle tasks. This is about a tiny detail that I didn’t know. I usually write/see tasks written like this

task countChicken {
    // count chicken
    ...
}

When written like this, the chicken are counted immediately every time the Gradle project is synced or built (task configuration).

What I had not known till this week was that anything directly inside that task definition block is executed at the task configuration stage. So, in this case the whole counting of chicken is happening at the task configuration stage, whether we explicitly execute the task or not.

If we only want to execute the task on demand, the task configuration needs to be separated from execution, like so:

task countChicken {
    doLast {
        // count chicken
        ...
    }
}

The doLast ensures that this code block is only executed during task execution. It basically tells Gradle during configuration to run this code block last while executing this task1. We can also use the doFirst code block to run something first while executing the task. Continue reading

Never again

mega-pr-3

mega-pr-1

2 mega PRs! They removed a lot of cruft and code debt, and created a huge amount of review debt. (The first one includes changes from its sub-PRs—the second PR above, and another small PR. Its own changed file count was ~140)

I could have split the second PR into three sub-PRs. But the old code was so deeply woven-in that each of those three sub-PRs would also have had 50-70 changed files. The choice was really between 4-5 large PRs with 50-70 files changed, or 2 massive PRs with 100+ changed files. I’m not sure I made the correct choice. I don’t know if there was a correct choice. Perhaps the correct choice was what was—just leave it be.

The relevant code was messy, it is now less messy. It’s still fairly noodley. I just hope this work made it easier to improve for the next person who has a go at it.

My lesson: never ever again do I want to create such massive PRs. Neither do I ever want to be at the other end of such massive PRs—reviewing them.

Continue reading

ADB over Wifi, pre Android 11

Yep, this is doable too. But it requires an initial connection over USB.

  1. Connect the device over USB, with USB debugging enabled
  2. Open a terminal and check the device is connected:
    > adb devices
  3. Ask the device to listen for adb connections on a particular port:
    > adb tcpip 12345 or
    > adb -s DEVICE_ID tcpip 12345
  4. Once the adb has restarted, disconnect the device from USB.
  5. Check the device IP address, and connect to it over wifi:
    > adb connect 192.168.0.2:12345
  6. Ready to develop.
  7. Once done with dev, return the device to USB mode to not let the tcp port open as as security risk:
    > adb usb

It’s a bit more work than the simple settings toggle on Android 11, but works just as well once connected.

Note: The devices need to be on the same wifi network, and not be connected through VPN.

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

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