Process resources for android gradle production Flavors - android

I'm looking for the way how to load my properties per product flavor.
I do have right now:
productFlavors {
flavor1 {
println 'Flavor 1'
loadProperties('flavor1.properties')
}
flavor2 {
println 'Flavor 2'
loadProperties('flavor2.properties')
}
I'm using that properties to filter/process some resources before build.
If I do configure my build for one flavor, and run it through Android studio - I could find that two flavors executed and there are two println in console:
Flavor 1
Flavor 2
Is that possible somehow to find active product flavor per build?
Something like get active (or current) product flavor and get access to all properties there?
Or even find a way to process resources per different flavors?
Or may be define some property underFlovors and re use it in another place?

Found the way how to get current build flavor:
android.applicationVariants.all{ variant ->
variant.processResources.doFirst {
println '' + project.getName() + ', flavorName: ' + flavorName
//Processing my resources there
}
}

Related

Gradle task to take a build of specific build flavors (more than one)

In my app, I have more than 30 build variants. Every time when I release the app, I need to publish it to different platforms, therefore I build 5 different build variants.
Currently, I am doing this:
switch to build variant A
wait for the Gradle build
build APK/Bundle of build variant A
the same steps for B, C, E, and D.
What I am looking for is a Gradle task that just builds me these specific build variants when I run it. I know there is a task to build all build variants but it is too much for me.
I searched SO but couldn't find anything related to a point that I started to think it is impossible.
Could someone point me in the right direction? Thanks.
Calling a gradle task from another gradle task is not the best idea. You should rather describe their relationship as it usually works with all other gradle parts - by using mustRunAfter, dependsOn and so on. For your purposes you can use GradleBuild. I think you're searching for this - if I got your point right :D
I would assume that your task will look something like that (add your flavours instead of mine mocked)
task assembleFlavourBuilds(type: GradleBuild) {
description = 'creating flavour builds for the provided config'
tasks = ['assembleFree', 'assemblePro']
}
If you can use CommandLine then Gradle has good support for that.
For example :
task makeDir(type: Exec) {
workingDir "."
commandLine("cmd", "/c", "mkdir", "example")
}
Am just trying to show an example of how this work, by creating a folder here named example.
You can even add this to be automated with an already defined task, Like a build task. This can be done by using finalizeBy
tasks.named("build") { finalizedBy("makeDir") }
This will only call the task after a successful build.
And my suggestion is to make a .bat file with all the needed commands and call it in the same way as the following code :
task BuildAll(type: Exec) {
workingDir "."
commandLine("cmd", "/c", "mybat.bat")
}
And mybat.bat will contain all the needed commands to
switch build variant
build
bundle
repeat
Switch Build variant
productFlavors {
variantA {
dimension "version"
versionNameSuffix ".a"
}
variantB {
dimension "version"
versionNameSuffix ".b"
}
variantC {
dimension "version"
versionNameSuffix ".c"
}
variantD {
dimension "version"
versionNameSuffix ".d"
}
variantE {
dimension "version"
versionNameSuffix ".e"
}
}
you can use gradle task. in your root build.gradle define task like bellow:
task assembleFlavorBuilds(type: GradleBuild) {
description = 'Description of task.'
tasks = ['assembleFlavorName1Debug', 'assembleFlavorName1Release']
}
each of item in tasks are like this: "assemble"+FlavorName(first of name must be capital)+build type(Debug or Release).
for instance if you have flavors with names "free" and "paid", your task must be like this:
task assembleFlavorBuilds(type: GradleBuild) {
description = 'Description of task.'
tasks = ['assembleFreeRelease', 'assemblePaidRelease']
}
after sync project you can see this task in gradle area in idea or just click on run icon next of this task.

Gradle: How do I define build type for specific flavors only?

I have been using gradle for creating different build variants for different companies for an Android app.
For example I have build flavors:
Company1
Company2
And then I have build types:
Production
Preview
Development
So this will create 6 build variants:
Company1Production
Company1Preview
Company1Development
Company2Production
Company2Preview
Company2Development
So the question is:
Actually I don't need the development build type for company 2, I only need it for company 1.
Is there a way I can specify only company 1 have the development build type?
I have a lot of companies in my projects, some of the build type just don't make sense for those companies, and it confuses people who want to build the app.
To answer my own question, I have found the documentation on the Gradle Plugin User Guide
Filtering Variants
When you add dimensions and flavors, you can end up with variants that don't make sense. For example you may define a flavor that uses your Web API and a flavor that uses hard-coded fake data, for faster testing. The second flavor is only useful for development, but not in release builds. You can remove this variant using the variantFilter closure, like this:
android {
productFlavors {
realData
fakeData
}
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("fakeData") && variant.buildType.name == "release") {
variant.ignore = true
}
}
}
With the configuration above, your project will have only three variants:
realDataDebug
realDataRelease
fakeDataDebug
You can't stop the Android plugin from creating the matrix of all builds, but you can cause the build to fail if it's not valid. For example, if you don't want Flavor2 Debug builds to work, you can stop them like this:
afterEvaluate {
tasks['prepareFlavor2DebugDependencies'].doFirst {
throw new IllegalArgumentException("This project is not valid")
}
}

Android Studio: Gradle Product Flavors: Define custom properties

I am building different product flavors of an Android App in Gradle (Android Studio).
Hence I defined the following product flavors:
android {
project.ext.set("customer", "")
project.ext.set("server", "")
//Configuration happens here - code removed for readability
buildTypes {
debug {
server = "test"
}
release {
server = "release"
}
}
//Available product flavors
productFlavors {
customerA{
customer = "a"
}
customerB{
customer = "b"
}
customerC{
customer = "c"
}
}
}
However, later on, when I access the defined project property "customer" (whose value is set in the product flavor i am currently building) in one of my build tasks, it always has the value "c" even though iam building customerA (in which case the property customer should be "a" rather than "c"). For instance I execute the following task later on:
preBuild << {
println "Building customer: " + customer
}
and it always prints:
Building customer: c
So i am guessing there is some overwriting happening? Possibly related to the configuration VS execution phase? Not sure how/why though, so any help is be greatly appreciated.
UPDATE: Alternatively it would already get me further to determine the name of the product flavor (without the build type name attached to it) and the build type (again: without the product flavor name prepended to it) during execution phase of the gradle build.
Considering the above configuration the expected product flavor names would be: customerA, customerB and customerC.
During evaluation phase, Gradle executes all of the code in your android block; it doesn't just execute the code relevant to the flavors you want to compile. In fact, during evaluation phase, it doesn't even really know what your flavors are; it has to evaluate that to find out.
So all three of your lines customer = "a", customer = "b", and customer = "c" will get executed.
This is one of the subtle things about Gradle that make it a little difficult to learn.
So I've explained why your code isn't working the way you expect, but this answer is incomplete because I haven't said a lot about what to do to make it work right, but it's hard to say what to do because I'm not sure what you're trying to accomplish. In general I can say that you should think of trying to accomplish what you want using user-defined tasks, and setting up intra-task dependencies to make sure things get executed in the right order. A gotcha with Android Gradle builds is that even those tasks don't get defined until evaluation phase (it can't know what tasks it needs to build all your flavors until it's evaluated the build file and knows what those flavors are), so do some SO sleuthing to see how to hook things onto Android Gradle build tasks -- you have to set up your tasks at the end of evaluation phase after the Android plugin has done its thing.
A lot of thanks goes to Scott Barta, for his suggestions and for explaining, why my solution did not work (which also made me reconsider a few things). I basically came up with different ways to accomplish what I needed.
Unless what you need to do can't be achieved by simply organizing your Android Resource tree based on build types and flavors (i.e. via convention) then I'd recommend option 2. Though I did keep option 1 for reference purposes since it covers the interesting subject of productFlavor property extension.
Custom property-based option: Product Flavors lets you define custom properties and thus extend a productFlavor. An example is provided here by Xavier Ducrohet: https://stackoverflow.com/a/17708357/1041533
I'll offer up a very simple and similar example as provided above, though in my case I needed a String property, rather than a boolean.
// This class will be used to create our custom property
class StringExtension {
String value
StringExtension (String value) {
this.value = value
}
public void setValue(String value) {
this.value = value
}
public String getValue() {
return value
}
}
android {
// Add our new property to each product flavor upon creation
productFlavors.whenObjectAdded { flavor ->
//I am suspecting the last argument is the default value
flavor.extensions.create("myProperty", StringExtension , '')
}
// then we can set the value on the extension of any flavor object
productFlavors {
customerA{
myProperty.value 'customerA'
}
customerB{
myProperty.value 'customerB'
}
}
}
//Adds a custom action to the preBuild task
preBuild << {
//Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
android.applicationVariants.all { variant ->
//Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
variant.productFlavors.each { flavor ->
//Access our custom property "customerName"
println "Building customer" + flavor.customerName.value
}
}
}
I then realized, that the above was totally unnecessary, because all I wanted was the name of my flavor (without the build type in it) and once I found the property that gives me the name of my flavor, I was able to change all of the above code as follows:
Simply use the name of your flavor as the customer's name by accessing the already existent product flavor property called "name".
android {
productFlavors {
customerA{
}
customerB{
}
}
}
//Adds a custom action to the preBuild task
preBuild << {
//Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
android.applicationVariants.all { variant ->
//Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
variant.productFlavors.each { flavor ->
//Access our product flavor name
println "Building customer" + flavor.name
}
}
}
The above makes a lot more sense too, because my directory structure for Android Resources is named after the actual flavors.
The latter also led me to my final solution for the original question:
Resource directory based approach
The intent was to modify a file in the xml folder of each customer based on whether it is a release or a debug build. This can be achieved by a corresponding folder structure. Based on the original question we have 3 customers, and each customer has a debug and a release build. The afore mentioned xml files are different for each customer and build type. Hence the following directory structure:
src/
- customerA
//Contains all relevant resource files specific to customer A
- customerB
//Contains all relevant resource files specific to customer B
- customerC
//Contains all relevant resource files specific to customer C
- customerADebug
//Contains debug server-settings file for customer A
- customerBDebug
//Contains debug server-settings file for customer B
- customerCDebug
//Contains debug server-settings file for customer C
- customerARelease
//Contains release server-settings file for customer A
- customerBRelease
//Contains release server-settings file for customer B
- customerCRelease
//Contains release server-settings file for customer C
So the main content for each product flavor was in the folder with the same name as the flavor (customerA, customerB etc. see first part of above snippet). Now this one file, that different based on whether it was a debug or release build for each customer is put into the appropriate folders such as customerADebug --> contains file with server settings for debug mode etc.
And when you build customerA for instance the correct file will be chosen if you build a debug or release build.
To answer the UPDATE part of my post:
Product flavor name (without buildType):
flavor.name (where flavor is a productFlavor)
The following worked for me to add custom properties to product flavors:
android {
// ...defaultConfig...
productFlavors.whenObjectAdded { flavor ->
// Add the property 'myCustomProperty' to each product flavor and set the default value to 'customPropertyValue'
flavor.ext.set('myCustomProperty', 'customPropertyValue')
}
productFlavors {
flavor1 {
}
flavor2 {
myCustomProperty = 'alternateValue'
}
}
}
flavor1 has the default value for the custom property, while flavor2 has the overridden value.
Here's an example how to access the custom property:
applicationVariants.all { variant ->
// Get the 'myCustomProperty' property from the variant's productFlavor (it's a list, but there should only be one)
def customProp = variant.productFlavors*.myCustomProperty[0]
}
I assume the same could be done to add custom properties to build types, but I haven't tested this.

Setting app label based on buildvariant

I have an Android with multiple productFlavors. I'd like the android:label for the debug build of, for example flavor1, to say Flavor1 Debug and the release build to say Flavor1.
Without productFlavors this is relatively simple, just create a different string resource in the debug and release source folders but with productFlavors, I don't want to be creating flavor1Debug, flavor2Debug, etc. folders for each flavor with just one string resource in there.
Is there a way to do this ? I'm guessing it requires merging resources somehow but am not sure how.
For extra points, it would be awesome if I could add an overlay to the app icon i.e. merge two images but I realize that might be taking it too far.
I worked out a solution for this. Basically, you use the following method that copies read the build file, modifies the property value, then rewrites the files.
def debugAppRename(variant, labelResource) {
def flavor = variant.productFlavors.get(0)
def buildtype = variant.buildType
// Append buildType name to app label
if(buildtype.debuggable) {
variant.mergeResources << {
def valuesFile = "$buildDir/res/all/${flavor.name}/${buildtype.name}/values/values.xml"
def values = (new XmlParser()).parse(valuesFile)
values.string.each { m->
if (m.#name == labelResource) {
m.value = m.text() + " " + buildtype.name.capitalize()
new XmlNodePrinter(new PrintWriter(new FileWriter(valuesFile)))
.print(values)
}
}
}
}
}
The way to use it is:
applicationVariants.all { variant ->
debugAppRename(variant, 'app_name') // where `app_name` is the string resource you use for the `app:label` property in your AndroidManifest.xml
}
The resulting app will have the buildType name appended to it if it is a debuggable buildType, for e.g. My App Debug, My App Staging, etc. The release build remains unaffected.
I also put it up on a gist.
You can apply it in your build.gradle using the apply from: directive with the raw gist URL or copy the above to your code (personally I find build scripts become too large so I prefer applying from).

Separate source files per build variant in Gradle (Android)?

Our application has a free and a paid version. We also make branded-versions, which means that the application vary in two dimensions.
Four versions could be:
The App, Nike ed. free
The APP, Nike ed. paid
The App, Adidas ed. paid
The App, Adidas ed. free
My solution now is to have two build-types, paid and free:
buildTypes {
paid {
packageNameSuffix ".paid"
}
free {
packageNameSuffix ".free"
}
}
And two build flavors:
productFlavors{
nike{
packageName "com.example.theapp.nike"
}
adidas{
packageName "com.example.theapp.adidas"
}
}
Every free-version of the app make us of a content-provider, a content provider which is specific per flavor-build type combination. The problem is that I don't understand where to put a source file based on build variant. Source files put into /src/nike or /src/free will be picked up depending on build type or flavor. But how about source files that are depending on the build variant (the combination of type and flavor)?
You can create a new folder under src for every build or flavor that you have. i.e: 'free', 'paid', 'nike', 'adidas'.
The files that you put in any of these folders gets picked up when building depending on the type and build both.
According to Gradle Plugin User Guide on Android Tools Project Site:
Similar to Build Types, Product Flavors also contribute code and
resources through their own sourceSets.
and
The following rules are used when dealing with all the sourcesets used
to build a single APK:
All source code (src/*/java) are used together as multiple folders generating a single output.
Manifests are all merged together into a single manifest. This allows Product Flavors to have different components and/or
permissions, similarly to Build Types.
All resources (Android res and assets) are used using overlay priority where the Build Type overrides the Product Flavor, which
overrides the main sourceSet.
Each Build Variant generates its own R class (or other generated source code) from the resources. Nothing is shared between
variants.
meaning that your java files for the buildType "free" will overwrite the ones for your flavors "nike" if they have the same name.
But if you're adding something to a manifest, according to the second point in the list above the final manifest will be a merge of all of the manifests.
If you need more customization you can put your files in your build variant's folder "src/freeNike/".
I had similar problem with build types overriding flavors due to the overlay rules.
I ended up redirecting the build type source sets into different folders depending on which flavor was built.
android.applicationVariants.all { variant ->
switch (variant.name) {
case "FreeNike":
variant.mergeResources.doFirst {
android.sourceSets.free.setRoot("src/freeNike")
}
break;
case "FreeAdidas":
variant.mergeResources.doFirst {
android.sourceSets.free.setRoot("src/freeAdidas")
}
break;
case "PaidNike":
variant.mergeResources.doFirst {
android.sourceSets.paid.setRoot("src/paidNike")
}
break;
case "PaidAdidas":
variant.mergeResources.doFirst {
android.sourceSets.paid.setRoot("src/paidAdidas")
}
break;
}
}
You are of course free to use a different folder structure. See example here: Folder naming convention for gradle build variants
Have a look at newest Gradle plugin it now allows to have variant specific resources
http://tools.android.com/tech-docs/new-build-system
And here You have example of usage
https://android.googlesource.com/platform/tools/build/+/master/tests/overlay3/
Have you tried to put the srcDir in the sourceSets ?
Like so:
sourceSets {
main {
java {
srcDirs 'src/java'
}
}
}
That should output a javaResources with two source codes, nike and adidas.

Categories

Resources