Build android with gradle, replace string each product flavor - android

Before i build android project to two different application paid and free.
I changed each values and strings so yesterday I made a big mistake.
So, I'm laerning how to use gradle to build my app.
My app have some differents.
app name (just add suffix '-Free') -> values/string.xml
change flag in my *.java
// signingConfigs is ommited.
productFlavors{
free{
packageName "my.app.free"
versionCode 20
signingConfig signingConfigs.freeConfing
copy{
from('/res'){
include '**/*.xml'
}
into 'build/res/'
filter{
String line -> line.replaceAll("android:label=\"#string/app_name\"", "android:label=\"#string/app_name_free\"")
}
}
copy{
from('/src'){
include '**/*.java'
}
into 'build/src/'
filter{
String line -> line.replaceAll("public static final Boolean IS_FULL_VER = true;", "public static final Boolean IS_FULL_VER = false;")
}
}
}
paid{
packageName "my.app.paid"
versionCode 20
signingConfig signingConfigs.paidConfing
}
}
but, built app changed nothing at all.
What i missed?

See the documentation on product flavors:
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-flavors
In your build.gradle, in each flavor, you can define flags to be generated in your BuildConfig.java file:
productFlavors {
free {
packageName "com.company.appfree"
buildConfig "public final static com.company.common.MonetizationType monetizationType = com.company.common.MonetizationType.FREE;"
}
paid {
packageName "com.company.apppaid"
buildConfig "public final static com.company.common.MonetizationType monetizationType = com.company.common.MonetizationType.PAID;"
}
}
This example uses an enum (that you need to define somewhere in your java code):
public enum MonetizationType {
PAID, FREE
}
You can now use this anywhere like this:
if (BuildConfig.monetizationType == MonetizationType.FREE) { ... }
For overriding resources, you can create different resource files in the source folders for each flavor:
Use the following structure
app/build.gradle
app/ [.. some other files...]
app/src/main/
app/src/main/java
app/src/main/res
app/src/main/assets
app/src/main/AndroidManifest.xml
app/src/free/res/values/apptitle.xml
app/src/paid/res/values/apptitle.xml
apptitle.xml would be a string resource file (just like strings.xml), but with only one string: the one you want to be different depending on the flavor.
(You don't need have a apptitle.xml in your main/res directory).
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_title">App Title (or whatever you want)</string>
</resources>
You might be able to override strings in different ways, but I like to keep the overridden strings separate from the rest for clarity.

The accepted answer is not working with the newer versions of Gradle.
You need to replace buildConfig with buildConfigField to get the same result:
productFlavors {
free {
packageName "com.company.appfree"
buildConfigField "com.company.common.MonetizationType", "MONETIZATION_TYPE", "company.common.MonetizationType.FREE"
}
paid {
packageName "com.company.apppaid"
buildConfigField "com.company.common.MonetizationType", "MONETIZATION_TYPE", "company.common.MonetizationType.PAID"
}
}

treesAreEverywhere's answer (as well as user name) is right on. But it's also very valuable to know that Android Studio will greatly simplify the process of creating build flavor or attribute specific resource files.
In the AS project window, right click on the res/values folder and select New > Values resource file. Then name it (e.g., "strings"), select the Source set if not the default, and select any desired qualifiers (e.g., Screen Width = 800). This is the easiest way to make sure you're putting your resource overrides where the compiler wants them.

This is how I did in my project. I created multiple build types instead of flavours. This solution will add a prefix to your package name based on what build type you are trying to assemble. For ex, for dev the package name will be com.sample.myapp.dev and similarly for beta package name will be changed to com.sample.myapp.release. You can tweak it to get Free and Paid prefixes. Hope it would help.
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.production
applicationIdSuffix '.release'
versionNameSuffix '-RELEASE'
}
dev {
signingConfig signingConfigs.debug
applicationIdSuffix '.dev'
versionNameSuffix '-DEV'
}
beta {
signingConfig signingConfigs.debug
applicationIdSuffix '.beta'
versionNameSuffix '-BETA'
}
debug {
signingConfig signingConfigs.debug
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
debuggable true
}
}

Related

How to set up different Constants in Kotlin for environment

We have an App in Kotlin ( Android Stdio) which has different constants by environment.
We are using Constants.kt
const val IMAGES_API = "https://localhost:3000/v1/images"
and we want to use the same variable in staging/qa/prod.
The App is building in Kotlin and we are using gradle (groovy scripts) to compiling and packing the different environment staging/qa/prod.
My first approach has been to create this properties on the gradle.properties and load the properties on the build.gradle file like this :
def loadProperties() {
def props = new Properties()
file("gradle.properties").withInputStream { props.load(it) }
def config = props
project.ext.config = config
}
And when I run gradle I can see the new properties, but I don't know how to get this value inside the App ( in the kotlin code).
My only idea is to create a task on build.gradle to copy a Constants.kt file by environment. But, I don't think, it's a good practice. I think, there must be another way to set different variables in the App.
Please, can anybody help me with this?
What you want is to configure build types in your app module's gradle file with buildConfigField in each:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
debug {
applicationIdSuffix ".debug"
debuggable true
buildConfigField "String", "SERVER_URL", '"http://test.this-is-so-fake.com"'
}
/**
* The `initWith` property allows you to copy configurations from other build types,
* then configure only the settings you want to change. This one copies the debug build
* type, and then changes the application ID.
*/
staging {
initWith debug
applicationIdSuffix ".debugStaging"
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
}
In code, you can refer to BuildConfig.SERVER_URL, and it will be populated with the string based on the build type you choose at compile time.
You can build different apk/app bundles to distribute.
Referencing this answer .
EDIT As an aside, in real life I have found this approach to be... annoying. It is easier to bundle inside the app a toggle that allows QA to switch between environments. This way you only have one bundle to deal with.

Different string.xml files according to app Build Variants

I want to assign a value to a string in string.xml different values depending on the Build Variant/buildType. I imagine something like this:
res/values-debug/string.xml
<string name="my_string">some debug value</string>
res/values-release/string.xml
<string name="my_string">some release value</string>
but I don't see anything like this out there.
Is this possible?
It possible via your build.gradle file
buildTypes {
release {
resValue "string", "my_string", "some release value"
}
debug {
resValue "string", "my_string", "some debug value"
}
}
Then you can just use this value like #string/my_string where you want
Yes it's possible!
In your build.gradle you can add something like this:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.release
}
debug {
applicationIdSuffix ".debug"
minifyEnabled false
debuggable true
signingConfig signingConfigs.release
}
}
Then in your src folder in your project you should have a folder called main, add one next to it called debug. Den as long as you are building your debug flavour any resources in your debug folder will replace those in main which is the release folder.
Should look like this:
src/
main/
java/ -- all your java code
res/
...
values/
strings.xml
debug/
res/
...
values/
strings.xml
EDIT: The approaches from the two other answers works fine as well. But if you have a lot of strings keeping them as xml might be easier to handle.
resValue 'string', '<string_name>', "some string"
define different ones in your build.gradle for different build variants/product flavors
You can do it directly from Android Studio. For example, if you need a different app name for your "staging" flavor, you can (Android Studio v3.5):
Right click on values folder -> New -> Values resource file
Select "staging" in the source set menu
specify strings.xml as filename
At this point Android Studio generates an additional strings.xml file for your particular build variant. Edit the created file with your "staging" app name (E.g. MyAppName - Staging)
build.gradle(app)
productFlavors {
stage {
applicationIdSuffix ".staging"
buildConfigField 'String', 'SITE_URL', '"[staging_link_here]"'
}
prod {
buildConfigField 'String', 'SITE_URL', '"[production_link_here]"'
}
}

Is it possible to have one proguard mappings.txt file for multiple flavor?

I would like to publish my app to multiple market. the version code should be same in all market and my app has IAP so i should have multiple flavor and one manifest per flavor.
And also i need the mapping file for the crash report(for example in tracepot).
My problem is: How can i have one mappings.txt file for multiple flavor?
build.gradle:
android {
productFlavors {
Market1 {
...
}
Market2 {
....
}
signingConfigs {
debug {
....
}
release {
....
}
}
buildTypes {
debug {
debuggable true
signingConfig signingConfigs.debug
}
release {
debuggable false
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Yes, the current settings in your build.gradle will apply the proguard file to all release builds of all flavors. You already have it there.
Update after the conversation with OP in comments below:
Now i understood the question better. You are trying to have a single mapping.txt file and are not talking about the proguard-rules.pro. The answer is No, you cannot have a single file for multiple generated apks. Proguard rules are applied to each flavor separately and each time it generates a separate mapping file. There might not be a any differences in your mapping.txt files right now because you may not be using different sourceSets (Java classes). But, if you have different sourceSets, the mappings.txt file will not be the same. Probably you have only minimal difference between your flavors right now like different drawables with same name, If i'm assuming right.

using getBuildConfigFields().getAt() in build.gradle returns null

In my android build.gradle file, I want to use a build config field value that is set in each flavor for my package name suffix.
Say my package is "com.my.app", and my config field's name is "myConfigFieldName".
I try to do:
applicationIdSuffix "." + getBuildConfigFields().getAt("myConfigFieldName")
And after running the build, my package is "com.my.app.null", when I expected something like "com.my.app.flavorA"
Is there a way to achieve what I'm trying to do?
OK, so this is a way to append the flavor name onto the app package.
I also check that the release type is release, so I will be able to switch between flavors during dev without installing more and more apps
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
//append flavor name to the package name in release builds
applicationVariants.all { variant ->
if(variant.buildType.name.equals('release')) {
variant.productFlavors.each() { flavor ->
variant.mergedFlavor.applicationId = variant.mergedFlavor.applicationId + '.' + flavor.name
}
}
}
}

Different API Host with gradle and android

I was wondering if it is possible to provide a different API Host per build using gradle. Ideally I would like to access the constant through my code the same so when I do a gradle build, it builds the release.apk to point to http://example.com and the debug.apk to point to http://debug.example.com.
I have achieved this using the following:
buildTypes {
debug {
buildConfig "public final static String API_HOST = \"http://debug.example.com\";"
}
release {
buildConfig "public final static String API_HOST = \"https://example.com\";"
}
}
However that seems pretty dirty
Cheers
I think a better alternative with today's Gradle features is to specify both productFlavors and buildTypes (example below).
The buildTypes control what certificate I sign with, and whether Proguard is run.
The productFlavors control the intended environment, which includes custom resources, as well as a different package name so I can install them both side by side.
Then I configure my server address in strings.xml for each variant and load it at runtime.
src/dev/res/values/strings.xml
src/staging/res/values/strings.xml
strings.xml example from the "dev" variant:
<string name="config_url">http://com.example.debug</string>
build.gradle snippet:
productFlavors {
dev {
packageName "com.example.dev"
}
staging {
packageName "com.example.staging"
}
}
buildTypes {
debug {
versionNameSuffix " debug"
signingConfig signingConfigs.debug
}
release {
// A release build runs Proguard, and signs with a release certificate
zipAlign true
runProguard true
proguardFile 'proguard-project.txt'
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
signingConfig signingConfigs.release
}
}
An alternative is to use buildConfigField:
buildTypes {
debug {
buildConfigField 'String', 'API_HOST', '"https://debug.example.com"'
}
release {
buildConfigField 'String', 'API_HOST', '"https://example.com"'
}
}
You would then refer to it in your code via BuildConfig.API_HOST
So I ended up speaking to one of the gradleware engineers about this.... My initial solution is the correct way. Google/Gradle will be improving this in the future.
To add multiple values you separate the the strings with comas.

Categories

Resources