17

I'm setting up the product flavors in my app and have run into one problem. Two of my product flavors are very similar to each other and only differ by a few resources, let's call them FlavorA and FlavorB. I would like to set it up so that FlavorA is the parent of FlavorB, that way FlavorB can just override a few resources of FlavorA, and then FlavorA overrides a bunch of resources from main. Is there a way to set up a flavor hierarchy such as this? Right now I need to duplicate lots of resources between the two in order for it to work and I would like to eliminate that duplication.

edit: OK. I've had a couple answers so far (both deleted) that were not related to my question so let me clarify. This does not have to do with Java OOP, I already know that quite well. :)

The issue I'm having is that I have a free/paid version of my app, and then several white labels that only include theme changes. The paid version only changes a few assets from the free version (apart from code changes). This is what my productFlavors look like:

productFlavors {
        whiteLabelA {
            applicationId "com.whiteLabelA.android"
        }

        whiteLabelB {
            applicationId "com.whiteLabelB.android"
        }

        mainFree {
            applicationId "com.mainFree.android"
        }

        mainPaid {
            applicationId "com.mainFree.android.paid"
        }
    }

I would like mainPaid to override mainFree since it only changes a few resources. I would like to do it this way rather than a free/paid build type because the white labels don't have paid versions, and it would be easier to have a flavor hierarchy like:

mainPaid extends mainFree {
    applicationId "com.mainFree.android.paid"
}
telkins
  • 10,440
  • 8
  • 52
  • 79
  • @travor-e Did you ever find an answer to this question? – Sean Barbeau Aug 18 '14 at 21:07
  • 1
    @SeanBarbeau Kind of. There's no answer yet as to whether flavors can be extended or not. I was able to solve my problem in a way that didn't require extending flavors so I left the question open. FYI, I solved my problem by putting my mainFree flavor resources in my main resources folder, since I realized that it should be the default build anyways. – telkins Aug 18 '14 at 21:18
  • 2
    Build types have an `initWith` command that allows duplication of a gradle configuration block (though the dependent build type will still need its own source sets), but as far as I am aware, there is no equivalent for flavors. – stkent Jan 12 '15 at 16:45
  • here is a link with some tips to have this behaviour. I need exactly the same but find no solution until now : https://groups.google.com/forum/?hl=fr#!topic/adt-dev/f2I9LQIogOM – Tobliug Nov 03 '15 at 23:20

3 Answers3

10

I found two solutions:

1. Use flavor dimensions.

android {
  ...
  flavorDimensions 'palette', 'color'

  productFlavors {
    blackAndWhite {
      dimension 'palette'
    }
    redAndWhite {
      dimension 'palette'
    }
    red {
      dimension 'color'
    }
    white {
      dimension 'color'
    }
  }
  variantFilter { variant ->
    def first = variant.getFlavors().get(0).name
    def second = variant.getFlavors().get(1).name
    if(!(first.equals('blackAndWhite') && second.equals('white')) ||
       !(first.equals('redAndWhite') && (second.equals('white') || second.equals('red')))) {
       variant.setIgnore(true);
    }
  }
  ...
}

Because of variantFilter we have 3 combinations instead of 4:

blackAndWhite => white

redAndWhite   => white
              => red

You can consider that as red extending redAndWhite and white extending blackAndWhite or redAndWhite.

This answer and variant filter documentation were helpful.

2. Edit source set.

productFlavors{
    flavor1 {
    }
    flavor2 {
    }
}

sourceSets {
    flavor2 {
        java.srcDirs = sourceSets.flavor1.java.srcDirs
        res.srcDirs = sourceSets.flavor1.res.srcDirs
        resources.srcDirs = sourceSets.flavor1.resources.srcDirs
        aidl.srcDirs = sourceSets.flavor1.aidl.srcDirs
        renderscript.srcDirs = sourceSets.flavor1.renderscript.srcDirs
        assets.srcDirs = sourceSets.flavor1.assets.srcDirs
    }
}

Code example was shamelessly copied from this blog post. Big thanks to its author.

Community
  • 1
  • 1
mixel
  • 25,177
  • 13
  • 126
  • 165
  • Good answer which shows how to handle inheritance of code, but for handling inheritance of **dependencies**, see my answer here: https://stackoverflow.com/questions/45579298/android-gradle-sharing-dependencies-between-product-flavors/54745210#54745210 – Jonathan Ellis Feb 18 '19 at 10:42
3

I was looking for a similar thing in gradle and found Multi-flavor variants. I have an app that should have versions A and B, and each version has dev and pro environments, so I ended up with this in my gradle:

flavorDimensions 'app', 'environment'

productFlavors {
    versionA {
        flavorDimension 'app'
    }
    versionB {
        flavorDimension 'app'
    }
    pre {
        flavorDimension 'environment'
    }
    pro {
        flavorDimension 'environment'
    }
}

And in my build variants I have versionAPreDebug, versionAPreRelease, versionBPreDebug, versionBPreRelease, etc. I think what you need is something like that.

allo86
  • 946
  • 1
  • 9
  • 23
2

I think you won't be able to do this in the android object directly, but you can use a configuration function to set common properties on a flavor. Put this after the android object:

void configureWhiteLabelFlavor(flavor) {
    flavor.useJack = true
}

configureWhiteLabelFlavor(android.productFlavors.whiteLabelA)

If needed, you can implement deep inheritance by calling other functions from this function.

Johan Stuyts
  • 3,607
  • 1
  • 18
  • 21