Use package name in XML - android

I'm using Android Studio to build my application. I would like to use gradle buildTypes. I add a suffix to the package name with applicationIdSuffix to modify the package name for a test build type.
buildTypes {
debug {
runProguard false
proguardFile 'proguard-rules.txt'
applicationIdSuffix '.dev'
versionNameSuffix '-dev'
}
}
Is it possible to use this information in a xml file. My plan is to modify the account type:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType=<<PACKAGE NAME HERE ??>>
android:icon="#drawable/icon"
android:label="#string/app_name"/>
Thanks!

Here's another temporary workaround until the Android Gradle plugin gets support for placeholders in resource files.
You can dynamically create a String resource via Gradle in the defaultConfig and/or buildTypes and/or productFlavors closure:
android {
defaultConfig {
applicationId 'com.foo.bar'
resValue 'string', 'package_name', applicationId
}
}
You can then reference it in your authenticator.xml file just like you do for the label:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="#string/package_name"
android:icon="#drawable/icon"
android:label="#string/app_name"/>

While uou can use ${packageName} to fill in the generated package name in the Manifest:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="${packageName}"
android:icon="#drawable/icon"
android:label="#string/app_name"/>
This currently (Android Gradle 0.11) doesn't work with resource files. :-(
Instead, you must create a separate version of the resource file for each build type / variant you want with a different account type:
src
debug/res/xml/authenticator.xml (with debug package name)
main/res/xml/authenticator.xml (with default package name)

On top of jenzz's answer, i had to do this, since my case is just like as question, i add applicationIdSuffix for debug version of app.
android {
...
defaultConfig {
applicationId "..."
...
}
....
buildTypes {
release {
...
resValue 'string', 'application_id', android.defaultConfig.applicationId
}
debug {
...
applicationIdSuffix '.debug'
resValue 'string', 'application_id', android.defaultConfig.applicationId + applicationIdSuffix
} 
}
Then in any xml resource, i can get application Id as like this: #string/application_id

I'm using ${applicationId} like follow for example:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abnerescocio.embaixadordorei.presentation">
...
<application
...
>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
...
</application>
</manifest>

I've just created a gradle plugin for this: com.inutilfutil.android-resource-placeholder
Just include it and placeholders will be replaced on all XML resources

Related

How to use different package names between flavors?

I'm trying to create a single project with 2 flavors: free and pro (versions).
My app is already in PlayStore with different packages (E.g.: com.example.appfree and com.example.app)
This is my build.gradle:
defaultConfig {
applicationId "com.example.appfree"
}
productFlavors{
lite {
applicationIdSuffix 'lite'
}
pro {
}
}
And this is my manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".SplashScreenActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"/>
</application>
</manifest>
I tried to upload a build apk for free and pro flavors. Pro flavor is ok, but free flavor isnt accepted by Google, because the package is incorrect. How can I solve this?
====== Solved: ======
applicationIdSuffix only works with buildTypes.
With the new Android Gradle build system, you can easily build multiple different versions of your app; for example, you can build both a "free" version and a "pro" version of your app (using flavors), and these should have different packages in the Google Play store such that they can be installed and purchased separately, both installed at the same time, and so on. Similarly, you may also build both "debug" and "alpha" and "beta" versions of your app (using build types) and these can also similarly contribute to unique package names.
app/build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "19.1"
defaultConfig {
applicationId "com.example.my.app"
minSdkVersion 15
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
...
app/build.gradle:
productFlavors {
pro {
applicationId = "com.example.my.pkg.pro"
}
free {
applicationId = "com.example.my.pkg.free"
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
....
from Android Studio Project Site - ApplicationId versus PackageName
flavorDimensions "version"
productFlavors {
demo {
// Assigns this product flavor to the "version" flavor dimension.
// This property is optional if you are using only one dimension.
dimension "version"
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
dimension "version"
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
Besides the basic gradle.build (app level) changes. Note that you only need to add the config you need to, not all of these:
productFlavors {
enterprise {
applicationId = "com.example.appname.enterprise"
}
consumer {
applicationId = "com.example.appname.consumer"
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
I wanted to add that if you are using Firebase and you are required to have a google-services.json file. You will see the following:
ERROR: No matching client found for package name "com.example.appname.debug"
To fix it, you will need to register the new package name in Firebase and download the new google-services.json which will have all your existing packages names. If not you will have a compile error saying no client was found for the new package name.

Android Gradle: Install all build types on one device

How do I configure my project to be able to install the debug version alongside the release version when using GCM, ContentProvider, AccountType? (without the use of flavors)
I keep getting errors such as: INSTALL_FAILED_CONFLICTING_PROVIDER or INSTALL_FAILED_DUPLICATE_PERMISSION
Installing a debug apk and the release apk on the same device is tricky if you are only using build types and not flavors (Why Build types and not flavors)
Most blog post are either outdated (talking about packageName) or force you to use flavors because the solution they propose does not support applicationIdSuffix and a build type cannot declare applicationId therefore you need to use a flavors
The solution I propose uses
an authority per build type
an account type per build type
a GCM permission per build type
For this to work I use applicationIdSuffix, manifest placeholders, BuildConfigField and resValue in the Gradle file.
The only problem left is when you want to have a different name for app and per language the string is not set as translatable (bug aosp tracker)
This forces you to set abortOnError false otherwise you won't be able to make a release build.
build.gradle
project.ext {
defaultApplicationId = "com.myapp.package"
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId defaultApplicationId
manifestPlaceholders = [ applicationIdWithSuffix: "${applicationId}" ]
buildConfigField "String", "ACCOUNT_TYPE", "\"${applicationId}\""
buildConfigField "String", "AUTHORITY", "\"${applicationId}.provider\""
resValue "string", "account_type", "${applicationId}"
resValue "string", "authority", "${applicationId}.provider"
}
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
manifestPlaceholders = [ applicationIdWithSuffix: defaultApplicationId + ".debug" ]
buildConfigField "String", "ACCOUNT_TYPE", "\"${defaultApplicationId}.debug\""
buildConfigField "String", "AUTHORITY", "\"${defaultApplicationId}.debug.provider\""
resValue "string", "account_type", "${defaultApplicationId}.debug"
resValue "string", "authority", "${defaultApplicationId}.debug.provider"
}
}
lintOptions {
abortOnError false
}
}
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mypackage" >
<permission
android:name="${applicationIdWithSuffix}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationIdWithSuffix}.permission.C2D_MESSAGE" />
<application
android:label="#string/app_name" >
<provider
android:name=".MyContentProvider"
android:authorities="${applicationIdWithSuffix}.provider"
android:exported="false"
android:multiprocess="true" />
</application>
</manifest>
Sync adapter xml
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="#string/authority"
android:accountType="#string/account_type"/>
Account authenticator
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="#string/account_type"
.../>
ContentProvider
I have a constant for Authority which takes it from the BuildConfig.
AUTHORITY = BuildConfig.AUTHORITY
Account type
To get the account type you take it from the BuildConfig too.
BuildConfig.ACCOUNT_TYPE
Multi language app name
If you want different names per app & language:
debug/values-en/strings.xml
<resources>
<string name="app_name">MyApp debug EN</string>
</resources>
debug/values-fr/strings.xml
<resources>
<string name="app_name">MyApp debug FR</string>
</resources>
main/values-en/strings.xml
<resources>
<string name="app_name">MyApp EN</string>
</resources>
main/values-fr/strings.xml
<resources>
<string name="app_name">MyApp FR</string>
</resources>

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

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'

custom strings for debug buildType

I have an android app and I want to change the app label for the debug and other buildTypes. I don´t have any flavors!
Here is the setup that I believe looks like it should work:
-src
|-debug
|-res
|-values
|-strings.xml
|-main
|-res
|-values
|-strings.xml
|-java
[...]
I have no custom sourcesets just a debug buildType:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
so I though
sourceSets.debug.res.srcDirs = ['src/debug/res']
would to the trick. But it doesn't. Any ideas?
How to change app name per Gradle build type does not work anymore...
I found another sweet solution to this, using manifest placeholders:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="${applicationLabel}">
<activity
android:label="${applicationLabel}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
and in your gradle file:
android {
defaultConfig {
manifestPlaceholders = [ applicationLabel:"#string/app_name"]
}
buildTypes {
debug {
applicationIdSuffix ".debug"
manifestPlaceholders = [ applicationLabel:"MyApp Debug"]
}
}
}
buildTypes {
release {
resValue 'string', 'APP_NAME', '"My App Release"'
}
debug {
resValue 'string', 'APP_NAME', '"My App Debug"'
}
}
value\strings.xml
< string name="app_name" >#string/APP_NAME< /string>
and use app_name everywhere
You have to use
|-debug
|-res
|-values
|-strings.xml
In your picture you have debug/res/strings.xml
Also you doens't need it (because it is the standard, but the issue isn't here).
sourceSets.debug.res.srcDirs = ['src/debug/res']
Forget the string.xml files. All can be easily configured in build.gradle.
First of all, you should maintain the string pointer "app_name" in AndroidManifest file, and delete all instances of "app_name" in string's resource files:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="#string/app_name">
<activity
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Secondly, the resource value of #string/app_name is currently not defined. So we must apply its default definition in build.gradle:
defaultConfig {
applicationId "com.example.myapp"
minSdkVersion 14
targetSdkVersion 22
versionCode 123423432
versionName "1.0.0"
resValue 'string', 'app_name', '"My app label"'
}
Currently, app_name is defined for all build types. By assuming you want to change the app label for the buildTypes, each build type must be defined with a string value in the same build.gradle branch:
buildTypes {
release {
resValue 'string', 'app_name', '"My app label Release"'
}
debug {
resValue 'string', 'app_name', '"My app label Debug"'
}
}
Since this resource value is set programmatically, we also need to add a certain translations lint ignore in case of a Release build:
lintOptions { disable 'MissingTranslation' }
In case you want to change it accordingly with a set of defined Flavours(dev, qua or prd) add the resValues definitions in productFlavours instead of buildTypes:
productFlavors {
dev {
applicationId "com.example.myapp.dev"
resValue 'string', 'app_name', '"My app label Dev"'
}
qua {
applicationId "com.example.myapp.qua"
resValue 'string', 'app_name', '"My app label Qua"'
}
prd {
applicationId "com.example.myapp.prd"
resValue 'string', 'app_name', '"My app label Prd"'
}
}
You can create a string in gradle that will be available in xml too:
buildTypes {
debug {
buildConfigField "String", "app_name", "AppDebug"
}
release {
buildConfigField "String", "app_name", "AppRelease"
}
And then use it in xml:
android:label="#string/app_name"
Just make sure app_name not specified in your strings.xml.
Try this.
buildTypes {
debug {
applicationIdSuffix ".debug"
}
sourceSets.debug.resources.srcDirs = ['src/debug/res']
}
Have you remembered to get rid of the directory listing inside your app's build.gradle?
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDir 'src'
// res.srcDir 'res' <--- This line should be removed
assets.srcDir 'assets'
I know this is quite an old question, but I've had the same problem and just solved it.
You should update these codes if you have one in your build.gradle for your app.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
These codes automatically generate your AppName.iml and set default debug and release directory to /build-types/debug/res, which is different from src/debug/res.

Using build types in Gradle to run same app that uses ContentProvider on one device

I have set up Gradle to add package name suffix to my debug app so I could have release version that I'm using and debug version on one phone. I was referencing this: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types
My build.gradle file looks like this:
...
android
{
...
buildTypes
{
debug
{
packageNameSuffix ".debug"
versionNameSuffix " debug"
}
}
}
Everything works fine until I start using a ContentProvider in my app. I get:
Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]
I understand that this happens because two apps (release and debug) are registering same ContentProvider authority.
I see one possibility to solve this. If I understand correctly, you should be able to specify different files to use when building. Then I should be able to put different authorities in different resource files (and from Manifest set authority as string resource) and tell Gradle to use different resource for debug build. Is that possible? If yes then any hints on how to achieve that would be awesome!
Or maybe it's possible to directly modify Manifest using Gradle? Any other solution on how to run same app with ContentProvider on one device is always welcome.
None of existing answers satisfied me, however Liberty was close. So this is how am I doing it.
First of all at the moment I am working with:
Android Studio Beta 0.8.2
Gradle plugin 0.12.+
Gradle 1.12
My goal is to run Debug version along with Release version on the same device using the same ContentProvider.
In build.gradle of your app set suffix for Debug build:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
In AndroidManifest.xml file set android:authorities property of your ContentProvider:
<provider
android:name="com.example.app.YourProvider"
android:authorities="${applicationId}.provider"
android:enabled="true"
android:exported="false" >
</provider>
In your code set AUTHORITY property that can be used wherever needed in your implementation:
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
Tip: Before it was BuildConfig.PACKAGE_NAME
That's it! It will work like a charm. Keep reading if you use SyncAdapter!
Update for SyncAdapter (14.11.2014)
Once again I will start with my current setup:
Android Studio Beta 0.9.2
Gradle plugin 0.14.1
Gradle 2.1
Basically, if you need to customise some values for different builds you can do it from the build.gradle file:
use buildConfigField to access it from the BuildConfig.java class
use resValue to access it from resources e.g. #string/your_value
As an alternative for resources, you can create separate buildType or flavour directories and override XMLs or values within them. However, I am not going to use it in example below.
Example
In build.gradle file add the following:
defaultConfig {
resValue "string", "your_authorities", applicationId + '.provider'
resValue "string", "account_type", "your.syncadapter.type"
buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type"'
}
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "your_authorities", defaultConfig.applicationId + '.debug.provider'
resValue "string", "account_type", "your.syncadapter.type.debug"
buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type.debug"'
}
}
You will see results in BuildConfig.java class
public static final String ACCOUNT_TYPE = "your.syncadapter.type.debug";
and in build/generated/res/generated/debug/values/generated.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from default config. -->
<item name="account_type" type="string">your.syncadapter.type.debug</item>
<item name="authorities" type="string">com.example.app.provider</item>
</resources>
In your authenticator.xml use resource specified in build.gradle file
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="#string/account_type"
android:icon="#drawable/ic_launcher"
android:smallIcon="#drawable/ic_launcher"
android:label="#string/app_name"
/>
In your syncadapter.xml use the same resource again and #string/authorities too
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="#string/authorities"
android:accountType="#string/account_type"
android:userVisible="true"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
/>
Tip: autocompletion(Ctrl+Space) does not work for these generated resource so you have to type them manually
New Android build system tip: ContentProvider authority renaming
I guess all of you have heard of the new Android Gradle-based build system. Let's be honest, this new build system is a huge step forward compared to the previous one. It is not final yet (as of this writing, the latest version is 0.4.2) but you can already use it safely in most of your projects.
I've personnaly switched most of my project to this new build system and had some issues because of the lack of support in some particular situations. One of which is the support for ContentProvider authority renaming
The new Android built system lets you deal with different types of your app by simply modifying the package name at build time. One of the main advantage of this improvement is you can now have two different versions of your app installed on the same device at the same time. For instance:
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
defaultConfig {
packageName "com.cyrilmottier.android.app"
versionCode 1
versionName "1"
minSdkVersion 14 // Listen to +Jeff Gilfelt advices :)
targetSdkVersion 17
}
buildTypes {
debug {
packageNameSuffix ".debug"
versionNameSuffix "-debug"
}
}
}
Using such a Gradle configuration, you can assemble two different APKs :
• A debug APK with the com.cyrilmottier.android.app.debug package name
• A release APK with the com.cyrilmottier.android.app package name
The only issue with that is you won't be able to install the two APKs at the same time if they both expose a ContentProvider with the same authorities. Pretty logically we need to rename the authority depending on the current build type … but this is not supported by the Gradle build system (yet? ... I'm sure it will be fixed soon). So here is a way to go:
First we need to move the provider Android manifest ContentProvider declaration to the appropriate build type. In order to do that we will simply have :
src/debug/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyrilmottier.android.app"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="com.cyrilmottier.android.app.debug.provider"
android:exported="false" />
</application>
</manifest>
src/release/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyrilmottier.android.app"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="com.cyrilmottier.android.app.provider"
android:exported="false" />
</application>
</manifest>
Make sure to remove the ContentProvider declaration from the AndroidManifest.xml in src/main/ because Gradle doesn't know how to merge ContentProviders having the same name but a different authority.
Finally we may need to access to the authority in the code. This can be done pretty easily using the BuildConfig file and the buildConfig method:
android {
// ...
final PROVIDER_DEBUG = "com.cyrilmottier.android.app.debug.provider"
final PROVIDER_RELEASE = "com.cyrilmottier.android.app.provider"
buildTypes {
debug {
// ...
buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_DEBUG
}
release {
buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_RELEASE
}
}
}
Thanks to this workaround you'll be able to use BuildConfig.PROVIDER_AUTHORITY in your ProviderContract and install two different versions of your app at the same time.
Originaly on Google+:
https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ
While Cyril's example works great if you only have a few build types, it quickly gets complicated if you have many build types and/or product flavors as you need to maintain lots of different AndroidManifest.xml's.
Our project consists of 3 different build types and 6 flavors totaling 18 build variants, so instead we added support for ".res-auto" in ContentProvider authorities, which expand to the current packagename and removes the need to maintain different AndroidManifest.xml
/**
* Version 1.1.
*
* Add support for installing multiple variants of the same app which have a
* content provider. Do this by overriding occurrences of ".res-auto" in
* android:authorities with the current package name (which should be unique)
*
* V1.0 : Initial version
* V1.1 : Support for ".res-auto" in strings added,
* eg. use "<string name="auth">.res-auto.path.to.provider</string>"
*
*/
def overrideProviderAuthority(buildVariant) {
def flavor = buildVariant.productFlavors.get(0).name
def buildType = buildVariant.buildType.name
def pathToManifest = "${buildDir}/manifests/${flavor}/${buildType}/AndroidManifest.xml"
def ns = new groovy.xml.Namespace("http://schemas.android.com/apk/res/android", "android")
def xml = new XmlParser().parse(pathToManifest)
def variantPackageName = xml.#package
// Update all content providers
xml.application.provider.each { provider ->
def newAuthorities = provider.attribute(ns.authorities).replaceAll('.res-auto', variantPackageName)
provider.attributes().put(ns.authorities, newAuthorities)
}
// Save modified AndroidManifest back into build dir
saveXML(pathToManifest, xml)
// Also make sure that all strings with ".res-auto" are expanded automagically
def pathToValues = "${buildDir}/res/all/${flavor}/${buildType}/values/values.xml"
xml = new XmlParser().parse(pathToValues)
xml.findAll{it.name() == 'string'}.each{item ->
if (!item.value().isEmpty() && item.value()[0].startsWith(".res-auto")) {
item.value()[0] = item.value()[0].replace(".res-auto", variantPackageName)
}
}
saveXML(pathToValues, xml)
}
def saveXML(pathToFile, xml) {
def writer = new FileWriter(pathToFile)
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.preserveWhitespace = true
printer.print(xml)
}
// Post processing of AndroidManifest.xml for supporting provider authorities
// across build variants.
android.applicationVariants.all { variant ->
variant.processManifest.doLast {
overrideProviderAuthority(variant)
}
}
Example code can be found here: https://gist.github.com/cmelchior/6988275
Since the plugin version 0.8.3 (actually 0.8.1 but it wasn't working properly) you can define resources within the build file so this could be a cleaner solution because you don't need to create strings files nor additional debug/release folders.
build.gradle
android {
buildTypes {
debug{
resValue "string", "authority", "com.yourpackage.debug.provider"
}
release {
resValue "string", "authority", "com.yourpackage.provider"
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourpackage"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="#string/authority"
android:exported="false" />
</application>
</manifest>
I don't know if anybody mention it. Actually after android gradle plugin 0.10+, the manifest merger will provide the official support for this function:
http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger
In AndroidManifest.xml, you can use ${packageName} like this:
<provider
android:name=".provider.DatabasesProvider"
android:authorities="${packageName}.databasesprovider"
android:exported="true"
android:multiprocess="true" />
And in your build.gradle you can have:
productFlavors {
free {
packageName "org.pkg1"
}
pro {
packageName "org.pkg2"
}
}
See full example here:
https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152
and here:
https://code.google.com/p/anymemo/source/browse/build.gradle#41
Use ${applicationId} placeholders in xml and BuildConfig.APPLICATION_ID in code.
You will need to extend the build script to enable placeholders in xml files other than the manifest. You could use a source directory per build variant to provide different versions of the xml files but maintenance will become cumbersome very quickly.
AndroidManifest.xml
You can use the applicationId placeholder out of the box in the manifest. Declare your provider like this:
<provider
android:name=".provider.DatabaseProvider"
android:authorities="${applicationId}.DatabaseProvider"
android:exported="false" />
Note the ${applicationId} bit. This is replaced at build time with the actual applicationId for the build variant that is being built.
In code
Your ContentProvider needs to construct the authority string in code. It can use the BuildConfig class.
public class DatabaseContract {
/** The authority for the database provider */
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".DatabaseProvider";
// ...
}
Note the BuildConfig.APPLICATION_ID bit. It is a generated class with the actual applicationId for the build variant being built.
res/xml/ files, e.g. syncadapter.xml, accountauthenticator.xml
If you want to use a Sync Adapter you will need to provide meta-data for the ContentProvider and AccountManager in xml files in the res/xml/ directory. The applicationId placeholder is not supported here. But you can extend the build script yourself to hack it in.
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="${applicationId}"
android:allowParallelSyncs="false"
android:contentAuthority="${applicationId}.DatabaseProvider"
android:isAlwaysSyncable="true"
android:supportsUploading="true"
android:userVisible="true" />
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="${applicationId}"
android:icon="#drawable/ic_launcher"
android:label="#string/account_authenticator_label"
android:smallIcon="#drawable/ic_launcher" />
Again, note the ${applicationId}. This only works if you add the below gradle script to the root of your module and apply it from build.gradle.
build.gradle
Apply the extra build script from the module build.gradle script. A good place is below the Android gradle plugin.
apply plugin: 'com.android.application'
apply from: './build-processApplicationId.gradle'
android {
compileSdkVersion 21
// etc.
build-processApplicationId.gradle
Below is working source for a res/xml/ placeholder build script. A better documented version is available on github. Improvements and extensions are welcome.
def replace(File file, String target, String replacement) {
def result = false;
def reader = new FileReader(file)
def lines = reader.readLines()
reader.close()
def writer = new FileWriter(file)
lines.each { line ->
String replacedLine = line.replace(target, replacement)
writer.write(replacedLine)
writer.write("\n")
result = result || !replacedLine.equals(line)
}
writer.close()
return result
}
def processXmlFile(File file, String applicationId) {
if (replace(file, "\${applicationId}", applicationId)) {
logger.info("Processed \${applicationId} in $file")
}
}
def processXmlDir(File dir, String applicationId) {
dir.list().each { entry ->
File file = new File(dir, entry)
if (file.isFile()) {
processXmlFile(file, applicationId)
}
}
}
android.applicationVariants.all { variant ->
variant.mergeResources.doLast {
def applicationId = variant.mergedFlavor.applicationId + (variant.buildType.applicationIdSuffix == null ? "" : variant.buildType.applicationIdSuffix)
def path = "${buildDir}/intermediates/res/${variant.dirName}/xml/"
processXmlDir(new File(path), applicationId)
}
}
Strings.xml
In my opinion there is no need to add placeholder support for resource strings. For the above use case at least it is not needed. However you could easily change the script to not only replace placeholders in the res/xml/ directory, but also in the res/values/ directory.
I would rather prefer a mixture between Cyril and rciovati. I think is more simplier, you only have two modifications.
The build.gradle looks like:
android {
...
productFlavors {
production {
packageName "package.name.production"
resValue "string", "authority", "package.name.production.provider"
buildConfigField "String", "AUTHORITY", "package.name.production.provider"
}
testing {
packageName "package.name.debug"
resValue "string", "authority", "package.name.debug.provider"
buildConfigField "String", "AUTHORITY", "package.name.debug.provider"
}
}
...
}
And the AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="package.name" >
<application
...>
<provider android:name=".contentprovider.Provider" android:authorities="#string/authority" />
</application>
</manifest>
gradle.build
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.example.awsomeapp"
minSdkVersion 9
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
}
productFlavors
{
prod {
applicationId = "com.example.awsomeapp"
}
demo {
applicationId = "com.example.awsomeapp.demo"
versionName = defaultConfig.versionName + ".DEMO"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
applicationIdSuffix ".debug"
versionNameSuffix = ".DEBUG"
debuggable true
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
// rename the apk
def file = output.outputFile;
def newName;
newName = file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk");
newName = newName.replace(project.name, "awsomeapp");
output.outputFile = new File(file.parent, newName);
}
//Generate values Content Authority and Account Type used in Sync Adapter, Content Provider, Authenticator
def valueAccountType = applicationId + '.account'
def valueContentAuthority = applicationId + '.authority'
//generate fields in Resource string file generated.xml
resValue "string", "content_authority", valueContentAuthority
resValue "string", "account_type", valueAccountType
//generate fields in BuildConfig class
buildConfigField "String", "ACCOUNT_TYPE", '"'+valueAccountType+'"'
buildConfigField "String", "CONTENT_AUTHORITY", '"'+valueContentAuthority+'"'
//replace field ${valueContentAuthority} in AndroidManifest.xml
mergedFlavor.manifestPlaceholders = [ valueContentAuthority: valueContentAuthority ]
}
}
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="#string/account_type"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:smallIcon="#drawable/ic_launcher" />
sync_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="#string/content_authority"
android:accountType="#string/account_type"
android:userVisible="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
android:supportsUploading="true"/>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.0" package="com.example.awsomeapp">
<uses-permission android:name="android.permission.GET_ACCOUNTS"/><!-- SyncAdapter and GCM requires a Google account. -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<!-- GCM Creates a custom permission so only this app can receive its messages. -->
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
<application....
.......
<!-- Stub Authenticator -->
<service
android:name="com.example.awsomeapp.service.authenticator.CAuthenticatorService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="#xml/authenticator"/>
</service>
<!-- -->
<!-- Sync Adapter -->
<service
android:name="com.example.awsomeapp.service.sync.CSyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="#xml/sync_adapter" />
</service>
<!-- -->
<!-- Content Provider -->
<provider android:authorities="${valueContentAuthority}"
android:exported="false"
android:name="com.example.awsomeapp.database.contentprovider.CProvider">
</provider>
<!-- -->
</application>
</manifest>
Code:
public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
Based on the sample by #ChristianMelchior, here's my solution, which fixes two issues in the previous solutions:
solutions that change values.xml in the build directory cause a full rebuild of resources (including aapt of all drawables)
for an unknown reason, IntelliJ (and probably Android Studio) do not reliably process the resources, causing the build to contain un-replaced .res-auto provider authorities
This new solution does things more the Gradle way by creating a new task and allows for incremental builds by defining input and output files.
create a file (in the example I put it in a variants directory), formatted like a resource xml file, which contains string resources. These will be merged into the app's resources, and any occurrence of .res-auto in the values will be replaced with the variant's package name, for example <string name="search_provider">.res-auto.MySearchProvider</string>
add the build_extras.gradle file from this gist to your project and reference it from the main build.gradle by adding apply from: './build_extras.gradle' somewhere above the android block
make sure you set a default package name by adding it to the android.defaultConfig block of build.gradle
in AndroidManifest.xml and other configuration files (such as xml/searchable.xml for auto-completion search providers), reference the provider (for example #string/search_provider)
if you need to get the same name, you can use the BuildConfig.PACKAGE_NAME variable, for example BuildConfig.PACKAGE_NAME + ".MySearchProvider"
https://gist.github.com/paour/9189462
Update: this method only works on Android 2.2.1 and later. For earlier platforms, see this answer, which has its own set of problems, since the new manifest merger is still very rough around the edges…
I've written a blogpost with Github sample project that tackles this problem (and other similar problems) in a slightly different way than Cyril's.
http://brad-android.blogspot.com/2013/08/android-gradle-building-unique-build.html
Unfortunately, the current version (0.4.1) of the android plugin doesn't seem to provide a good solution for this. I haven't had time to try this yet, but a possible workaround for this problem would be to use a string resource #string/provider_authority, and use that in the manifest: android:authority="#string/provider_authority". You then have a res/values/provider.xml in the res folder of each build type that should override the authority, in your case this would be src/debug/res
I've looked into generating the xml file on the fly, but again, there doesn't seem to be any good hooks for it in the current version of the plugin. I'd recommend putting in a feature request though, I can imagine more people will run into this same issue.
The answer in this post works for me.
http://www.kevinrschultz.com/blog/2014/03/23/using-android-content-providers-with-multiple-package-names/
I use 3 different flavours so I create 3 manifest with content provider in each flavour as kevinrschultz said:
productFlavors {
free {
packageName "your.package.name.free"
}
paid {
packageName "your.package.name.paid"
}
other {
packageName "your.package.name.other"
}
}
Your main Manifest not include providers:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Permissions -->
<application>
<!-- Nothing about Content Providers at all -->
<!-- Activities -->
...
<!-- Services -->
...
</application>
And your manifest in your each flavour including provider.
Free:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.free"
android:exported="false" >
</provider>
</application>
</manifest>
Paid:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.paid"
android:exported="false" >
</provider>
</application>
</manifest>
Other:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.other"
android:exported="false" >
</provider>
</application>
</manifest>
Why not just add this?
type.packageNameSuffix = ".$type.name"
My solution is to use placeholder replacement in AndroidManifest.xml. It also handles 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.

Categories

Resources