Is it possible to create two .properties files for two product flavors in Gradle?
I'm building an Android app with two flavors and I want to have separate properties for them, but have problems with overwritten variable here:
productFlavors {
flavor1 {
customProperties = getCustomProperties("flavor1.properties");
}
flavor2 {
customProperties = getCustomProperties("flavor2.properties");
}
}
My customProperties variable is always get values from flavor2 properties, even I build flavor1.
What am I doing wrong? :)
upd: defaultConfig:
defaultConfig {
minSdkVersion 17
targetSdkVersion 22
// Enabling multidex support.
multiDexEnabled true
}
I have use something like this:
flavorDimensions "default"
productFlavors {
stand {
def standPropsFile = file("${project.projectDir}/../stand.properties")
Properties standProps = new Properties()
if (standPropsFile.canRead()) {
standProps.load(new FileInputStream(standPropsFile))
}
manifestPlaceholders = standProps
applicationId = manifestPlaceholders["APP_ID"]
}
google {
def standPropsFile = file("${project.projectDir}/../google.properties")
Properties standProps = new Properties()
if (standPropsFile.canRead()) {
standProps.load(new FileInputStream(standPropsFile))
}
manifestPlaceholders = standProps
applicationId = manifestPlaceholders["APP_ID"]
}
}
manifestPlaceholders - is just setter for Map<String, Object> mManifestPlaceholders, so after "manifestPlaceholders = standProps" you can use values right by key how you have write it in .properties file.
You should also change the build variant and select the right flavor to bring every configuration for the desired flavor into effect.
Alternatively, if you have only a few fields in the properties file and just want different values for each flavor: you could use BuildConfigFields as follows:
buildConfigField 'String', 'FIELD1', '"value1"'
buildConfigField 'String', 'FIELD2', '"value2"'
buildConfigField "String", "FIELD3", "value2"
These can be accessed anywhere throughout the project using the BuildConfig class as follows:
BuildConfig.FIELD1
BuildConfig.FIELD2
BuildConfig.FIELD3
Related
Is there an easy way to include common flavor settings in a flavor when the flavors are split out by build settings?
Take the below example. I'd like to have all the settings in the flavor1 group below included in both the Debug and Release flavors. This is obviously a simplified example, as I have 4 flavors and over 20 settings common between them and I don't wan to duplicate them.
android {
productFlavors {
flavor1Debug {
resValue 'string', 'application_name', 'DebugName'
}
flavor1Release {
resValue 'string', 'application_name', 'RelaseName'
}
//flavor1 {
// buildConfigField 'String', 'DEFAULT_LANGUAGE_CODE', '"en"'
//}
}
}
There is a defaultConfig section, you can provide the base configuration for all flavors in the defaultConfig block, and each flavor can change any of these default values.
for example:
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]
...
}
buildTypes {
debug{...}
release{...}
}
flavorDimensions "version"
productFlavors {
demo {
dimension "version"
manifestPlaceholders = [hostName:"www.otherexample.com"]//this flavor change manifestPlaceholders
...
}
full {
dimension "version" //this flavor extends manifestPlaceholders from `defaultConfig`
...
}
}
I don't figure out how can I set a resource value (resValue) in my build.gradle, depending on the build variant selection.
Here a bit of explanation.
I'm working with the Skype For Business SDK (hereafter referred to as Sfb) and during is implementation, it ask me to add a resource value named ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE.
So I was looking in their application example (available here) and found that in build.gradle they have added as follow the corresponding value :
android {
...
defaultConfig {
applicationId "com.microsoft.office.sfb.sfbdemo"
...
resValue ("string", "ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE", "${applicationId}"
}
...
}
This value then used in a SfbSDK class, checking if it match the application package name.
And here is my trouble, I work with different flavorDimensions as describe in my build.gradle below.
apply plugin: 'com.android.application'
...
android {
...
defaultConfig {
applicationId "com.tsp.test"
...
resValue ("string", "ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE", "${applicationId}"
}
...
flavorDimensions("customer", "version")
productFlavors {
a {
dimension "customer"
applicationIdSuffix ".a"
}
b {
dimension "customer"
applicationIdSuffix ".b"
}
alpha {
dimension "version"
applicationIdSuffix ".alpha"
}
beta {
dimension "version"
applicationIdSuffix ".beta"
}
release {
dimension "version"
applicationIdSuffix ".release"
}
}
}
...
Depending on my build variant selection, this will generate me 6 different APK :
com.tsp.test.a.alpha ; com.tsp.test.a.beta ; com.tsp.test.a.release
com.tsp.test.b.alpha ; com.tsp.test.b.beta ; com.tsp.test.b.release
So when the match checking is does, my application crash with the message error
Caused by: java.lang.RuntimeException: ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE string not set to applicationId
at com.microsoft.office.sfb.appsdk.Application.initialize(Application.java:110)
at com.microsoft.office.sfb.appsdk.Application.getInstance(Application.java:144)
at com.tsp.test.RootActivity.onCreate(RootActivity.java:89)
Of course, because com.tsp.test doesn't match com.tsp.test.a.alpha (or any other APK).
How can I achieve a dynamic resValue depending on the build variant selected that match the right application package name ?
EDIT :
To explain a bit more. First I choose the Build Variants as follow :
Customer : A
Version : Alpha
Then, in my RootActivity#onCreate() (my launcher activity), I start to configure the SfbSDK with an application instance depending on the SfbSDK :
this.mApplication = com.microsoft.office.sfb.appsdk.Application.getInstance(this.getApplication().getApplicationContext());
Somewhere in getInstance() method, the SfbSDK do an equals() between the context.getPackageName() and context.getString(string.ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE);
So, for debug, in my RootActivity#onCreate() I just wrote this two lines
String whatIWant = this.getPackageName(); // give me *com.tsp.test.a.alpha*
String whatIGet = this.getString(R.string.ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE); // give me *com.tsp.test*
This doesn't match ! So in SfbSDK the condition wasn't passed.
Well, thanks to Radesh and this link he provided to me, I've found my solution.
I removed resValue from the defaultConfig block then I added a new block in the android plugin task, that create the resValue for each variant.
apply plugin: 'com.android.application'
...
android {
...
defaultConfig {
applicationId "com.tsp.test"
...
}
...
flavorDimensions("customer", "version")
productFlavors {
a {
dimension "customer"
applicationIdSuffix ".a"
}
b {
dimension "customer"
applicationIdSuffix ".b"
}
alpha {
dimension "version"
applicationIdSuffix ".alpha"
}
beta {
dimension "version"
applicationIdSuffix ".beta"
}
release {
dimension "version"
applicationIdSuffix ".release"
}
}
...
applicationVariants.all { variant ->
variant.resValue "string", "ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE", "\"${applicationId}\""
}
}
...
This generate correctly the resValue with the package name of the selected variant.
For variant customer A and version Alpha, I get resValue = com.tsp.test.a.alpha.
you must declear variable ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE in gradle.properties like below
//default
org.gradle.jvmargs=-Xmx1536m
ENTERPRISE_AUTHENTICATOR_ACCOUNT_TYPE=xxx
Suppose I have an app that shows locations on a map.
Now I want to make several different variations of this app, one showing shops, one showing restaurants, etc.
In some of these apps I want to include ads, in others I don't.
How can I selectively add certain srcDirs to one flavor but not another? I tried the following:
productFlavors {
Shops {
applicationId "com.me.shops"
ext.hasAds = true
}
Restaurants {
applicationId "com.me.restaurants"
ext.hasAds = false
}
... more flavors with ext.hasAds either true or false
}
applicationVariants.all { variant ->
String withAdsDir = 'src/withAds'
String withoutAdsDir = 'src/withoutAds'
if (variant.productFlavors.get(0).ext.hasAds) {
variant.sourceSets java.srcDir withAdsDir + '/java'
variant.sourceSets res.srcDir withAdsDir + '/res'
}
else {
variant.sourceSets java.srcDir withoutAdsDir + '/java'
variant.sourceSets res.srcDir withoutAdsDir + '/res'
}
}
But this fails because for one, the syntax in the if-structure is incorrect.
I am trying to avoid having to specify the folders for every flavor individually, therefore using the .ext.hasAds boolean.
How can I achieve specifying particular srcDirs depending on a certain boolean?
UPDATE
I 'fixed' it. Meaning I get what I want. But I'm not very happy about how:
flavorDimensions "adsornot", "typeofapp"
productFlavors {
Shops {
applicationId "com.me.shops"
ext.hasAds = true
dimension "typeofapp"
}
Restaurants {
applicationId "com.me.restaurants"
ext.hasAds = false
dimension "typeofapp"
}
Ads {
ext.hasAds = true
dimension "adsornot"
}
NoAds {
ext.hasAds = false
dimension "adsornot"
}
}
sourceSets {
String withAdsDir = 'src/withAds'
String withoutAdsDir = 'src/withoutAds'
Ads {
java.srcDir withAdsDir + '/java'
res.srcDir withAdsDir + '/res'
}
NoAds {
java.srcDir withoutAdsDir + '/java'
res.srcDir withoutAdsDir + '/res'
}
}
variantFilter { variant ->
boolean shouldHaveAds = variant.getFlavors().get(0).ext.hasAds
boolean variantHasAds = variant.getFlavors().get(1).ext.hasAds
variant.setIgnore(shouldHaveAds != variantHasAds)
}
Explanation:
I create 2 flavor dimensions: type-of-app, and ads-or-not
This means there will be 4 variants generated. But I only want 2. The variantfilter checks each variant's 2 flavors, they should have matching ext.hasAds (defined in the flavors). If they do not match, ignore this variant.
If you use Android Studio, your app folder contains main folder with default sources, xmls, assets etc. for all configurations. You can also create app/[flavor] folder (for example, app/Shops, app/Restaurants) and put in all specified for the flavor resources.
For example, your build.gradle file contains following flavors:
flavorDimensions "deployment"
productFlavors {
development {
dimension "deployment"
applicationId "com.example.myapp.dev"
buildConfigField "boolean", "USE_ADS", "false"
}
production {
dimension "deployment"
applicationId "com.example.myapp"
buildConfigField "boolean", "USE_ADS", "true"
}
}
To create custom resources for development configuration, you need simply create app/development folder. It should have the same structure as the app/main.
After that check custom flags provided in gradle file in your code:
// check gradle flags
if (BuildConfig.USE_ADS) {
// insert ads
}
I have the next chunk of code in my build.gradle:
productFlavors {
//DIMENSION - APP
slid {
dimension "app"
applicationId "com.slid"
versionCode 1
versionName "1.0.0"
manifestPlaceholders = [one_app_id : "273e0-b8f-4c7f-87-0f4eb68da"]
}
}
What I want to do is access one_app_id from manifestPlaceholders inside my java code.
As I was unable to do so (or at least I was unable to find a way), I want to define a buildConfigField String with the same value 273e0-b8f-4c7f-87-0f4eb68da but I'd like to make it a reference to manifestPlaceholders/one_app_id so I don't write the ID two times.
My question is:
buildConfigField "String", "ONE__ID", '[reference_here]'
What do I set instead of [reference_here] ?
OR
How can I do i the other way, i.e. have the ID set as a buildConfigField and then set that reference in the manifestPlaceholder
For example manifestPlaceholders = [one_app_id: [?!reference_here?!]]
P.S. don't worry, the ID is not valid :)
Based on CommonsWare's suggestion I've managed to do it like so:
ext {
oneAppId = ""
}
android {
productFlavors {
//DIMENSION - APP
slid {
dimension "app"
applicationId "com.slid"
versionCode 1
versionName "1.0.0"
ext.oneAppId = '"273e0-8567-558da"'
manifestPlaceholders = [one_app_id : ext.oneAppId] //Used by the third party library
buildConfigField "String", "ONE_APPID", ext.oneAppId //I'll use this in java
}
}
}
I have inherited a project which has the following flavor set up in gradle build file:
productFlavors {
def STRING = "String"
def BOOLEAN = "boolean"
def TRUE = "true"
def FALSE = "false"
def BASE_ENDPOINT = "BASE_ENDPOINT"
mock {
applicationId "com.example.myapp.mock"
buildConfigField STRING, BASE_ENDPOINT, '"mock://localhost/gt"'
}
qa {
applicationId "ca.example.myapp"
buildConfigField STRING, BASE_ENDPOINT, '"https://qa-somesite.com/gt"'
}
qaInternal {
applicationId ".ca.example.myapp.int"
buildConfigField STRING, BASE_ENDPOINT, '"https://internal-somesite.com/gt"'
}
beta {
applicationId "com.example.myapp.beta"
buildConfigField STRING, BASE_ENDPOINT, '"https://somesite.com/gt"'
}
prod {
applicationId "ca.ca.example.myapp"
buildConfigField STRING, BASE_ENDPOINT, '"https://somesite.com/gt"'
}
}
nothing special going on there just flavors for production qa, mocking , etc.
This is just for for a particular country though. Its for USA. So these flavors are all for USA. I need to use the same app. I'd like to create flavors for another country called France. The france flavors have different configurations.
i was thinking i could do something like this:
flavorDimensions "country","buildtype"
to get me the flavors by country. But then how would i create my own mock,qa,qaInternal,etc flavors for the new country.
So to be clear my end goal is to have product flavors for a new country called france given the code i've pasted above all in android studio.
UPDATE: Let me be more precise on what i desire and the issue. Look at the current product flavors: mock, qa, qaInternal,beta and prod. They all pretain to information about a USA build. This already exists in code. The code is currently built for USA customers. Now i have been asked to make the code also available for French customers so i need a french build.
The issue is things like the applicationID, and many buildConfigField are going to be different in the french build. How can i engineer a solution where i can have for example, a french mock, a french qa, a french qaInternal ,french beta, and a french prod just like i currently have for the USA build ?
The issue has nothing to do with locale per say, its just that we have two products a USA product and we want a french product which will have a different configuration then USA.
take for example the mock flavor. For french i need this to happen:
mock {
applicationId "french-com.example.mock"
versionName = versionOverride + 'french-mock'
buildConfigField STRING, BASE_ENDPOINT, '"mock://localhost/french-gt"'
}
What you are doing would work if you assign a flavorDimension to every productFlavor. So in your case the build.gradle could look like
flavorDimensions "country","buildtype"
productFlavors {
def STRING = "String"
def BOOLEAN = "boolean"
def TRUE = "true"
def FALSE = "false"
def BASE_ENDPOINT = "BASE_ENDPOINT"
mock {
dimension 'buildtype'
applicationId "com.example.myapp.mock"
versionName = versionOverride + '-mock'
buildConfigField STRING, BASE_ENDPOINT, '"mock://localhost/gt"'
}
qa {
dimension 'buildtype'
applicationId "ca.example.myapp"
versionName = versionOverride + '-qa'
buildConfigField STRING, BASE_ENDPOINT, '"https://qa-somesite.com/gt"'
}
qaInternal {
dimension 'buildtype'
applicationId ".ca.example.myapp.int"
versionName = versionOverride + '-qaInt'
buildConfigField STRING, BASE_ENDPOINT, '"https://internal-somesite.com/gt"'
}
beta {
dimension 'buildtype'
applicationId "com.example.myapp.beta"
versionName = versionOverride + '-beta'
buildConfigField STRING, BASE_ENDPOINT, '"https://somesite.com/gt"'
}
prod {
dimension 'buildtype'
applicationId "ca.ca.example.myapp"
versionName = versionOverride + '-prod'
buildConfigField STRING, BASE_ENDPOINT, '"https://somesite.com/gt"'
}
usa {
dimension 'country'
// whatever else you want
}
france {
dimension 'country'
// whatever else you want
}
}
With the flavorDimensions set up, you will now have a combination of productFlavors with dimension&buildtype, In this case it will be mockUsa, mockFrance, qaUsa, qaFrance etc..
Given the fact that there are actual buildtypes debug and release, you might end up with a huge list of variants!
I think you should create multiple buildTypes instead of flavors.
if you have build types France and USA, and flavors mock, qa and ... , then you would have these build variants : qaUSA , qaFrance, mockUSA, mockFrance and etc.
here is build.gradle example :
productFlavors {
mock {
...
}
qa {
...
}
}
buildTypes {
USA {
...
}
France {
...
}
}