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"
}
}
Related
I have an Android app with multiple flavors, each flavor defines the app_name string on its own strings.xml file. Recently I incorporated a new library that defines its own value for app_name at its own strings.xml file. The problem is that the final value for app_name in the merged resources is the app_name value defined by the library. How I can bring priority to the flavor app_name value?
Currently I'm define specific values for app_name in the individual strings.xml file per flavor.
I expect that the app_name value was the flavor value and not the library value.
UPDATE:
My flavors are defined as below:
productFlavors {
doctoronline {
dimension "client"
applicationId 'com.doctoronline.doctoronline'
resValue 'string', 'filebrowser_provider', 'com.doctoronline.doctoronline.aditya.fileprovider'
buildConfigField 'String', 'CORPORATE_DOMAIN', '"e4c4aa0f523941d2a332d15101f12e9e"'
buildConfigField 'String', 'SHORT_NAME', '"DRONLINE"'
buildConfigField 'String', 'WAITING_MESSAGE', '"DRONLINE_WAITING_MESSAGE_PATIENT_DEFAULT"'
}
mapfre {
dimension "client"
applicationId 'com.doctoronline.mapfre'
resValue 'string', 'filebrowser_provider', 'com.doctoronline.mapfre.aditya.fileprovider'
buildConfigField 'String', 'CORPORATE_DOMAIN', '"3282a15f144e288bac4c07b1598e9234"'
buildConfigField 'String', 'SHORT_NAME', '"mapfre"'
buildConfigField 'String', 'WAITING_MESSAGE', '"MAPFRE_WAITING_MESSAGE_PATIENT_DEFAULT"'
}
...
//4 or 5 extra flavors
}
The library that I'm adding is this.
If you want to change your app name based on product flavor you build, you have two options:
1. using resValue. this helps you to add some resources to your app like string resource, color resource.
2. using manifestPlaceHolder. this helps you to add variables to android manifest file from your build.gradle file and change these variables through build types or product flavors.
this is how to add resValue to your product flavor
productFlavors {
flavor1 {
dimension = 'test'
resValue("string", "app_name", "MY APP NAME")
}
flavor2 {
dimension = 'test'
resValue("string", "app_name", "MY APP NAME")
}
}
if your strings.xml has string with name app_name you have to remove it to avoid duplication res values message.
to check if app name changed through different product flavors change your product falvor and build the project then check what values appear in application tag in the manifest file.
The second way is using manifestPlaceHolder. in your product flavor add manifestPlaceHolder and then use this variable inside your application tag in the manifest file
productFlavors {
flavor1 {
dimension = 'test'
manifestPlaceholders=[appName: 'MY APP NAME']
}
flavor2 {
dimension = 'test'
manifestPlaceholders=[appName: 'MY APP NAME']
}
}
then navigate to your android manifest file
<application
android:name=".DemoApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="${appName}"
android:supportsRtl="true"
android:theme="#style/AppTheme">
...
</application>
Hope this answer is helpful. Happy coding.
Is it possible to build apk with more than 1 product flavor?
For example:
I have a Project with 3 flavors (App1 - App2 - App3).
and each app has its own configurations such as applicationId and so on.
and now I want to build different templates (different XML layouts), and the user should be able to switch from layout to another from inside the app.
My problem is the res folder will be huge and it will be hard to maintain, so I'm trying to find a way to separate the different layouts and keep it clean as much as possible.
And if it's possible to do so, then how do I intent or restart the app to the other flavors build?
Another thing I had in mind was to build all XML files in the main res and choose different qualifiers like when we do while creating different screen size (sm - larg - etc.. ) but i couldnt find anyway to add custom qualifiers.
My Gradle code is like this :
flavorDimensions "default"
productFlavors {
demo {
applicationId "test.demo"
versionCode 2
versionName "1.1.2"
resValue "string", "backage_name_file", "test.demo.fileprovider"
resValue "string", "bc", "com.demo"
resValue "string", "bc_e", "extra_data.com.demo"
resValue "string", "default_hostname", "demo.test.com"
resValue "string", "default_username", "demo"
resValue "string", "default_password", "demo"
}
AppOne {
applicationId "test.AppOne"
versionCode 2
versionName "1.1.2"
resValue "string", "backage_name_file", "test.AppOne.fileprovider"
resValue "string", "bc", "com.AppOne"
resValue "string", "bc_e", "extra_data.com.AppOne "
resValue "string", "default_hostname", "AppOne.test.com"
resValue "string", "default_username", "AppOne"
resValue "string", "default_password", "AppOne"
}
AppTwo {
applicationId "test.AppTwo"
versionCode 2
versionName "1.1.2"
resValue "string", "backage_name_file", "test.AppTwo.fileprovider"
resValue "string", "bc", "com.AppTwo"
resValue "string", "bc_e", "extra_data.com.AppTwo"
resValue "string", "default_hostname", "demoAppTwotest.com"
resValue "string", "default_username", "AppTwo"
resValue "string", "default_password", "AppTwo"
}
}
You cannot build an APK with multiple flavours, just as you cannot build one in both debug and release buildTypes. The selected flavour's config / resources gets pulled into the actual APK metadata / manifest, so cannot be modified at runtime.
You will need to include all of your content inside the res folder, but there are a few ways that may help make it easier to manage. I'd recommend the first 3 options, and the 4th if you have a very large number of code + layout files with different behaviour:
Use Fragments to avoid most of your Java / Kotlin code needing to be duplicated.
Include XML layouts instead of redefining everything each time to reuse common elements.
Carefully name your files, for example template1_background, template2_background.
Use multiple modules, one for each "template". You'll then have multiple sensible res folders.
I can see why flavours might have seemed like the solution, but since you need all flavours in one app, this approach is unfortunately not going to work. You'll likely find step #2 will remove almost all of the duplicate files, avoiding the issue entirely!
You can merge resources from different flavours using sourceSets command.
SourceSet Allows you to congifure buildVariants resources folders
for example you can configure your App2 flavour to include App2 res folder and App1 res folder.
Example code:
sourceSets {
App2Debug{
res.srcDirs = ['src/App1/res', 'src/App2/res']
}
}
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.
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]"'
}
}
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.