How to define custom source set in android gradle.kts? - android

I want to release my application in three stores. I have the rate section and update dialog in my application (which has a button to go to the store for update application). to manage these features I decide to make three flavors and three sources set to have different implementations of rate and update sections in different kotlin file.
this is my Gradle file:
android {
flavorDimensions("appStores")
productFlavors{
create("p1Flavor"){
dimension="appStores"
sourceSets {
create("p1SourceSet"){
java.srcDirs("src/main/java","src/firstStoreDir")
}
}
}
create("p2Flavor"){
dimension="appStores"
sourceSets {
create("p2SourceSet"){
java.srcDirs("src/main/java","src/secondStoreDir")
}
}
}
create("p3Flavor"){
dimension="appStores"
sourceSets {
create("p3SourceSet"){
java.srcDirs("src/main/java","src/thirdStoreDir")
}
}
}
}
}
I want to add a directory that contains two files (updateStore.kt and rate.kt) to src for every flavor. I mean I have three directories with three different names which every directory contains updateStore.kt and rate.kt with different implementations.
when I build my project I get this error:
The SourceSet 'p1SourceSet' is not recognized by the Android Gradle Plugin. Perhaps you misspelled something?
What should I do?

I realized that using source set is not appropriate for this situation and i decide to use buildConfigField.
android {
flavorDimensions("appStores")
productFlavors{
create("p1Flavor"){
dimension="appStores"
buildConfigField("String","APP_STORE_NAME","\"p1store\"")
}
create("p2Flavor"){
buildConfigField("String","APP_STORE_NAME","\"p2store\"")
dimension="appStores"
}
create("p3Flavor"){
buildConfigField("String","APP_STORE_NAME","\"p3store\"")
dimension="appStores"
}
}
}
and every where that i need to understand which flavor is selected, i use these codes :
when(BuildConfig.APP_STORE_NAME){
"p1store" -> {
//TODO
}
"p2store" -> {
//TODO
}
"p3store" ->{
//TODO
}
}

Related

Replace Java file from main in just one product flavor

I'm trying to configure a build for Android. The project has many flavors, and in one flavor, I want to replace some classes from main. The structure is like this:
main - MyClass.java
flavorA, flavorB... flavorF use main's MyClass.java, no override
flavorG needs its own MyClass.java
I don't want to copy MyClass.java into each flavor's directory & remove it from main entirely, that would not be maintainable. Is there a way to use the exclude command to accomplish this? Something like...
+ src
+ main
+ java
MyClass.java
other common files
AndroidManifest.xml
+ flavorA
+ java
other flavor-specific files
AndroidManifest.xml
+ flavorG
+ java
MyClass.java
other flavor-specific files
AndroidManifest.xml
With a gradle setup like this...
productFlavors {
flavora {
}
...
flavorg {
}
}
sourceSources {
flavora {
}
flavorb {
}
...
flavorg {
main.java {
exclude 'com/myapp/mypackage/MyClass.java'
}
}
}
The above doesn't work, am I doing something wrong? Is there another alternative?
You can use the following notation to control the JAR manifest (and specifically the 'Main-Class' attribute)
jar {
manifest {
attributes 'Main-Class': 'com.example.MyMain'
}
}
And like everything in Gradle, it's a groovy script, so you can wrap portions of it with conditions to fork the behavior according to your needs.
Assuming you're passing the flavor as a -P flag (See Gradle properties and system properties) e.g. gradle build -Pflavor=flavorg the following should work:
jar {
manifest {
if (flavor == "flavorg") {
attributes 'Main-Class': 'com.example.FlavoredMain'
} else {
attributes 'Main-Class': 'com.example.GenericMain'
}
}
}
P.S, keep in mind that the Jar closure as stated above is evaluated in configuration time, not execution timel meaning the main class condition is decided before tasks are being evaluated (See Build lifecycle).
Android
Per #Opals comment (sorry didn't notice that). To address the same issue in Android the quickest option is to have an AndroidManifest.xml per flavorg and the rest
android {
sourceSets {
main {
manifest.srcFile 'main/AndroidManifest.xml'
}
flavorg {
manifest.srcFile 'main/other/AndroidManifest.xml'
}
}
}
In the AndroidManifest.xml you have your main activity defined.
A better option would be to ensure you adhere to a proper folder structure
+ src
+ main
+ java
+ res
+ AndroidManifest.xml
+ flavorg
+ java
+ res
+ AndroidManifest.xml
The AndroidManifest.xml in flavorg should contain only the difference from the main AndroidManifest.xml (namely the main activity change) and they will be merged according to the selected flavor
I suggest you review the following Merge multiple manifest files and Gradle flavors for android with custom source sets - what should the gradle files look like?
Android - Revised question
I would instead use a factory pattern that returns the right instance of "MyClass" in accordance to the activated flavor:
productFlavors {
main {
buildConfigField "string", "APP_FLAVOR", "main"
}
flavorg {
buildConfigField "string", "APP_FLAVOR", "flavorg"
}
}
Then, in the factory you can use:
if (BuildConfig.APP_FLAVOR.Equals("flavorg") {
return new FlavorgMyClass();
}
return new MyClass();
See Gradle Tips, specifically section "Share custom fields and resource values with your app's code"

How to change Android Studio's default build flavor?

I got a project configured with multiple variants and flavors:
buildTypes {
debug {
}
release {
}
}
flavorDimensions "default"
productFlavors {
mock {
}
alpha {
}
beta {
}
prod {
}
}
Whenever I open the project from another one (so starting Android Studio), it selects the mockDebug variant by default. Often I end up build this one first, then realizing I'm on the wrong variant.
Is there a way to tell Android Studio to defaults to one variant, let's say betaDebug?
Technicals: Android Studio 3.1.4, Gradle wrapper 4.4, Android Gradle 3.1.4.
With Android Studio 3.5+ you can set default falvors:
android {
flavorDimensions "stage", "target"
productFlavors {
develop {
getIsDefault().set(true) // that does the magic
dimension "stage"
...
When using KTS it lookes like this:
android {
flavorDimensions("stage", "target")
productFlavors {
create("develop") {
isDefault = true
dimension("stage")
...
Change the order in which you define them in productFlavors. The IDE always loads the first flavor it finds there as the default.
What actually worked for me is enabling "Android Studio Preferences -> Experimental -> Only sync the active variant". It keeps the selected build variant when reopening AS or when re-syncing, basically solving the original problem.
AS/AGP v4.1.

launch two different apk from one android project

I want to launch two different android apps from one neutral project but the installed app will be only neutral one, and in that there will be two different buttons to launch(open in that app only) resp. project.
you need use flavores in gradle.
apply plugin: 'com.android.application'
android {
defaultConfig {
//...
}
buildTypes {
//...
}
productFlavors {
app1 {
// config for app 1
}
app2 {
// config for app 2
}
}
}
dependencies {
//...
}
see this link for learn about flavors

Why omitted flavor can not be inferred automatically?

While trying to run gradle's assemble task for specific build variants, I've noticed some weird thing. Consider next configuration:
android {
buildTypes {
release {
}
}
productFlavors {
flavorDimensions "shape", "color"
rect {
dimension "shape"
}
circle {
dimension "shape"
}
red {
dimension "color"
}
green {
dimension "color"
}
}
}
In such case build system recognizes tasks like assembleRect, assembleRectRed, assembleRectRedRelease.
But when I ask for assembleRectRelease it claims that there is no such task. IMO it is pretty clear that I want to assemble RectRedRelease and RectGreenRelease artifacts, isn't it?
Does anybody know what is the reasons of such behavior? Or maybe there are some build system limitations?
P.S. This is related at least to gradle plugin version 2.1.0.

Android build.gradle importing flavors from another file

I have put the product Flavors into another file called other.gradle and it looks like this:
project.ext.myflavors = {
mock {
applicationId "com.mysite.mock"
}
}
and i am able to successfully access the closure in my build.gradle file like this:
myflavors()
but i get an error that mock method is not defined.
Error:Gradle DSL method not found: 'mock()'
Is there no way to just define code from another file and import the code itself into the build file ? Or how can i import the flavors from another file ?
The build flavors could be defined in a separate file (build_flavors.gradle) like this:
android {
productFlavors {
flavorA {
// ...
}
flavorB {
// ...
}
}
}
and then imported into build.gradle:
apply plugin: 'com.android.application'
apply from: './build_flavors.gradle'
android {
// the rest of your android configuration
}
It is perfectly fine to access productFlavors multiple times. So adding some or all flavors in a script included by your build script will work.
Create a Gradle script that contains the logic that decides which flavor(s) should be added:
if (someCondition()) {
android {
productFlavors {
one {
applicationId = 'com.example.oneapp'
}
}
}
} else {
android {
productFlavors {
two {
applicationId = 'com.example.twoapp'
}
}
}
}
Now include this script from your build script. Reusing the scripts is easy if you put it under (a subfolder of) the root project. For example:
apply from: rootProject.file('build-scripts/flavor-picker.gradle')
Note that your IDE may not notice changes to the flavor selection script or gradle.properties, so if you make changes to them you will probably have to manually reimport the Gradle files to see the correct set of available tasks.

Categories

Resources