Different app names for different build flavors?

AndroidGradle

Android Problem Overview


I have 2 build flavors, say, flavor1 and flavor2.

I would like my application to be named, say, "AppFlavor1" when I build for flavor1 and "AppFlavor2" when I build for flavor 2.

It is not the title of activities I want to change. I want to change the app name as it's displayed on the phone menu and elsewhere.

From build.gradle I can set various parameters for my flavors but, it seems, not the app label. And I can not change the app label programmatically based on some variable, too.

So, how do people handle this?

Android Solutions


Solution 1 - Android

Remove app_name from strings.xml (else gradle will complain of duplicate resources). Then modify build file like this:

productFlavors {
    flavor1{
        resValue "string", "app_name", "AppNameFlavor1"
    }

    flavor2{
        resValue "string", "app_name", "AppNameFlavor2"
    }
   } 

Also make sure @string/app_name value is assigned for android:label attribute in the manifest.

<application
        ...
        android:label="@string/app_name"
        ...

This is less disruptive than creating new strings.xml under different built sets or writing custom scripts.

Solution 2 - Android

Instead of changing your main strings.xml with a script and risk messing up your source control, why not rely on the standard merging behavior of the Android Gradle build?

My build.gradle contains

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }

    release {
        res.srcDir 'variants/release/res'
    }

    debug {
        res.srcDir 'variants/debug/res'
    }
}

So now I can define my app_name string in the variants/[release|debug]/res/strings.xml. And anything else I want to change, too!

Solution 3 - Android

If you want to maintain localization for the app name in different flavors, then you can achieve it as follows:

  1. Specify android:label in <application> available in AndroidManifest.xml as follows:

  2. Specify default value fo appLabel in app level build.gradle:

manifestPlaceholders = [appLabel:"@string/defaultName"]

  1. Override the value for product flavors as follows:

    productFlavors { AppFlavor1 { manifestPlaceholders = [appLabel:"@string/flavor1"] } AppFlavor2 { manifestPlaceholders = [appLabel:"@string/flavor2"] }

    }

  2. Add string resources for each of Strings (defaultName, flavor1, flavor2) in your strings.xml. This will allow you to localize them.

Solution 4 - Android

You can add a strings resource file to each flavor, then use those resource files to change your app name. For example in one of my apps, I have a free and paid version. To rename them "Lite" and "Pro", I created a meta_data.xml file, and added my app_name value to that XML and removed it from strings.xml. Next, in app/src create a folder for each flavor (see below for example structure). Inside these directories, add res/values/<string resource file name>. Now, when you build, this file will be copied into your build and your app will be renamed.

File structure:

app/src
   /pro/res/values/meta_data.xml
   /lite/res/values/meta_data.xml

Solution 5 - Android

Another option that I actually use is change the manifest for each application. Instead of copy the resource folder, you can create a manifest for each flavour.

sourceSets {
  main {
 }

  release {
    manifest.srcFile 'src/release/AndroidManifest.xml'
 }

  debug {
    manifest.srcFile 'src/debug/AndroidManifest.xml'
 }
}

You have to have a principal AndroidManifest in your src main that will be the principal. Then you can define a manifest with only some options for each flavour like (src/release/AndroidManifest.xml):

<manifest package="com.application.yourapp">
  <application android:icon="@drawable/ic_launcher">
  </application>
</manifest>

For debug, AndroidManifest (src/debug/AndroidManifest.xml):

<manifest package="com.application.yourapp">
  <application android:icon="@drawable/ic_launcher2">
  </application>
</manifest>

Compiler will do a merge of the manifest and you can have a icon for each flavour.

Solution 6 - Android

This can easily be accomplished under buildTypes

buildTypes {
    debug {
        buildConfigField("String", "server_type", "\"TEST\"")
        resValue "string", "app_name", "Eventful-Test"
        debuggable true
        signingConfig signingConfigs.debug_key_sign
    }

    stage {
        buildConfigField("String", "server_type", "\"STAGE\"")
        resValue "string", "app_name", "Eventful-Stage"
        debuggable true
        signingConfig signingConfigs.debug_key_sign
    }

    release {
        buildConfigField("String", "server_type", "\"PROD\"")
        resValue "string", "app_name", "Eventful"
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        //TODO - add release signing
    }
}

Just make sure to remove app_name from strings.xml

Solution 7 - Android

First of all, answer this question: "Can the user install both flavors of your application on the same device?"

I use a Python script that patches the source. It contains some reusable functions and, of course, knowledge what needs be patched in this particular project. So the script is application-specific.

There is a lot of patching, the data for patching are kept in a Python dictionary (including application package names, they BTW are different from the Java package name), one dictionary per flavor.

As to l10n, strings may point to other strings, e.g. in my code I have:

<string name="app_name">@string/x_app_name_xyz</string>

<string name="x_app_name_default">My Application</string>
<string name="x_app_name_xyz">My App</string>

Solution 8 - Android

How do I make string/app_name different per flavor though?

I wanted to write an update, but realized that it is bigger than the original answer saying that I use a Python script that patches the source.

The Python script has a parameter, a directory name. That directory contains per-flavor assets, resources like launcher icons, and the file properties.txt with a Python dictionary.

{ 'someBoolean' : True
, 'someParam' : 'none'
, 'appTitle' : '@string/x_app_name_xyz'
}

The Python script loads the dictionary from that file and replaces the value between <string name="app_name"> and </string> by the value of properties['appTitle'].

The below code is provided on the as-is/as-was basis etc.

for strings_xml in glob.glob("res/values*/strings.xml"):
    fileReplace(strings_xml,'<string name="app_name">',properties['appTitle'],'</string>',oldtextpattern=r"[a-zA-Z0-9_/@\- ]+")

to read properties from one or more such file:

with open(filename1) as f:
    properties = eval(f.read())
with open(filename2) as f:
    properties.update(eval(f.read()))

and the fileReplace function is:

really = True
#False for debugging

# In the file 'fname',
# find the text matching "before oldtext after" (all occurrences) and
# replace 'oldtext' with 'newtext' (all occurrences).
# If 'mandatory' is true, raise an exception if no replacements were made.
def fileReplace(fname,before,newtext,after,oldtextpattern=r"[\w.]+",mandatory=True):
    with open(fname, 'r+') as f:
        read_data = f.read()
        pattern = r"("+re.escape(before)+r")"+oldtextpattern+"("+re.escape(after)+r")"
        replacement = r"\g<1>"+newtext+r"\g<2>"
        new_data,replacements_made = re.subn(pattern,replacement,read_data,flags=re.MULTILINE)
        if replacements_made and really:
            f.seek(0)
            f.truncate()
            f.write(new_data)
            if verbose:
                print "patching ",fname," (",replacements_made," occurrence" + ("s" if 1!=replacements_made else ""),")",newtext,("-- no changes" if new_data==read_data else "-- ***CHANGED***")
        elif replacements_made:
            print fname,":"
            print new_data
        elif mandatory:
            raise Exception("cannot patch the file: "+fname+" with ["+newtext+"] instead of '"+before+"{"+oldtextpattern+"}"+after+"'")

The first lines of the script are:

#!/usr/bin/python
# coding: utf-8

import sys
import os
import re
import os.path
import shutil
import argparse
import string
import glob
from myutils import copytreeover

Solution 9 - Android

In AndroidManifest file, in the application tag you have this line:

android:label

And there you can say how to application label will appear in applications menu on device

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexander KulyakhtinView Question on Stackoverflow
Solution 1 - AndroidGanesh KrishnanView Answer on Stackoverflow
Solution 2 - AndroidPierre-Luc PaourView Answer on Stackoverflow
Solution 3 - AndroidSagarView Answer on Stackoverflow
Solution 4 - AndroidJamesView Answer on Stackoverflow
Solution 5 - AndroidJuanma JuradoView Answer on Stackoverflow
Solution 6 - AndroidDroid ChrisView Answer on Stackoverflow
Solution 7 - Android18446744073709551615View Answer on Stackoverflow
Solution 8 - Android18446744073709551615View Answer on Stackoverflow
Solution 9 - AndroidHitmanView Answer on Stackoverflow