I have got manifest placeholder for service:
<service
android:name=".MyService"
android:process="${processName}">
In my build.gradle I replaced it.
android {
defaultConfig {
manifestPlaceholders = [
processName: ':Proc'
]
}
}
But for my espresso tests (androidTest) I need empty value in the processName. How can I do this?
I have tried to do this with buildTypes, but it doesn't work (BuildType names cannot start with "androidTest")
This example works for me with Gradle plugin 3.4.1:
android {
unitTestVariants.all {
it.mergedFlavor.manifestPlaceholders += [
placeHolderName: "placeHolderValue"
]
it.mergedFlavor.addResValue(new ClassFieldImpl("string", "resValueName", "resValueValue"))
}
}
I think that the best approach here would be to write a second AndroidManifest just for your instrumented tests, as suggested by the documentation
../app/src/androidTest/AndroidManifest.xml
<service
android:name=".MyService"
android:process="">
Simply adding manifest will not help, because you need to provide merging strategy for it just use:
<service
android:name=".MyService"
android:process=""
tools:node="replace">
Than in merged manifest you should get correct value.
Related
Integrating some libraries like Branch-io in Android need to define meta-data in the project manifest. some of these variables are like TestMode
<meta-data android:name="io.branch.sdk.TestMode" android:value="true" />
So, when we want to publish the application we should change it to False.
Is there any way to define a variable somewhere according to BuildType and assign it to the Meta-data to that?
You can do it by adding manifestPlaceholders to your build.gradle file:
android {
...
buildTypes {
debug {
manifestPlaceholders = [isBranchSdkInTestMode:"true"]
...
}
release {
manifestPlaceholders = [isBranchSdkInTestMode:"false"]
...
}
}
...
}
In AndroidManifest.xml, you can use it as ${isBranchSdkInTestMode}:
<meta-data android:name="io.branch.sdk.TestMode" android:value="${isBranchSdkInTestMode}" />
Yes you can inject build variables from gradle to manifest, it is done by adding variable to the build.gradle:
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]
}
deployConfg {
manifestPlaceholders = [hostName:"www.prod-server.com"]
}
...
}
And then in your manifest you can get it by:
<intent-filter ... >
<data android:scheme="http" android:host="${hostName}" ... />
...
</intent-filter>
You can read more about how this works here.
I set a breakpoint in my MyAppApplication.onCreate() method. It gets hit with 1 product flavor but not the other. This is the only breakpoint that doesn't get hit. I'm guessing it has something to do with the applicationId, but I'm not understanding why,
build.gradle
productFlavors {
prod { //the flavor that works
applicationId 'com.my.app'
versionName "1.2.3"
}
staging { //the flavor that doesnt
applicationId 'com.my.app.staging'
versionName "1.2.3-staging"
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.my.app">
...
<application android:name="com.my.app.MyAppApplication"
...
I've tried replacing the manifest package to ${applicationId}, but that gives me an error. The package names don't change with the flavor, only the appliationId. From what I understand, the package name in manifest gets swapped out by gradle during build time anyway.
It's hard to repro what went wrong for you, here're some guidance how to make your app multi-flavourable, so I hope you'll find the missing/wrong step by following it.
I've created a test project with package: klogi.com.flavourapp:
In app's module, build.gradle add:
productFlavors {
production {
applicationId "klogi.com.flavourapp"
}
staging {
applicationId "klogi.com.flavourapp.staging"
}
}
To show flavour effect, I'll change name of app in the Toolbar. To do it, in FlavourApp\app\src directory create directory staging\res\values and strings.xml file inside:
Then the values from this FlavourApp\app\src\staging\res\values\strings.xml will override default FlavourApp\app\src\main\res\values\strings.xml.
Last step is to create Application's extension:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(getApplicationContext(), "Hi " + BuildConfig.FLAVOR, Toast.LENGTH_LONG).show();
}
}
and assign it in AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="klogi.com.flavourapp">
<application
android:name=".MyApplication"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I checked, there's no difference between android:name="klogi.com.flavourapp.MyApplication" and android:name=".MyApplication" , but I still think the second one is better.
Here's the result:
And the test project you can find: here;
I hope, it helps
I want to create a global variable similar with applicationId.
It is set value in build.gradle and will be used in manifest. Is it possible?
You can set them, for instance I'm setting it for different product flavors
productFlavors {
production {
applicationId = "com.myapp.app"
resValue "string", "authority", "com.facebook.app.FacebookContentProvider5435651423234"
}
development {
applicationId = "com.myapp.development"
resValue "string", "authority", "com.facebook.app.FacebookContentProvider2134564533421"
}
qa {
applicationId = "com.myapp.qa"
resValue "string", "authority", "com.facebook.app.FacebookContentProvider29831237981287319"
}
}
And use it like this
<provider
android:name="com.facebook.FacebookContentProvider"
android:authorities="#string/authority"
android:exported="true" />
If you just want to use the application id set in gradle in your manifest, you can simply use:
${applicationId}
For instance:
<provider
android:authorities="${applicationId}.ShareFileProvider" ... >
...
</provider>
If you want the same behavior with custom variables, you can use manifestPlaceholders, like this:
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]
}
}
And in your manifest:
<intent-filter ... >
<data android:scheme="http" android:host="${hostName}" ... />
...
</intent-filter>
See https://developer.android.com/studio/build/manifest-build-variables.html for more information.
While Marko's answer seems to work, there's currently a better solution that doesn't require adding variables to the string resource files.
The manifest merger accepts placeholders:
For custom placeholders replacements, use the following DSL to
configure the placeholders values :
android {
defaultConfig {
manifestPlaceholders = [ activityLabel:"defaultName"]
}
productFlavors {
free {
}
pro {
manifestPlaceholders = [ activityLabel:"proName" ]
}
}
will substitute the placeholder in the following declaration :
<activity android:name=".MainActivity" android:label="${activityLabel}" >
You can also manipulate those strings with groovy functions.
To use the string in Manifest, you can directly make it in strings.xml.
Like this,
<string name="variable_name">value</string>
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.
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 = ...
}
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!
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"
}
}
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>
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.
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" />
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}" />
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"
}
}
Is it possible to access a BuildConfig value from AndroidManifest.xml?
In my build.gradle file, I have:
defaultConfig {
applicationId "com.compagny.product"
minSdkVersion 16
targetSdkVersion 21
versionCode 1
versionName "1.0"
// Facebook app id
buildConfigField "long", "FACEBOOK_APP_ID", FACEBOOK_APP_ID
}
FACEBOOK_APP_ID is defined in my gradle.properties files:
# Facebook identifier (app ID)
FACEBOOK_APP_ID=XXXXXXXXXX
To use Facebook connect in my app, I must add this line to my AndroidManifest.xml:
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="#string/applicationId"/>
I want to replace #string/applicationId by the BuildConfig field FACEBOOK_APP_ID defined in gradle, like this:
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="FACEBOOK_APP_ID"/>
Is that possible using BuildConfig? If not, how can I achieve this?
Replace
buildConfigField "long", "FACEBOOK_APP_ID", FACEBOOK_APP_ID
with
resValue "string", "FACEBOOK_APP_ID", FACEBOOK_APP_ID
then rebuild your project (Android Studio -> Build -> Rebuild Project).
The two commands both produce generated values - consisting of Java constants in the first case, and Android resources in the second - during project builds, but the second method will generate a string resource value that can be accessed using the #string/FACEBOOK_APP_ID syntax. This means it can be used in the manifest as well as in code.
Another way to access Gradle Build Config values from your AndroidManifest.xml is through placeholders like this:
android {
defaultConfig {
manifestPlaceholders = [ facebookAppId:"someId..."]
}
productFlavors {
flavor1 {
}
flavor2 {
manifestPlaceholders = [ facebookAppId:"anotherId..." ]
}
}
}
and then in your manifest:
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="${facebookAppId}"/>
See more details here: https://developer.android.com/studio/build/manifest-build-variables.html
(Old link just for reference: http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger)
note: when you use resValue the value can accidentally be overridden by the strings resource file (e.g. for another language)
To get a true constant value that you can use in the manifest and in java-code, use both manifestPlaceholders and buildConfigField: e.g.
android {
defaultConfig {
def addConstant = {constantName, constantValue ->
manifestPlaceholders += [ (constantName):constantValue]
buildConfigField "String", "${constantName}", "\"${constantValue}\""
}
addConstant("FACEBOOK_APP_ID", "xxxxx")
}
access in the manifest file:
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="${FACEBOOK_APP_ID}"/>
from java:
BuildConfig.FACEBOOK_APP_ID
If the constant value needs to be buildType-specific, the helper addConstant needs to be tweaked (to work with groovy closure semantics), e.g.,
buildTypes {
def addConstantTo = {target, constantName, constantValue ->
target.manifestPlaceholders += [ (constantName):constantValue]
target.buildConfigField "String", "${constantName}", "\"${constantValue}\""
}
debug {
addConstantTo(owner,"FACEBOOK_APP_ID", "xxxxx-debug")
}
release {
addConstantTo(owner,"FACEBOOK_APP_ID", "xxxxx-release")
}
Access build.gradle properties in your manifest as in following example:
For example you have a property "applicationId" in your build.gradle and you want to access that in your AndroidManifest:
Access "applicationId" in AndroidManifest:
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
Similarly, we can create string resources for other constants and access them in code files as simple as:
context.getString(R.string.GCM_SENDER_ID);
#stkent is good but forgets to add that you need to rebuild your project afterwards
Replace
buildConfigField "long", "FACEBOOK_APP_ID", FACEBOOK_APP_ID
with
resValue "string", "FACEBOOK_APP_ID", FACEBOOK_APP_ID
then
Android Studio -> Build -> Rebuild Project
This will allow android generate the string resource accessible via
R.string.FACEBOOK_APP_ID
Another option: use a different string resource file to replace all Flavor-dependent values:
Step 1:
Create a new folder in the "src" folder with the name of your flavor, im my case "stage"
Step 2:
Create resource files for all files that are dependent on the flavor
for example:
Step 3:
I am also using different icons, so you see the mipmap folders as well. For this quetion, only the "strings.xml" is important. Now you can overwrite all important string resources. You only need to include the ones you want to override, all others will be used from the main "strings.xml", it will show up in Android Studio like this:
Step 4:
Use the string resources in your project and relax:
You can use long value as below
buildConfigField 'long', 'FLAVOR_LONG', '11500L'