3

I have a few manifest placeholders in my AndroidManifest.xml file

In build.gradle I want to dynamically populate those values depending on my flavor and build type.

How can I do that ?

I wrote a function that does the following

def getKey() {
    def KeyToReturn = ""
    android.applicationVariants.all { variant ->
        printout("getKey: ${variant.name}")
        def flavor = "default";
        if (variant.productFlavors.size() > 0)
            flavor = variant.productFlavors.get(0);


        def buildType = variant.buildType.name

        if (buildType == "debug" || buildType == "staging") {
            if (flavor.name == "one") {
                KeyToReturn = test_key_1
            }
            if (flavor.name == "two") {
                KeyToReturn = test_key_2
            }

        }
        if (buildType == "release") {
            if (flavor.name == "one") {
                KeyToReturn = live_key_1
            }
            if (flavor.name == "two") {
                KeyToReturn = live_key_2
            }
        }
    }
    printout("KeyToReturn:" + KeyToReturn)
    return KeyToReturn
}

I have this in my android.defaultConfig

defaultConfig {
    minSdkVersion 15
    targetSdkVersion 23
    versionCode getVersionCode1()
    versionName getVersionName1() + ""
    manifestPlaceholders = [key: getKey()]
}

This is what my AndroidManifest.xml relevant part contains

<meta-data
            android:name="key"
            android:value="${key}"/>

The problem is when I look in the built AndroidManifest.xml file the ${key} value is an empty string.

How do I populate this value correctly ?

Lena Bru
  • 13,521
  • 11
  • 61
  • 126

1 Answers1

9

You define manifest placeholders based on product flavors (and AFAIK build type) by adding them to the proper closure.

In this sample app, I use product flavors to use different rules for Android 7.0's network security configuration:

productFlavors {
    thawte {
        resValue "string", "app_name", "CA Validation Demo"
        applicationId "com.commonsware.android.downloader.ca.thawte"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_thawte']
        buildConfigField "String", "URL", WARES
    }
    verisign {
        resValue "string", "app_name", "Invalid CA Validation Demo"
        applicationId "com.commonsware.android.downloader.ca.verisign"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_verisign']
        buildConfigField "String", "URL", WARES
    }
    system {
        resValue "string", "app_name", "System CA Validation Demo"
        applicationId "com.commonsware.android.downloader.ca.system"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_verisign_system']
        buildConfigField "String", "URL", WARES
    }
    pin {
        resValue "string", "app_name", "Cert Pin Demo"
        applicationId "com.commonsware.android.downloader.ca.pin"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_pin']
        buildConfigField "String", "URL", WARES
    }
    invalidPin {
        resValue "string", "app_name", "Cert Pin Demo"
        applicationId "com.commonsware.android.downloader.ca.invalidpin"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_invalid_pin']
        buildConfigField "String", "URL", WARES
    }
    selfSigned {
        resValue "string", "app_name", "Self-Signed Demo"
        applicationId "com.commonsware.android.downloader.ca.ss"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_selfsigned']
        buildConfigField "String", "URL", SELFSIGNED
    }
    override {
        resValue "string", "app_name", "Debug Override Demo"
        applicationId "com.commonsware.android.downloader.ca.debug"
        manifestPlaceholders=
                [networkSecurityConfig: 'network_override']
        buildConfigField "String", "URL", SELFSIGNED
    }
}

I can then reference the placeholder in the manifest normally:

<application
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:networkSecurityConfig="@xml/${networkSecurityConfig}">
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I have a different value that depends on both whether it is debug or not, and the flavor type. how do i take BOTH into consideration ? – Lena Bru Dec 01 '16 at 13:02
  • @LenaBru: Off the top of my head, I do not know. The approach that you outlined in your answer will not work. At best, you will have the same value for all build variants, where that value will be the last of the variants your loop processes. Please understand that `build.gradle` does not build your app, but builds an object model of what is in `build.gradle`. That object model -- seconds, minutes, or hours later -- builds your app. So, your function gets evaluated N times with the same N build variants in the loop. – CommonsWare Dec 01 '16 at 13:27
  • A library is forcing me to work with meta-data in the manifest file, instead of passing the value in the code, and you say there is absolutely nothing I can do? That I need separate applications ? – Lena Bru Dec 01 '16 at 13:32
  • 1
    @LenaBru: "you say there is absolutely nothing I can do?" -- I did not say that. I *am* saying that the approach in your question will not work, and I do not know how to manipulate `manifestPlaceholders` on a per-build-variant basis. In your case, you do not need `manifestPlaceholders` -- put your value in string resources in build-variant-specific directories (e.g., `oneDebug/res/values/strings.xml`). This is reminiscent of John O'Reilly's solution, just not generated from Gradle. – CommonsWare Dec 01 '16 at 13:36