I am generating multiple apk's based on different config files.
So i defined a variable on build.gradle file
def isPRODBuild = false
I want to change this variable in productFlavors scope, so i did following.
productFlavors {
dev {
isPRODBuild= false
}
prod {
isPRODBuild= true
}
}
But it seems like that its not changing the value of isPRODBuild value.
Do in need to use some other type of variable in build.gradle file?
At build time, Gradle generates the BuildConfig class so your app code
can inspect information about the current build. You can also add
custom fields to the BuildConfig class from your Gradle build
configuration file using the buildConfigField() method and access
those values in your app's runtime code. Likewise, you can add app
resource values with resValue().
So you could simply use buildConfigField to define your variable.
productFlavors {
dev {
buildConfigField "boolean", "isPRODBuild", "false"
}
prod {
buildConfigField "boolean", "isPRODBuild", "true"
}
}
Then to access the aforementioned variable simply use BuildConfig.isPRODBuild
Related
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.
I'm trying to cover as much as possible a Kotlin Android library and I'm encountering an issue about custom BuildConfig variable, better known as buildConfigField.
I would like to mock this variable to test both true and false values.
Extract from Gradle file :
android {
defaultConfig {
buildConfigField "boolean", "ENABLE_LOG", "false"
}
flavorDimensions "log"
productFlavors {
loggable {
buildConfigField "boolean", "ENABLE_LOG", "true"
dimension "log"
}
notloggable {
dimension "log"
}
}
}
Extract of the Kotlin function to be tested :
fun buildClient(): MyClient {
var myClientBuilder : MyClient.Builder = MyClient.Builder();
if (BuildConfig.ENABLE_LOG) {
val interceptor = LoggingInterceptor();
interceptor.setLevel(LoggingInterceptor.Level.ALL);
myClientBuilder.addInterceptor(interceptor);
}
return myClientBuilder.build()
}
Unit test :
#Test
fun buildClient_enableLog_oneInterceptor() {
// GIVEN
Mockito.mock(BuildConfig::class.java)
Mockito.doReturn(true).`when`(BuildConfig.ENABLE_LOG)
// WHEN
val myClient = myService!!.buildClient()
// THEN
assertNotNull(myClient)
assertNotNull(myClient.interceptors())
assertEquals(1, myClient.interceptors().size)
}
I tried different things and it never works.
If someone have already done this work, it can help me a lot (and others I guess).
Thanks
ReflectionHelpers.setStaticField(BuildConfig::class.java, "ENABLE_LOG", true)
By default, all tests run against the debug build type. You can change this to another build type by using the testBuildType property in your module-level build.gradle file. For example, if you want to run your tests against your "staging" build type, edit the file as shown in the following snippet.
android {
...
testBuildType "staging"
}
but this is causing other options to fail
Little late to the party but this is how you should test any thing related to BuildConfig file.
BuildConfig.java is generated for each variant of your app. In your case you have atleast 4 variants.
LoggableDebug
LoggableRelease
NotloggableDebug
NotloggableRelease
ENABLE_LOG will be false for options 3 and 4.
If you want to Unit test this, I recommend writing UnitTest in src/testNotLoggable/java/com/.../TestFile.java.
In that TestFile.java your BuildConfig.ENABLE_LOG should be false.
You can check BuildConfig.java file for each variant under /build/source/buildConfig/flavorname/debug/com/project/../BuildConfig.java
How do I take an environment variable at build-time and make it available as R.string.api_key at app runtime?
A typical pattern is to put the string in gradle.properties:
API_KEY=whatever-it-is
If you really want it to be a string resource, you can then use resValue in build.gradle:
defaultConfig {
// other stuff here
resValue "string", "api_key", API_KEY
}
(as values in gradle.properties get exposed as global variables to your Gradle script)
Or, if you need the value in Java code, you could use buildConfigField:
defaultConfig {
// other stuff here
buildConfigField "String", "API_KEY", '"'+API_KEY+'"'
}
then reference it as BuildConfig.API_KEY.
And, of course, do not check gradle.properties into version control.
How to use a property from gradle.properties inside android activity? Whenever I build the code it throws error. Is there a particular way that the properties can be accessed inside the activities?
You can't access the gradle.properties from your Activity.
The gradle.properties is used while you are building the app, it doesn't exist in runtime or inside your apk.
However you can set some values inside the BuildConfig class from your build.gradle script (reading from the gradle.properties for example).
Just use somenthing like:
buildConfigField "boolean", "MY_FLAG", "true"
buildConfigField "String" , "MY_KEY" , "\"XXXXX-XXXXX-XXX\""
in gradle.properties:
SIMPLE_STRING=ABC
in build.gradle:
android {
buildTypes {
debug {
buildConfigField 'boolean', 'PREPROD', 'true'
buildConfigField "String" , "MY_KEY" , SIMPLE_STRING
}
}
}
you can use buildConfigField
for example:
android {
buildToolsVersion "22.0.1"
defaultConfig {
buildConfigField "String", "USED_TOOLS", "\"${android.getBuildToolsVersion()}\""
}
}
will add field USED_TOOLS to your application BuildConfig.java
public static final String USED_TOOLS = "22.0.1";
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
}
}