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 ?
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}">
Related
I updated gradle 4.0.1 to 7.0.3 because I need the support of new gradle.
I let the auto updater run and after it was done, when I run the code I get the following error:
C:\Users\me\Projects\proj\proj\proj\app\build\generated\source\buildConfig\stage\debug\proj\BuildConfig.java:22: error: illegal forward reference
public static final String APPLICATION_LIST_URL = BACKEND_HOST + "/page";
In build.gradle the buildConfigField is declared like this:
defaultConfig {
applicationId "my.app.id"
minSdkVersion 21
versionCode getBuildTimestamp()
versionName "2.0.0"
buildConfigField 'String', 'APPLICATION_LIST_URL', 'BACKEND_HOST + "/page"'
}
I tried Invaldiate cache/restart and I do not know what else I can try.
EDIT
BACKEND_HOST is also defined:
productFlavors {
local {
dimension "type"
targetSdkVersion 30
buildConfigField 'String', 'APK_DOWNLOAD_RESOLVE_URL', 'BACKEND_HOST + "DOES_NOT_EXIST"'
...
}
remote {
dimension "type"
targetSdkVersion 30
applicationIdSuffix ".remote"
buildConfigField 'String', 'APK_DOWNLOAD_RESOLVE_URL', 'BACKEND_HOST + "/remote/download"'
}
def backendRemote= '"https://myUrl"'
android.applicationVariants.all {
variant ->
def appName = "myApp"
def backendHost = backendRemote
variant.resValue "string", "app_name", appName
resValue "string", "app_version", "${appName} ${variant.versionName}"
variant.buildConfigField "String", "AUTH_HOST", backendHost
variant.buildConfigField "String", "BACKEND_HOST", backendHost
}
}
And I was building it with remote flavor
It's unclear how the build tools determine the order of the field declarations in the BuildConfig. What works though is this (note the BuildConfig.BACKEND_HOST instead of just BACKEND_HOST):
buildConfigField 'String', 'BACKEND_HOST', 'my.backend.host.com'
buildConfigField 'String', 'APPLICATION_LIST_URL', 'BuildConfig.BACKEND_HOST + "/page"'
Chapter 8.3.3 of https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html explains what forward references are legal and which ones are illegal.
Here's a minimal code sample showing how the BACKEND_HOST can be defined in each flavor:
defaultConfig {
applicationId "com.example.myapplication"
minSdk 30
targetSdk 31
versionCode 1
versionName "1.0"
buildConfigField 'String', 'APPLICATION_LIST_URL', 'BuildConfig.BACKEND_HOST + "/page"'
}
flavorDimensions "version"
productFlavors {
free {
dimension "version"
buildConfigField "String", "BACKEND_HOST", '"www.free.com"'
}
paid {
dimension "version"
buildConfigField "String", "BACKEND_HOST", '"www.paid.com"'
}
}
This works because more specific BuildConfig fields (flavors) are evaluated first before the less specific ones (defaultConfig).
This code is copied from the OP's question and modified to compile by making the references to BACKEND_HOST static:
defaultConfig {
applicationId "my.app.id"
minSdkVersion 21
versionCode getBuildTimestamp()
versionName "2.0.0"
buildConfigField 'String', 'APPLICATION_LIST_URL', 'BuildConfig.BACKEND_HOST + "/page"'
buildConfigField "String", "BACKEND_HOST", '"www.paid.com"'
}
flavorDimensions "type"
productFlavors {
local {
dimension "type"
targetSdkVersion 30
buildConfigField 'String', 'APK_DOWNLOAD_RESOLVE_URL', 'BuildConfig.BACKEND_HOST + "DOES_NOT_EXIST"'
}
remote {
dimension "type"
targetSdkVersion 30
applicationIdSuffix ".remote"
buildConfigField 'String', 'APK_DOWNLOAD_RESOLVE_URL', 'BuildConfig.BACKEND_HOST + "/remote/download"'
}
}
def backendRemote= '"https://myUrl"'
android.applicationVariants.all {
variant ->
def backendHost = backendRemote
variant.buildConfigField "String", "AUTH_HOST", backendHost
variant.buildConfigField "String", "BACKEND_HOST", backendHost
}
I want to make HostName field dynamic. so I can have different HostName for both app1 and app2, for develop and release
**app1 {
dimension "client"
applicationId = "com.app1.random"
}
app2 {
dimension "client"
applicationId = "com.app2.random"
}
develop {
dimension "environment"
buildConfigField "String", "HOSTNAME", "\"https://app1.example.com\""
}
release {
dimension "environment"
buildConfigField "String", "HOSTNAME", "\"https://app1.com\""
}**
Hope this help you
flavorDimensions "default"
productFlavors{
develop {
buildConfigField "String", "HOSTNAME", "\"https://app1.example.com\""
}
release {
buildConfigField "String", "HOSTNAME", "\"https://app1.com\""
}
}
I want use something like this, i know that's not possible, but i wanna know if have another way to use combine Flavor With build type
productFlavors{
demo{
release {
buildConfigField "String", "URL", '"192.1.1.1"'
}
debug {
buildConfigField "String", "URL", '"192.2.2.2"'
}
}
full{
release {
buildConfigField "String", "URL", '"192.2.2.2"'
}
debug {
buildConfigField "String", "URL", '"192.3.3.3"'
}
}
}
Rather than product flavors you can create multiple build types to suit your requirement.
for eg create build types like below
release {
buildConfigField "String", "URL", '"192.2.2.2"'
}
debug {
buildConfigField "String", "URL", '"192.3.3.3"'
}
releasedemo {
buildConfigField "String", "URL", '"192.1.1.1"'
}
debugdemo {
buildConfigField "String", "URL", '"192.2.2.2"'
}
I have a gradle config as setup below. To allow for side by side installs of different builds/flavors
buildTypes {
release {
}
debug {
applicationIdSuffix ".debug"
// Somehow add debug suffix to app ID?
}
}
productFlavors {
ci {
applicationId "com.myapp.ci"
ext.betaDistributionGroupAliases = "mobileworkforce.ci"
resValue "string", "app_name", "AppName.CI"
}
staging {
applicationId "com.myapp.staging"
resValue "string", "app_name", "AppName.Staging"
}
production {
applicationId "com.myapp"
resValue "string", "app_name", "AppName"
}
The issue is that I cannot figure out how to update the app_name string resource to have the suffix "Debug" to the app_name string resource (used as the label for the application)
I was able to create a viable solution using manifestPlaceholders instead of generating string resources. It is not ideal because the result is AppName.Debug.CI instead of AppName.CI.Debug but it works.
buildTypes {
release {
manifestPlaceholders = [ activityLabel:"AppName"]
}
debug {
applicationIdSuffix ".debug"
manifestPlaceholders = [ activityLabel:"AppName.Debug"]
}
}
productFlavors {
def addActivityLabelSuffix = { placeholders, suffix ->
def appName = placeholders.get("activityLabel")
placeholders.put("activityLabel", appName + suffix);
}
ci {
applicationId "com.myapp.ci"
ext.betaDistributionGroupAliases = "mobileworkforce.ci"
addActivityLabelSuffix getManifestPlaceholders(), ".CI"
}
staging {
applicationId "com.myapp.staging"
addActivityLabelSuffix getManifestPlaceholders(), ".Staging"
}
production {
applicationId "com.myapp"
resValue "string", "app_name", "AppName"
}
}
Using getManifestPlaceholders().get("...") I always get null, so I was looking for another solution... and maybe is better because I can get correct order in app name...
// build.gradle
def appNameBase = "My App"
buildTypes {
release {
manifestPlaceholders = [ appNameBase: appNameBase, appNameSuffix: "" ]
}
debug {
manifestPlaceholders = [ appNameBase: appNameBase, appNameSuffix: " Debug" ]
applicationIdSuffix ".debug"
}
}
productFlavors {
one {
manifestPlaceholders = [ appNameFlavor: "One" ]
}
two {
manifestPlaceholders = [ appNameFlavor: "Two" ]
}
}
// AndroidManifest.xml
<application android:label="${appNameBase} ${appNameFlavor}${appNameSuffix}" ...>
In my case I wanted for prod just "My App One" and "My App Two" ad for debug "My App One Debug" and "My App Two Debug" and code above works great.
I have a product with multiple product flavors like:
buildTypes {
debug {
}
release {
}
}
productFlavors {
flavor1 {
buildConfigField "String" "country" "se"
buildConfigField "String" "language" "sv-SE"
buildConfigField "String" "appName" "Flavor1"
}
flavor2 {
buildConfigField "String" "country" "se"
buildConfigField "String" "language" "sv-SE"
buildConfigField "String" "appName" "Flavor2"
}
flavor3 {
buildConfigField "String" "country" "se"
buildConfigField "String" "language" "sv-SE"
buildConfigField "String" "appName" "Flavor3"
}
flavor4 {
buildConfigField "String" "country" "se"
buildConfigField "String" "language" "sv-SE"
buildConfigField "String" "appName" "Flavor4"
}
flavor5 {
buildConfigField "String" "country" "se"
buildConfigField "String" "language" "no-NO"
buildConfigField "String" "appName" "Flavor5"
}
}
I would prefer a common section with all properties and only override those that are different. Is this possible?
I would also like to put all flavors (and perhaps buildTypes) in it's own file to make it more readable. So whenever you have to change a flavor, you can easily find it in its own file, and not have to scroll over thousands of line which it will be if I have all flavors and buildTypes together with all the rest in the main build file.
Selvin is correct, use the defaultConfig closure - there is no neater way! In the following example, flavors 1, 2 & 5 would set the default country and language to de. Flavors 3 & 4 override this with their own languages.
defaultConfig {
buildConfigField "String", "country", "de"
buildConfigField "String", "language", "de"
}
buildTypes {
debug {
}
release {
}
}
productFlavors {
flavor1 {
buildConfigField "String", "appName", "Flavor1"
}
flavor2 {
buildConfigField "String", "appName", "Flavor2"
}
flavor3 {
buildConfigField "String", "country", "uk"
buildConfigField "String", "language", "en_GB"
buildConfigField "String", "appName", "Flavor3"
}
flavor4 {
buildConfigField "String", "country", "fr"
buildConfigField "String", "language", "fr"
buildConfigField "String", "appName", "Flavor4"
}
flavor5 {
buildConfigField "String", "appName", name.capitalize()
}
}
NOTE
Just an FYI that you can use name.capitalize() to turn the name of any flavour, e.g. flavor5, into the app name of Flavor5 by using the capitalize() method - which will capitalize the first character in the String. However, this MUST go in the flavor, not defaultConfig