How to replace strings resources with Android Gradle - android

I made a new app with gradle in Android Studio, and now I need to make about 10 versions with different package names and values in resources. I made custom flavors as in example and want to replace some strings in this custom flavors with custom values. I found example like this:
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: ['version': '2.2'])
But i don't know where to put it. As i understand i need to put it into separate task, but how to make this task called by IDE?
Also i need to replace few variables inside Java classes and Content Provider's auth, maybe i need to do this by copy files into flavor1 folder and let gradle to merge it, but it seems like wrong solution to store many copies of files with difference in one line... Maybe i need to user some other solution for all this?
Here is build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.4.2'
}
}
apply plugin: 'android'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile project(':JazzyListView')
compile project(':ABS')
compile project(':Volley')
}
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
defaultConfig {
versionCode 5
versionName "3.0"
minSdkVersion 8
targetSdkVersion 17
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java']
res.srcDirs = ['src/main/res']
}
}
productFlavors {
flavor1 {
packageName "com.example.flavor1"
}
flavor2 {
packageName "com.example.flavor2"
}
}
}

I had a similar problem. I wanted to add the Jenkins build number to the strings that get merged from strings.xml. Here's my solution as of Android Gradle plugin 0.12.+.
// Insert the build number into strings.xml
android.applicationVariants.all{ variant ->
variant.mergeResources.doLast{
ext.env = System.getenv()
def buildNumber = env.BUILD_NUMBER
if (buildNumber != null) {
File valuesFile = file("${buildDir}/intermediates/res/${variant.dirName}/values/values.xml")
println("Replacing revision number in " + valuesFile)
println("Build number = " + buildNumber)
String content = valuesFile.getText('UTF-8')
content = content.replaceAll(/devBuild/, buildNumber)
valuesFile.write(content, 'UTF-8')
}
}
}
You might want to hook into a different Gradle task depending on what you want to do. Take a look at the tasks that are part of the Android build to figure that out.
http://tools.android.com/tech-docs/new-build-system/user-guide
UPDATE: At some point, the Android Gradle plugin changed the way to iterate through application variants keyword from each to all. My answer has been updated to reflect the change, but try switching to each if this code doesn't print anything to the console.

I was trying to get similar functionality as Maven resource filtering.
This is what I came up with. My solution could use some changes to be more robust (i.e. pulling from a properties file, etc).
My example just shows how to replace a single value, which is all that I needed. The variables follow the ${some.property} convention. This solution also works with product flavors that have their own resource files.
import org.apache.tools.ant.filters.*
...
android.applicationVariants.all{ variant ->
// Perform resource filtering
variant.mergeResources.doLast {
filterResources(variant)
}
}
def filterResources(buildVariant) {
//Setup temp directory to filter the resources
File resFiltered = file("${buildDir}/res/all/filtered/${buildVariant.dirName}")
if(resFiltered.exists()){
resFiltered.delete()
}
//Copy and filter the resources.
copy {
from(buildVariant.processResources.resDir) {
include '**/*.xml'
//Could be improved upon to pull from a properties file, etc.
ant.properties['app.version'] = project.version
filter(ExpandProperties, project: ant.project)
}
from(buildVariant.processResources.resDir) {
exclude '**/*.xml'
}
into resFiltered
}
//Delete all the original resource files
file(buildVariant.processResources.resDir).deleteDir()
//Replace with the filtered ones.
resFiltered.renameTo(file(buildVariant.processResources.resDir))
//Delete the original 'filtered' directory
file( "${buildDir}/res/all/filtered").deleteDir()
}
Example in strings.xml
...
<string name="app_version">${app.version}</string>
...

These links may be helpful:
Using Gradle for building Android applications
Gradle Plugin User Guide
And my filter definition using regex to replace something:
from('res/') {
include '**/*.xml'
filter {
line -> line.replaceAll(/YOUR_REGEX_TO_REPLACE_SOMETHING/, 'REPLACED_RESULT_EXPRESSION')
}
}
into 'build/path/to/your/filtered/resources/'

In your "src" folder, create "flavor1" and "flavor2" folders with the same hierarchy as your "main" folder. If you have strings.xml in your main/res/values, you can do it in flavor1/res/values as well and have the replacement values in there. It may show errors in the IDE but it should still build and run.

Related

Project with path 'x' could not be found in project 'y' when using flavor implementation

I have an android library with flavors defined
flavorDimensions "player"
productFlavors {
educator {
dimension "player"
}
learner {
dimension "player"
}
}
and dependencies like so
dependencies {
learnerImplementation project(':learnerFeatureModule')
}
Now when I use this library in my Educator app, I get error
Project with path ':learnerFeatureModule' could not be found in project ':myLibrary'
because I have not defined :learnerFeatureModule in my settings.gradle.
I have defined missing strategy in Educator app/build.gralde too.
missingDimensionStrategy 'player', 'educator'
But learnerFeatureModule is learnerImplementation. Why is educator app even trying to resolve this?
I need a way, where Educator app does not look for this module as its not requried.
1 workaround that solves it for me (this does not use flavor as such)
in library build.gradle
dependencies {
def skipLearnerModule = rootProject.ext.get("skipLearnerModule")
if(!skipLearnerModule)
implementation project(':learnerFeatureModule')
and in educator app's root build.gradle
buildscript {
ext {
skipLearnerModule = true
}
I believe the reason why flavor does not work is because gradle's project configure step tries to resolve module for all variant of the app

Android Studio does not show the res directory

I have an Android project checked out in Android Studio (4.0.1). The build.gradle contains the following sourceSets block (snippet shown below). It is meant to prevent certain values resource files from being included in the packaged aar (I only want to include values-en-rUS and values-es-rUS in the aar).
The aar gets packaged correctly and contains only values-en-rUS and values-es-rUS, as expected.
sourceSets {
main {
def resSrc = fileTree(dir: 'src/main/res').matching { exclude { details ->
(!details.file.canonicalPath.matches('.*values-(en|es)-rUS.*')
&& details.file.canonicalPath.matches('.*values-.*'))
} }
...
res.srcDirs = [ resSrc ]
...
}
}
However, Android Studio does not show me the res directory (the entire directory!) anymore in the "Project" sidebar when "Android" is selected, and the Java code files can no longer resolve the ids or layouts (e.g. findViewById(R.id.bottom_sheet_layout) -- they show as red squiggly lines).
How can I make it so that Android Studio continues to show the res directory in this case?
Thanks.
Try this:
sourceSets {
main {
def resSrc = fileTree(dir: 'src/main/res').matching { exclude { details ->
(!details.file.canonicalPath.matches('.*values-(en|es)-rUS.*')
&& details.file.canonicalPath.matches('.*values-.*'))
} }
res.srcDirs = [resSrc, 'src/main/res']
}
}
Note: If two or more resource directories contain the same resource file, an error occurs during resource merging.
For more information: https://developer.android.com/studio/write/add-resources

Android Project Structure is incorrect; only one build.gradle

I have an incorrect project structure. I need a top-level build-gradle, and a module on the same level that contains its own build.gradle.
See picture of how it is organized now. What you see is almost two different levels merged into on.e The build.gradle here is the only one in the project. The one you see has the note that it is the top-level version.
What is the correct way to straighten this out? Do I need to reimport or can I reorganize it?
Other info, I have Gradle 2.10 installed.
EDIT: MORE INFO
Usually I have my top-level Gradle file that contains something like this:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.google.gms:google-services:3.0.0'
}
}
allprojects {
repositories {
jcenter()
}
}
But in the setup above, without having that second Gradle file, where do I put the other info ... for example:
apply plugin: 'com.android.application'
repositories {
mavenCentral()
maven {
url "https://jitpack.io"
}
}
android {
defaultConfig {
// edited
}
dependencies {
// edited
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
apply plugin: 'com.google.gms.google-services'
When I run the program, I get this error:
Error:A problem was found with the configuration of task ':checkDebugManifest'.
> File 'C:\--\src\main\AndroidManifest.xml' specified for property 'manifest' does not exist.
Is this related?
This way is still assuming a flat hierarchy without the extra module asked by OP, but since it's based on my own Eclipse to AS migration I know it worked... for me.
To recognize eclipse defaults without moving the files you need this:
android {
defaultConfig {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
res.srcDirs = ['res']
}
test.java.srcDirs = ['src/test/java', 'build/generated/source/debug']
}
This will most likely allow you to use both eclipse and Android Studio with the same folders in place.
The second way is about not changing gradle but moving folders so gradle finds things where it expects to.
move AndroidManifest.xml, it must go into src/main
move res into src/main/res
move src/com into src/main/java/com (can you confirm where is your com folder currently?
You can either move files or direct gradle to where they are, it's your choice - but don't do both. The only step I don't remember is the build/generated/source/debug for test, I can't remember if I used that because I use groovy or if it was another eclipse maven/AS gradle mismatch.
It's because Gradle looks for AndroidManifest in a default place --> App/src/main/AndroidManifest.xml
You can define where Gradle can search for your AndroidManifest.
How to tell Gradle to use a different AndroidManifest from the command line?
select "android" from the drop down menu instead of "project"

google-services.json causing problems in creating product flavors of my app, possible fix?

I am working on an app that needs two productFlavors, so I modified my gradle file this way:
productFlavors {
free {
applicationId = "com.udacity.gradle.builditbigger.free"
}
paid {
applicationId = "com.udacity.gradle.builditbigger.paid"
}
}
Now since I use Google Ads inside my app, they provided the google-services.json file, now this is causing a lot of trouble.
Since this is not something new, I tried this thread.
And I modified my dependencies to:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.google.gms:google-services:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Now the problem is , when i sync the gradle file, respective directories for free and the paid versions are not created, also, I get an error
Error:Execution failed for task
':app:processPaidReleaseGoogleServices'.
No matching client found for package name 'com.udacity.gradle.builditbigger.paid'
above which Parsing json file:D:\ud867\FinalProject\app\google-services.json failed.
Now, if anyone experienced similar problem, please do share the fix you applied to solve the problem, Thanks!
If you already modified your json file this way:
app/src/
flavor1/google-services.json
flavor2/google-services.json
...
as discussed in Adding the JSON File.
Modifying your gradle file in the following format:
android {
def myFlavors = [
flavor1: [
packageName: "com.example.flavor1"
],
flavor2: [
packageName: "com.example.flavor2"
]
]
productFlavors {
myFlavors.each { name, config ->
"$name" {
packageName config.packageName
}
}
}
}
as given in this SO post - Dynamically generating product flavors might help.

How can I add flavors in a module with Android Studio?

I have a suite of projects that use the same module, which contains nearly all the actual code. The project is setup like:
project/
- app/
- build.gradle
- libraries/
- module/
- build.gradle
- build.gradle
- settings.gradle
The dependencies are all setup correctly, and I can build and run apps great, however I can only add flavors to the project, which is not the ideal solution. settings.gradle contains the following:
include ':app', ':libraries:module'
In the app directory's build.gradle file, I added the following block:
productFlavors {
alpha
production
}
Using gradle 0.11, this syncs and creates assembleAlphaDebug, assembleAlphaRelease, assembleProductionDebug, assembleProductionRelease tasks. When I attempt to do this in the module instead, I get the error:
No resource found that matches the given name (at 'theme' with value '#style/MyCustomTheme')
in the built app/src/main/AndroidManifest.xml. For some reason, the module is not being built, so the custom theme is not working. What am I doing wrong?
In the library module's build.gradle, you need a couple extra lines to tell it to export the flavors and which build variant to use by default if not specified when being included from another module:
android {
defaultPublishConfig "productionRelease"
publishNonDefault true
productFlavors {
alpha {
}
production {
}
}
}
That publishNonDefault bit is only necessary if someone would want to depend on something other than the productionRelease variant. Presumably this is the case if you set up multi-flavors in your library in the first place.
Now if you add a dependency from another module via this in its build.gradle:
dependencies {
compile project(':module')
}
it will depend on the productionRelease variant by default. If you'd like to depend on a non-default variant:
dependencies {
compile project(path: ':module', configuration:'alphaDebug')
}
First add below gradle code snippet to your app/build.gradle
flavorDimensions "env"
productFlavors {
dev {
dimension "env"
}
pre {
dimension "env"
}
prod {
dimension "env"
}
}
Second, add below gradle code snippet to your module/build.gradle
flavorDimensions "env"
productFlavors {
register("dev")
register("pre")
register("prod")
}
I have posted an ansower in this
Use different library module for each android flavor

Categories

Resources