How to replace a string for a buildvariant with gradle in android studio?
I solved the problem on my own, so here is the solution "step by step" - perhaps it will help some other newbies to gradle :)
Copy Task in General:
copy{
from("pathToMyFolder"){
include "my.file"
}
// you have to use a new path for youre modified file
into("pathToFolderWhereToCopyMyNewFile")
}Replace a line in General:
copy {
...
filter{
String line -> line.replaceAll("<complete line of regular expression>",
"<complete line of modified expression>")
}
}I think the biggest problem was to get the right paths, because I had to make this dynamically (this link was very helpful for me). I solved my problem by replacing the special lines in the manifest and not in the String-file.
The following example shows how to replace the "meta-data"-tag in the manifest to use youre google-maps-api-key (in my case there are different flavors that use different keys ):
android.applicationVariants.each{ variant ->
variant.processManifest.doLast{
copy{
from("${buildDir}/manifests"){
include "${variant.dirName}/AndroidManifest.xml"
}
into("${buildDir}/manifests/$variant.name")
// define a variable for your key:
def gmaps_key = "<your-key>"
filter{
String line -> line.replaceAll("<meta-data android:name=\"com.google.android.maps.v2.API_KEY\" android:value=\"\"/>",
"<meta-data android:name=\"com.google.android.maps.v2.API_KEY\" android:value=\"" + gmaps_key + "\"/>")
}
// set the path to the modified Manifest:
variant.processResources.manifestFile = file("${buildDir}/manifests/${variant.name}/${variant.dirName}/AndroidManifest.xml")
}
}
}
Replacing a string in AndroidManifest.xml for a buildvariant doesn't work for Gradle Android Plugin Version 0.5.4
I had the same problem as you and read through the release notes for 0.5.5 where I found the answer.
"access to the variants container don't force creating the task.
This means android.[application|Library|Test]Variants
will be empty during the evaluation phase. To use it, use .all
instead of .each
"
Build android with gradle, replace string each product flavor
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.
Replace word in strings.xml with gradle for a buildType
Ok, found a correct solution, which is not a workaround:
For the required buildType
add the following
applicationVariants.all { variant ->
variant.mergeResources.doLast {
def dir = new File("${buildDir}/intermediates/res/merged/${variant.dirName}") //iterating through resources, prepared for including to APK (merged resources)
println("Resources dir " + dir)
dir.eachFileRecurse { file ->
if(file.name.endsWith(".xml")) { //processing only files, which names and with .xml
String content = file.getText('UTF-8')
if(content != null && content.contains("Bill")) {
println("Replacing name in " + file)
content = content.replace("Bill", "Will") //replacing all Bill words with Will word in files
file.write(content, 'UTF-8')
}
}
}
}
Gradle task replace string in .java file
May be you should try something like ant's replaceregexp:
task myCopy << {
ant.replaceregexp(match:'aaa', replace:'bbb', flags:'g', byline:true) {
fileset(dir: 'src/main/java/android/app/cfg', includes: 'TestingConfigCopy.java')
}
}
This task will replace all occurances of aaa
with bbb
. Anyway, it's just an example, you can modify it under your purposes or try some similar solution with ant.
Gradle-only solution to modify App name based on Build Variant
The first try was a closer solution to the right answer than the updated code.
Further refactoring could possibly done by moving all the manifestPlaceholders
code inside the applicationVariants.all
section; however, this is a working copy of a semi-clean, gradle-only solution...
android {
ext {
APP_NAME = "App Name"
HUB_NAME = "Hub"
}
defaultConfig {
manifestPlaceholders = [ applicationLabel: APP_NAME ]
}
productFlavors {
entity_1 {
versionNameSuffix ' - Entity_1'
applicationIdSuffix 'entity_1'
}
...
entity_n {
versionNameSuffix ' - Entity_n'
applicationIdSuffix 'entity_n'
}
hub {
versionNameSuffix ' - Hub'
applicationIdSuffix 'hub'
manifestPlaceholders = [ applicationLabel: HUB_NAME ]
}
}
applicationVariants.all { variant ->
// Don't modify the release build or the hub flavor. They are good already.
if (variant.buildType.name == "release" || variant.flavorName == "hub") return
variant.mergedFlavor.manifestPlaceholders = [applicationLabel: APP_NAME + variant.mergedFlavor.versionNameSuffix]
}
Notes:
BEFORE the applicationVariants.all { ... }
code runs, this is what all the applicationLabel look like. We are close, but need to ADD to them...
Flavor Debug App Name Release App Name
-------- -------------- ----------------
entity_1 App Name App Name
entity_2 App Name App Name
... ... ...
entity_n App Name App Name
hub Hub Hub
AFTER the applicationVariants.all { ... }
code runs, this is what all the applicationLabel look like. We are done!
Flavor Debug App Name Release App Name
-------- -------------- ----------------
entity_1 App Name - Entity_1_name App Name
entity_2 App Name - Entity_2_name App Name
... ... ...
entity_n App Name - Entity_n_name App Name
hub Hub Hub
Also...
defaultConfig
does not have a way of accessing information within the individual productFlavors
. Although defaultConfig
is a Flavor
kind of, only the specified Flavors
can read information from within the defaultConfig
. There are no mechanism to go the other way (that I am aware of). So you need to set the most generic type in the defaultConfig
Any information within the buildTypes
block will get the final say, and the code within applicationVariants.all
will not override that. In order to overcome that, you have to remove the needed code from the buildType
block and move it within the applicationVariants.all
block (with the correct logic statements)
Override resources with gradle depending on buildType
You should strive to come up with a solution that doesn't involve writing any custom code in your build files, especially code that does tricky things with reassigning source sets on the fly. Custom Gradle code is a little funky to write, and it's difficult to debug and maintain. The new build system is extremely powerful and already has tons of flexibility, and it's likely that you can already do what you want; it's just a matter of learning how.
Especially if you're just learning the ins and outs of Android-Gradle projects (and it's so new that we all are), it's best to try hard to work with the functionality built into the system before thinking outside the box.
Some recommendations:
- It's unlikely you need to vary resources based on build type. A build type in Android-Gradle is supposed to be something like debug or release, where the difference is in debuggability, compiler optimization, or signing; build types are supposed to be functionally equivalent to each other. If you look at the properties you can set on a build type through the Groovy DSL, you can see the intent:
debuggable
,jniDebugBuild
,renderscriptDebugBuild
,renderscriptOptimLevel
,packageNameSuffix
,versionNameSuffix
,signingConfig
,zipAlign
,runProguard
,proguardFile
,proguardFiles
. - If you still think you want to vary resources based on build type, there's already an easy way to do that with the current build system. You can have a build-type-specific resource directory, put your resources in there, and the resource merging in the build system will take care of things for you at build time. This is one of the powerful features in Android/Gradle. See Using Build Flavors - Structuring source folders and build.gradle correctly for information on how to make that work.
- If you want to vary something based on build type and your needs are very quick and simple, you might want to do the switch in Java code instead of resources and instead of in the build system. There's the
BuildConfig
mechanism for that sort of thing -- it's a Java class that defines aDEBUG
flag based on debug/release build status, and you can add your own custom Java code from different build types to do more meaningful things.BuildConfig
was intended for allowing small functional differences between build types, for cases where a debug build might want to perform some wasteful operation to assist in development, like doing more extensive data validation or creating more detailed debug logging, and those wasteful things are best optimized out of release builds. Having said that, it might be an appropriate mechanism to do what you want. - Consider using flavors for what you're using build types for now. Conceptually a flavor is kind of like a build type in that it's another variant of your application that can be built; the build system will create a matrix of flavors vs. build types and can build all combinations. However, flavors address a different use case, where different flavors share most code but can have significant functional differences. A common example is a free vs. paid version of your application. Inasmuch as a different resource in different variants of your app represents different functionality, that might indicate a need for a different flavor. Flavors can have different resource directories that are merged at build time in the same way as build configs; see the question linked above for more info.
Related Topics
How to Set a Fragment Tag by Code
Replace Fragment with Another Fragment Inside Viewpager
How to Overlap Items in Linearlayoutmanager - Recyclerview (Like Stacking Cards)
Combining Wrap_Content on Parent and Fill_Parent on Child
How to Get Spinner Selected Item Value to String
Glide Showing Error: Failed to Find Generatedappglidemodule
Android Active Link of Url in Textview
How to Get Total Ram Size of a Device
How to Add Cardview in Layout Xml in Androidx
Uninstall App Silently with System Privileges
Gradle Warning: Variant.Getoutputfile() and Variant.Setoutputfile() Are Deprecated
Upload Photo Using Httppost Multipartentitybuilder
Android - How to Get View from Context
Stuck with Gradle Build Running
Android - Save Images in an Specific Folder
How to Get Current Process Name in Android