id: 161 View:article
Tutorials  Build  Gradle
The About Process

How about that?

Here's the third in a series covering some handy tools which an Android developer can wire into their build system. Ideally, this would be right when the project is started, but adding to an existing one shouldn't be too difficult either.

This one's focused on something we're all familiar with. Pretty much every app needs some way inside the app to identify itself further to the user, and the usual way is an About box.  A basic one might just show the version number, but more sophisticated ones can vary according to the kind of build it relates to, for example debug or release. There are plenty of other areas it's useful to cover too, for example does this build expire? Where can the user report bugs? Who wrote it? Even functionality, such as a "Rate us" link, can be added as boilerplate then tweaked later. Or your logo, cutely being animated ;-)

When you've done this a few times too many (!) you realise there ought to be a way to automate most of this,  and a skeleton one could be used so the developer can, ahem, flesh out the apps info later with most of the basics being dealt with already.

The source for this project is available on GitHub, and if you just want to run the app, allow unknown sources on your Android device and click here.

Gettting build info into the app

Thinking about it, we have an interesting situation. What we want is to get information from the environment at the point the app was built, and we need this in a form we can use in the app itself at runtime. We're writing Android software, so that means Kotlin or Java. In other words, we have to use some mechanism which "stamps" this build info in a form which our development language can handle natively. You might think this means a bunch of static files containing these entries, but Gradle has a smarter way.

about dialog 296x525

BuildConfig to the rescue

Imagine if this file was available to your app, even though it didn't appear in the regular source list in Android Studio. It's nothing more than a class full of statics, so referring to them anywhere in your code wouldn't even need anything instantiating, it's just the usual syntax of BuildConfig.APPLICATION_ID, etc:

BuildConfig.java

/**
 * Automatically generated file. DO NOT MODIFY
 */
package com.otamate.android.theaboutprocess;

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.otamate.android.theaboutprocess.debug";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  // Fields from build type: debug
  public static final Boolean BUILD_EXPIRES = true;
  // Fields from default config.
  public static final String BUILD_DATE = "03-Apr-2018 12:03 PM";
  public static final String GENERATOR = "dev";
  public static final String SVN_REVISION = "497";
  public static final String USERNAME = "Carl";
}

Imagine no more, because that's what gradle has been doing all along to your apps. Even better, there's a way to define your own variables inside build.gradle which get added to this structure. The technique is via the buildConfigField mechanism, which declares and defines the exposed variable in one. It's syntax is shown here:

section of app-level build.gradle

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.otamate.android.theaboutprocess"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        buildConfigField 'String', 'GENERATOR', '"' + getGenerator() + '"'
        buildConfigField 'String', 'BUILD_DATE', '"' + getDate() + '"'
        buildConfigField 'String', 'USERNAME', '"' + getUsername() + '"'
        buildConfigField 'String', 'SVN_REVISION', '"' + getSvnRevision() + '"'
    }
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            sourceSets.debug.resources.srcDirs = ['src/debug/res']
            buildConfigField 'Boolean', 'BUILD_EXPIRES', 'true'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField 'Boolean', 'BUILD_EXPIRES', 'false'
        }
    }
}

Look at the entry for BUILD_EXPIRES. It's true for debug builds but false for release ones. This means the app, which for this setup will be created in 2 different APK's, can perform a single test using the same code for both variants, but take different action depending on the result. Debug builds which expire could then test the system date against the build date, for example. There is no messy "am I a debug/release build" test needed in the code.

Using this, the About box can be populated with as many build-time strings as desired. The earlier article showing how to extract the version id of the build from CI is another obvious use.

Self-maintaining automation - about time, too ;-)