How to Replace a String for a Buildvariant with Gradle in Android Studio

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 a DEBUG 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



Leave a reply



Submit