Using Build Types in Gradle to Run Same App That Uses Contentprovider on One Device

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

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

Android Gradle: Install all build types on one device

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>

Two flavors of the app on the same device

since you have a content provider in your app, you need to follow this

Android - Set two built types to the same source folder

I was so close. I just needed to add the location of the android manifest file and the assets folder.

sourceSets {
qa {
manifest.srcFile 'src/debug/AndroidManifest.xml'
java.srcDirs = ['src/debug/java']
res.srcDirs = ['src/debug/res']
assets.srcDirs = ['src/debug/assets']
}
}


Related Topics



Leave a reply



Submit