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.
Related
I am trying to integrate Braze into my application for push notification. Braze need us to create a braze.xml file inside src/main/res/values where we add the API key and other braze related stuff(here is the documentation).
Now I need to differentiate prod and qa environment meaning they will have 2 different API keys.
I was wondering how I could use a different braze.xml for different flavours.
I found this:
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['test']
}
}
}
and I was wondering how to use it to replace my braze.xml for different build variants.
You can create multiple source sets for different flavours of your project. By default there is only main/ source set created by studio which contains the common code that will be shared across different variants. For more details on how to create and maintain the source sets check official documentation.
EDIT - 1
To elaborate more you can create multiple flavor of your project by using the build.gradle (module level file) and specifying flavors like -
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
sit {
initWith debug
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Once you have created your flavors Gradle Sync your project.
Now you can create the braze.xml fike for each flavor by right click Values folder >> New >> Values Resource File. Type the name of file example braze.xml and under Source Set select name of flavor for which you want to create this file. As shown in the image name below
You can repeat this step until all the flavors are covered then whenever you switch the gradle flavor from the build variant then IDE will automatically start using the designated file for that flavor.
I have 3 mode of build types- debug, stage, release.
FCM, Firebase analytics had been integrated on my app.
My goal is to link different Firebase projects in each mode of my build.
As an example, if I build my app in stage mode, then Firebase project project_stage should be linked to my app, so all firebase settings should be worked with project_stage.
If I do some action, then analytics will be logged to project_stage, and I would need to get notification from project_stage.
Same for project_live and project_debug.
I have tried several ways but didn't work for me.
Pass the desired name as buildConfigField FIREBASE_APP_NAME or R.string.firebase_app_name:
android {
// default configuration, don't touch.
defaultConfig {
buildConfigField "String", "FIREBASE_APP_NAME", "\"default\""
resValue("string", "firebase_app_name", "default")
}
buildTypes {
debug {}
staging {}
release {}
}
sourceSets {
main {} // default source-set
foo {}
bar {}
}
productFlavors {
foo {
buildConfigField "String", "FIREBASE_APP_NAME", "\"foo\""
resValue("string", "firebase_app_name", "foo")
}
bar {
buildConfigField "String", "FIREBASE_APP_NAME", "\"bar\""
resValue("string", "firebase_app_name", "bar")
}
}
}
One can likely either configure productFlavors or buildTypes, unless combining app names. However, then one can instance FirebaseApp with the name inside the application package:
FirebaseApp.getInstance(BuildConfig.FIREBASE_APP_NAME);
Or with string resource:
FirebaseApp.getInstance(getString(R.string.firebase_app_name));
There's also an android.environments Gradle plugin for generating these buildConfigField.
Likewise, one could just use a string resource in the proper res directory. When configuring productFlavors and sourceSets with the same names (and obviously also the corresponding directories & files), one can configure all the rest with string resources, instead of Android DSL.
Gradle sourceSets permit for more than just alternate string resources ...
I am working on making a large project modular. I have separated the code and layout files and everything is working fine except one issue.
I needed to create 3 build types in the module namely, beta, stage and live (for a reason mentioned later). Now there is one thing I couldn't understand.
When running from Android Studio directly I can select which build variant of the library module to use while building. but
When generating a signed apk, it doesn't ask for the build type of the library.
There must be some
defaults which could be set in order to configure this. I am looking for those config options which can be used
and
Which build type of the library is by default selected when generating signed apk ?
The reason I created build variants within the library module is that I am using Content Provider in the module and I was CONTENT_PROVIDER_AUTHORITY_CONFLICT error on installing multiple build types simultaneously in the same device.
So in order to install beta, stage and live builds simultaneously I added the Content Authority as a string resource in the build.gradle
apply plugin: 'com.android.library'
android {
compileSdkVersion Integer.parseInt(project.COMPILE_SDK_VERSION)
buildToolsVersion project.BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(project.MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(project.TARGET_SDK_VERSION)
vectorDrawables.useSupportLibrary true
}
buildTypes {
beta {
minifyEnabled false
resValue("string", "account_type", "com.****.****.dev")
resValue("string", "_authority", "com.****.****.dev.syncadapter.finance")
buildConfigField "String", "FinanceContentProvider", "\"com.****.****.dev.syncadapter.finance\""
}
staging {
minifyEnabled false
resValue("string", "account_type", "com.****.****.stage")
resValue("string", "_authority", "com.****.****.stage.syncadapter.finance")
buildConfigField "String", "FinanceContentProvider", "\"com.****.****.stage.syncadapter.finance\""
}
live {
minifyEnabled false
resValue("string", "account_type", "com.****.****.live")
resValue("string", "_authority", "com.****.****.live.syncadapter.finance")
buildConfigField "String", "FinanceContentProvider", "\"com.****.****.syncadapter.finance\""
You need to use this below build types:
flavorDimensions 'tier'
productFlavors {
beta {
buildConfigField("Your_field", "Data", "Data")
}
staging {
buildConfigField("Your_field", "Data", "Data")
}
live {
buildConfigField("Your_field", "Data", "Data")
}
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "AppName_${variant.productFlavors[0].name}-${buildType.name}-${defaultConfig.versionCode}.apk"
}
}
}
Hope this helps.
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
}
}
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.