androidandroid-gradle-pluginandroid-productflavorsandroid-build-typeandroid-variants

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


Solution

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