Two flavors of PSLab Android App to support Google Maps (in Play Store flavor) and Open Street maps (in Fdroid flavor)

What are the flavors of an App? And why are they needed in PSLab Android App?

While working on the PSLab Android Project, I ran into the need to create different variants of the app with different dependencies. In this blog, I have tried to explain the process of creating various flavors of the app in the easiest way possible. 

Android Allows Developers to create different variants of the same app with the same code base but having some functionalities different across the variants. These functionalities may include some special/pro features, some different dependencies, etc. Such variants are called flavors of the App. Most common flavors are Paid and Free version of the app.

In the PSLab Android Application, we needed to generate flavors, when we required to use Google Maps in the App. The app is also published on the Fdroid, which doesn’t allow dependencies of Google Maps. Hence 2 flavors of the app have been created, 

  1. Play Store Flavor (With Google Maps)
  2. F-Droid Flavor (With Open Street Maps)

Declaring Flavors in the build.gradle File

In order to create flavors of the app, first, we need to declare flavors in the Gradle file. In PSLab Android app we are creating 2 flavors, which are declared in the build.gradle file as under

flavorDimensions 'default'
productFlavors {
   fdroid {
       dimension = 'default'
   }
   playstore {
       dimension = 'default'
   }
}

flavorDimensions is used to package flavors if there are many flavors for an App. Since we have only two flavors fdroid and playstore, hence we are using single dimension default for both the flavors. Once this has been added to the build.gradle file we need to sync the gradle. 

After the Sync is complete, if we open the Build Variants tab from the left corner of the Android Studio, it would look something like this: 

(Figure 1: Build Variant Window of Android Studio)

As can be seen in the screenshot above, once the gradle is successfully synced, Android Studio automatically creates debug and release build variants for each flavor and we can easily toggle between variants and build/ run / make apk for each variant. Congratulations! We have successfully finished the first step towards creating flavors of an app.

Directory Structure after creating Flavors

Apart from creating the build variants of different flavors, Android studio also creates src/<flavor name> folders for us. Now if we want to add new activities and classes to these flavors we can create java, res, values folders inside this folder. We can define separate Manifest file as well for each flavor individually. The directory structure of the PSLab Android project after creating required packages inside the automatically generated src/fdroid and src/playstore folders looks like below,  

(Figure 2: Directory structure after creating flavors)

Defining Flavor specific dependencies

We can have some dependencies for one app flavor and some for others. For example, in PSLab Android app, we need Google Maps dependencies only in playstore flavor and Open Street Maps dependencies only in fdroid flavor. We can easily define flavor specific dependencies by adding flavor name before Implementation command in gradle file. Like below,

// Map libraries
fdroidImplementation "org.osmdroid:osmdroid-android:$rootProject.osmVersion"
fdroidImplementation "org.osmdroid:osmdroid-mapsforge:$rootProject.mapsforgeVersion"
fdroidImplementation "org.osmdroid:osmdroid-geopackage:$rootProject.geoPackageVersion"
playstoreImplementation "com.google.android.gms:play-services-maps:$rootProject.googleMapsVersion"

Same Activity/Class with different Flavor 

Now the main purpose of creating flavors is to have some different functionalities between the flavors. For that we need the base app to call different class/activity from the src/<flavor name> folder depending on the selected flavor. We will discuss this in reference to PSLab Android app. 

So, for PSLab android app we want app to open Google Maps in Play Store flavor and Open Street Maps in froid flavor. For this we need to create a duplicate Activity. Which means we will have two separate implementation of same Activity MapsActivity.java , one in the F-Droid source folder and one in playstore source folder. So MapsActivity.java will only be declared once in the src/main/AndroidManifest.xml file, but there will be two different classes for this activity in each flavor folder. Now when the main app will call MapsActivity.class from any intent depending on the selected build variant either playstore version of MapsActivity will be launched or the F-Droid version. So after creating two instances of the MapsActivity.java the directory structure would look something like given in the screenshot below,

(Figure 3: Directory structure after creating MapsActivity)

As can be seen in the directory structure, now both Play Store and F-Droid folders have their own instances of MapsActivity.java , and now we can easily implement code for Open Street Maps and Google Maps in the respective MapsActivity.java and we have two versions of the app working flawlessly. 

References

Tags: GSoC ‘19, PSLab, Android, Flavors, GoogleMaps, OpenStreetMaps, Build Variants

Continue ReadingTwo flavors of PSLab Android App to support Google Maps (in Play Store flavor) and Open Street maps (in Fdroid flavor)

Flavors in Gradle

Ever wondered how do people maintain different versions of the same app on play store with some customisations in each version. For example, Lite version and a pro version which signify free and paid versions with extra features in paid one.

With the arrival of gradle as a build tool, we got gradle flavors which is an excellent way to have some variations in your app versions.

It can also be leveraged to do hermetic testing with prod/mock flavors. Some other examples could be free/paid flavors and stable/experimental flavors etc.

Now let’s talk about on how to proceed :

The process is very simple

  1. Create the flavors in you build.gradle file
android {  
      productFlavors {  
           mock {   
                applicationIdSuffix = ".mock"  
           }  
           prod  
      }  
 }

Here prod and mock are two flavors and the customisations we add are that mock has a applicationIdSuffix. We can also add altogether different applicationIds as well as define a different version for different flavors and a lot of other different things

2. Now we sync the project

If you now open Build Variants tool window either using the quick access menu located in the status bar in the bottom left hand corner of the Android Studio main window or using the Build Variant tool window bar. Once loaded, clicking in the Build Variant cell for the app module should now list the four build variants: mockDebug, mockRelease and prodDebug, prodRelease

After this we get different folders corresponding to the different flavors. We go ahead and add the different folders where we want the changes to occur. For example if I want to show two different maps for two different versions, where one version is for fdroid and another one is for googleplay and hence we replace google maps with OpenStreet Maps in the fdroid version. Hence, We add res/values and res/layout folders for different resources and we also add different java folders and add the classes that would be different for both the flavors. Since both of these will require different set of permissions we can also add AndroidManifest.xml for these were we define the permissions etc. we want.

Not that we only add the changes for each file. All the other essential things can be added from the Main flavor of the android project.

Any variable available through your code

Another thing to know is that the you can have add variables like this so that you can use different variables for different flavors. For examle if you have different api’s and different options for each flavor like if you want to report crashes in one of the flavors and not in other. Here HOST variable is not the only one you can expose in your code. You can do it with whatever you want:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

You can access them as follows:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES

So I think this is a really useful thing to know and is used in almost every app. We all need some configuration differences in our apps and gradle flavors is the best way to go about it. Go nuts with your imagination on the usage of flavors 😛

Adios!

Continue ReadingFlavors in Gradle