Android Flavors, ContentProviders, SyncAdapters and AccountManager - android

I have defined a few flavors and buildTypes for my android app
productFlavors {
X {
applicationId = "com.x.xxxx"
}
Y {
applicationId = "com.y.yyyy"
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard_rules.txt'
}
debug {
applicationIdSuffix ".debug"
}
}
That does the job of creating different flavors/buildType combinations.
Now to make sure that ContentProviders works and I do not get a Failure [INSTALL_FAILED_CONFLICTING_PROVIDER] error, I have changed the android:authorities="${applicationId}" and AUTHORITY references in code to BuildConfig.APPLICATION_ID.
That makes the ContentProvider work fine i.e it does not throw an error when I install two different types on the same phone. It lets me install them.
Next step is where the error is. I use SyncAdapter and AccountManager to control a few things. I have modified the ACCOUNT_TYPE to use the APPLICATION_ID. Because of the issue with gradle not being able to use ${applicationId} in non-manifest xml resource files, I have created a different directory structure for each flavor and copied the syncadapter.xml and authenticator.xml with corresponding authority and account type strings.
I have also tried the technique described in this blog - http://blog.jensdriller.com/android-gradle-workaround-for-missing-placeholder-support-in-resource-files/
And the top voted answers on Using build types in Gradle to run same app that uses ContentProvider on one device but things don't seem to work.
Anyone been able to get this to work recently?

Related

Google Play upload problem: "Your Android App Bundle needs to have the package name com.x.x.base"

I am trying to upload an application under development to Google Play Console, internal testing track. The application has two flavor dimensions and and two dynamic features, the last two being resources only (no code). I am relying on Android Studio to generate the directory structure and the signed bundle.
The name of the package is com.something.something. The upload fails with the message "Your APK or Android App Bundle needs to have the package name com.something.something.base."
I cannot track down the source of the problem, though it looks like it should have something to do with the flavor dimensions. On the other hand, I had no problem uploading a single apk, without the dynamic features.
I am not sure which part of the code is relevant here, which is probably part of my problem, but my main build gradle looks like this
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId 'com.something.something'
....
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
//testCoverageEnabled false
}
}
flavorDimensions "version"
productFlavors {
small {
dimension "version"
}
full {
dimension "version"
}
}
dynamicFeatures = [":feat1", ":feat2"]
}
dependencies {
...
}
I do not want to burden the question with irrelevant code, but can provide more if there is some intuition about where the problem might be.
I would be grateful for any suggestion on how to approach the debugging here. (Uploading is painfully slow, trial and error is not much of an option.)
Thank you all for taking a shot at the answer. Here is what I learned about this issue and how I "solved" it.
My problem starts with Google Play Console (GPC) insisting that an app name be associated with the very first package name you have uploaded. This is in addition to having one app = one package name rule. If you are still in the draft stage, you can delete the package from your "All applications" list in GPC, and upload a package with different name, but once it's published - no such luck. The way GPC is designed, all my future uploads should have the suffix ".base", if this is what my first upload had.
Next, Android Studio (AS), does something called manifest file merging. (#Fantômas, your audience may not be as omniscient as you are - the behavior of AS is relevant here, and with your permission I would return the tag.) When you choose the flavor for the bundle, the name of the flavor is suffixed to create the package name in the merged AndroidManifest file, irrespective of the name you specify in your main AndroidManifest.xml, as you can check if you choose to "analyze" (the name of the link after the bundle is generated) newly created bundle:
Thus, in my first upload I had a flavor called base, and I did not realize that AS tacked it as the suffix on the name of my package. From that point on, GPC will not take a package for my app by any other name.
One thing that is irrelevant here are dynamic features - they just happened to be part of my second upload attempt.
I have asked Google to delete my app so I can start from scratch.
Since your problem is not related to codes, try changing your package name to something else, something more "unique".
com.myname.myappname is an example. In this case you need to change everything related to your package name. Try it and report back
You do not need to delete your app from Google console. Simply open Build.gradle(Module:app) in your android studio and change the Application Id to the name google is requesting from you.
You will need to change your Application Id in your Build.Gradle(Module: app)
You may also need to change the name in your google-services.json file under this section:
"client_info": {
"mobilesdk_app_id": "...",
"android_client_info": {
"package_name": "com.yourpackagename.yourpackagename"
}
good coding!!
You can avoid "Manifest file merging" (of Android Studio...) to altering the package name of the "merged manifest" in the build variant, by excluding the applicationIdSuffix definition on the build type or flavour dimmension definition in the build.gradle (app: module), like in my following example, in what I want to avoid to adding the respective package name suffixes ".release" and ".full" in my "release" and/or "full" versions of my App:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// applicationIdSuffix ".release"
versionNameSuffix "-release"
}
debug {
proguardFiles 'proguard-project.txt'
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
}
}
// Specifies one flavor dimension.
flavorDimensions "version"
productFlavors {
free {
dimension "version"
applicationIdSuffix ".free"
versionNameSuffix "-free"
}
full {
dimension "version"
// applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
Regards,
P.D.:
And thanks a lot for all the other answers, that help me in this moment to take this my decission of doing the above posted... Thanks, especially for #celaeno, that explained how works the "Manifest merging" in Android Studio, and over all, advertised how App Id SUFFIXES may CHANGE the App.Id in Google Play Console terms...
change the applicationId in android/app/build.gradle to the same id of the previos versions.
defaultConfig {
applicationId "com.xxxxxxx.xxxx"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 12
versionName "3.2.0"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
targetSdkVersion 30
missingDimensionStrategy 'react-native-camera', 'general'
}

Android Studio + Gradle to create different configurations

I am using Android Studio and Gradle to build Android applications. I would like to have different strings within the Java code based on which type of build it is (debug vs. release). What is the best way to do this?
For example - I want to have different URLs if I am in debug or release. Also, I want to specify different GUIDs and other keys / strings.
The obvious hacky way to do this is to do a search and replace of a string in AndroidManifest.xml or worse yet, in a Java file. This approach seems error prone and hacky to me - is there a better way to do this?
There are many ways you can do this, although I usually do
android {
buildTypes {
release {
buildConfigField("String", "URL", "your_url_on_release")
}
debug {
buildConfigField("String", "URL", "your_url_on_debug")
}
}
}
You then can access them on your java code by using:
BuildConfig.URL
You can test this using Android Studio Build Variants, by changing your application variant to debug or release ( e.g. http://prntscr.com/8waxkw)
You have many solutions to do this, here's a simple case:
buildTypes {
debug { ... }
release { ... }
}
productFlavors {
staging { ... }
production { ... }
}
build types are for build management proguarding, debugging, signing, etc.
productFlavors are for all app internal configuration.
If you want to add resources related to the flavours you can create and add to src/(flavor_name)/res/values/ folder your urls.xml config file.
With this, in android studio, you'll directly see, all the builds variants in the corresponding window and the right urls.xml file associated to the current context and leave the gradle config clean.
Of course, this method works also for any resource you would need in your app.
For more detail, you can read this : http://developer.android.com/tools/building/configuring-gradle.html#workBuildVariants
I would do it with product flavors as explained in this post.

Application name as gradle parameter

I want to do something like that: gradle build DemoApp, and have DemoApp.apk with application name "DemoApp" as output.
Also, can I change application icon if with the same flow?
And some inner parameters?
Product flavors - not a secret for me. I do not need different configurations, I need to change application name at the build time. For example, I have some Rest API, that allows to pass parameter and return apk with application named as that parameter. The same with icon and other.
Checkout Manifest Merger specifically placeholders. Below is an example of setting the app name in gradle file.
// AndroidManifest.xml
<application
android:label="${applicationName}"
// build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: "MobileWorkforce"]
}
The other option is to use #string/app_name and define different string.xml files based upon buildType/flavor.
Given that you don't know how to use flavours properly, a mix of other solutions.
AndroidManifest.xml
<application
android:label="${applicationName}"
MyHttpThing.java
callserver(BuildConfig.ENDPOINT + "/api/v3/", "stuff")
build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: myappname]
buildConfigField "String", "ENDPOINT", myendpoint
}
and call with
gradle -Pmyappname=namy_name_name -Pmyendpoint="http://google.com" build DemoApp
Looks like you're looking for Product Flavors, they're exactly for having multiple customized versions of the app with shared codebase. You can customize the icon, as long as any other resource. And yes, the .apk will be named according to the name of the flavor.

How to change app-id but keep the same resource-id?

In an Android project, the resource ids are fully identified by the application id. For example, if my appid is com.mycompany.myapp, the resource id would be com.mycompany.myapp.R.blah.
In my case, I need to create two versions of the app - beta version and release version. Both the versions may be installed on the same device. This can happen only if the appids are not the same. My strategy is, during the nightly build, I will programmatically modify the manifest file and change the appid to com.mycompany.myappbeta. However, if I do this, I would need to touch a number of source files that are using the resource ids.
I am wondering if there is some token in the manifest file where I can explicitly say how the resource ids be qualified? Regards.
Edit
It turns out app id and package id are indeed two different concepts. I wanted to change the app-id but not the package-id. However, it seems this is not possible under Eclipse. As other posts have pointed out, Gradle build can handle changing the app-id but retaining the package id. I am moving over to Android Studio now.
the resource ids are fully identified by the application id
Technically, they are identified by the package name, from the package attribute in the root manifest.
My strategy is, during the nightly build, I will programmatically modify the manifest file and change the appid to com.mycompany.myappbeta. However, if I do this, I would need to touch a number of source files that are using the resource ids.
Which is why your nightly build should be using Gradle and the Gradle for Android plugin. Then, you skip all of what you described, and instead use build types. Two build types (debug and release) are pre-defined, and if you want to invent another one (e.g., beta), you can. Then, in the build type configuration in your build.gradle file, you use applicationIdSuffix to give non-release builds a distinct suffix. That will be added to the application ID for the purposes of unique installations, but your package name is unaffected, so your resources are unaffected.
For example:
android {
compileSdkVersion 19
buildToolsVersion "21.1.2"
defaultConfig {
versionCode 2
versionName "1.1"
minSdkVersion 14
targetSdkVersion 18
}
signingConfigs {
release {
storeFile file('HelloConfig.keystore')
keyAlias 'HelloConfig'
storePassword 'laser.yams.heady.testy'
keyPassword 'fw.stabs.steady.wool'
}
}
buildTypes {
debug {
applicationIdSuffix ".d"
}
release {
signingConfig signingConfigs.release
}
beta.initWith(buildTypes.release)
beta {
applicationIdSuffix ".beta"
debuggable true
}
}
}
Here I:
Give the debug build type an application ID suffix of .d
Leave the release build type along from the standpoint of an application ID suffix
Create a new beta build type, cloned from the release build type, where I give it a .beta application ID suffix and mark it as debuggable
However, if I do this, I would need to touch a number of source files that are using the resource ids.
No, you will not. You just need to change package id in your Manifest file only, ensuring however all services and activities listed in manifest file are using full class path, i.e.:
android:name="com.mycompany.myapp.MainActivity"
not just shortened notation:
android:name=".MainActivity"
as this make your app not working when package Id will not match with your code packages.

How do you manage multiple environments while developing Android apps?

We're building an Android app that connects to the cloud. We have a test URL for our APIs and a production URL. We connect the app to our local development machines to talk to the database when developing but find ourselves modifying a global API URL to the production URL every time we generate an APk for the Play Store.
Is there a better way to manage environments for Android? Can we also have two versions of the app (development version) and the Play Store version? I am not able to have two versions as both the apps have the same signature. How do we best manage this?
With android studio and gradle its simple now.
inside your app build.gradle edit signing configs
signingConfigs {
debug {
storeFile file("debug.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
prod {
storeFile file("prod.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
dev {
storeFile file("dev.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
}
add buildTypes
buildTypes {
debug {
buildConfigField 'String', 'BASE_URL', '"http://127.0.0.1:8080/"'
......
signingConfig signingConfigs.debug
}
prod {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL', '"http://prod.example.com"'
......
signingConfig signingConfigs.prod
}
dev {
buildConfigField 'String', 'BASE_URL', '"http://dev.example.com"'
......
signingConfig signingConfigs.dev
}
}
In your code take base url configured in gradle file by this code.
public final static String BASE_URL = BuildConfig.BASE_URL;
You can also put different KEY or whatever which is build type specific in gradle file and in code it will take according to the build type you are running.
Its even possible to have different package name.
productFlavors {
my_prod {
applicationId "com.example.packtwo"
}
my_dev {
applicationId "com.example.packone"
}
}
In recent gradle config, there are some updates in specifying package name. You have to add flavourDimensions if using productFlavours. See below code with added flavourDimensions
flavorDimensions "pack"
productFlavors {
flavor_dev {
applicationId 'com.example.packtwo'
dimension "pack"
}
flavor_prod {
applicationId 'com.example.packone'
dimension "pack"
}
}
This will give you more details about product flavours and dimensions
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
Check for more possibilities...
But if you are using different flavors you might have to deal with Manifest merging and all.
This is can be achieved using product flavours.
For achieving this requirement:
First of all, Create 2 files under the app folder of your project say development.props and production.props. Or you can add these 2 files in a package under app folder say config.
Basically, these 2 files contain keys and values. This key is same for both files. But their values are different. These files contain one key say “SERVER_URL” and its value. It would be written like this:
SERVER_URL=”Server_url_value”
In this case, only URL is different. So, I have added only one key-value pair in Props file. You can add more.
Then, create ProductFlavours in the app build.gradle file say development and production. Now, access different props files containing URLs in their correseponding flavours like this:
productFlavors {
development {
getProps('./config/development.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
production {
getProps('./config/production.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
}
def getProps(path) {
Properties props = new Properties()
props.load(new FileInputStream(file(path)))
return props
}
Now, For each flavour, there is a build type And this BuildType is added in app build.gradle. For example, Build type is Debug and release. And I have two flavours i.e. development and production. So, gradle task will be created using both flavour and build type like this:
assemble{flavourName}{BuildType}
Now, you need to type these commands only. It would generate required APK with its corresponding URL. Commands are:
./gradlew assembleProductionRelease would generate release build with Production URL.
./gradlew assembleDevelopmentDebug would generate debug build with Development URL.
./gradlew assembleProductionDebug would generate debug build with Production URL.
./gradlew assembleDevelopmentRelease would generate release build with development URL.
Top three gradle task would be very helpful. But the last task would generate Release build with development URL. But this is not recommended. So, we should stop developer to execute this task i.e. ./gradlew assembleDevelopmentRelease
Now To restrict developer to generate release build using Development URL, add this snippet in your app build.gradle file:
android.variantFilter { variant ->
if(variant.buildType.name.equals('release')
&& variant.getFlavors().get(0).name.equals('development')) {
variant.setIgnore(true);
}
}
Now, If we try to execute task i.e. ./gradlew DevelopmentRelease. Gradle would stop generating the build and throw exception and would say: This task assembleDevelopmentRelease is not found in the root project.
Use Ant to build at least the production versions. This way you can set certain config values/flags during building. Let's say you have a config.xml file that contains the URL to the server. You can have different Ant build targets that will change the URL to point to the appropriate server. Check out this tutorial. It explains exactly how that is done.
This I think is considered as the bast practice in case you use android studio with gradle.
You may want to look at this article: http://tulipemoutarde.be/2013/10/06/gradle-build-variants-for-your-android-project.html
Also available in youtube video: https://www.youtube.com/watch?v=7JDEK4wkN5I
This also allows you to have two different package name for the same app.
It uses gradle flavors to achieve exactly what you are looking for and is very easy to implement.
You can try gradle buildType and productFlavor. It will allow you to specifiy different Environment variables like url, versionName, etc And applicationId which will allow you to have dev and prod builds.
For more details http://developer.android.com/tools/building/configuring-gradle.html
I don't know what's the best practice in that case, but I do like this:
You could make your app a LIB and create 2 apps: production app and testing app. Import your lib for those apps and build their manifests (it's almost copy paste of the old one). Then you replace your /res/ files that are different in each app... (you could create a config.xml file that have the URL).

Categories

Resources