I have a bunch of product flavors defined for my build. However in some scenarios I want to build without a flavor.
However when I try to build a release with no flavor, ie
gradlew assembleRelease
It goes through and builds all of the variants, which takes a really long time. I would like to kick off a release build and ignore all flavors. Is that possible?
I am sure I could add an empty flavor called 'default, and build that. Seems like I should not have to do that.
EDIT:
defaultConfig {
resValue "string", "hello", "Hello default"
}
productFlavors {
foo {
resValue "string", "hello", "Hello Foo"
}
bar {
resValue "string", "hello", "Hello Bar"
}
}
Seems the answer at the moment is to provide your own 'default' flavor
productFlavors {
defaults {
// yup its empty cause I just want to build with the defaults
// that are already defined.
}
foo {
resValue "string", "hello", "Hello Foo"
}
bar {
resValue "string", "hello", "Hello Bar"
}
}
I found out that "main" as a flavor works. So i dont need to add an additional folder with google-services.json or anything in it
productFlavors {
main{
}
flav1 {
applicationId 'id'
}
}
Related
So I've been working on a bit of a white label style project for a while, and I've run into a bit of an issue with extensibility. Specifically, the way the project works, I need to be able to support multiple backend environments, each with unique credentials, on a per-client basis.
I've baked this behavior into the gradle using two flavor dimensions, one for the client and one for the backend, but I'm having a bit of trouble in getting them to gel together as seamlessly as I'd like.
Here's an example of the gradle setup:
productFlavors {
client1 {
applicationId "com.app.client1"
dimension "customer"
resValue "string", "app_name", "Client 1"
}
client2{
applicationId "com.app.client2"
dimension "customer"
resValue "string", "app_name", "Client 2"
}
dev {
ext {
packageExtension = ".DEV"
}
dimension "environment"
resValue "string", "url", "https://dev.hostname.com/"
}
uat {
ext {
packageExtension = ".UAT"
}
dimension "environment"
resValue "string", "url", "https://uat.hostname.com/"
}
stage {
ext {
packageExtension = ".STAGE"
}
dimension "environment"
resValue "string", "url", "https://stg.hostname.com/"
}
prod {
ext {
packageExtension = ""
}
dimension "environment"
resValue "string", "url", "https://hostname.com/"
}
}
This works great for granting access to the right environment and unique code for each client, but it means that I can't include the client ID in the gradle. I have to stick it in a resource file out in the client1Dev directory and the like in order to get the right value at run time.
My question is, is there a way to move the client ID into gradle and get the right value based on the combination of customer and environment flavors?
For example, what I would like to do is something like:
client1 {
applicationId "com.app.client1"
dimension "customer"
resValue "string", "app_name", "Client 1"
dev{
resValue "string", "clientId", "1032145d8eefa00aff0098b08c9d"
}
uat{
resValue "string", "clientId", "8654684561584798531985964/56"
}
}
client2{
applicationId "com.app.client2"
dimension "customer"
resValue "string", "app_name", "Client 2"
dev{
resValue "string", "clientId", "89612ad8967a00aff0098b08c08e"
}
uat{
resValue "string", "clientId", "8612ad890981237409d0ab08c08f"
}
}
Something along these lines that would allow me to store my client ID in the gradle file along with the other backend setup info. I can't find anything about this kind of thing though, and I'm hoping someone on here can shed some light on how to do it, or at least tell me it's impossible.
Thanks!
You could always use separate source trees for the multi-flavor combination and store the Strings directly in the correct resource (since that's essentially what resValue "string" "{ID}" "{VALUE}" is doing anyway).
src/
main/
java/
res/
values/
strings_ids.xml
<string name="clientId">override_me</string>
client1Dev/
client1Uat/
client2Dev/
res/
values/
strings_ids.xml
<string name="clientId">89612ad8967a00aff0098b08c08e</string>
client2Uat/
res/
values/
strings_ids.xml
<string name="clientId">8612ad890981237409d0ab08c08f</string>
Or, if you really want to keep in inside the build.gradle file, you could script it with something (less elegant) along the lines of:
android.applicationVariants.all { variant ->
if (variant.getFlavorName().equalsIgnoreCase("client1Dev")) {
variant.resValue "string", "clientId", "1032145d8eefa00aff0098b08c9d"
}
else if (variant.getFlavorName().equalsIgnoreCase("client1Uat")) {
variant.resValue "string", "clientId", "8654684561584798531985964/56"
}
}
But you can see the latter will get nasty, pretty quickly
Is there a way to specify different app label (name) for different productFlavors in build.gradle? For example something like this:
productFlavors {
stage {
app name with "-stage" suffix
}
preprod {
app name with "-preprod" suffix
}
prod {
app name without any suffix
}
}
You can achieve it as follows:
Ensure you have specified android:label="#string/app_name" in your AndroidManifest.xml.
Then remove app_name from strings.xml
Change your build.gradle as follows:
productFlavors {
stage {
resValue "string", "app_name", "stage"
}
preprod {
resValue "string", "app_name", "preprod"
}
prod {
resValue "string", "app_name", "prod"
}
}
Step #1: Define a string resource in your main source set to use for your android:label value.
Step #2: In the manifest in your main source set, apply that string resource to the android:label attribute (e.g., android:label="#string/whatever").
Step #3: For any build type, product flavor, or build variant where you want a different value for android:label, create a source set (e.g., app/src/stage/ alongside the existing app/src/main/) and put your desired value in the string resource for that source set (e.g., app/src/stage/res/values/strings.xml with a whatever string resource).
When you build your app, Android will use the build-specific source set for your string resource, falling back to main for situations where you did not override it.
If you definitely want to define it in Gradle — for example, you are generating the label value programmatically at build time — use resValue statements instead of defining the string resources in XML. You would still use that string resource in the manifest, and I would recommend still having a default value in app/src/main/res/values/strings.xml as a fallback.
You can create with resValue "string", "app_name", "label (name) ". Remove app_name from your string.xml file.
like :
productFlavors {
stage {
resValue "string", "app_name", "Stage"
}
preprod {
resValue "string", "app_name", "Preprod"
}
prod {
resValue "string", "app_name", "Prod"
}
}
A friend of mine suggested a very nice solution based on injecting build variables into the manifest. It even allows to use localized app labels. So here it is:
1) Specify android:label in AndroidManifest.xml as follows:
android:label="${appLabel}"
2) Specify a default value in app level build.gradle:
manifestPlaceholders = [appLabel:"#string/appName"]
3) Override the value for needed product flavours:
productFlavors {
stage {
manifestPlaceholders = [appLabel:"#string/appNameStage"]
}
preprod {
manifestPlaceholders = [appLabel:"#string/appNamePreprod"]
}
prod {
// Just let it use a default value
}
}
4) Add string resources which you are referring to (appName, appNameStage, appNamePreprod). Localize them if needed.
The resValue method (or whatever it's called) allows you to set a resource value in buildTypes or productFlavors. Is there a corresponding way to get a resource value that was set by resValue?
It appears that productFlavors is evaluated before buildTypes, so a resValue set in buildTypes takes precedence. I want to append "Debug" to the app name in debug builds, but I need to get the value that was set in the product flavor in order to append to it.
Edit: I tried Marcin Koziński's suggestion to use a variable, but all product flavors are evaluated before any build type. Therefore, this does not work:
android {
String appName = ""
productFlavors {
Foo {
appName = "Foo"
}
Bar {
appName = "Bar"
}
}
buildTypes {
release {
resValue "string", "app_name", appName
}
debug {
resValue "string", "app_name", appName + " Debug"
}
}
}
In buildTypes, appName always has the value from the last product flavor. So in this example, all builds receive the name "Bar" or "Bar Debug".
Basically, I need a resValueSuffix analogous to applicationIdSuffix. Apparently no such animal exists. Does the com.android.application plugin expose anything that I could use to achieve this?
If you are only trying to set the App Label (or other manifest values) you can solve this with manifest placeholders.
android {
productFlavors {
Foo {
applicationId "com.myexample.foo"
manifestPlaceholders.appName = "Foo"
}
Bar {
applicationId "com.myexample.bar"
manifestPlaceholders.appName = "Bar"
}
}
buildTypes {
release {
manifestPlaceholders.appNameSuffix =""
}
debug {
manifestPlaceholders.appNameSuffix =".Debug"
applicationIdSuffix ".debug"
}
}
}
Then in your Android Manifest you simply use both placeholders for your app name (or other values)
<application
android:label="${appName}${appNameSuffix}"
...
</application>
This allow you to install all 4 variants side by side on a single device as well as give them different names in the app drawer / launcher.
EDIT 11/22/2019
Updated how placeholders values are set based on feedback from #javaxian
You can check the build variants like this
Define values in gradle
buildTypes {
debug{
buildConfigField "String", "Your_string_key", '"yourkeyvalue"'
buildConfigField "String", "SOCKET_URL", '"some text"'
buildConfigField "Boolean", "LOG", 'true'
}
release {
buildConfigField "String", "Your_string_key", '"release text"'
buildConfigField "String", "SOCKET_URL", '"release text"'
buildConfigField "Boolean", "LOG", 'false'
}
}
And to access those values using build variants:
if(!BuildConfig.LOG)
// do something with the boolean value
Or
view.setText(BuildConfig.yourkeyvalue);
To have an alternative version of a resource in debug builds you can use the debug source set.
strings.xml can be found under following path src/main/res/values, which means it's in the main source set. If you create a new directory src/debug/res/values you can put a new strings.xml file in there with values that should be overridden in debug builds. For example:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">My Application Debug</string>
</resources>
This will replace whatever value app_name has in your main file. You don't have to duplicate all the strings in here - ones you don't include here are simply taken from the main file.
I'm interested in defining my many flavors of my apps more so in the strings.xml files rather than the build.gradle. Given multiple flavors, I'd like a simplified release/debug variant:
buildTypes {
release {
signingConfig signingConfigs.release
resValue "string", "app_name", "#string/prod_name"
}
debug {
applicationIdSuffix ".beta"
resValue "string", "app_name", "#string/beta_name"
}
Then in each of my build flavors' custom res/values/strings.xml files, I would define each their own "prod_name" and "beta_name". I also want to use this similar framework for defining providers' authorities etc...
This currently will build fine via gradle command-line, but fails to be recognized by Android Studio.
Android Studio Error:
I find this within 'generated.xml'
<!-- Values from build type: debug -->
<string name="app_name">#string/beta_name</string>
Which is typical of how one string references another. But this time Android Studio gives me this error:
Error:(7, 29) No resource found that matches the given name
(at 'app_name' with value '#string/beta_name').
I'm using Android Studio 2.1 Preview 5
This is actually possible. All you have to do is declare those strings as empty in your defaultConfig, like this:
defaultConfig {
resValue "string", "prod_name", ""
resValue "string", "beta_name", ""
}
In my experience you can't resolve a #string/my_string in the resValue DSL. Gradle put the value as a simple string inside the resource file.
In this case you can use different folder to achieve it:
Just use:
src/release/res/values/strings.xml
src/debug/res/values/strings.xml
If you would like to use different resource for each build variant (build type+flavor) you can use:
src/flavor1Release/res/values/strings.xml
src/flavor1Debug/res/values/strings.xml
src/flavor2Release/res/values/strings.xml
src/flavor2Debug/res/values/strings.xml
It is possible you can add res values inside the gradle for your build flavors
productFlavors {
prod {
dimension "default"
resValue "string", "app_name", "your prod app name"
}
staging {
dimension "default"
resValue "string", "app_name", "your staging app name"
}
}
I'm trying to work with build flavors. In my build.gradle I've defined 2 flavors, a normal flavor and an admin flavor.
Basicly, the admin flavor has an extra button on the main activity.
I understand that I can define different packages/classes for different flavors. But is there a way to make a sort of if case to add/remove a piece of code depending on the flavor?
Basicly, I would need two versions of an Activity. But I don't want two entire different versions of the activity and maintain them.
So in my activity I would like to do
=> gradle check if flavor is 'admin'
=> if yes, add this code of the button
Is this possible? Or would you need two different physical activities and thus maintain both of them when you add functionality afterwards.
BuildConfig.FLAVOR gives you combined product flavor.
So if you have only one flavor dimension:
productFlavors {
normal {
}
admin {
}
}
Then you can just check it:
if (BuildConfig.FLAVOR.equals("admin")) {
...
}
But if you have multiple flavor dimensions:
flavorDimensions "access", "color"
productFlavors {
normal {
dimension "access"
}
admin {
dimension "access"
}
red {
dimension "color"
}
blue {
dimension "color"
}
}
there are also BuildConfig.FLAVOR_access and BuildConfig.FLAVOR_color fields so you should check it like this:
if (BuildConfig.FLAVOR_access.equals("admin")) {
...
}
And BuildConfig.FLAVOR contains full flavor name. For example, adminBlue.
To avoid plain string in the condition, you can define a boolean property:
productFlavors {
normal {
flavorDimension "access"
buildConfigField 'boolean', 'IS_ADMIN', 'false'
}
admin {
flavorDimension "access"
buildConfigField 'boolean', 'IS_ADMIN', 'true'
}
}
Then you can use it like this:
if (BuildConfig.IS_ADMIN) {
...
}
You can define either different build configuration fields or different resource values (like string values) per flavor, e.g. (as per Google's gradle tips and recipes), e.g.,
android {
...
buildTypes {
release {
// These values are defined only for the release build, which
// is typically used for full builds and continuous builds.
buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
resValue("string", "build_time", "${minutesSinceEpoch}")
...
}
debug {
// Use static values for incremental builds to ensure that
// resource files and BuildConfig aren't rebuilt with each run.
// If they were dynamic, they would prevent certain benefits of
// Instant Run as well as Gradle UP-TO-DATE checks.
buildConfigField("String", "BUILD_TIME", "\"0\"")
resValue("string", "build_time", "0")
}
}
}
So in this case, something like
productFlavors {
normal {
dimension "access"
buildConfigField("boolean", "IS_ADMIN", "false")
}
admin {
dimension "access"
buildConfigField("boolean", "IS_ADMIN", "true")
}
}
and then use it like
if (BuildConfig.IS_ADMIN) {
...
} else {
...
}
or if it is just to have different string values for different flavors, it can be done with different resValues and then you don't even need the if/then
You can try this way
productFlavors {
def app_name = "you app name"
development {
versionCode 1
versionName "1.0.1"
buildConfigField 'String', 'varibalename', ""
}
release {
versionCode 1
versionName "1.0.1"
buildConfigField 'String', 'varibalename', ""
}
}
if(BuildConfig.varibalename){}