Fabric Beta and APK splits - android

I'm splitting my app based on ABI, not on density, like so:
splits {
abi {
enable true
reset()
include 'x86', 'armeabi', 'armeabi-v7a', 'mips', 'arm64-v8a'
universalApk true
}
}
I have multiple flavors, and 2 build types (debug and release). I want to put the universal apk file, that has native libs for all platforms, up on fabric beta. From what I understand, this is supported through the ext.betaDistributionApkFilePath attribute.
I can define this either at the buildType level, or at the flavor level. The problem is I need both build type and flavor to pick up my variant - something like this:
android.applicationVariants.all { variant ->
variant.ext.betaDistributionApkFilePath = "${buildDir}/outputs/apk/app-${variant.productFlavors[0].name}-universal-${variant.buildType.name}.apk"
}
or
gradle.taskGraph.beforeTask { Task task ->
if(task.name ==~ /crashlyticsUploadDistribution.*/) {
System.out.println("task name: ${task.name}");
android.applicationVariants.all { variant ->
System.out.println("match: crashlyticsUploadDistribution${variant.name}");
if(task.name ==~ /(?i)crashlyticsUploadDistribution${variant.name}/){
ext.betaDistributionApkFilePath = "${buildDir}/outputs/apk/app-${variant.productFlavors[0].name}-universal-${variant.buildType.name}.apk"
System.out.println(ext.betaDistributionApkFilePath);
}
}
}
Unfortunately this doesn't seem to work - is there any way to do this currently?

For every existing variant you can add an action that will execute before Crashlytics tasks and set ext.betaDistributionApkFilePath according to the variant name. That's how it looks like:
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
// Filter is null for universal APKs.
def filter = output.getFilter(com.android.build.OutputFile.ABI)
if (filter == null) {
tasks.findAll {
it.name.startsWith(
"crashlyticsUploadDistribution${variant.name.capitalize()}")
}.each {
it.doFirst {
ext.betaDistributionApkFilePath = output.outputFile.absolutePath
}
}
tasks.findAll {
it.name.startsWith(
"crashlyticsUploadSymbols${variant.name.capitalize()}")
}.each {
it.doFirst {
ext.betaDistributionApkFilePath = output.outputFile.absolutePath
}
}
}
}
}

Related

Set noCompress for only one productFlavor

I'm trying to set androidResources.noCompress for only one product flavor
When I try this
flavorDimensions "ring"
productFlavors {
innerRing {
dimension "ring"
}
outerRing {
dimension "ring"
androidResources {
noCompress 'so'
}
}
}
Both innerRing and outerRing end up with .so files uncompressed. I believe this is due to gradle configuring all the product flavors statically. (see here)
But when I try to change noCompress afterEvaluate
afterEvaluate {
android.applicationVariants.all { variant ->
def ring = variant.getProductFlavors().get(0).name
if (ring == "outerRing") {
println("Don't compress .so files for outer ring build")
android.androidResources.noCompress = ['so']
}
}
}
I get this error
com.android.build.gradle.internal.dsl.AgpDslLockedException:
It is too late to modify internalNoCompressList
It has already been read to configure this project.
Consider either moving this call to be during evaluation,
or using the variant API.
How can I use the variant API to fix this?
Any help is appreciated!
If you are on AGP 7.2
android {
...
androidComponents {
onVariants(selector().all()) { variant ->
if(variant.name.startsWith("outerRing")) {
variant.androidResources.noCompress.add('so')
}
}
}
...
}
If AGP is below 7.2 then from the documentation theoretically the following code should work, but I've had no success with it
android {
...
androidComponents {
onVariants(selector().all()) { variant ->
if(variant.name.startsWith("outerRing")) {
def aaptParams = variant.androidResources.aaptAdditionalParameters
aaptParams.add("-0")
aaptParams.add("so")
}
}
}
...
}

Disable DexGuard only for certain product flavors

I use DexGuard and have 4 product flavors (prod, qa, dev, mock) for 2 build types (release and debug) and I don't enable DexGuard for debug builds and hence I get 4 variants (dexguardProdRelease...dexguardMockRelease) however, I don't want to enable DexGuard for dev and mock flavors.
I learnt about variantFilter that Gradle provides however, I don't know how to use that command in relation to a plugin, DexGuard, in this case.
I was able to solve like so:
//added to filter dexguard for these flavors
variantFilter { variant ->
def names = variant.flavors*.name
if(names.contains("mock") && variant.buildType.name == "release") {
variant.ignore = true
} else if (names.contains("dev") && variant.buildType.name == "release") {
variant.ignore = true
}
}
And now, Gradle doesn't show tasks like dexguardMockRelease or dexguardDevRelease.
I was approaching this problem incorrectly by looking at filtering out DexGuard instead I should be looking at removing certain flavors. Here are the relevant parts of my build.gradle:
apply plugin: 'dexguard'
...
android {
buildTypes {
release {
proguardFile getDefaultDexGuardFile('dexguard-release-aggressive.pro')
proguardFile 'dexguard-project.pro'
}
debug {
//dontshrink, dontoptimize and dontobfuscate are turned off
}
}
productFlavors {
prod {
...
}
qa {
...
}
dev {
...
}
mock {
...
}
}
//added to filter dexguard for these flavors
variantFilter { variant ->
def names = variant.flavors*.name
if(names.contains("mock") && variant.buildType.name == "release") {
variant.ignore = true
} else if (names.contains("dev") && variant.buildType.name ==
"release") {
variant.ignore = true
}
}
}

Gradle: android.variantFilter cause build fail after upgrade to Gradle 2.2.1

I am working in an Android project which has many flavors, I used this code in build.gradle to set the package name for each flavor:
flavorDimensions "type", "feature"
productFlavors {
abc { flavorDimension "type" }
def { flavorDimension "type" }
ABC { flavorDimension "feature" }
DEF { flavorDimension "feature" }
}
android.variantFilter { variant ->
def flavorString = ""
def flavors = variant.getFlavors()
for (int i = 0; i < flavors.size(); i++) {
flavorString += flavors[i].name;
}
if(flavorString.equalsIgnoreCase("abcABC")) {
variant.getDefaultConfig().applicationId "com.my.app.abc.abc"
}
if(flavorString.equalsIgnoreCase("abcDEF")) {
variant.getDefaultConfig().applicationId "com.my.app.abc.def"
}
if(flavorString.equalsIgnoreCase("defABC")) {
variant.getDefaultConfig().applicationId "com.my.app.def.abc"
}
if(flavorString.equalsIgnoreCase("defDEF")) {
variant.getDefaultConfig().applicationId "com.my.app.def.def"
}
}
Everything was fine until I updated my gradle from 1.10.0 to 2.2.1, the build is failed and I get this error message:
Error:(63, 0) No signature of method: com.android.build.gradle.internal.api.ReadOnlyProductFlavor.applicationId() is applicable for argument types: (java.lang.String) values: [com.hac.apps.megahd.acc]
Possible solutions: getApplicationId()
It seems like the variant.getDefaultConfig().applicationId is working different now. I searched the internet for the document for android.variantFilter but it seems not to be exist.
Anyone can tell me how do I get this code work in gradle 2.2.1? Thanks a lot.
The variantFilter is meant to be used only to allow or disallow builds of certain combinations. The defaultConfig object is shared and read-only. If you want to assign applicationId's to variants, I think you should something more similar to this:
buildTypes {
applicationVariants.all { variant ->
def projectFlavorNames = []
variant.productFlavors.each() { flavor ->
projectFlavorNames.add(flavor.name)
}
project.logger.debug('Application variant ' + variant.name + '. Flavor names list: ' + projectFlavorNames)
if (projectFlavorNames.contains('customer1') && projectFlavorNames.contains('variant1')) {
variant.mergedFlavor.applicationId = 'com.customer1.variant1'
} else if (projectFlavorNames.contains('customer2') && projectFlavorNames.contains('variant2')) {
variant.mergedFlavor.applicationId = 'com.customer2.variant2'
} // else use standard package name
project.logger.debug('Using project name: ' + variant.packageName)
}
// ...
}
From Dynamically generate package name for multi-flavors configuration

Exclude specific build variants

I have the two default build types: debug / release and a couple of flavors: prod / dev.
Now I want to exclude the build variant dev-release, but keep all other possible combinations. Is there a way to achieve this?
Variant filter
Use the variantFilter of the gradle android plugin to mark certain combinations as ignored. Here is an example from the official documentation that works with flavor dimensions and shows how it can be used:
android {
...
buildTypes {...}
flavorDimensions "api", "mode"
productFlavors {
demo {...}
full {...}
minApi24 {...}
minApi23 {...}
minApi21 {...}
}
variantFilter { variant ->
def names = variant.flavors*.name
// To check for a certain build type, use variant.buildType.name == "<buildType>"
if (names.contains("minApi21") && names.contains("demo")) {
// Gradle ignores any variants that satisfy the conditions above.
setIgnore(true)
}
}
}
As the comment says, you can also check the buildType like so:
android {
variantFilter { variant ->
def names = variant.flavors*.name
if(variant.buildType.name == 'release' && names.contains("myforbiddenflavor")) {
setIgnore(true)
}
}
}
Using variant filters like others I found it was easiest to do this by comparing the variant name against a list of variants that I want to keep.
So in my app/build.gradle file I have something like:
android {
variantFilter { variant ->
def needed = variant.name in [
'stagingQuickDebug', // for development
'stagingFullDebug', // for debugging all configurations
'stagingFullCandidate', // for local builds before beta release
'stagingFullRelease', // for beta releases
'productionFullCandidate', // for local builds before going public
'productionFullRelease' // for public releases
]
variant.setIgnore(!needed)
}
buildTypes {
debug {
}
release {
}
candidate.initWith(release)
}
flavorDimensions "server", "build"
productFlavors {
staging {
dimension "server"
buildConfigField "String", "API_URL", '"https://example-preprod.com/"'
}
production {
dimension "server"
buildConfigField "String", "API_URL", '"https://example.com/"'
}
quick {
dimension "build"
minSdkVersion 21
resConfigs("en", "xxhdpi")
}
full {
dimension "build"
}
}
}
When working with flavor dimensions try this one
variantFilter { variant ->
def dim = variant.flavors.collectEntries {
[(it.productFlavor.dimension): it.productFlavor.name]
}
if (dim.dimensionOne == 'paid' && dim.dimensionSecond == 'someVal') {
variant.setIgnore(true);
}
}
If you use flavor dimensions do this:
flavorDimensions "device", "server"
productFlavors {
emulator {
dimension = "device"
}
phone {
dimension = "device"
}
staging {
dimension = "server"
}
production {
dimension = "server"
}
}
android.variantFilter { variant ->
def device = variant.getFlavors().get(0).name
def server = variant.getFlavors().get(1).name
def isRelease = variant.buildType.name.equals('release')
def isDebug = variant.buildType.name.equals('debug')
// Disable emulatorProductionRelease build variant
if (device.equals('emulator') && server.equals('production') && isRelease) {
variant.setIgnore(true)
}
}
It's easy to read and you can target specific build variants.
The solutions here didn't work for me - I run into this post and added this to build.gradle in my app and it solved the issue for me
gradle.taskGraph.whenReady { graph ->
graph.allTasks.findAll { it.name ==~ /.*MyVariant.*/ }*.enabled = false
}
This is what it does - waits for gradle to assemble the complete list of tasks to execute and then it marks all the tasks that match the name pattern as disabled
NOTE
The match is exact - the expression above lets you match any task that has "MyVariant" somewhere in it's name and it is case sensitive
One more simpler way
android.variantFilter { variant ->
if (variant.name == "qaDebug" || variant.name == "devRelease") {
setIgnore(true)
}
}
Or if you place this code inside android {} closure, android. can be omitted
android {
// Please always specify the reason for such filtering
variantFilter { variant ->
if (variant.name == "qaDebug" || variant.name == "devRelease") {
setIgnore(true)
}
}
}
Please always put a meaningful comment for things like this.
UPD: For Kotlin Gradle DSL there is another way:
android {
variantFilter {
ignore = listOf("qaDebug", "devRelease").contains(name)
}
}
The answer of #ade.se didn't work for me. But I've struggled a little, and written this, that works great:
android {
compileSdkVersion 22
buildToolsVersion '20.0.0'
variantFilter { variant ->
if (variant.buildType.name.equals('debug') || variant.buildType.name.equals('release')) {
variant.setIgnore(true);
}
}
defaultConfig {
applicationId "com.fewlaps.quitnow"
minSdkVersion 15
targetSdkVersion 22
versionCode 35
versionName "1.35"
}
The code you have to add is the variantFilter one, but I've pasted a little of the context to make it easy to understand.
See Variant filter answer above.
Old Answer:
It's not possible at the moment, but it's something we want to add. Probably soon.
In the meantime you could disable the assemble task I think. Something like this:
android.applicationVariants.all { variant ->
if ("devRelease".equals(variant.name)) {
variant.assembleTask.enabled = false
}
}
In Gradle's Kotlin DSL (i.e. build.gradle.kts), that would be:
variantFilter {
ignore = buildType.name == "release" &&
flavors.map { it.name }.contains("dev")
}

Android Gradle - Add packageNameSuffix on specific productFlavor

I have a Gradle Android Project with this product Groups and Flavors configuration:
/*
* Define different flavor groups
*/
flavorGroups 'market', 'version'
/*
* Defile product flavors
*/
productFlavors {
amazon {
flavorGroup 'market'
}
google {
flavorGroup 'market'
}
flav1 {
flavorGroup 'version'
packageName 'com.company.flav1'
}
flav2 {
flavorGroup 'version'
packageName 'com.company.flav2'
}
flav3 {
flavorGroup 'version'
packageName 'com.company.flav3'
}
}
// .. Other stuff
It works great. All sources and resources are merged correctly.
But for specific reasons I need the package suffix to be .amz for the amazon product flavor. How can I achieve that?
I tried this way:
amazon {
flavorGroup 'market'
packageNameSuffix '.amz'
}
but gradle throws an exception.
This is not possible at the moment.
packageNameSuffix is strictly a property of Build Type.
I'd like to offer a way to customize the ProductFlavor's values for a given variant (ie a given combination of all flavor dimensions and of build type) but it's not possible at the moment.
Instead of a new dimension of flavor, you could do a "amzRelease" buildtype that extends the existing release buildtype and add a suffix.
If your current "amazon" flavor does more (configure versionCode/Name/etc...), it won't work though. You could then use both your amazon flavor and a amzRelease build type. It'll create a lot more variants than you need but it'd work until we have some better.
You could use a variant of the solution I've written about here: https://stackoverflow.com/a/26585241/4177090
In short, you can find the combined variants using variantFilter and then update the appliciationId from there:
android.variantFilter { variant ->
def flavorString = ""
def flavors = variant.getFlavors()
for (int i = 0; i < flavors.size(); i++) {
flavorString += flavors[i].name;
}
if(flavorString.contains("amazon")) {
variant.getDefaultConfig().applicationId variant.getDefaultConfig().applicationId + ".amz"
}
}
Working solution for gradle 1.0.0+:
android.applicationVariants.all { variant ->
def flavorName = variant.getVariantData().getVariantConfiguration().getFlavorName()
def mergedFlavour = variant.getVariantData().getVariantConfiguration().getMergedFlavor();
if (flavorName.toLowerCase().contains("amazon")) {
mergedFlavour.setApplicationId(mergedFlavour.getApplicationId() + ".amz")
}
}

Categories

Resources