Conditional AndroidManifest attribute based on build profile? - android

I'd like to release a demo and a full version of my app.
How can I:
define different build profiles so that if I build the apk I get two apps, one full and one for demo?
Change the <application android:label="#string/app_name">? Eg for full version I want to use #String/app_name, but for demo build profile use app_name_demo? The app names are also localized!
Do I somehow have to work with the gradle productFlavors part?
Like:
productFlavors {
demo {
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
}
But I could not find how to achieve my points above.

Edit
go with productFlavors
productFlavors {
demo {
applicationIdSuffix ".demo"
versionNameSuffix "-Demo"
}
release {
minifyEnabled true
shrinkResources true
debuggable false
}
}
Create two folder under /app/src
1. demo
2. release
place constant.xml file and put your application label there accordingly. Based on your build type your application name would be picked up.
also inside each product flavour folder you can create the same directory structure as you would do with app main folder.
So your final structure would go like
/demo/res/values/constant.xml
/demo/res/values-fr/constant.xml
something like this.

Related

How to Deploy a New Version of an Application to Run Side-by-side with an Existing Version?

I need the current version and the previous version of my app to be on the device at the same time. How can this be done?
Currently the next version just rewrites the previous version.
Thanks
In general it is not possible to run two application with same package name at same time. But you can do it with different package name.
You can use productFlavors with applicationIdSuffix to create different package name for same application in different flavors.
android {
...
defaultConfig {...}
// Specifies one flavor dimension.
flavorDimensions "version"
productFlavors {
prod {
dimension "version"
applicationIdSuffix ".prod"
versionNameSuffix "-prod"
}
dev {
dimension "version"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
}
}
Above configuration create two flavors named prod and dev with applicationId suffixed prod and dev respectively. So, you can run your old version in prod mode and new one with dev mode. Hope this will help you.
In Android Studio use View -> Tool Windows -> Build Variants to change flavor
I think that you can not do this since every application has a unique identifier which is called "package name" (com.example.appname)
But what you can do is to have 2 Emulators and run different per device

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

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

Config for specific gradle flavor combination

I am wondering if it is possible to supply files or configuration settings for a specific flavor group combination. For example, I have 2 flavor groups, with 2 different flavors each:
Flavor Group: Version
v2
v3
Flavor Group: Type
free
full
When ordered "Type", "Version" I am able to build 4 different flavors of my app:
FreeV2
FreeV3
FullV2
FullV3
And my source tree looks like this:
/src
/free
/res
/full
/res
/v2
/java
/res
AndroidManifest.xml (features and permissions for v2 flavors)
/v3
/java
/res
AndroidManifest.xml (features and permissions for v3 flavors)
This is exactly what I want, and works very well for my project. However, I wish I could provide files for a specific flavor group combination. For example, I want to provide different AndroidManifests for FullV3 and FullV2. I don't think this is possible, or? For example:
/src
/free
/res
/full
/res
/v2
/java
/res
AndroidManifest.xml (features and permissions for v2 flavors)
/v3
/java
/res
AndroidManifest.xml (features and permissions for v3 flavors)
/fullv3
AndroidManifest.xml (features and permissions for full v3 only!)
/fullv2
AndroidManifest.xml (features and permissions for full v2 only!)
I would be nice to be able to do this in the gradle build file as well:
productFlavors {
free {
packageName ...
flavorGroup "type"
}
full {
packageName ...
flavorGroup "type"
}
v2 {
packageName ...
flavorGroup "version"
}
v3 {
packageName ...
flavorGroup "version"
}
fullv2 {
... <-- specifically for the full-v2 combination
}
fullv3 {
... <-- specifically for the full-v3 combination
}
}
Note: one solution would be to only have 1 flavor group and define the 4 flavors explicitly:
freeV2
freeV3
fullV2
fullV3
However, this is not a viable solution for me since I would have to duplicate all the version specific code for the free and full flavors of each version.
This functionality was added in version 0.7.0 of the Android plugin for Gradle
http://tools.android.com/tech-docs/new-build-system <--As of 1/6/14, this link is active
You can now have a variant specific source folder if you have flavors.
Only for app (not library or test). Name is src/flavorDebug/... or src/flavor1Flavor2Debug/
Note the camelcase naming, with lower case for first letter.
Its components (res, manifest, etc...) have higher priority than components from build type or flavors.
There is also a "flavor combination" source folder available when more than one flavor dimension is used. For instance src/flavor1Flavor2/
Note that this is for all combinations of all dimensions.
Its priority is higher than single-flavor sourcesets, but lower than build-types.
Update 1/30/2014
IntelliJ IDEA v13.0.2 (Build 133.696) now supports Android Gradle Plugin changes made in 0.7.0
Files will be merged when the flavors come together. So if you have a manifest in full and in v3, they will be combined when fullV3 is generated. This way you can get the combo generated.
As per the question asked you want different AndroidManifest file for Full version of both v2 and v3. So instead of going with flavors for full and free, you can use combination of flavors and buildTypes as per your convenience.
For example
I know full and free should be product flavors not build types but your requirement says that full and free are your buildTypes and v2 and v3 are your app flavors
buildTypes {
debug {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
full{
runProguard false/true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.prodSigning // write your signingConfig
}
free{
runProguard false/true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.prodSigning // write your signingConfig
}
}
productFlavors{
v2 {
//required configuration for full version
}
v3 {
//required configuration for full version
}
}
Now your project structure should look like this .
--src
---main
----java
----res
----Manifest
---full
----java
----res
----Manifest(Manifest you want for Full Version)
---free
----java
----res
----Manifest (Manifest you want for Free Version)
---v2
----java
----res
----Manifest
---v3
----java
----res
----Manifest
After Sync with Gradle this will give combinations of build types in build variant tab in left pane in AS and will create different tasks for the same.
Go to Build Variant and generate the build from drop down according to your choice.
It will give you Build Variants like this
To set configuration for each flavor combination in your build file you could use the solution I proposed here: https://stackoverflow.com/a/26585241
You can then use and variantFilter to iterate through all flavor combinations and match on the name of each combination (just like the name used when creating "flavor combination" source folders where you can place specific resources as mentioned in the answer from #abest):
android.variantFilter { variant ->
def flavorString = ""
def flavors = variant.getFlavors()
for (int i = 0; i < flavors.size(); i++) {
flavorString += flavors[i].name;
}
if(flavorString.equalsIgnoreCase("fullv2")) {
variant.getDefaultConfig().applicationId "com.example.fullv2"
}

Categories

Resources