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
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>