I have a project lets say Project A( Main Project/ Master). I need to create same projects but with same code and different package name Project B and Project C.
If the changes are applicable to all then I will make changes in Project A, so that if I update Project B and Project C automatically they should get the changes.
But if any customization came for project level then I need to change in Project B only. It should not affect to Project A or C. Like this how can I handle all three projects using git.
Will it work with using branches.?
Thanks in Advance...
Use build variants in your code, instead of relying on multiple branches in git. This way, a single source code can be generated as multiple applications.
android {
compileSdkVersion ...
buildToolsVersion ...
defaultConfig {
applicationId "com.yourapp"
...
}
// Specify the build dimension and flavors
flavorDimensions "type"
productFlavors {
typeA {
dimension "type"
applicationIdSuffix '.typea' // Add this to differentiate the application ID
versionNameSuffix "-typea" // Add this to differentiate version naming
}
typeB {
dimension "type"
applicationIdSuffix '.typeb'
}
typeC {
dimension "type"
applicationIdSuffix '.typec'
}
}
}
It doesn't matter if your classes are in the same package, as long as your application ID differs, you can install multiple version of the app at the same time on your device.
To build each specific app, simply change the chosen variant in Android Studio.
For specific flavor implementation in your code, you can use BuildConfig.FLAVOR to check which flavor the current application is.
when (BuildConfig.FLAVOR) {
"typeA" -> {
// Do stuff for typeA app
}
"typeB" -> ...
"typeC" -> ...
}
Related
I have two applications: Application Module A and Application Module B. They both use Library Module X.
Inside Library Module X I have some different behaviors depending of which app is using the library. I thougth about three alternatives to anchieve that.
1º Hardcoded mentioning the applications. someClass would be injected by the application or just check the package. (The worst one I think):
if (someClass.isApplicationA()) {
doThis()
} else {
doThat()
}
2º Hardcoded mentioning what is about to be done. someClass would be injected by the application. (I don't like it to much either):
if (someClass.shouldDoThis()) {
doThis()
} else {
doThat()
}
3º Using flavors. I would define a flavor A for Application Module A and a flavor B for Application Module B. In Library Module X I would define both flavors. So I would put the custom code in some specific classes that have different versions for each flavor.
customClassByFlavor.justDoIt()
Everywhere I only see flavors been used for a single application. Is this a correct use of it?
The elegant way to achieve this is
Ask the app to specify a identifier / key in their manifest
Ask the app to specify a variable in their build.gradle file
defaultConfig {
resValue "string", "identifier", "identifierConstant"
}
Use the variable in you app, like you acesss a string constant
context.getString(R.string.identifier);
Pick the package name of app from default applicationId in build.gradle file
defaultConfig {
applicationId "com.google.example"
}
I have an Android App, which has two flavors: Basic and Advanced.
The Basic is already on the AppStore, with a package name of form com.domain.something. I would like to publish the second as com.domain.something.advanced. Is it possible? Or is the fact that the second is a sub package of the first would cause trouble?
Yes, this is possible and will not cause any problems in the Play Store.
You will want to read the Configure Build Variants guide for more information on setting up a product flavor for your "advanced" version. You can use the applicationIdSuffix in your build types or product flavors to set a suffix on your application ID for that particular variant.
Your build.gradle will end up looking something like this:
android {
defaultConfig {
applicationId "com.domain.something"
}
buildTypes {...}
productFlavors {
basic {...}
advanced {
applicationIdSuffix ".advanced"
}
}
}
You can then either programmatically check your product flavor with the generated BuildConfig class, or put your code for the advanced version in the advanced product flavor's source folder (/src/advanced/java, /src/advanced/res, etc.).
I am using gradle.build for auto building my app. I want to generate three different APK's each pointing to different Service URL's.
How can I make use of buildVariants (productFlavors in gradle). But I'm not able to figure out where to set the three URL's in the Gradle.
How can I do this?
It is really easy to do with gradle.
productFlavors {
first_server {
buildConfigField "String", "SERVER_URL", "\"https://first_server_url/\""
}
second_server {
buildConfigField "String", "SERVER_URL", "\"https://second_server_url/\""
}
}
You may want to find more information here.
So later you can easy access this variable by BuildConfig.SERVER_URL
You can use as like following,
In Gradle:
productFlavors{
serverone {
applicationId "com.example.krishna.mysample.serverone"
version 1.1
}
servertwo {
applicationId "com.example.krishna.mysample.servertwo"
version 1.1
}
serverthree {
applicationId "com.example.krishna.mysample.serverthree"
version 1.1
}
}
In folder structure In App:
src
-->main
-->serverone
-->servertwo
-->serverthree
If MainActivity is required to different logic then, that MainActivity is place in serverone, servertwo and serverthree folder remaining classes are keep in main folder only. And do diffent functionality on that Activities.
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.
I want to have 3 product flavors, and one of them will have less language support than Main.
For example, only support /values-fr.
Is there a filter function in Gradle? Thanks.
From Android Gradle Build System, since version 0.7.0:
New option on product Flavor (and defaultConfig) allow filtering of resources through the -c option of aapt
You can pass single value (resConfig) or multiple values (resConfigs) through the DSL.
All values from the default config and flavors get combined and passed to aapt.
See "basic" sample.
In the "basic" sample:
defaultConfig {
...
resConfig "en"
resConfigs "nodpi", "hdpi"
}
So, try the following to achieve what you asked for:
productFlavors {
...
frOnly {
resConfig "fr"
}
...
}
Note that you might also want to include *dpi, port, land, etc.. as well