Gradle power – Automatically generate Android App Version Code and Name

One recommended practice with Android app development is upgrading app version code and name in gradle file for every code change. We have been doing this manually all this time, there is an additional overhead (time) of gradle syncing for every change that happens in build gradle files. This trick automates that process using simple gradle methods and logic. Even if you forget to upgrade the version, this trick will take care of it.

It is not mandatory but I recommend going through the basics of product flavours in Android. We will be upgrading versions of default config and all the flavours. You can Google it up or check out this link.

Android apps version code and name are defined in the module’s build.gradle file. Same goes for all the product flavours. All are defined inside Android block. Here is a sample:

android {
...
    defaultConfig {
        applicationId "app.package.default"
        versionCode 1
        versionName '1.0'
        ...
    }

    productFlavors {
        free {
            applicationId "app.package.free"
            versionCode 101
            versionName '1.101.1'
            ...
        }
        pro {
           applicationId "app.package.pro"
           versionCode 301
           versionName '1.301.3'
           ...
        }
        freemium {
           applicationId "app.package.freemium"
           versionCode 501
           versionName '1.501.5'
           ...
        }
    }
...
}

FYI, version code and name can be overridden in flavours as shown above. In fact, any property of default config can be overriden in flavours. Both use the same DSL object for configuration.

As you can see, we have to manually write “1.0” and 1 in version name and code respectively. And when we change that, we need to wait for gradle sync to finish. Now let’s see how to automate this process.

We are going to create a method that calculates an upgraded number and we will use this method in version name and code. Like this:

versionCode getCustomNumber()
versionName "1.0." + getAnotherOrSameCustomNumber()

This method in defined in project level build.gradle file. You can use any logic in getCustomNumber() and getAnotherOrSameCustomNumber() that returns an upgraded number every time it is called. When you define this, make sure it returns an upgraded number as a lower version code will not upgrade your app. A simple logic based on timestamp is defined below.

def getCustomNumber() {
    def date = new Date()
    def formattedDate = date.format('yyMMddHHmm')
    def code = formattedDate.toInteger()
    return code
}

allprojects {
    version = '1.0.' + getCustomVersionCode()
    repositories {
        jcenter()
        mavenCentral()
    }
}

Defining the base version in allProjects block is important, or else you will see a null version code and name and may result in build failure. Here’s how we can use the above defined method in app level’s build.gradle file (including product flavours).

android {
    ...
    defaultConfig {
        applicationId "app.package.default"
        versionCode getCustomVersionCode()
        versionName '1.0.' + getCustomVersionCode()
        ...
    }

    productFlavors {
        free {
            applicationId "app.package.free"
            versionName '1.101.' + getCustomVersionCode()
            ...
        }
        pro {
            applicationId "app.package.pro"
            versionName '1.301.' + getCustomVersionCode()
            ...
        }
        freemium {
            applicationId "app.package.freemium"
            versionName '1.501.' + getCustomVersionCode()
            ...
        }
        ...
    }
}

You can even use the revision of the source control you use in your project, just need to find the right gradle plugin your source control.

If you remove the base version in allProject block of project level build.gradle file, you will see a “null” in version code in generated manifest. And your app may or may not run. So please avoid it.

Let me know what you think.

-Kaushal D (@drulabs twitter/github)

drulabs@gmail.com

Gradle power – android product flavours and configuration

gardle-android

Product Flavours have been around for quite some time now. I feel Product flavours are one of the coolest things about android studio. I am so smitten by product flavour that I decided to write a post about it :p. What follows will be introduction, application, grouping, filtering and configuration of product flavours in android. If you have already applied these and have any questions, shoot in comments below.

Product flavours are very useful when you want to create multiple versions of your app like demo, free, paid etc. You must have seen various versions of same app in google play like angry birds free and angry birds hd. You can have a single source code and generate n number of variants of your app. If you want to write common code that can be used in various types of apps, product flavours is not the way to go, what you are looking for is android library.

Let’s see how to create product flavours

android {...
    defaultConfig {...}
    productFlavors {
        flavour1 {...}
        flavour2 {...}
        flavour3 {...}
    }
    ...
    buildTypes{...}
}

As you can see, this skeleton goes in android block in build.gradle file (app/module level). By writing this we have created 3 flavours called flavour1, flavour2 and flavour3 (duh..). By default android has 2 debug types called debug and release (more can be added like jnidebug), a build variant or variant is a combination of build type and product flavour. So now there will be 6 build variants. flavour1-debug, flavour1-release, flavour2-debug, flavour2-release, flavour3-debug and flavour3-release. Inside these flavour blocks are flavour specific properties and methods like applicationId (the app package name), versionCode, buildConfigField etc. We will see these a little later. Click here to see ProductFlavour DSL (domain specific language) object that is used to configure flavour specific properties and methods.

We all know about default config. Whatever properties and methods that are defined in default config are inherited by all product flavours. Default config block is also uses ProductFlavour DSL (domain specific language) object. This means that everything that goes inside a default config block can go inside a flavour block. Each product flavour can override the properties and methods defined in default config block.

android {...
    defaultConfig {
        applicationId "the.default.packagename"
        minSdkVersion 8
        versionCode 10
    }
    productFlavors {
        flavour1 {
            applicationId "the.default.packagename.flavour1"
            minSdkVersion 15
        }
        flavour2 {
            applicationId "the.default.packagename.flavour2"
            versionCode 20
        }
        flavour3 {...}
    }
    ...
    buildTypes{...}
}

In the above example some properties are overwritten and rest are inherited from default config block. Click here to see list of all the properties and methods that can be configured inside default config and flavour block (or groovy closure). ApplicationId property is to assign different default package to each flavour. This makes sure that different variants of your app can be installed on the same device.

SourceSets for product flavours

This creates 6 source sets:

"src/flavour1" - android.sourceSets.flavour1
"src/flavour2" - android.sourceSets.flavour2
"src/flavour3" - android.sourceSets.flavour3
"src/androidTestFlavour1" - android.sourceSets.androidTestFlavour1
"src/androidTestFlavour2" - android.sourceSets.androidTestFlavour2
"src/androidTestFlavour3" - android.sourceSets.androidTestFlavour3

// All these folder follow the java/src/main/.. structure 
// for any flavour and test specific customization. click 
// below link to know more about android sourceSets.
// http://goo.gl/NvAg74

This flavour specific folder structure is where the flavour specific code resides. Like flavour1 customization must be done inside “src/flavour1/ {java, res, assets}” folder. The common code lives in “src/main/…” directory. If you want to change the app name for say flavour2, all you need to do is define app_name (or whichever string resource you are using as app name in manifest) in “src/flavour2/res/values/strings.xml“. Just whatever you want to override, do not copy the entire xml or res file in flavour specific folder, let android resource merger do its job. Say you are using “@drawable/ico_app or @mipmap/ico_app” as app icon, this can be easily configured for each flavour by keeping flavour specific icons in respective folder structure. e.g. for flavour3, just name the flavour3 specific icon as ico_app and keep it in drawable or mipmap folder (whichever you are using) in flavour3 specific directory.

Multi-flavour variants

Now this is useful when the variants of your app is decided by one dimension. e.g. say the dimension is price, you can create flavours like free, paid, freemium etc. what if the requirement is to create variants based on multiple dimensions, like for environment dimension there can be three flavours “dev“, “staging” and “production“, and three for price dimension “free“, “freemium” and “paid“, and may be another dimension. In this case you can select either of the six flavours plus debug or release. But the product flavour we are interested in is dependent on both dimensions. Something like free-dev-debug, paid-production-release etc. Here we are trying to group product flavours, which is not allowed by default. This can be enabled via dimension attribute of product flavours. We can set dimensions of product flavours via flavorDimensions attribute of android block, and then we can assign a dimension to each flavour. Here is an example:

android {...
    flavorDimensions "country", "price"
    productFlavors {
        free {dimension "type"...}
        pro {dimension "type"...}
        India {dimension "country"...}
        China {dimension "country"...}
        Russia {dimension "country"...}
    }
    ...
    buildTypes{...}
}

In this example as we can see there are two flavour dimensions, country and price. Country flavour dimension has three flavours India, China and Russia, price has two flavours free and pro. This in turn creates 12 build variants for us:

India-free-debug
India-free-release
India-pro-debug
India-pro-release
China-free-debug
China-free-release
China-pro-debug
China-pro-release
Russia-free-debug
Russia-free-release
Russia-pro-debug
Russia-pro-release

As you can see just by defining the flavorDimensions and dimension attribute, the product flavours can be grouped, and android creates variants that are all possible combinations of flavours of all types of dimensions and build types. These variants are reflected everywhere, including the build variants tab on lower left side of android studio.

Filtering product flavours

Now look at the list of build variants above. What if we want to filter this list. Say I don’t want Russia-free and India-paid variant for some reason. This is possible by ignoring some of the build variants based on some condition. Below is an example of ignoring Russia-free variant.

//Filtering variants
android {
...
    variantFilter { variant ->
        def names = variant.flavors*.name
        def buildTypeName = variant.buildType.name
        // if buildtype is required for filtering use
        // the above field
        if (names.contains("Russia") && names.contains("free")) {
            variant.ignore = true
            // or variant.setIgnore(true)
        }
    }
    ...
}

The build variants can be ignored by setting the ignore field or by setting setIgnore to false. This global filtering is reflected everywhere including the build variants tab on lower left side of android studio, assemble, install tasks etc.

Flavour specific dependency

You must have seen testCompile “junit…” in your projects. This is flavour specific dependency. junit for example is a testing library and must not be shipped with the release apk (why increase the size of your apk with something that is required only for testing). Adding “flavourCompile” syntax dependency section of app level build.gradle adds the dependency for that particular flavour. If you are familiar with facebook’s stetho library, then you know it is only for development purpose and must not be shipped with release version. This is an awesome feature that comes with android product flavours. Some examples:

testCompile 'junit:junit:4.12'
stethoFlavourCompile 'com.facebook.stetho:stetho:1.3.1'

There are a couple of things to discuss before we can conclude product flavours.

BuildConfig constants and res values

Now we know that flavour specific code goes in flavour specific sourceSet, but sometimes we need flavour specific code in main code base (“src/main/…“). The problem here is: the main source code doesn’t know which flavour is or will be getting generated. For this there is something called BuildConfig. It is an auto generated file and must not be tempered with. This file contains flavours specific constants and can be used in main source code. Check out  ProductFlavour DSL object for available properties and methods. You can set flavour specific constants and resources like this:

android {...
    flavorDimensions "country", "price"
    productFlavors {
        free {dimension "type"
            buildConfigField("String", "featureList", "restricted")
            resValue("boolean", "ads", "true")
        ...}
        pro {dimension "type"
            buildConfigField("String", "featureList", "all")
            resValue("boolean", "ads", "false")
        ...}
       India { applicationId "my.app.india"
            dimension "country"
            buildConfigField("String", "shortcode", "IN")
       ...}
       China { applicationId "my.app.china"
            dimension "country"
            buildConfigField("String", "shortcode", "CHN")
       ...}
       Russia { applicationId "my.app.russia"
            dimension "country"
            buildConfigField("String", "shortcode", "RU")
       ...}
    }
    ...
    buildTypes{...}
}

This is then used to generate BuildConfig class and dynamic resources (check our resources in generated folder). For a Russian-pro-debug build variant (from flavour definition above), the generated build config will look something this:

//Build config for Russia-pro-debug build variant
public final class BuildConfig {
    public static final boolean DEBUG = Boolean.parseBoolean("true");
    public static final String APPLICATION_ID = "my.app.russia";
    public static final String BUILD_TYPE = "debug";
    public static final String FLAVOR = "proRussia";
    public static final int VERSION_CODE = 1;
    public static final String VERSION_NAME = "1.0";
    public static final String FLAVOR_price = "pro";
    public static final String FLAVOR_country = "Russia";
    // Fields from product flavor: pro
    public static final String featureList = "all";
    // Fields from product flavor: Russia
    public static final String shortcode = "RU";
}

As you can see the constants are specific to a selected flavour. This can then be used in the main source. Easy peasy isn’t it? Build config file is in this location (“<app or module>\build\generated\source\buildConfig\<variant>\<debug or release>\my\app\package“).

Dynamic manifest

Sometimes we need to use app’s package in manifest file. But with product flavours the app’s package or application id is no longer fixed. In this case we can make the package dynamic in manifest with groovy syntax. By doing so, the application Id is picked from build.gradle at run time. Here is a sample:

<manifest xmlns:android="http://schemas.android.com/apk/res/android......
...
<permission 
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
...
<application...

This is an example for a declared permission for android GCM. Notice the groovy syntax around applicationId, that is where the magic is happening.

Let me know what you think. Happy coding!!!

Have questions? Did I miss anything? Shoot below in comments.

-Kaushal D (@drulabs twitter/github)

drulabs@gmail.com

Gradle power – changing default apk name via script plugin

Android studio is the IDE for android application development which uses gradle for build automation. Yet android developers (including me) are not aware of the power gradle packs with it. The level of customization that gradle offers is amazing. This is the first post of “power of gradle in android studio” from my blog, I hope it helps you become a better android developer.

This post is not about gradle basics, it is about using gradle to suit our needs. I recommend taking a look at this presentation for understanding just enough gradle to get started, it is optional though if all you want is to change the default apk name.

There are many ways to change that default apk name that goes something like “app-flavor1-flavor2…-debug.apk“. One way is to create a gradle task, another is to loop through all the build variants etc. In this post we will see how to use a script plugin to change that name.

There are basically two types of plugins in gradle, script plugin and binary plugin. I am sure you have seen binary plugins. Whenever we use apply plugin syntax we are applying binary plugin. for example this is how android plugin is applied in android applications: apply plugin: “com.android.application”. Script plugins are separate gradle files that are included in the application using the syntax: apply from “../newgradlefile.gradle”.

Let’s see how we can create a script plugin for changing the default apk name in android applications. Below is the content of a gradle file that I named apknomenclature.gradle, this file in the root directory of android project:

android.applicationVariants.all { variant ->;
     def appName
     //Check if an applicationName property is supplied;
     //if not use the name of the parent folder name.
     if (project.hasProperty("applicationName")) {
          appName = applicationName
     } else {
          appName = parent.name
     }
    variant.outputs.each { output ->;
         def newApkName
         if (output.zipAlign) {
              newApkName = "${appName}-${variant.name}.apk"
              output.outputFile = new File(output.outputFile.parent, newApkName)
         }
    }
}

This is the content of apknomenclature.gradle file in the root folder of my android application. What this is basically doing is looping through all the build variants of android application and changing the output file. It checks if there is any property called applicationName in gradle.properties file (applicationName=MyCustomAppName). If it is there, it takes that name or else picks the root folder’s name and renames the apk as per logic defined. This change doesn’t change anything except the name of the apk, you can run and debug your application like you did earlier.

The zipAlign if condition is only to change the name of signed and aligned apk not the unaligned one. We don’t need to bother about unaligned apks anyway.

Notice the variable called newApkName, you can add your custom logic to change this variable’s value, which will be reflected in resulting apk name. You can add timestamp or version code of your app, but I wouldn’t recommend using that in the apk name as the name is cached in android studio. So if you change version code very frequently in your app you will notice this error “apk “your_apk_name_timeinmillis_…” doen’t exist…“. So just keep the name in the code above or use something that doesn’t change frequently.

Now that our apknomenclature.gradle file or our script plugin is ready, we need to apply it to app level build gradle file like this “apply from “../apknomenclature.gralde”“.

This will change the apk name in the outputs directory. If you run into any issues, just comment the apply from line of code run the app, then uncomment it and run again.

I know its not much to change the apk name, but its a start. Watch out this space for more.

Let me know what you think. Happy coding!!!

Have questions? Did I miss anything? Shoot below in comments

-Kaushal D (@drulabs twitter/github)

drulabs@gmail.com