How can I access a BuildConfig value in my AndroidManifest.xml file?
Asked Answered
B

7

178

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?

Blisse answered 10/3, 2015 at 0:16 Comment(1)
Can I know any specific reason to keep it in build. As keeping FACEBOOK_APP_ID in build or direct string will have same impact and will be visible after decompilation. Any other way to secure this key and access in manifest?Ledbetter
V
241

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.

Verdure answered 10/3, 2015 at 0:28 Comment(11)
This is the exact code I'm using but I'm facing a problem: Facebook SDK reads the string as an integer for some reason and then throws a ClassCastException because Integer can not be cast to String. Has anyone faced this issue?Abdicate
@Abdicate If you look at the generated resources file, does the resource type match what you expect (string) there?Verdure
it's string: <item name="facebook_app_id" type="string">15233522...</item>Abdicate
Hmm... try resValue "string", "FACEBOOK_APP_ID", \"FACEBOOK_APP_ID\" to see if that helps. Other than that, not sure what else to suggest.Verdure
Nah, that creates a syntax error Expression Expected :/Abdicate
are these two variables ('buildConfigField' and 'resValue') part of the java language, or part of AndroidStudio ??? ... ... and where can I find these in documentation?? ... AS Help offers two search paths: Help Topics and Online Documentation, neither of which show the definition for buildConfigField ...Sprout
Those symbols (actually method names) are defined as part of the Android Studio Gradle plugin. This plugin extends Gradle's DSL with Android-specific constructs. Build config fields and resource values are examples of such constructs. You can read more about the plugin here, and explore the DSL in more depth here.Verdure
resValue feature into Android Gradle plugin according to Official doc of android gradle dsl -> google.github.io/android-gradle-dsl/current/…Pteranodon
make sure you write resValue "string" and NOT resValue "String"Coelenteron
resvalue with string (make sure it is in small characters) worked!Downatheel
If you want to use a boolean value, make sure you type resValue "bool" instead of "boolean" -> tanelikorri.com/tutorial/android/set-variables-in-build-gradleMigrate
J
100

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)

Jester answered 26/11, 2015 at 9:56 Comment(13)
This is a great solution.Lardy
Yeah, this is a nice method for manifest-only string injection!Verdure
How would you get a value from manifestPlaceholders in java? This would be useful for constants used in both java code and in manifest.Sunstroke
@LouisCAD I suppose you can try to define a variable in your build file, then use it to define your placeholder and (in addition) BuildConfig field, so you can use it in your code.Jester
@Gregory Using this approach, right? Is there a way to reference constants from a java source file instead, to organize them instead of listing them inside the build.gradle file, while keeping them visible for both java and gradle?Sunstroke
@LouisCAD I didn't try that, but you can try the following: var placeholder = 'your placeholder' (new line) buildConfigField "String", "PLACEHOLDER_NAME", placeholder (new line) manifestPlaceholders = [ placeholderName:placholder] (new line) Would be glad to know if it works for youJester
@Gregory I'm pretty certain it'd work from my experience with gradle. I'll finally don't need to use this because of an implementation choice I made (activity-aliases, vs activities that extend the intended activity to then switch their component enabled state).Sunstroke
I also had to use the null-terminator as suggested here: stackoverflow.com/a/41425953. Otherwise the value would have parsed as a float.Simony
Thank you! Exactly what i was looking for!Rid
What if we need to set android:name string from build gradle file?Orthocephalic
can i define the facebookAppId:"anotherId..." in .properties file where I would be saving my store password and all. Then i access those in manifestplaceholders which would indirectly be accessed in AndroidManifest.xml. Can i do that?Hahnert
the value is being visible from decoded apk in AndroidManifest.xmlOliy
Not sure, why this is not marked as the answer. This is the recommended approach from android as well. developer.android.com/studio/build/gradle-tipsSix
M
60

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")
    }
Martinic answered 14/11, 2016 at 15:38 Comment(12)
Not work it shows invalid app id, from manifest,but when set from java then it workOptimistic
I think this wont work if you try to add more than one constant!Savino
@GregEnnis Also more constants work fine for me: e.g. I just call addConstant() twice (with different constantNames of course). What error do you get?Martinic
@Martinic you dont get an error, but you re-assign the entire 'manifestPlaceholders' array every time it is called, only storing the last value used.Savino
@GregEnnis you are absolutely righ! I've had a different code in my test-project. I've already updated my answer: we just need to use the += operator like this manifestPlaceholders += [...]Martinic
will the same solution also work for android:name="com.facebook.sdk.ApplicationId" if we specify the string in build gradle?Orthocephalic
@Orthocephalic Since it's just another constant, I see no problem. What error do you get?Martinic
How can I wrap the addConstant logic in method?Factual
@Martinic getting error when trying to build: No signature of method: build_2q0m6xqak601h5kc25oj7k8ai$_run_closure3$_closure12$_closure20.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[HOCKEYAPP_APP_ID:xxxxxxxxxx]] Possible solutions: any(), any(), any(groovy.lang.Closure), each(groovy.lang.Closure), any(groovy.lang.Closure), each(groovy.lang.Closure)Honeycutt
@KristyWelsh I am not sure what you did wrong - maybe just some syntax error. I'd suggest that you create a new Question on SO and post your configuration.Martinic
can i define the facebookAppId:"anotherId..." in .properties file where I would be saving my store password and all. Then i access those in manifestplaceholders which would indirectly be accessed in AndroidManifest.xml. Can i do that?Hahnert
@sniper: not sure what you mean. You can of course access property files in your gradle.build file How to read a properties files and use the values in project Gradle script?. And nothing stops you from using these properties anywhere in the gradle.build file.Martinic
H
6

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:

enter image description here

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);
Heathenry answered 18/3, 2016 at 12:15 Comment(1)
can i define the facebookAppId:"anotherId..." in .properties file where I would be saving my store password and all. Then i access those in manifestplaceholders which would indirectly be accessed in AndroidManifest.xml. Can i do that?Hahnert
F
3

@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
Firelock answered 15/7, 2016 at 20:18 Comment(1)
Could not get unknown property 'FACEBOOK_APP_ID' for ProductFlavor_ ...Apothecium
A
1

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:

enter image description here

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:

enter image description here

Step 4: Use the string resources in your project and relax:

enter image description here

Aery answered 6/10, 2015 at 8:11 Comment(2)
A good alternative, especially if you have many other strings you need to set per-variant.Verdure
can i define the facebookAppId:"anotherId..." in .properties file where I would be saving my store password and all. Then i access those in manifestplaceholders which would indirectly be accessed in AndroidManifest.xml. Can i do that?Hahnert
L
0

You can use long value as below

buildConfigField 'long', 'FLAVOR_LONG', '11500L'
Lundin answered 20/3, 2020 at 5:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.