Stoph
Stoph

Reputation: 723

Using a different manifestPlaceholder for each Build Variant

I will start by saying that I am very new to Gradle, so I apologize if this has already been answered.

I'm working on an Android application that uses an API key to access a 3rd party tool. A different API key needs to be used depending on both the flavor and build type of the app.

Here is a basic outline of what I'm trying to do:

android {
    defaultConfig {
        manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
    }

    buildTypes{
        debug{
            // Some debug setup
        }
        release{
            // Some release setup
        }
    }

    productFlavors {
        // List of flavor options
    }
    productFlavors.all{ flavor->
        if (flavor.name.equals("someFlavor")) {
            if (buildType.equals("release")) {
                manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
            } else {
                manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
            }
        } else {
            if (buildType.equals("release")) {
                manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
            } else {
                manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
            }    
        }
    }
}

So far the manifestPlaceholders statement is working in a very simple case, but I don't know how to reference the buildType from within the productFlavors block so that I can use it as a conditional.

Upvotes: 35

Views: 25266

Answers (8)

Ali
Ali

Reputation: 1532

You may set manifestPlaceholders inside applicationVariants by accessing mergedFlavor for specific applicationVariant.

android.applicationVariants.all { variant ->
    def mergedFlavor = variant.getMergedFlavor()
    mergedFlavor.manifestPlaceholders = [appPackageId: "myPackageExample"]
}

If you're using the Kotlin DSL, you should use something like this:

android.applicationVariants.all { // don't put 'variant ->' here or you'll get the 'all' extension function
    // no need to define 'mergedFlavor' because 'this' _is_ the variant so 'mergedFlavor' is already available.
    mergedFlavor.manifestPlaceholders = ...
}

Upvotes: 52

rayworks
rayworks

Reputation: 796

As a complement to @Eric's post, for AGP version com.android.tools.build:gradle:4.x, this code snippet

applicationVariants.all{ variant->
    if (variant.productFlavors.get(0).name.equals("someFlavor")) {
        if (variant.buildType.name.equals("release")) {
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
        } else {
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        }
    } else {
        if (variant.buildType.name.equals("release")) {
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
        } else {
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        }    
    }
}

should be updated to

androidComponents {
    onVariants(selector().withBuildType("debug")) {
        manifestPlaceholders.apiKey = "DEBUG_KEY"
    }

    onVariants(selector().withBuildType("release")) {
        if(flavorName.equals("someFlavor")) 
            manifestPlaceholders.apiKey = "RELEASE_KEY_1"
        else 
            manifestPlaceholders.apiKey = "RELEASE_KEY_2"
    }
}

Upvotes: 0

Mudassar Ashraf
Mudassar Ashraf

Reputation: 972

You don't need duplicate files
Build.gradle

productFlavors {
    prod {
        applicationId "com.example.prod"
        dimension "mode"
        manifestPlaceholders = [hostName:"some String"]
    }
    dev {
        applicationId "com.example.dev"
        dimension "mode"
        manifestPlaceholders = [hostName:"some String"]
    }

Manifiest use "${hostName}". Example below

<meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${hostName}" />

Upvotes: 1

Luis E. Fernandez
Luis E. Fernandez

Reputation: 846

I found this great solution in https://azabost.com/android-manifest-placeholders/

android {
    ...
    buildTypes {
        release {
            ...
            manifestPlaceholders.screenOrientation = "portrait"
        }
        debug {...}
    }
}

or

android {
    ...
    flavorDimensions "features"

    productFlavors {

    paid {
        dimension "features"
        manifestPlaceholders.hostName = "www.paid-example.com"
    }
    free {
        dimension "features"
        manifestPlaceholders.hostName = "www.free-example.com"
    }
}

Upvotes: 10

Nilesh Deokar
Nilesh Deokar

Reputation: 2985

What i did is copied current AndroidManifest.xml into app/src/debug

and changed the key there debug Manifest :

 <meta-data
            android:name="com.crashlytics.ApiKey"
            tools:replace="android:value"
            android:value="@string/crashlytics_debug" />

app/src/main Manifest is like :

<meta-data
        android:name="com.crashlytics.ApiKey"
        android:value="@string/crashlytics_live" />

Upvotes: 1

Nelson Ramirez
Nelson Ramirez

Reputation: 8004

Similarly to the accepted answer, you could do it with string resources, if you didn't want to duplicate your manifests.

For example, if you had two flavors (flavor1 and flavor2) You'd end up w/ the following source sets.

app/
  src/
    main/
      res/
         values/strings.xml
    flavor1Release/
      res/
         values/strings.xml
    flavor1Debug/
      res/
         values/strings.xml

    flavor2Release/
       res/
         values/strings.xml
    flavor2Debug/
       res/
         values/strings.xml

You could then just use a string resource for your key value

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="@string/apiKey" tools:replace="android:value"/>
    </application>    
</manifest>

One further optimization to keep all your keys in one place is to define them all in strings.xml in your main source set. and then have the flavor/build source sets reference those.

for example:

<resources>
    <string name="flavor1ReleaseKey">flavor1ReleaseKey</string>
    <string name="flavor1DebugKey">flavor1DebugKey</string>
    <string name="flavor2ReleaseKey">flavor2ReleaseKey</string>
    <string name="flavor2DebugKey">flavor2DebugKey</string>
</resources>

then in each of your flavor/build sourceSets, you just reference those keys.

flavor1Release/res/values/strings.xml

<resources>
     <string name="apiKey">@string/flavor1ReleaseKey</string>
</resources>

Upvotes: 8

Eric Labelle
Eric Labelle

Reputation: 1967

I would guess that you are referring to Fabric ApiKey? :) I just spent hours trying to do it in a similar way with the placeholders and specifying the ApiKey in the gradle file although it does not seem possible as of com.android.tools.build:gradle:1.3.1. It is possible to specify a placeholder for a specific flavor but not for a flavor AND buildType.

Just to correct your syntax, the way you would have to do it (if it was possible) would be something like that but manifestPlaceholders are unknown to variants.

applicationVariants.all{ variant->
    if (variant.productFlavors.get(0).name.equals("someFlavor")) {
        if (variant.buildType.name.equals("release")) {
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
        } else {
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        }
    } else {
        if (variant.buildType.name.equals("release")) {
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
        } else {
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        }    
    }
}

What you actually need to do is to keep the key in the AndroidManifest.xml and handle it with multiple manifest file

src/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="DEBUG_KEY" tools:replace="android:value"/>
    </application>    
</manifest>

src/someFlavorRelease/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="RELEASE_KEY_1" tools:replace="android:value"/>
    </application>    
</manifest>

src/someOtherFlavorRelease/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="RELEASE_KEY_2" tools:replace="android:value"/>
    </application>    
</manifest>

The manifestMerger will handle the replacement and you will end up with the proper key in every scenario. I just implemented it successfully. I just hope you were really referring to the Fabric key! :)

Hope this helps!

Upvotes: 14

Mimmo Grottoli
Mimmo Grottoli

Reputation: 5773

I believe that you need a manifestPlaceHolder to read that value in your Java code, right? If this is the case, you can already read the FLAVOR name in your generated BuildConfig.java. For example, if you define a flavor whose name is smartphone you can access that value using BuildConfig.FLAVOR String; then in your code you can use a simple if (BuildConfig.FLAVOR.equals("smartphone"))...

But maybe you need to read a sort of configuration of your app, an apiKey. In that case, the best way to go is to create a Class or a string resource for every flavor; this is the link for you.

Upvotes: 1

Related Questions