Custom Gradle Test Task For Android - android

Problem
I want to create a custom gradle test task to only run JUNIT tests and omit Robolectric tests. I am attempting to achieve this task by creating a new test annotation and omitting any tests that include that new annotation.
Error
JUNIT packages are not included when I run the the gradle task.
error: package android.test.suitebuilder.annotation does not exist
import android.test.suitebuilder.annotation.SmallTest;
Details
New Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface RobolectricTest {
}
Gradle File
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
repositories {
mavenCentral()
maven { url 'http://artifactory.ops.am1.qa.ext.bamgrid.com/artifactory/mobile-resources' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.google.gms:google-services:1.3.0-beta1'
}
}
android {
compileSdkVersion rootProject.ext.compileSDKVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.example"
minSdkVersion 17
targetSdkVersion rootProject.ext.targetSdkVersion
buildConfigField "String", "BUILD_TIME", "\"" + getDateAndTime() + "\""
buildConfigField "String", "VERSION_BUILD", "\"" + project["VERSION_BUILD"] + "\""
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
signingConfigs {
debug {
storeFile file(project.DEBUG_KEYSTORE)
storePassword project.DEBUG_KEYSTORE_PASSWORD
keyAlias project.DEBUG_KEYSTORE_ALIAS
keyPassword project.DEBUG_KEY_PASS
}
release {
storeFile file(project.RELEASE_KEYSTORE)
storePassword project.RELEASE_KEYSTORE_PASSWORD
keyAlias project.RELEASE_KEYSTORE_ALIAS
keyPassword project.RELEASE_KEY_PASS
}
}
sourceSets {
main {
res.srcDirs = ['src/main/res/',
'src/main/abc']
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
zipAlignEnabled true
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
proguardFile 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
testCoverageEnabled = true
debuggable true
signingConfig signingConfigs.debug
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile ('junit:junit:4.12')
testCompile ('org.apache.maven:maven-ant-tasks:2.1.3')
testCompile ('org.robolectric:robolectric:3.0')
testCompile ('org.robolectric:shadows-support-v4:3.0')
}
sourceSets {
unitTest {
java.srcDirs = ['src/test/java']
resources.srcDirs = ['src/test/resources']
}
}
ClassLoader getClassLoader() {
List urls = sourceSets.test.runtimeClasspath.collect {
it.toURI().toURL()
}
return URLClassLoader.newInstance( urls as URL[] )
}
/**
* Filters out files that have specific annotation
* #param map - map of things to filter
* #return - list of acceptable files after filter
*/
List annotationFilter( Map map ) {
map.prefix = map?.prefix ?: '' // prefix: provide convenience for passing in annotation names
ClassLoader loader = classLoader
List result
// filter with annotations
if( !map.includes ) {
result = map?.names
} else {
result = []
map?.names.each { name ->
Class klass = loader.loadClass( name )
map?.includes.each { annotationName ->
String fullName = map.prefix + annotationName
Class annotation = loader.loadClass( fullName ).asSubclass( Annotation )
if( !klass.isAnnotationPresent( annotation ) ) {
result << name
}
}
}
}
if( result?.size() == 0 ) result = [ 'no.tests.to.run' ]
return result
}
/**
* Gradle task to run only robolectric tests.
*/
task unitTest( type: Test, description: 'Run all junit tests' ) {
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split('/')
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
sourceSets.unitTest.compileClasspath += files(buildDir)
sourceSets.unitTest.runtimeClasspath += files(buildDir)
}
testClassesDir = sourceSets.unitTest.output.classesDir
classpath = sourceSets.unitTest.runtimeClasspath
doLast {
println "Doing Last"
List names = testClassNames()
List filtered = annotationFilter( names: names, includes: ['testUtils.RobolectricTest'] )
println 'Running ' + filtered.size() + ' tests:\n' + filtered*.toString()*.replaceAll('^','\t').join('\n')
filter {
setIncludePatterns( filtered as String[] )
}
}
}

Not so much Robolectric-specific, but in regards to declaring a custom test task for Android with Gradle, I ran into a lot of trouble with this. As you found, all of the documentation and examples are using the Java plugin, but the Android plugin subverts most of it.
The only solution I found with the Android plugin is to create another build type, which will then result, under the hood, in the Android plugin creating new test tasks for me. Then I can easily modify the tasks.
Here is the relevant setup, which I keep in a <rootProject>/gradle/test.gradle file (This specific example uses Category annotation to filter unit tests into one task and integration tests into a separate task. For information on that part of it see https://github.com/junit-team/junit4/wiki/Categories):
android {
buildTypes {
integration
}
testOptions {
unitTests.all {
useJUnit()
if (it.name == 'testIntegrationUnitTest') {
options {
excludeCategories 'com.example.categories.UnitTest'
}
} else {
options {
excludeCategories 'com.example.categories.IntegrationTest'
}
}
}
}
}
Then the <module>/build.gradle file applies from this file with apply from: "../gradle/test.gradle"
This causes the android closure from the two files to be merged, results in a new integration build type, which in turn results in a new testIntegrationUnitTest task on the project in addition to the standard testDebugUnitTest and testReleaseUnitTest tasks.
But now, testDebugUnitTest runs only "real" unit tests, while testIntegrationUnitTest runs only integration tests.
Your mileage/implementation may vary. Once you're inside the unitTest.all closure you can do whatever manipulation you need to the options.

Custom Gradle Task To Only Run Specific Tests
Building on #alphonzo79 answer, I was able to solve the issue.
The things to know
Android gradle omits common testing features in gradle, like exclude and custom sourceSets.
Retention and Target was not helpful. Only categories worked.
You don't need a new build type, you only need to change the test option when compiling your own tasks.
Don't use this - https://github.com/pkainulainen/gradle-examples/blob/master/integration-tests/build.gradle
The complete answer was to create a custom task that changed a flag for the android testOptions to excludeCategories.
LINK
CODE
def integrationTests = false
...
testOptions {
unitTests.all {
useJUnit()
if (integrationTests.toBoolean()) {
println "Integration Tests Only for " + it.name
options {
excludeCategories 'com.example.reactivemvp.categories.UnitTest'
}
} else {
println "Unit Tests Only for " + it.name
options {
excludeCategories 'com.example.reactivemvp.categories.IntegrationTest'
}
}
}
}
...
task integrationTest(
type: Test,
description: 'Run integration tests only. Pass in \'-Pintegration=true\'',
dependsOn: ['testDebugUnitTest', 'clean'] ) {
//Here for task completion, not actually used since sub task of testDebugUnitTest
testClassesDir = file("src/integrationTest/java/");
classpath = files("$System.env.ANDROID_HOME/sources/android-18")
//
//Turn on integration testing when argument exists and is true
//
if (project.hasProperty('integration')) {
println integration
if (integration == 'true') {
integrationTests = true
}
}
}

We can use the following configuration to exclude multiple tests by name:
def integrationTests = project.hasProperty('integrationTests') ? project.getProperty('integrationTests') : false //Default value false
android {
//...
testOptions {
unitTests {
includeAndroidResources = true
returnDefaultValues = true
all {
test {
filter {
if (integrationTests.toBoolean()) {
includeTestsMatching "*IntegrationTest"
} else {
includeTestsMatching "*UnitTest"
}
}
}
}
}
}
}
In command line:
gradlew test // Run only *UnitTest, Default value is false.
gradlew test -PintegrationTests=false // Run only *UnitTest
gradlew test -PintegrationTests=true // Run only *IntegrationTest

You said Robolectric can't find AndroidManifest.xml when testing multidimensional flavor project on Ubuntu
try to explicitly give the path to your manifest file in the #Config annotation
#Config(constants = BuildConfig.class, manifest = "../<path to>/AndroidManifest.xml")

Related

Unable to connect firebase with instant app with flavors and dimensions [duplicate]

This question already has an answer here:
Firebase Analytics not working with Instant App or Normal App
(1 answer)
Closed 4 years ago.
I have just converted my app into an instant app, the thing is that it has a setup that is a little complex, since I have 3 dimensions:
experience
- instant
- installed
type
- demo
- controlled
- user
adjustment
- normal
- perf
so the conversion process to instant app was not straight forwards, had to add some gradle code to ignore some permutations of flavors, to setup source and manifest location for build variants, deal with deep links in activities in different manifests and a series of issues I've managed to tackle down. Everything seems to work fine, except firebase.
I'm trying to understand how firebase routes the google-services.json file, how to set a different location or how to make it work repeating the file or something.
When I add firebase and add the google-services.json file to the installed directory I get the following error:
File google-services.json is missing. The Google Services Plugin cannot
function without it.
Searched Location:
myapp_project/base/src/feature/instant/user/normal/release/google-services.json
... and so on with other flavors and combinations
This doesn't make too much sense to me since the directory tree structure is not well formed. My directory tree is as follows:
- myapp_project
- base
- src
- controlled
- demo
- main
- user
- userNormal
- userPerf
- installed
- instant
I have tried creating the directories asked for, and adding the file in all of them. It then compiles perfectly, but it doesn't connect to firebase. Update on this matter, I realized that gradle searched for the file in many directories but among them was my "base" directory so just added the google-services.json file there and now compiles without the need of creating a file per directory. I'm also changing the package string inside the file to .base so that it doesn't throw the package not found error. But, it still compiles well but silently fails to connect to firebase server.
base gradle file
apply plugin: 'com.android.feature'
apply plugin: 'kotlin-android'
import org.apache.tools.ant.taskdefs.condition.Os
android {
baseFeature true
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
def keystoreProperties = Os.isFamily(Os.FAMILY_WINDOWS) ?
"KeyStoreWin.properties" : "KeyStore.properties"
Properties props = new Properties()
props.load(new FileInputStream(file(project.property(keystoreProperties))))
signingConfigs {
storeSignature {
storeFile file(props['KEYSTORE'])
storePassword props['KEYSTORE_PASSWD']
keyAlias props['KEYSTORE1']
keyPassword props['KEYSTORE_PASSWD1']
}
}
buildTypes {
debug {
debuggable true
}
release {
signingConfig signingConfigs.storeSignature
debuggable false
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
renderscriptDebuggable false
}
}
flavorDimensions "experience", "type", "adjustment"
productFlavors {
instant {
dimension "experience"
versionCode 1
}
installed {
dimension "experience"
versionCode 2
}
user {
dimension "type"
}
demo {
dimension "type"
}
controlled {
dimension "type"
}
normal {
dimension "adjustment"
}
perf {
dimension "adjustment"
}
}
variantFilter { variant -> def names = variant.flavors*.name
if (names.contains("controlled") && names.contains("perf")) {
setIgnore(true)
}
if (names.contains("demo") && names.contains("perf")) {
setIgnore(true)
}
if (names.contains("user") && names.contains("perf") &&
variant.buildType.name == 'debug') {
setIgnore(true)
}
if (names.contains("instant") && names.contains("perf")) {
setIgnore(true)
}
if (names.contains("instant") && names.contains("demo")) {
setIgnore(true)
}
if (names.contains("instant") && names.contains("controlled")) {
setIgnore(true)
}
}
sourceSets {
instantUserNormal {
java.srcDirs = ['src/userNormal/java', 'src/userNormal/java/']
res.srcDirs = ['src/userNormal/res', 'src/userNormal/res/']
manifest.srcFile 'src/userNormal/AndroidManifest.xml'
}
installedUserNormal {
java.srcDirs = ['src/userNormal/java', 'src/userNormal/java/']
res.srcDirs = ['src/userNormal/res', 'src/userNormal/res/']
manifest.srcFile 'src/userNormal/AndroidManifest.xml'
}
installedUserPerf {
java.srcDirs = ['src/userPerf/java', 'src/userPerf/java/']
res.srcDirs = ['src/userPerf/res', 'src/userPerf/res/']
manifest.srcFile 'src/userPerf/AndroidManifest.xml'
}
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
packagingOptions {
exclude 'META-INF/proguard/androidx-annotations.pro'
}
}
repositories {
google()
maven { url "https://jitpack.io" }
mavenCentral()
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
... all dependencies here
}
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'
installed gradle:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.domain.myapp"
minSdkVersion 21
targetSdkVersion 28
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
def keystoreProperties = Os.isFamily(Os.FAMILY_WINDOWS) ?
"KeyStoreWin.properties" : "KeyStore.properties"
Properties props = new Properties()
props.load(new
FileInputStream(file(project.property(keystoreProperties))))
signingConfigs {
storeSignature {
storeFile file(props['KEYSTORE'])
storePassword props['KEYSTORE_PASSWD']
keyAlias props['KEYSTORE_UPS']
keyPassword props['KEYSTORE_UPS_PASSWD']
}
}
buildTypes {
debug {
debuggable true
versionNameSuffix "v1d"
}
release {
signingConfig signingConfigs.storeSignature
debuggable false
versionNameSuffix "v1r"
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
renderscriptDebuggable false
}
}
flavorDimensions "experience", "type", "adjustment"
productFlavors {
instant {
dimension "experience"
}
installed {
dimension "experience"
}
user {
dimension "type"
}
demo {
dimension "type"
applicationIdSuffix ".demo"
}
controlled {
dimension "type"
applicationIdSuffix ".controlled"
}
normal {
dimension "adjustment"
}
perf {
dimension "adjustment"
}
}
variantFilter { variant -> def names = variant.flavors*.name
if (names.contains("controlled") && names.contains("perf") {
setIgnore(true)
}
if (names.contains("demo") && names.contains("perf")) {
setIgnore(true)
}
if (names.contains("user") && names.contains("perf") && variant.buildType.name == 'debug') {
setIgnore(true)
}
if (names.contains("instant") && names.contains("perf")) {
setIgnore(true)
}
if (names.contains("instant") && names.contains("demo")) {
setIgnore(true)
}
if (names.contains("instant") && names.contains("controlled")) {
setIgnore(true)
}
}
packagingOptions {
exclude 'META-INF/proguard/androidx-annotations.pro'
}
}
repositories {
google()
maven { url "https://jitpack.io" }
mavenCentral()
}
dependencies {
implementation project(':base')
}
Update2 my logs show the following errors:
E/FA: GoogleService failed to initialize, status: 10, Missing google app id value from from string resources with name google_app_id.
E/FA: Missing google_app_id. Firebase Analytics disabled. See https....
However the google_app_id is present at the build directory auto-generated values.xml file for every build variant
How can I resolve this issue?
UPDATE3 SOLVED I had to apply the firebase pluggin twice in the base and installed gradle files. As shown in the post shared in one of the comments.
apply plugin: 'com.google.gms.google-services'
The plugin is telling you where it is searching for the google-services.json files. Are you able to place the correct files in the locations that are specified? It is okay to have the same or different google-services.json file in different variants.

Run task once at the end in gradle

I need some task what will run at the end. Lets say some println. I seen multiple examples of how to run task at the end and they always depend on other task, so I don't really get how to do it and I don't have tasks. For now I have some piece of code:
if (releaseBol)
{
// Some of my code
// Run twice, one for lab and other for prod
println fileName
}
That piece of code run twice, once for lab flavor and one for prod flavor in case I run:
MyApp:assembleRelease -PRELEASE=1
I need this peace of code to run only once, so for that I need to run it once at the end.
My full gradle:
import org.tmatesoft.svn.core.wc.*
apply plugin: 'com.android.application'
def BuildNumberFile = new File('./BuildNumber.txt')
def BuildNumber = 'BRND'
def _versionName = '1.0.1'
def _applicationId = 'com.myapp.android.pic'
def _versionCode = 17
def fileName = ''
def obfuscated = false
def releaseBol = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
lintOptions {
abortOnError false
}
def lines = BuildNumberFile.readLines()
BuildNumber = 'S'+lines.get(0)
defaultConfig {
applicationId _applicationId
minSdkVersion 15
targetSdkVersion 23
versionCode _versionCode
versionName _versionName
multiDexEnabled true
resValue 'string', 'BUILD_NUMBER_RES', BuildNumber
}
if (project.hasProperty('RELEASE') && project.ext.RELEASE == '1')
releaseBol = true
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(".apk", "-${variant.versionName}." + BuildNumber + "-" + getSvnRevision() + ".apk"))
fileName=output.outputFile.name
}
variant.assemble.doLast {
variant.outputs.each { output ->
println "aligned " + output.outputFile
println "unaligned " + output.packageApplication.outputFile
File unaligned = output.packageApplication.outputFile;
File aligned = output.outputFile
if (!unaligned.getName().equalsIgnoreCase(aligned.getName())) {
println "deleting " + unaligned.getName()
unaligned.delete()
}
}
if (releaseBol) {
// Some of my code
// Run twice, one for lab and other for prod
println fileName
}
}
}
signingConfigs {
release {
storeFile file('myapp')
storePassword "myapp123"
keyAlias 'myapp'
keyPassword "myappmobile"
}
}
productFlavors {
prod {
}
lab {
}
}
buildTypes {
release {
minifyEnabled obfuscated
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-android.txt'
signingConfig signingConfigs.release
}
}
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4g"
jumboMode true
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':trunk')
}
It just help me if you can add some println to this gradle that will run once at the end.
You need to add an instance of BuildListener:
class BA extends BuildAdapter {
void buildFinished(BuildResult result) {
println "finished"
}
}
gradle.addListener(new BA())
EDIT
When it comes to releaseBol it can be done in the following way:
project.ext.releaseBol = false
class BA extends BuildAdapter {
Project project
BA(Project project) {
this.project = project
}
void buildFinished(BuildResult result) {
println "finished $project.releaseBol"
}
}
gradle.addListener(new BA(project))
EDIT 2
You can also utilize gradle's TaskExecutionGraph. With whenReady closure you can get info that the graph is ready and filled with tasks. At this time there's no option to modify the graph (add/remove task, change order) but you can (via getAllTasks) get the last one and... add an action. Here's how it looks like:
task someTask << {
println "run"
}
gradle.taskGraph.whenReady {
gradle.taskGraph.allTasks[-1] << {
println 'finished'
}
}

Gradle use different tasks / arguments for each build type

I'm in the early stages of migrating an android project to gradle, using the experimental plugin 0.4.0
As part of the build process I have a number of scripts that should run prior to compiling my code / building the apk. The arguments or tasks themselves will be different for a debug or release build.
I'm struggling to find a straight forward way to achieve this.
I've stripped everything back to a simple hello world project while I figure this out.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.4.0'
}
}
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
}
android.buildTypes {
debug {
//
}
release {
//runProguard, sign etc.
}
}
android.productFlavors {
create("free")
{
applicationId = "com.example.app.free"
versionName = "1.0-free"
}
create("full")
{
applicationId = "com.example.app.full"
versionName = "1.0-full"
}
}
}
repositories {
jcenter()
}
dependencies {
compile 'joda-time:joda-time:2.7'
}
task runScript(type: Exec) {
executable "sh"
args "-c", "echo SomeScriptHere"
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn runScript
}
task wrapper(type: Wrapper) {
gradleVersion = '2.5'
}
Ideally I want to differentiate by buildType ( not productFlavor ) as the tasks required as based on the type of build not the variety of app produced.
Is it possible to specify that a task should only run on a release or debug build?
Alternatively is it possible to define different arguments to be used for my runScript task based on being a release or debug build?
Apologies if I'm missing something obvious, I'm pretty new to using gradle.
using onlyIf() would be one option gradle docs here but having to define properties seemed awkward especially as a project gets larger and more complicated.
ZXStudio has a good blog post / example here where instead of using properties or rules will iterate over the existing tasks and create a new task based on the buildType / flavor.
So for my original question the answer mean removing the runScript task above and replacing tasks.withType(JavaCompile) as follows;
Could also be extended to match build flavors and create tasks appropriately.
tasks.withType(JavaCompile) {
def newTaskName = "runScript_" + name;
def isDebug = false;
if( name.contains("Debug") )
{
isDebug = true;
}
//Create a new task
tasks.create( newTaskName, Exec ) {
if( isDebug )
{
executable "sh"
args "-c", "echo this is a DEBUG task"
}
else
{
executable "sh"
args "-c", "echo this is a RELEASE task"
}
}
dependsOn newTaskName
}

Manifest mergeing - Android studio 0.8.1 upgrade build error: property 'manifestFile' does not exist

I have just upgraded to Android Studio 0.8.1 and upgraded build tools ect. from Android Studio 0.6
But then I get this build error:
A problem was found with the configuration of task
':processDevelopmentDebugResources'.
File 'C:\ProjectFolder\build\manifests\DevelopmentDebug\Development\debug\AndroidManifest.xml'
specified for property 'manifestFile' does not exist.
But I can't figure out what the problem is. The folder manifests under build does not exists.
I suspect it has something to do with the last part of my code that replaces values in the manifest file. There is something about "Fixes in the manifest mergers" changed in build tools changelist, but I don't know if that is related. But then again - the folder does not exists and this code should change some files in it.
Any clue?
Edit 1:
I just tried to comment the "variant.processManifest.doLast" part out and it works, so the issue is with this code. (Why haven't I tried that before..)
But what has changed in the last version that make this code fail? It worked before the upgrade.
Edit 2:
See comments under ianhanniballake's answer.
This is my build.gradle file:
buildscript {
repositories {
mavenCentral()
maven { url 'http://download.crashlytics.com/maven' }
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'com.crashlytics.tools.gradle:crashlytics-gradle:1.+'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.2'
}
}
repositories {
mavenCentral()
maven { url 'http://download.crashlytics.com/maven' }
}
apply plugin: 'com.android.application'
apply plugin: 'crashlytics'
apply plugin: 'android-apt'
dependencies {
compile 'com.crashlytics.android:crashlytics:1.+'
compile fileTree(dir: 'libs', include: '*.jar')
apt "org.androidannotations:androidannotations:3.0.1"
compile "org.androidannotations:androidannotations-api:3.0.1"
}
apt {
arguments {
resourcePackageName "dk.packagename"
androidManifestFile variant.processResources.manifestFile
}
}
android {
packagingOptions { //Fix: http://stackoverflow.com/a/20675331/860488
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
}
compileSdkVersion 10
buildToolsVersion "20.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 10
buildConfigField "int", "appId", "2"
}
lintOptions {
checkReleaseBuilds false
}
signingConfigs {
//Use terminal command: gradle assembleKonnektRelease
releaseKonnekt {
}
}
productFlavors{
def konnektSigningConfig = signingConfigs.releaseKonnekt
Development {
applicationId "dk.packagename"
versionCode 1
versionName "1.0.0"
buildConfigField "int", "appId", "2"
}
}
buildTypes {
testflight.initWith(buildTypes.debug)
debug {
applicationIdSuffix ".debug"
}
testflight {
applicationIdSuffix ".test"
}
release {
}
}
// Override Data in Manifest
android.applicationVariants.all { variant ->
variant.processManifest.doLast {
copy {
// *** SET COPY PATHS ***
try {
from("${buildDir}/manifests") {
//println "from: ${buildDir}/manifests"
include "${variant.dirName}/AndroidManifest.xml"
//println "included: ${variant.dirName}/AndroidManifest.xml"
}
} catch (e) {
println "error: " + e
}
into("${buildDir}/manifests/${variant.name}")
def variantName = variant.name.toString()
def appName = "empty"
def facebookId = "empty"
// *** SET APP NAME ***
if (variantName.contains("Development")) {
appName = "Development"
} else if (variantName.contains("Konnekt")) {
appName = "Konnekt"
facebookId = "**"
}
if(variantName.contains("Debug")){
appName = appName + " debug"
} else if(variantName.contains("Test")){
appName = appName + " test"
}
// *** REPLACE LINES IN MANIFEST ***
filter {
String line -> line.replaceAll("<application android:allowBackup=\"true\" android:icon=\"#drawable/ic_launcher\" android:label=\"todo\" android:name=\"dk.packagename.App\">", // implicit "." is replaced with: "dk.packagename."
"<application android:allowBackup=\"true\" android:icon=\"#drawable/ic_launcher\" android:label=\"" + appName + "\" android:name=\"dk.packagename.App\">");
}
filter {
String line -> line.replaceAll("<activity android:label=\"todo\" android:name=\"dk.packagename.SplashActivity\">",
"<activity android:label=\"" + appName + "\" android:name=\"dk.packagename.SplashActivity\">");
}
filter{
String line -> line.replaceAll("<meta-data android:name=\"com.facebook.sdk.ApplicationId\" android:value=\"\"/>",
"<meta-data android:name=\"com.facebook.sdk.ApplicationId\" android:value=\"" + facebookId + "\"/>")
}
}
}
// *** SET PATH TO NEW MANIFEST ***
variant.processResources.manifestFile = file("${buildDir}/manifests/${variant.name}/${variant.dirName}/AndroidManifest.xml")
//println "newManifest: ${buildDir}/manifests/${variant.name}/${variant.dirName}/AndroidManifest.xml"
}
}
This is most likely due to the new manifest merging becoming the default. One benefit of the new manifest merging is that you don't have to use this approach - instead, you can define custom placeholders and have them inserted into the merging process:
android {
defaultConfig {
manifestPlaceholders = [ activityLabel:"defaultName"]
}
productFlavors {
free {
}
pro {
manifestPlaceholders = [ activityLabel:"proName" ]
}
}
will substitute the placeholder in the following declaration:
<activity android:name=".MainActivity" android:label="${activityLabel}" >
Note: you can also compose multiple placeholders together such as android:label="${appName}${appType}" to segment a string appropriately and reduce retyping the same information.
Your problem is from this:
from("${buildDir}/manifests")
This file has been moved under $buildDir/intermediates/manifests/. Since your copy is in place (it seems), nothing is happening.
Then the process resource task is failing to find your modified file and it doesn't like it.
You could either update the path, or use the new manifest merger to do all the things you are trying to do in a simpler way.
Documentation is here: http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

Replacing a string in AndroidManifest.xml for a buildvariant doesn't work for Gradle Android Plugin Version > 0.5.4

Just a few weeks ago I had the following question: How to replace a string for a buildvariant with gradle?
I also answered the question on my own.
Everything works fine until now: I just realized that my copy task doesn't work anymore. I spent a few hours on the problem until I realized that it depends on the Gradle Android Plugin Version: Everything until 0.5.4 works fine. For upper versions I won't get into my copy task.
Here is the console output:
// gradle android plugin version: 0.5.6 and 0.5.5 --> copy tas doesn't work
:etscanner:prepareFlavor1Flavor1ReviewDependencies
:etscanner:compileFlavor1Flavor1ReviewAidl
:etscanner:generateFlavor1Flavor1ReviewBuildConfig
:etscanner:mergeFlavor1Flavor1ReviewAssets
:etscanner:compileFlavor1Flavor1ReviewRenderscript
:etscanner:mergeFlavor1Flavor1ReviewResources
:etscanner:processFlavor1Flavor1ReviewManifest
:etscanner:processFlavor1Flavor1ReviewResources
:etscanner:compileFlavor1Flavor1ReviewNote: Some input files use or override a d
eprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: <path>.DetailAdapter
.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:etscanner:dexFlavor1Flavor1Review
:etscanner:processFlavor1Flavor1ReviewJavaRes UP-TO-DATE
:etscanner:validateFlavor1Flavor1Signing
:etscanner:packageFlavor1Flavor1Review
:etscanner:zipalignFlavor1Flavor1Review
// gradle android plugin version: 0.5.4 --> copy task work
:etscanner:prepareFlavor1Flavor1ReviewDependencies
:etscanner:compileFlavor1Flavor1ReviewAidl
:etscanner:generateFlavor1Flavor1ReviewBuildConfig
:etscanner:mergeFlavor1Flavor1ReviewAssets
:etscanner:compileFlavor1Flavor1ReviewRenderscript
:etscanner:mergeFlavor1Flavor1ReviewResources
:etscanner:processFlavor1Flavor1ReviewManifest
...hey you are in the copy task!
:etscanner:processFlavor1Flavor1ReviewResources
:etscanner:compileFlavor1Flavor1ReviewNote: Some input files use or override a d
eprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: <path>DetailAdapter
.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:etscanner:dexFlavor1Flavor1Review
:etscanner:processFlavor1Flavor1ReviewJavaRes UP-TO-DATE
:etscanner:validateFlavor1Flavor1Signing
:etscanner:packageFlavor1Flavor1Review
:etscanner:zipalignFlavor1Flavor1Review
:etscanner:assembleFlavor1Flavor1Review
It's really a strange thing...
Has anyone an idea how to fix this?
UPDATE 1 2013-08-23
My build.gradle file:
buildscript {
repositories {
// maven central repo doesn't work with gradle android plugin version 0.5.+
// error message is describe in this post:
// https://plus.google.com/117954254390243608387/posts/RVBjoDMkLV5
//mavenCentral()
maven {
url 'http://nexus/content/groups/public/'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.4'
// copy task doesn't work for following versions:
//classpath 'com.android.tools.build:gradle:0.5.5'
//classpath 'com.android.tools.build:gradle:0.5.6'
//classpath 'com.android.tools.build:gradle:0.5.+'
}
}
apply plugin: 'android'
dependencies {
compile 'com.android.support:support-v4:13.0.+' // support lib
//compile 'com.actionbarsherlock:actionbarsherlock:4.4.0#aar'
compile project(':libraries:actionbarsherlock')
compile project(':libraries:google-play-services_lib')
}
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 17
versionName = "2.3"
versionCode = 4
}
// SIGN CONFIGS
signingConfigs {
flavor1 {
storeFile file("keystore/myKeystore.keystore")
storePassword = "store_password"
keyAlias = "alias"
keyPassword = "key_password"
}
flavor2 {
storeFile file("keystore/myKeystore.keystore")
storePassword = "store_password"
keyAlias = "alias"
keyPassword = "key_password"
}
debug {
storeFile file("keystore/debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
}
// FLAVORS
productFlavors {
flavor1 {
packageName 'myPackageName'
signingConfig signingConfigs.flavor1
}
flavor2 {
packageName 'myPackageName'
signingConfig signingConfigs.flavor2
}
}
// BUILDTYPES
buildTypes {
falvor1Review {
versionNameSuffix = versionNameSuffixOfReviewVersion
signingConfig signingConfigs.flavor1
}
flavor2Review {
versionNameSuffix = versionNameSuffixOfReviewVersion
signingConfig signingConfigs.flavor2
}
debug {
packageNameSuffix ".debug"
versionNameSuffix = versionNameSuffixOfReviewVersion
signingConfig signingConfigs.debug
}
}
// Override Data in Manifest
android.applicationVariants.each { variant ->
variant.processManifest.doLast {
copy {
// *** SET COPY PATHS ***
try {
from("${buildDir}/manifests") {
//println "from: ${buildDir}/manifests"
include "${variant.dirName}/AndroidManifest.xml"
//println "included: ${variant.dirName}/AndroidManifest.xml"
}
} catch (e) {
println "error: " + e
}
into("${buildDir}/manifests/${variant.name}")
//println "into (neues Manifest): ${buildDir}/manifests/${variant.name}"
// *** DEFINE VARS ***
def brandVersion = variant.buildType.name
def brandVersionString = brandVersion.toString()
def appName = "empty"
// *** SET APP NAME ***
if (brandVersionString.contains("flavor1")) {
appName = "my app name for flavor 1"
} else if (brandVersionString.contains("flavor2")) {
appName = "my app name for flavor 2"
}
println "...hey you are in the copy task"
// *** REPLACE LINES IN MANIFEST ***
// --- add appName
filter {
String line ->
line.replaceAll("<application android:allowBackup=\"true\" android:icon=\"#drawable/ic_launcher\" android:label=\"todo\" android:theme=\"#style/AppTheme\">",
"<application android:allowBackup=\"true\" android:icon=\"#drawable/ic_launcher\" android:label=\"" + appName + "\" android:theme=\"#style/AppTheme\">");
}
}
}
// *** SET PATH TO NEW MANIFEST ***
variant.processResources.manifestFile = file("${buildDir}/manifests/${variant.name}/${variant.dirName}/AndroidManifest.xml")
//println "newManifest: ${buildDir}/manifests/${variant.name}/${variant.dirName}/AndroidManifest.xml"
}
}
UPDATE 2 2013-08-23
Yesterday I had another strange behaviour of AS 0.2.5, it made some really strange builds:
As you see in my filter my previous appName in the Manifest is "todo":
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="todo"
android:theme="#style/AppTheme">
When I made the build then the appName in the app was the right one. But in the Application Launcher and in Settings/Apps there was shown "todo" as appName.
After building the project in AS 0.2.0 everything works fine.
I had the same problem as you and read through the release notes for 0.5.5 where I found the answer.
"access to the variants container don't force creating the task.
This means android.[application|Library|Test]Variants will be empty during the evaluation phase. To use it, use .all instead of .each"
If you'd like to try Alécio's solution and have deprecation warnings on a newer version of gradle, such as
WARNING [Project: :] variant.getProcessManifest() is deprecated. Call
it on one of variant.getOutputs() instead.
You need to use 'variant.outputs.each { output ->' to iterate over each output, see the code below
applicationVariants.all { variant ->
def flavor = variant.productFlavors[0].name
def flavorPackageName = variant.productFlavors[0].applicationId
variant.outputs.each { output ->
output.processManifest.doLast {
println("configuring AndroidManifest.xml for " + flavor);
copy {
from("${buildDir}/intermediates/manifests") {
include "${variant.dirName}/AndroidManifest.xml"
}
into("${buildDir}/intermediates/filtered_manifests")
}
def manifestFile = new File("${buildDir}/intermediates/filtered_manifests/${variant.dirName}/AndroidManifest.xml")
def content = manifestFile.getText()
def updatedContent = content.replaceAll("STRING_TO_BE_REPLACED", "NEW_STRING")
manifestFile.write(updatedContent)
}
output.processResources.manifestFile = new File("${buildDir}/intermediates/filtered_manifests/${variant.dirName}/AndroidManifest.xml")
}
}
The following code can be placed inside the android block in your gradle build.
android {
...
buildTypes {
...
applicationVariants.all { variant ->
def flavor = variant.productFlavors[0].name
def flavorPackageName = variant.productFlavors[0].applicationId
variant.processManifest.doLast {
println("configuring AndroidManifest.xml for " + flavor);
copy {
from("${buildDir}/intermediates/manifests") {
include "${variant.dirName}/AndroidManifest.xml"
}
into("${buildDir}/intermediates/filtered_manifests")
}
def manifestFile = new File("${buildDir}/intermediates/filtered_manifests/${variant.dirName}/AndroidManifest.xml")
def content = manifestFile.getText()
def updatedContent = content.replaceAll("STRING_TO_BE_REPLACED", "NEW_STRING")
manifestFile.write(updatedContent)
}
variant.processResources.manifestFile = new File("${buildDir}/intermediates/filtered_manifests/${variant.dirName}/AndroidManifest.xml")
}
}

Categories

Resources