27

I have and old android app that I am trying to migrate to the android gradle build system. The app is currently built in a multi project setup and published as four different apps (two different data sets included and free/paid versions for both datasets). I have managed to get away from the multi project setup by using flavorDimensions (previously called flavorGroups), but I can not figure out how to set a different applicationId for each flavor combination.

Since the app versions are already published, I need to keep the same applicationid as they currently have. Because of how my original package naming was done, I can not simply use flavor-buildtype combination with "packageNameSuffix" (which would have been a great option if it was an unpublished app).

https://stackoverflow.com/a/20956353/4177090 is answering how to use different source folders for flavor combinations, but not how (if even possible) to set specific configuration for each combination in the build file.

Gradle build file snippet:

flavorDimensions "price", "dataset"

productFlavors {
    free { flavorDimension "price" }
    paid { flavorDimension "price" }
    dataset1 { flavorDimension "dataset" }
    dataset2 { flavorDimension "dataset" }
}

I would like to have something like the following in my gradle build file (notice how unlogic my naming is, which is why I cannot use packageNameSuffix):

freeDataset1 { applicationId "com.beansys.freeappdataset1" }
freeDataset2 { applicationId "com.beansys.freedataset2" }
paidDataset1 { applicationId "com.beansys.dataset1paid" }
paidDataset2 { applicationId "com.beansys.mypaiddataset2" }
Community
  • 1
  • 1
Fredrik
  • 487
  • 5
  • 12
  • Why don't you just ditch the dimensions and create separate flavors? – Simas Oct 24 '14 at 13:54
  • Thank you for your suggestion. I could do that, but in that case I would have to duplicate the flavor source folders, i.e. dataset1 data would be duplicated to both folder freeDataset1 and to folder paidDataset1. If duplicating code and data is my only option, I'd rather go "back" to using a multi project solution (not very clean, but at least better than duplicating my source folder content). – Fredrik Oct 24 '14 at 14:04
  • Why'd you link the answer which suggests multiple folders then? – Simas Oct 24 '14 at 14:18
  • I linked to that answer since it shows how "flavor combination" source folders are used (i.e. that it since android plugin version 0.7.0 is possible to use source folders with combined flavor names). The question in the comment from mm2001 to that answer is however very close to my question. Sadly, mm2001 has not yet got any response to that question which is why I thought it would be a good idea to give a bit more extensive explanation of the problem here. – Fredrik Oct 24 '14 at 14:33

4 Answers4

29

The solution proposed by Fredrik stopped working after upgrading Android Studio to 1.0.2 (and gradle plugin to 1.0.0) so I had to add following changes, current as of gradle plugin 1.3.1:

flavorDimensions "price", "dataset"

productFlavors {
    free { dimension "price" }
    paid { dimension "price" }
    dataset1 { dimension "dataset" }
    dataset2 { dimension "dataset" }
}

android.applicationVariants.all { variant ->
    def mergedFlavor = variant.mergedFlavor
    switch (variant.flavorName) {
        case "freeDataset1":
            mergedFlavor.setApplicationId("com.beansys.freeappdataset1")
            break
        case "freeDataset2":
            mergedFlavor.setApplicationId("com.beansys.freedataset2")
            break
        case "paidDataset1":
            mergedFlavor.setApplicationId("com.beansys.dataset1paid")
            break
        case "paidDataset2":
            mergedFlavor.setApplicationId("com.beansys.mypaiddataset2")
            break
    }
}
Joe Bowbeer
  • 3,574
  • 3
  • 36
  • 47
Iwo Banas
  • 892
  • 8
  • 12
  • 2
    This is how I did it too. I try to convert to Kotlinscript but the applicationId of the mergedFlavor is a val and therefore doesnt offer a setter. Do you have an idea how to fix this? – Henning Feb 06 '20 at 15:32
  • Solution for Gradle Kotlin DSL is here: https://stackoverflow.com/a/60103604/2793394 – Henning Feb 17 '20 at 07:17
8

I finally managed to solve this. I think the solution is elegant (although the actual code could most likely be written a lot nicer by someone with groovy knowledge).

Solution for setting a specific applicationId for each combined flavor:

flavorDimensions "price", "dataset"

productFlavors {
    free { flavorDimension "price" }
    paid { flavorDimension "price" }
    dataset1 { flavorDimension "dataset" }
    dataset2 { flavorDimension "dataset" }
}

android.variantFilter { variant ->
    def flavorString = ""
    def flavors = variant.getFlavors()
    for (int i = 0; i < flavors.size(); i++) {
        flavorString += flavors[i].name;
    }
    if(flavorString.equalsIgnoreCase("freeDataset1")) {
        variant.getDefaultConfig().applicationId "com.beansys.freeappdataset1"
    }
    if(flavorString.equalsIgnoreCase("freeDataset2")) {
        variant.getDefaultConfig().applicationId "com.beansys.freedataset2"
    }
    if(flavorString.equalsIgnoreCase("paidDataset1")) {
        variant.getDefaultConfig().applicationId "com.beansys.dataset1paid"
    }
    if(flavorString.equalsIgnoreCase("paidDataset2")) {
        variant.getDefaultConfig().applicationId "com.beansys.mypaiddataset2"
    }
}
Fredrik
  • 487
  • 5
  • 12
1

I had problems converting the answers given to the gradle kotlin dsl. I found a solution and shared it here: https://stackoverflow.com/a/60103604/2793394

Henning
  • 2,202
  • 1
  • 17
  • 38
1

With AGP 7.0, you should use onVariants:

androidComponents {
   onVariants(selector().all(), { variant ->
      switch (variant.name) {
         case "freeDataset1":
            variant.applicationId = "com.beansys.freeappdataset1"
            break
         case "freeDataset2":
            variant.setApplicationId = "com.beansys.freedataset2"
            break
         ...
         ...
         ...
      }
   })
}

You can even pass your custom "selector" and do something for example for only a specific variant:

androidComponents {
   onVariants(selector().withName("paidDataset1"), { variant ->
      variant.applicationId = "com.beansys.dataset1paid"
   })
}
PesaThe
  • 7,259
  • 1
  • 19
  • 43