176

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?

stkent
  • 19,772
  • 14
  • 85
  • 111
anthony
  • 7,653
  • 8
  • 49
  • 101
  • 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? – Anukool srivastav Aug 11 '21 at 10:04

7 Answers7

236

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.

Community
  • 1
  • 1
stkent
  • 19,772
  • 14
  • 85
  • 111
  • 1
    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? – W.K.S Jul 30 '15 at 13:49
  • @W.K.S If you look at the generated resources file, does the resource type match what you expect (string) there? – stkent Jul 30 '15 at 14:00
  • it's string: `15233522...` – W.K.S Jul 30 '15 at 14:03
  • Hmm... try `resValue "string", "FACEBOOK_APP_ID", \"FACEBOOK_APP_ID\"` to see if that helps. Other than that, not sure what else to suggest. – stkent Jul 30 '15 at 14:07
  • Nah, that creates a syntax error `Expression Expected` :/ – W.K.S Jul 30 '15 at 14:10
  • 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` ... – dsdsdsdsd Apr 27 '16 at 15:58
  • 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](http://tools.android.com/tech-docs/new-build-system), and explore the DSL in more depth [here](https://github.com/google/android-gradle-dsl). – stkent Apr 27 '16 at 17:13
  • `resValue` feature into `Android Gradle plugin` according to `Official doc of android gradle dsl` -> https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.ProductFlavor.html#com.android.build.gradle.internal.dsl.ProductFlavor:resValue(java.lang.String,%20java.lang.String,%20java.lang.String) – ahmed hamdy May 30 '16 at 16:48
  • 16
    make sure you write resValue "string" and NOT resValue "String" – keybee Mar 29 '17 at 11:26
  • `resvalue` with `string` (make sure it is in small characters) worked! – sud007 Feb 28 '19 at 17:53
  • 1
    If you want to use a boolean value, make sure you type resValue "bool" instead of "boolean" -> https://www.tanelikorri.com/tutorial/android/set-variables-in-build-gradle/ – reavcn Oct 21 '19 at 10:28
98

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)

GregoryK
  • 3,011
  • 1
  • 27
  • 26
  • 2
    This is a great solution. – BinaryShrub Mar 29 '16 at 18:32
  • 2
    Yeah, this is a nice method for manifest-only string injection! – stkent Oct 19 '16 at 00:35
  • 2
    How would you get a value from manifestPlaceholders in java? This would be useful for constants used in both java code and in manifest. – Louis CAD Dec 12 '16 at 10:21
  • @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. – GregoryK Dec 13 '16 at 14:38
  • @Gregory Using [this approach](http://stackoverflow.com/a/17201265/4433326), 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? – Louis CAD Dec 13 '16 at 14:46
  • 1
    @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 you – GregoryK Dec 13 '16 at 16:10
  • @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). – Louis CAD Dec 19 '16 at 08:43
  • I also had to use the null-terminator as suggested here: http://stackoverflow.com/a/41425953. Otherwise the value would have parsed as a float. – Omri Sivan Mar 14 '17 at 15:31
  • Thank you! Exactly what i was looking for! – Vitaliy A Apr 22 '18 at 11:25
  • What if we need to set android:name string from build gradle file? – WISHY Jul 30 '18 at 08:56
  • 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? – Aman Verma Jan 22 '19 at 12:48
  • the value is being visible from decoded apk in AndroidManifest.xml – Adi May 29 '20 at 14:26
  • Not sure, why this is not marked as the answer. This is the recommended approach from android as well. https://developer.android.com/studio/build/gradle-tips – arango_86 Nov 19 '20 at 08:10
59

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")
    }
Community
  • 1
  • 1
TmTron
  • 17,012
  • 10
  • 94
  • 142
  • 1
    Not work it shows invalid app id, from manifest,but when set from java then it work – Brajendra Pandey Mar 24 '17 at 12:14
  • I think this wont work if you try to add more than one constant! – Greg Ennis Jan 11 '18 at 18:39
  • 1
    @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? – TmTron Jan 12 '18 at 08:40
  • 1
    @TmTron you dont get an error, but you re-assign the entire 'manifestPlaceholders' array every time it is called, only storing the last value used. – Greg Ennis Jan 13 '18 at 12:30
  • @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 += [...]` – TmTron Jan 13 '18 at 12:53
  • will the same solution also work for android:name="com.facebook.sdk.ApplicationId" if we specify the string in build gradle? – WISHY Jul 30 '18 at 09:00
  • @WISHY Since it's just another constant, I see no problem. What error do you get? – TmTron Jul 30 '18 at 09:05
  • How can I wrap the addConstant logic in method? – Vivek Singh Oct 26 '18 at 10:32
  • @TmTron 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) – Kristy Welsh Dec 12 '18 at 13:08
  • @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. – TmTron Dec 13 '18 at 08:10
  • 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? – Aman Verma Jan 22 '19 at 12:49
  • @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?](https://stackoverflow.com/questions/37101589/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. – TmTron Jan 22 '19 at 15:47
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);
Vinay
  • 1,859
  • 22
  • 26
  • 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? – Aman Verma Jan 22 '19 at 12:49
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
F.O.O
  • 4,730
  • 4
  • 24
  • 34
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

Björn Kechel
  • 7,933
  • 3
  • 54
  • 57
  • A good alternative, especially if you have many other strings you need to set per-variant. – stkent Oct 07 '15 at 12:32
  • 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? – Aman Verma Jan 22 '19 at 13:22
0

You can use long value as below

buildConfigField 'long', 'FLAVOR_LONG', '11500L'

Keyur Thumar
  • 608
  • 7
  • 19