Switch GCM Client on Development and Production - android

Just implement the new GCM. For official document,
Copy the google-services.json file you just downloaded into the app/ or mobile/ directory of your Android Studio project.
Anyone know how to setup gradle to switch development and production to use different google-services.json?

I have just answered a similar question here for different productFlavors.
In your case it's debug/production. I don't know why you need to switch between production and debug but i think you can do the same as what I proposed for flavors.
Create two extra folders src/release and src/debug , in each of the folders you put the corresponding google-services.json , so you will have: src/release/google-services.json and src/debug/google-services.json
Now in gradle add this :
android {
// set build config here to get the right gcm configuration.
//def myBuildConfig = "release"
def myBuildConfig = "debug"
// this will copy the right google-services.json file to app/ directory.
if (myBuildConfig.equals("release")) {
println "--> release copy!"
copy {
from 'src/release/'
include '*.json'
into '.'
}
} else {
println "--> debug copy!"
copy {
from 'src/debug/'
include '*.json'
into '.'
}
}
// other stuff
}

Related

Build type specific folder not recognized for a product flavor

I have configured my project to have multiple product flavors. I have the following code in my module build.gradle:
android {
// Rest of the configuration omitted for clarity
buildTypes {
debug {
// ...
}
release {
// ...
}
}
productFlavors {
paid
free
}
}
If I create a file (any file: Java, resources, ...), in the paid, free, debug or release, it is recognized by Android Studio and I can use it in my project.
However, if the same file is created in paidDebug (or a similar folder) it is not recognized in Android Studio. Do I need any extra configuration for this to work? Is this not supported (yet)?
Source code files with the same name should be placed in all of the alternative source sets (but not in the 'main').
Either:
app/src/free/java/com/domain/myapp/MainActivity.java
app/src/paid/java/com/domain/myapp/MainActivity.java
Or:
app/src/freeDebug/java/com/domain/myapp/MainActivity.java
app/src/freeRelease/java/com/domain/myapp/MainActivity.java
app/src/paidDebug/java/com/domain/myapp/MainActivity.java
app/src/paidRelease/java/com/domain/myapp/MainActivity.java
Resource files with the same name can be placed in any source set, including 'main'.
app/src/main/res/layout/activity_main.xml
app/src/paidDebug/res/layout/activity_main.xml
app/src/paidRelease/res/layout/activity_main.xml
In this case, when building the 'free' flavor, the layout file from 'main' set will be used. But, during the build of the 'paid' flavor, the specific version of the layout will be used.
You can specify specific source directories in your build.gradle file. For example, to add testFreeRelease to unit test sources and testFree to android integration test sources:
android {
[...]
sourceSets {
main.java.srcDirs += 'src/main/java'
testFreeRelease.java.srcDirs += 'src/testFreeRelease/java'
androidTestFree.java.srcDirs += 'src/androidTestFree/java'
}
}
This also works for Kotlin. Just switch java with kotlin.

Gradle/Jenkins : Create a gradle file to direct to sub projects

I've got a directory with three android projects in it.
The MainDir looks like that :
/.gradle
/.git
/project1
/project2
/project3
.gitignore
.Jenkinsfile
.README.md
In jenkins I can't run a shell script during the build that launchs gradle tasks for eauch of those projects because he doesn't know these are projects (he says "no sub-project").
In a project dir it looks like :
/.gradle
/app
/build
/gradle
.gitignore
.build.gradle
.gradle.properties
.gradlew
Is there a way to make jenkins understand these are three projects he can launch gradle taks in ? Like creating a build.gradle file in the main directory doing that ?
Or should I just create 3 Jenkins items?
You could make three builds in jenkins but unless there is a need to build the libs seperately then it might just end up being extra effort. Sounds like what you really want is a multi project build [1]. A simple example could sit at the folder above your lib projects as two files, build.gradle and settings.gradle
The settings.gradle will define what projects are included in your build's scope.
For example given your project1, project2 and project3 example your settings.gradle may look like this.
rootProject.name = 'myRootProjectName'
// note the name is not required to match the actual path
include ":project1"
// but if the name is not the same as the path then we can just
// let gradle know where the project is expected
project(":project1").projectDir = new File(settingsDir, "pathToProject1")
include ":project2"
project(":project2").projectDir = new File(settingsDir, "pathToProject2")
include ":project3"
project(":project3").projectDir = new File(settingsDir, "pathToProject3")
//##### below would be instead of the code above, same thing just manual
// project setup vs letting gradle find the subprojects
// note sometimes you have lots of subprojects in that case it's sometimes
// easier to just use a little logic for finding and setting up the subprojects.
// don't use the code above ##### and below only use one or the other
// or you will have errors. The method below is the most scaleable since
// adding projects requires zero modifications to the root project
rootProject.name = 'myRootProjectName'
// set up a couple file filters to find the dirs we consider subprojects
FileFilter projectFilter = { File pathname ->
FileFilter gradleProjectFilter = { File file -> file.name == 'build.gradle' }
// add this folder if is a directory and that directory contains a build.gradle file
// here note `File#listFiles` is true if it's `size() > 0` due to
// groovy's concept of truth (details: http://groovy-lang.org/semantics.html#Groovy-Truth)
return pathname.isDirectory() && pathname.listFiles(gradleProjectFilter)
}
settingsDir.listFiles(projectFilter).each { dir ->
include ":$dir.name"
project(":$dir.name").projectDir = dir
}
now running gradle projects task should show the three submodules.
As for your build.gradle file you could specify some common properties to all the modules if needed or just leave the file blank, it must exist but can be empty. If you wanted to share some configurations then you might set up the build.gradle with something like this.
project.subprojects { Project subproject ->
// anything that is defined here will be executed before the subproject's build.gradle file
subproject.buildscript {
repositories {
jcenter()
// your private maven repo if needed
maven { url 'http://1.2.3.4:8081/nexus/content/repositories/release' }
}
dependencies {
// some plugin that is now available to be applied in any subproject
classpath 'my.sweet.gradle:plugin:0.1'
}
}
subproject.afterEvaluate {
// this block is executed after the subproject's build.gradle file
if (project.tasks.withType(org.gradle.jvm.tasks.Jar)) {
// for example you might want to set the manifest for each subproject
manifest {
attributes 'Implementation-Title': "Lib $subproject.name",
'Implementation-Version': version
}
}
}
}
[1] https://docs.gradle.org/current/userguide/multi_project_builds.html

Crashlytics (Fabric) separate organizations for application variants (build types, product flavors)

This is self-answered question to share my knowledge.
I have a project with multiple product flavors and I want to integrate Fabric using separate organizations for each product flavor.
I tried to integrate Fabric using Android Studio Fabric Plugin. It adds
<meta-data
android:name="io.fabric.ApiKey"
android:value="DEFAULT_ORGANIZATION_API_KEY" />
entry to AndroidManifest.xml of main source set.
I decided to rewrite this entry in application variant specific source sets:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<meta-data
android:name="io.fabric.ApiKey"
android:value="SECOND_ORGANIZATION_API_KEY"
tools:replace="android:value" />
</application>
</manifest>
Then I discovered that Fabric Gradle plugin generates crashlytics.properties file with fabric api secret (AKA build secret) during build and I should include this file to source control. But this file is overwritten each time I build specific application variant because api secret is unique for each application.
How can I integrate Fabric using separate organizations for each application variant?
During the build fabricGenerateResources task is called and it looks for a file named fabric.properties with following content:
apiSecret=YOUR_BUILD_SECRET
apiKey=YOUR_API_KEY
So all we need is to generate fabric.properties file before this.
I found this solution and slightly modified it to fully support application variants not only build types.
Add this code to android section of build.gradle:
File crashlyticsProperties = new File("${project.projectDir.absolutePath}/fabric.properties")
applicationVariants.all { variant ->
variant.productFlavors.each { flavor ->
def variantSuffix = variant.name.capitalize()
def generatePropertiesTask = task("fabricGenerateProperties${variantSuffix}") << {
Properties properties = new Properties()
properties.put("apiKey", flavor.fabricApiKey)
properties.put("apiSecret", flavor.fabricApiSecret)
properties.store(new FileWriter(crashlyticsProperties), "")
}
def generateResourcesTask = project.tasks.getByName("fabricGenerateResources${variantSuffix}")
generateResourcesTask.dependsOn generatePropertiesTask
generateResourcesTask.doLast {
println "Removing fabric.properties"
crashlyticsProperties.delete()
}
}
}
It iterates over application variants and for each application variant creates task that generates fabric.properties file and task that deletes this file after Fabric Gradle plugin generates application resources.
All you need now is to define product flavor or build type specific fabricApiKey and fabricApiSecret:
productFlavors {
flavor1 {
ext.fabricApiKey = "FLAVOR1_API_KEY"
ext.fabricApiSecret = "FLAVOR1_API_SECRET"
}
}
ext is an ExtraPropertiesExtention object provided by every ExtensionAware object. It allows new properties to be added to existing object. In my case flavor1 is ExtensionAware object and it can be extended with new properties by using ext.someProperty = "value" syntax and later these properties can be used as flavor.someProperty, flavor.fabricApiKey.
Also it's better to include fabric.properties to .gitignore.
And do not forget to remove ext.enableCrashlytics = false from debug build type if you used it to disable Crashlytics during debug. Instead of this you can disable it in Application.onCreate:
Fabric.with(this, new Crashlytics.Builder().core(
new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()).build());
If you're not opposed to using an application id suffix, you don't need separate organizations. The crashes and answers will be treated as separate apps.
For instance, let's say my application id is io.example
In your build.gradle:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
release {
//options
}
}
After you deploy the debug version to a device or emulator, on the Fabric site you will see two apps:
io.example
io.example.debug
One thing that is nice about this approach is that you can also keep track of other build flavors seprately: io.exmaple.free, io.exmaple.paid, io.example.exterprise, and so on.
A simpler solution, which is also compatible with Gradle 5.x+ is to create separate fabric.properties files for each of the build variants that needs a unique Fabric API key and secret. Create the fabric.properties files as:
#Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public.
apiSecret=YOUR_API_SECRET
apiKey=YOUR_API_KEY
replacing YOUR_API_SECRET with the build variant's API secret and YOUR_API_KEY with the build variant's API key.
Then place each variant's fabric.properties under the project src/variant folder, e.g. app/src/debug or app/src/release. See documentation on build variants for additional details.
At build time, the fabric.properties for the variant being built will be used.

Exclude assets for release build type

I'm importing an android library in an application built with gradle, like that:
dependencies {
compile 'com.example:great-lib:0.1-SNAPSHOT'
}
This library contains only assets, js, css and images to be used in a webview, with a layout like that:
assets/
|-> great.css
|-> great.min.js
|-> great.min.js.map
|-> js/
| |-> plop.js
| |-> foo.js
| ...
|-> img/
| ...
The js folder contains source files (to be used with source maps). I would like to include it and the .map file for the debug builds, and have only the minified js in release builds, but I can't find a way to do that.
So far I've tried : 
android {
// this doesn't exclude anything
packageOptions {
exclude 'assets/js'
}
buildTypes {
release {
// this does exclude the js folder, but in both release and debug
aaptOptions {
ignoreAssetsPattern "!js"
}
}
}
}
Any idea if what I want is possible to achieve, and if so how?
(I've also thought of publishing two versions of the library (great-lib and great-lib-debug), and have the dependency in debugCompile and releaseCompile, but I'd prefer avoiding that and publishing a single version)
I had success with this approach (updated 2019-5-13 for TaskProvider support; see edit history for older versions):
android {
⋮
applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.mergeAssetsProvider.configure {
doLast {
delete(fileTree(dir: outputDir, includes: ['**/js', '**/*.js.map']))
}
}
}
}
⋮
}
This should address the issues with #Xavier's answer:
The deletion is done as part of the variant's mergeAssets task so the deletion is reflected in the task's output and up-to-date checking should be unaffected.
The paths are calculated without magic strings. You may need to adjust the include patterns in case my example is too permissive.
The variant is being selected by the buildType name, which is less problematic than matching the entire variant name (though it is still stringly typed).
Note that this approach also works for res files rather than assets: just replace mergeAssets with mergeResources.
Other answers mentioning packagingOptions and aaptOptions are barking up the wrong tree, as these are scoped to all variants (they are defined in the android scope, not buildType or productFlavor).
I ended up doing the following:
android.applicationVariants.all { variant ->
if (variant.name.contains('Release')) {
// exclude source and sourcemap from release builds
def noJsSourceTask = task("delete${variant.name}JsSource", type: Delete) {
delete "${buildDir}/intermediates/assets/${variant.dirName}/js"
delete "${buildDir}/intermediates/assets/${variant.dirName}/great.min.js.map"
}
variant.mergeAssets.finalizedBy noCeJsSourceTask
}
}
It works ok, but there are a few things I don't really like:
I'm touching at the files produced by a task after it is done (the finalizedBy), so it doesn't work well with "up-to-date" checking. But it's only for release builds, I'm doing debug ones more often
the path of the files to delete is manually built. I'm not sure if it's generic enough to be reused in other projects as-is
I'm selecting the variants based on their name. I would have liked something more structured.
Gradle provides "aaptOptions, ignoreAssetsPattern" to filter/exclude assets folders and files from release or debug build.
Example for debug build (js folder and great.css files):
debug {
aaptOptions {
ignoreAssetsPattern '!js:!great.css:'
}
}
Example for release build (js folder and great.css files):
release {
aaptOptions {
ignoreAssetsPattern '!js:!great.css:'
}
}
I think you can use proguard. Proguard is include with android studio,obfuscate code, and remove not used classes, and if you want remove all resources that app not used. Only put in your build.gradle this:
release {
minifyEnabled true //remove classes, obfuscate code and zipalign
shrinkResources true //remove resources
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//autogenerated files
}
This is link information about that:
http://developer.android.com/tools/help/proguard.html
You can personalize, exclude particular files or ignore particular files
It's not possible through a filter.
You could have 2 assets folders though. a main one (src/main/assets) used for both debug and release and one (src/debug/assets) used only for the debug build.
source

Android studio doesn't recognise source folders

I'm using a standard Android Studio directory structure and I created different build types:
buildTypes {
debug {
runProguard false
packageNameSuffix ".debug"
signingConfig signingConfigs.debug
}
preview.initWith(buildTypes.debug)
preview {
packageNameSuffix ".preview"
}
release {
runProguard false
signingConfig signingConfigs.release
}
}
everything compiles fine, but AS doesnt recognize all of the source folders.
Only folders under main and debug are marked as source, folders under preview and release are displayed as normal folders
In effect there is no error checking in those folders
I checked the .iml file and sourceFolder tags were not added.
If I edit the project iml file manually adding the lines:
<sourceFolder url="file://$MODULE_DIR$/src/preview/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/preview/res" type="java-resource" />
It seems to work fine.
...until I sync with my gradle file - which removes the above lines.
Is this a bug in gradle plugin, or am I doing something wrong?
You have to switch it in the build variants list, then AS will pick up the appropriate source sets.
First, try re-importing the project. Delete all of your build directories, .iml files and the .idea folder. Then import the project.
If that doesn't work then you can try this to "force it".
Checkout this response from Bernd Bergler. Note that this is a hack and ideally isn't necessary
Here's a slightly modified version of his code.
task addPreview {
def src = ['src/preview/java']
def file = file("app.iml")
doLast {
try {
def parsedXml = (new XmlParser()).parse(file)
def node = parsedXml.component[1].content[0]
src.each {
def path = 'file://$MODULE_DIR$/' + "${it}"
def set = node.find { it.#url == path }
if (set == null) {
new Node(node, 'sourceFolder', ['url': 'file://$MODULE_DIR$/' + "${it}", 'isTestSource': "false"])
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
file.text = writer.toString()
}
}
} catch (FileNotFoundException e) {
// nop, iml not found
}
}
}
// always do the addPreview on prebuild
gradle.projectsEvaluated {
preBuild.dependsOn(addPreview)
}
Simply drop that in your build.gradle file outside of the android section.
Description from this source:
Android Studio automatically generates .iml project files from gradle
build files. This task edits the Android Studio project file app.iml
and adds the test directory. The changes are lost whenever Android
Studio rescans the gradle files, but right after that it runs a build
and the task is hooked into that, so it’s all good. This version has a
couple of tweaks, such as adding the new task into the normal build
cycle a bit differently, and gracefully handling the absence of the
.iml file.
This has worked to an extent for me: The IDE recognizes it as a src tree now but doesn't want to link it with any other src trees.
In my case only File -> Invalidate Caches / Restart have helped me, so if solutions above does not work for you - try this.
Add this to your module's build.gradle file:
sourceSets {
main.java.srcDirs += 'src/preview/java'
main.java.srcDirs += 'src/release/java'
}

Categories

Resources