Is there way to conditionally include meta-data element in Android manifest file based on value set in Gradle. I am able to do following (using resValue to set <some_value> in build.gradle) but haven't been able to find way to include/exclude complete meta-data element.
<meta-data
android:name="<some_setting>"
android:value="#string/<some_value>" />
Somewhat of a hack but only way I've been able to address this is by having something like following (have changed names in example) in build.gradle (am using this in context of having different build flavors as well but isn't dependent on that)...I think there's probably some additional smarts that can be done around manifest merging that would make this a bit cleaner as well. myFlag is coming from a project.hasProperty() value I'm passing in as part of Fastlane script
sourceSets {
flavor1 {
if (myFlag == "true") {
manifest.srcFile "src/flavor1_flag/AndroidManifest.xml"
} else {
manifest.srcFile "src/flavor1/AndroidManifest.xml"
}
}
}
<meta-data android:name="string"
android:resource="resource specification"
android:value="string" />
Related
I'm trying to build an app with platform-independent activities, but I also need to build an app that is multiplatform. So, I created the config:
sourceSets {
getByName("any") {
// How to add here two manifests: mobile and tv?
manifest.srcFile("src/platform/any/AndroidManifest.xml")
java.srcDirs("src/platform/any/java")
kotlin.srcDirs("src/platform/any/java")
res.srcDirs("src/platform/any/res")
}
getByName("mobile") {
manifest.srcFile("src/platform/mobile/AndroidManifest.xml")
java.srcDirs("src/platform/mobile/java")
kotlin.srcDirs("src/platform/mobile/java")
res.srcDirs("src/platform/mobile/res")
}
getByName("tv") {
manifest.srcFile("src/platform/tv/AndroidManifest.xml")
java.srcDirs("src/platform/tv/java")
kotlin.srcDirs("src/platform/tv/java")
res.srcDirs("src/platform/tv/res")
}
}
The problem is that the manifest file in any must be always a merge of mobile and tv manifest files. For now, I have the third manifest file. But sometimes developers forget to add needed activity or receiver to any manifest file and any build may be broken or vice versa.
Is there any solution to this?
I do have a structure like below. I wanna use different manifestPlaceHolder like this, projectXTest, projectXDev, projectXProd all use different manifestPlaceHolder while projectYTest,Prod,Dev uses the same manifestPlaceHolder. What I can do besides putting that values to string.xml for all different flavors
android {
buildTypes{
debug{
// Some debug setup
}
release{
// Some release setup
}
}
flavorDimensions "project" , "default"
productFlavors {
projectX{
dimension 'project'
}
projectY{
dimension 'project'
}
Test{
dimension 'default'
}
Dev{
dimension 'default'
}
Prod{
dimension 'default'
}
}
}
Since I could find an out of this. I followed this steps. I hope, If somebody find her/himself in this situation, this helps.
Firstly
Put a manifest to desired flavors directory, for my example I've put android manifest to src/projectY and src/projectX
Beware these not to be full manifest. It should be layered like below
<manifest>
<application>
<activity>
android:name=".x.loginActivity"
tools:node="merge" // this is must
// your flavor spesific code
</activity>
</application>
</manifest>
Secondly
Delete your code to minimum in main XML. What i mean that manifest you've created in src/flavor will be merged with main one. so that delete what you want to seperate and leave what you want to used common in main.
Thirdly
Give different manifestPlaceholder name if you need to seperate prod,dev and staging.
For example,
src/projectX/manifest.xml
<activity>
.
.
android:host='${projectXHost}'
.
</activity>
src/projectY/manifest.xml
<activity>
.
.
android:host='${projectYHost}'
.
</activity>
in gradle
productFlavors{
prod {
manifestPlaceholders = [projectXHost : 'xxx' , projectYHost : 'yyy']
}
}
Since this is my first answer, I'm sorry with layout and expressions. I hope you can understand what I mean. Have a good day
I'm new to React Native. The task at hand is to set my Google API key in AndroidManifest.xml without exposing it, when pushed to GitHub.
I set up an environment variable called API_KEY, but however I want to access it in the .xml, I get an error when trying to spin up the dev server.
So far I tried:
android:value=${env.API_KEY}
android:value="${env.API_KEY}"
android:value="${API_KEY}"
Thank you!
Based on the second comment (from kenmistry), the solution that worked for me was indeed to create a placeholder in build.gradle, but since, for whatever reason, configuring and referring a .env file did't work, I invoked my environment variables like so in build.gradle:
manifestPlaceholders = [API_KEY: "$System.env.API_KEY"]
and accessed it in the .xml as suggested by kenmistry:
android:value="${API_KEY}"
assuming that you have defined the key in your .env file, you can set that up on build.gradle as manifestPlaceholders.
android {
defaultConfig {
manifestPlaceholders = [API_KEY: "$process.env.your_key"]
}
...
}
on your AndroidManifest.xml,
android:value="${API_KEY}"
I am using Gradle with Product flavors where I set a different package name for each one.
productFlavors {
appone {
packageName "com.dg.app1"
}
apptwo {
packageName "com.dg.app2"
}
appthree {
packageName "com.dg.app3"
}
appfour {
packageName "com.dg.app4"
}
}
I need to be able to replace the package name inside the manifest for each corresponding app.
My manifest has this:
<receiver android:name="com.parse.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.dg.example" />
</intent-filter>
</receiver>
So I need to replace com.dg.example for each app flavor's package name. What is the best way to do this?
Gradle Plugin v0.12 and higher:
Use ${applicationId} instead of ${packageName}.
Gradle Plugin v0.11 and higher:
As of v0.11, you no longer need to specify not to use the old manifest merger.
Gradle Plugin v0.10 and higher:
Assuming you're using version 0.10 or higher, this is now officially supported:
buildscript {
repositories {
mavenCentral()
}
dependencies {
// Make sure this is at least 0.10.+
classpath 'com.android.tools.build:gradle:0.10.+'
}
}
As of v0.10, you'll also have to manually enable the new manifest merger, although I'd expect that requirement to go away in a version or two whenever the new merger becomes the default:
android {
useOldManifestMerger false
}
Then, just use ${packageName} anywhere in AndroidManifest.xml that you would normally hardcode the package name. For example:
<category android:name="my.package.name"/>
would become
<category android:name="${packageName}"/>
Gradle Plugin v0.9 and below:
So, referencing this post, it appears this is not yet officially supported through Gradle. A simple workaround is the following:
Replace the package name with a custom tag (e.g. <category android:name="my.package.name"/> becomes <category android:name="_PACKAGENAME_"/>
Add the following to your build.gradle, under the android scope:
applicationVariants.all { variant ->
// After processing the manifest, replace all instances of your tag
// with the variant's actual package name.
variant.processManifest << {
def manifestOutFile = variant.processManifest.manifestOutputFile
def newFileContents = manifestOutFile.getText('UTF-8').replace("_PACKAGENAME_", variant.packageName)
manifestOutFile.write(newFileContents, 'UTF-8')
}
}
To do something like this, I use buildTypes in my gradle file but I am pretty sure this will work with flavours as well. For me I am trying to set the label field in the activities.
I have a strings xml file for each of my buildTypes.
Then I have a sourceSet for each buildType which includes the correct strings file.
Then in the manifest I do not use a hard coded string but rather "#string/my_var" which will pull the correct string depending on how the sourceSets are defined.
This google+ post and related gist may help.
Something else to do is to put a AndroidManifest.xml file into the src/flavour which only contains the bits which are relevant to each flavour. Then take those bits out of the main manifest file. At build time the Manifest files will be merged into one file. You can see the result all of the merged manifests in build/manifests.
I had the same problem and implemented a placeholder replace method in Gradle. It does exactly what you'd expect but also takes care about packageNameSuffix attributes so you can have debug and release as well as any other custom builds on the same device.
applicationVariants.all { variant ->
def flavor = variant.productFlavors.get(0)
def buildType = variant.buildType
variant.processManifest.doLast {
println '################# Adding Package Names to Manifest #######################'
replaceInManifest(variant,
'PACKAGE_NAME',
[flavor.packageName, buildType.packageNameSuffix].findAll().join()) // ignores null
}
}
def replaceInManifest(variant, fromString, toString) {
def flavor = variant.productFlavors.get(0)
def buildtype = variant.buildType
def manifestFile = "$buildDir/manifests/${flavor.name}/${buildtype.name}/AndroidManifest.xml"
def updatedContent = new File(manifestFile).getText('UTF-8').replaceAll(fromString, toString)
new File(manifestFile).write(updatedContent, 'UTF-8')
}
I have it up on a gist too if you want to see if it evolves later.
I found to be a more elegant approach than the multiple resources and XML parsing approaches.
Option Gradle:
Use grade attributes API. Some thing like this
manifest.attributes(["attr1":"value1", "attr2":"value2"])
Option 1
How about converting your project to Android - library project, and making extra project for each company. Than you can edit the Manifest file as you wish.
Option 2
Write a batch file.
I currently use a MapActivity in my application.
I use it with 2 API Keys. One for debugging, and one for "production"
I am fed up with changing these values in the xml layout:
<view class="com.google.android.maps.MapView"
android:id="#+id/myGmap"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="#string/api_key_prod" />
I am fed up trying to change the apikey each time and replace prod by debug each time.
Is that possible to change this key within the onCreate() of my application.
Imagine that I have a boolean preference that look like: isDebug.
I may check thi preference on my phone and disable it by default on user application. and make something like:
if (isDebug)
myMap.setApiKey(R.string.api_key_debug)
else
myMap.setApiKey(R.string.api_key_prod)
Thank a lot for any help.
You cannot both have the widget in your layout and set the API key in Java.
If you dynamically create the MapView via its constructor, you can supply the API key that way from Java code, but then you will need to dynamically add it to your layout.
That being said, I'd deal with the problem via your build process (e.g., based on debug/production build, copy the right XML file into the right directory).
This works for me.
This variant of MapView constructor is documented here:
https://developers.google.com/maps/documentation/android/reference/com/google/android/maps/MapView
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
String mapApiKey = <your choice logic here>
mMapView = new MapView(this, mapApiKey);
setContentView(mMapView);
You should use Product Flavors.
For example:
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
dev {
resValue "string", "google_maps_api_key", "DEV_API_KEY"
}
prod {
resValue "string", "google_maps_api_key", "PROD_API_KEY"
}
}
}
You have to create google maps object dynamically. Your layout will contaion only parent layout for creating object.
For extra security put your API key and secrets in the local.properties file and access it using BuildConfig as follows:
In your top-level settings.gradle file, include the Gradle plugin portal, Google Maven repository, and Maven central repository under the pluginManagement block. The pluginManagement block must appear before any other statements in the script.
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
In Android Studio, open your project-level build.gradle file and add the following code to the dependencies element under buildscript:
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false
}
Open your module-level build.gradle file and add the following code to the plugins element.
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
Save the file and sync your project with Gradle.
Open the local.properties in your project level directory, and then add the following code. Replace YOUR_API_KEY with your API key.
MAPS_API_KEY=YOUR_API_KEY
Save the file.
Access the key within AndroidManifest.xml file:
In your AndroidManifest.xml file, go to com.google.android.geo.API_KEY and update the android:value attribute with MAPS_API_KEY as follows:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
Access the key within Java/Kotlin code: Just use BuildConfig as follows:
BuildConfig.MAPS_API_KEY
More can be found in docs.