Update: GCM is deprecated, use FCM
I'm implementing the new Google Cloud Messaging following the guides from the Google Developers page here
I've successfully run and test it. But my problem now is I have different product flavors with different applicationId/packageName and different Google Cloud Messaging Project Id. The google-services.json
have to be put at the /app/google-services.json
not the flavors folder.
Is there any way to make the google-services.json
config different for many flavors?
apply plugin: 'com.google.gms.google-services'
in gradle file seems to put gcm
strings into app/build/generated/res/google-services/debug/values/values.xml
...
Google included support for flavors in version 2.0 of the play services plugin. Since this version of the gradle plugin com.google.gms:google-services:2.0.0-alpha3
you can do this
Step 1: add to gradle
// To auto-generate google map api key of google-services.json
implementation 'com.google.android.gms:play-services-maps:17.0.0'
Step 2: add to AndroidManifest.xml in the application tag
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_api_key" />
Step 3: download each flavor JSON file from firebase and add it
app/src/
flavor1/google-services.json
flavor2/google-services.json
Version 3.0.0 of the plugin searches for the JSON file in these locations (considering you have a flavor
flavor1 and a build type debug
):
/app/src/debug/google-services.json
/app/src/debug/flavor1/google-services.json
/app/google-services.json
This worked for me even using flavorDimensions. I have free & paid in one dimension and Mock & Prod in the other dimension. I also have 3 buildTypes: debug, release, and staging. This is how it looks in my project for the FreeProd flavor:
https://i.stack.imgur.com/yekEo.png
How many google-services.json files will depend on your project's characteristics, but you will need at least one JSON file for every Google project.
If you want more details about what this plugin does with these JSON files, here it is: https://github.com/googlesamples/google-services/issues/54#issuecomment-165824720
Link to the official docs: https://developers.google.com/android/guides/google-services-plugin
Blog post with updated info: https://firebase.googleblog.com/2016/08/organizing-your-firebase-enabled-android-app-builds.html
And go here to check the latest version of this plugin: https://mvnrepository.com/artifact/com.google.gms/google-services?repo=google
UPDATE: The following explanation is for one Android Studio project, with one Firebase Project and different Firebase Apps inside that project. If the aim is to have different JSON files for different Firebase Apps in different Firebase Projects inside the same Android Studio project, (or if you don't know what's the difference) look here..
You need one Firebase App per Android Application ID (usually package name). Is common to have one Application ID per Gradle build variant (This is gonna be likely if you use Gradle build types and Gradle build flavours)
As of Google Services 3.0 and using Firebase it's not necessary to create different files for different flavours. Creating different files for different flavours can be not clear or straightforward in case you have productFlavours and Build types which compose with each other.
In the same file you'll have the all the configurations you need for all your build types and flavours.
In the Firebase console you need to add one app per package name. Imagine that you have 2 flavours (dev and live) and 2 build types (debug and release). Depending on your config but it's likely that you have 4 different package names like:
com.stackoverflow.example (live - release)
com.stackoverflow.example.dev (live - dev)
com.stackoverflow.example.debug (debug - release)
com.stackoverflow.example.dev.debug (debug - dev)
You need 4 different Android Apps in the Firebase Console. (On each one you need to add the SHA-1 for debug and live for each computer you are using)
When you download the google-services.json file, actually it doesn't really matter from what app you download it, all of them contain the same info related to all your apps.
Now you need to locate this file in app level (app/).
https://i.stack.imgur.com/zTTGg.png
If you open that file you'll see that if contains all the information for all your package names.
A pain point use to be the plugin. In order to get it working you need to locate the plugin at the bottom of your file. So this line..
apply plugin: 'com.google.gms.google-services'
...needs to be on the bottom of your app build.gradle file.
For most of the said here, it applies to previous versions as well. I've never had different files for different configs, but now with the Firebase console is easier because they provide one single file with everything you need for all you configs.
Firebase Docs
Firebase Cloud Messaging
Firebase Console
Wrote a Medium post on this issue.
Had the a similar problem (using BuildTypes instead of Flavors), and fixed it like so.
Take advantage of Gradle's dependency management system. I created two tasks, switchToDebug
and switchToRelease
. Require that any time assembleRelease
is run, that switchToRelease
is also run. Same for debug.
def appModuleRootFolder = '.'
def srcDir = 'src'
def googleServicesJson = 'google-services.json'
task switchToDebug(type: Copy) {
def buildType = 'debug'
description = 'Switches to DEBUG google-services.json'
from "${srcDir}/${buildType}"
include "$googleServicesJson"
into "$appModuleRootFolder"
}
task switchToRelease(type: Copy) {
def buildType = 'release'
description = 'Switches to RELEASE google-services.json'
from "${srcDir}/${buildType}/"
include "$googleServicesJson"
into "$appModuleRootFolder"
}
afterEvaluate {
processDebugGoogleServices.dependsOn switchToDebug
processReleaseGoogleServices.dependsOn switchToRelease
}
EDIT: use processDebugFlavorGoogleServices
/processReleaseFlavorGoogleServices
task to modify it at a per-flavor level.
According to ahmed_khan_89's answer, you can put you "copy code" inside product flavors.
productFlavors {
staging {
applicationId = "com.demo.staging"
println "Using Staging google-service.json"
copy {
from 'src/staging/'
include '*.json'
into '.'
}
}
production {
applicationId = "com.demo.production"
println "Using Production google-service.json"
copy {
from 'src/production/'
include '*.json'
into '.'
}
}
}
Then you don't have to switch settings manually.
Well I am running into the same problem and couldn't get any perfect solution. It's just a workaround. I am wondering how Google didn't think about flavors...? And i hope they will propose soon a better solution.
What I am doing:
I have two flavors, in each one I put the corresponding google-services.json : src/flavor1/google-services.json
and src/flavor2/google-services.json
.
Then in build gradle I copy the file depending on the flavor to the app/
directory:
android {
// set build flavor here to get the right gcm configuration.
//def myFlavor = "flavor1"
def myFlavor = "flavor2"
if (myFlavor.equals("flavor1")) {
println "--> flavor1 copy!"
copy {
from 'src/flavor1/'
include '*.json'
into '.'
}
} else {
println "--> flavor2 copy!"
copy {
from 'src/flavor2/'
include '*.json'
into '.'
}
}
// other stuff
}
Limitation: you will have to change myFlavor
manually in gradle each time you want to run for a different flavor (because it's hardcoded).
I tried many ways to get the current build flavor like afterEvaluate
close... couldn't get any better solution until now.
Update, Another solution: one google-services.json for all the flavors:
You can also, have different package names for each flavor and then in the google developer console you don't have to create two different apps for each flavor, but just two different clients in the same app. Then you will have only one google-services.json
that contains your both clients. Of course, this depends on how you're implementing the backend of your flavors. If they're not separated then this solution will not help you.
google-services.json
for both release
and debug
worked for me, as mentioned in your update. I think this is the simplest solution if you're only trying to split your debug
build, like I am. For reference, you can generate the file here: developers.google.com/mobile/add?platform=android
I'm using the google-services.json file, created from here: https://developers.google.com/mobile/add?platform=android&cntapi=gcm&cnturl=https:%2F%2Fdevelopers.google.com%2Fcloud-messaging%2Fandroid%2Fclient&cntlbl=Continue%20Adding%20GCM%20Support&%3Fconfigured%3Dtrue
In the JSON-structure there is a JSON-array called clients. If you have multiple flavors, just add the different properties here.
{
"project_info": {
"project_id": "PRODJECT-ID",
"project_number": "PROJECT-NUMBER",
"name": "APPLICATION-NAME"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:PROJECT-NUMBER:android:HASH-FOR-FLAVOR1",
"client_id": "android:PACKAGE-NAME-1",
"client_type": 1,
"android_client_info": {
"package_name": "PACKAGE-NAME-1"
}
},
"oauth_client": [],
"api_key": [],
"services": {
"analytics_service": {
"status": 1
},
"cloud_messaging_service": {
"status": 2,
"apns_config": []
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"google_signin_service": {
"status": 1
},
"ads_service": {
"status": 1
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:PROJECT-NUMBER:android:HASH-FOR-FLAVOR2",
"client_id": "android:PACKAGE-NAME-2",
"client_type": 1,
"android_client_info": {
"package_name": "PACKAGE-NAME-2"
}
},
"oauth_client": [],
"api_key": [],
"services": {
"analytics_service": {
"status": 1
},
"cloud_messaging_service": {
"status": 2,
"apns_config": []
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"google_signin_service": {
"status": 1
},
"ads_service": {
"status": 1
}
}
}
],
"client_info": [],
"ARTIFACT_VERSION": "1"
}
In my project I'm using the same project-id and when I'm adding the second package-name in the above url, google provides me with a file containing multiple clients in the json-data.
1.) What does google-services.json really do?
Follow this : https://stackoverflow.com/a/31598587/2382964
2.) How does google-services.json file affects your android studio project?
Follow this : https://stackoverflow.com/a/33083898/2382964
just in short for second url, if you add google-services.json in your project there must be a auto-generated google-services
folder for debug
variant in this path
app/build/generated/res/google-services/debug/values/values.xml
3.) What to do, to make it done?
add google-services dependency in project_level
build.gradle, you can also use version 3.0.0
if you are using app_compact library.
// Top-level build.gradle file
classpath 'com.google.gms:google-services:2.1.2'
now in app_level
build.gradle you have to add at the bottom.
// app-level build.gradle file
apply plugin: 'com.google.gms.google-services'
Note: Adding this line at the bottom of the gradle file is really important. Otherwise Gradle builds won't give you any errors, but it won't just work properly.
4.) Where to put google-service.json file in your structure.
case 1.) if you have no build_flavor just put it in inside /app/google-service.json
folder.
case 2.) if you have multiple build_flavor and you have different-different google_services.json files put inside app/src/build_flavor/google-service.json
.
case 3.) if you have multiple build_flavor and you have single google_services.json file put inside app/google-service.json
.
google-services.json file is unnecessary to receive notifications. Just add a variable for each flavour in your build.gradle file:
buildConfigField "String", "GCM_SENDER_ID", "\"111111111111\""
Use this variable BuildConfig.GCM_SENDER_ID instead of getString(R.string.gcm_defaultSenderId) while registering:
instanceID.getToken(BuildConfig.GCM_SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Firebase now supports multiple application ids with one google-services.json file.
This blog post describes it in detail.
You'll create one parent project in Firebase that you'll use for all of your variants. You then create separate Android applications in Firebase under that project for each application id that you have.
When you created all of your variants, you can download a google-services.json that supports all of your applications ids. When it's relevant to see the data separately (i.e. Crash Reporting) you can toggle that with a dropdown.
We have a different package name for debug builds (*.debug) so I wanted something that works based on flavor and buildType, without having to write anything flavor-related in the pattern of processDebugFlavorGoogleServices
.
I created a folder named "google-services" in each flavor, containing both the debug version and the release version of the json file :
https://i.stack.imgur.com/o8sRo.png
In the buildTypes section of your gradle file, add this :
applicationVariants.all { variant ->
def buildTypeName = variant.buildType.name
def flavorName = variant.productFlavors[0].name;
def googleServicesJson = 'google-services.json'
def originalPath = "src/$flavorName/google-services/$buildTypeName/$googleServicesJson"
def destPath = "."
copy {
if (flavorName.equals(getCurrentFlavor()) && buildTypeName.equals(getCurrentBuildType())) {
println originalPath
from originalPath
println destPath
into destPath
}
}
}
It will copy the right json file at the root of your app module automatically when you'll switch build variant.
Add the two methods called to get the current flavor and current build type at the root of your build.gradle
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( "assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() ) {
println matcher.group(1).toLowerCase()
return matcher.group(1).toLowerCase()
}
else
{
println "NO MATCH FOUND"
return "";
}
}
def getCurrentBuildType() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
if (tskReqStr.contains("Release")) {
println "getCurrentBuildType release"
return "release"
}
else if (tskReqStr.contains("Debug")) {
println "getCurrentBuildType debug"
return "debug"
}
println "NO MATCH FOUND"
return "";
}
That's it, you don't have to worry about removing/adding/modifying flavors from your gradle file, and it get the debug or the release google-services.json automatically.
According to Firebase docs you can also use string resources instead of google-services.json.
Because this provider is just reading resources with known names, another option is to add the string resources directly to your app instead of using the Google Services gradle plugin. You can do this by: Removing the google-services plugin from your root build.gradle Deleting the google-services.json from your project Adding the string resources directly Deleting apply plugin: 'com.google.gms.google-services' from your app build.gradle
Example strings.xml
:
<string name="google_client_id">XXXXXXXXX.apps.googleusercontent.com</string>
<string name="default_web_client_id">XXXX-XXXXXX.apps.googleusercontent.com</string>
<string name="gcm_defaultSenderId">XXXXXX</string>
<string name="google_api_key">AIzaXXXXXX</string>
<string name="google_app_id">1:XXXXXX:android:XXXXX</string>
<string name="google_crash_reporting_api_key">AIzaXXXXXXX</string>
<string name="project_id">XXXXXXX</string>
remove the existing google-services.json from your project. Build > Clean Project compile and run your app look at the error message that comes up to figure out where you can put your google-services.json..mine looked like this File google-services.json is missing. The Google Services Plugin cannot function without it. Searched Location: C:\Users\username\Desktop\HelloWorld\app\src\devSuffixYes_EnvQaApistaging_\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\devSuffixYes_EnvQaApistaging_Debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\devDebug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\devSuffixYes_EnvQaApistaging_\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\debug\devSuffixYes_EnvQaApistaging_\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffixDebug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_Debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\envDebug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qa\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qaDebug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qa\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qa\apistaging_\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qa\apistaging_Debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\src\dev\suffix\yes_\env\qa\apistaging_\debug\google-services.json C:\Users\username\Desktop\HelloWorld\app\google-services.json
NOTE: it also cares about the order of the declaratios in the flavorDimensions
. mine was flavorDimensions "dev_suffix", "environment"
No need for any additional gradle scripting.
Google started adding different package name in the name of 'android_client_info'. It looks like below in google-services.json
"android_client_info": {
"package_name": "com.android.app.companion.dev"
}
so, following steps are enough to have different google-services.json selection.
Have 2 flavours Add a new dev flavour's package to google analystics configuration page and download google-services.json. Notice in the new configuration file, both of your flavour's package ids are there Prepare any of your flavour build.
That is it!..
I know, you doubt about google-services.json
file should be put in the root
app
folder, yes? I will break down the myth — not necessarily. You can put google-services.json file to the flavor folder
too. Like this:
https://i.stack.imgur.com/T4Geh.png
Based on @ZakTaccardi's answer, and assuming you don't want a single project for both flavors, add this to the end of your build.gradle
file:
def appModuleRootFolder = '.'
def srcDir = 'src'
def googleServicesJson = 'google-services.json'
task switchToStaging(type: Copy) {
outputs.upToDateWhen { false }
def flavor = 'staging'
description = "Switches to $flavor $googleServicesJson"
delete "$appModuleRootFolder/$googleServicesJson"
from "${srcDir}/$flavor/"
include "$googleServicesJson"
into "$appModuleRootFolder"
}
task switchToProduction(type: Copy) {
outputs.upToDateWhen { false }
def flavor = 'production'
description = "Switches to $flavor $googleServicesJson"
from "${srcDir}/$flavor/"
include "$googleServicesJson"
into "$appModuleRootFolder"
}
afterEvaluate {
processStagingDebugGoogleServices.dependsOn switchToStaging
processStagingReleaseGoogleServices.dependsOn switchToStaging
processProductionDebugGoogleServices.dependsOn switchToProduction
processProductionReleaseGoogleServices.dependsOn switchToProduction
}
You need to have the files src/staging/google-services.json
and src/production/google-services.json
. Replace the flavor names for the ones you use.
I have found that the google-services plugin is quite useless for projects that want to add GCM. It only generates the following file which simply adds your project ID as a string resource:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Your API key would be on the following line -->
<string name="gcm_defaultSenderId">111111111111</string>
</resources>
It appears that you only need it if you copied the sample code verbatim directly from the Cloud Messaging for Android guide. Here is the example line:
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Solution
If you want to be able to switch API projects for different build types or product flavors you can just define your own constants and choose the appropriate one when calling the getToken()
API.
private static final String SENDER_ID = "111111111111";
private static final String SANDBOX_SENDER_ID = "222222222222";
String token = instanceID.getToken(
BuildConfig.DEBUG ? SENDER_ID : SANDBOX_SENDER_ID,
GoogleCloudMessaging.INSTANCE_ID_SCOPE,
null);
For Product Flavors
The above code works for switching between debug and release builds. For product flavors you would define different API keys in a java source file and place the files in their corresponding product flavor directory. For reference: Gradle Build Variants
Simplifying what @Scotti said. You need to create Multiples apps with different package name for a particular Project depending on the product flavor.
Suppose your Project is ABC having different product flavors X,Y where X has a package name com.x and Y has a package name com.y then in the firebase console you need to create a project ABC in which you need to create 2 apps with the package names com.x and com.y. Then you need to download the google-services.json file in which there will be 2 client-info objects which will contain those pacakges and you will be good to go.
Snippet of the json would be something like this
{
"client": [
{
"client_info": {
"android_client_info": {
"package_name": "com.x"
}
{
"client_info": {
"android_client_info": {
"package_name": "com.y"
}
]
}
So if you want to programmatically copy google-services.json
file from all your variants into your root folder. When you switch to a specific variant here's a solution for you
android {
applicationVariants.all { variant ->
copy {
println "Switches to $variant google-services.json"
from "src/$variant"
include "google-services.json"
into "."
}
}
}
https://i.stack.imgur.com/AGldc.png
Short answer:
Implementation: By default, you should copy google-services.json to app dir.
For other flavor, copy google-services.json
to app/src/{flavor-name} dir
Test: Try to build, open Build tab, then check output message with Parsing json file:.....
The point of the google-services plugin is to simplify integration of Google features.
Since it only generates android-resources from the google-services.json file, over-complicated gradle-logic negates this point, I think.
So if the Google-docs don’t say which resources are needed for specific Google-features, I would suggest to generate the JSON-file for each relevant buildtype/flavor, see what resources get generated by the plugin and then put those resources manually into their respective src/buildtypeORflavor/res directories.
Delete the references to google-services plugin and the JSON-file after that, and you are done.
For detailed information about the inner workings of google-services gradle-plugin see my other answer:
https://stackoverflow.com/a/33083898/433421
Hey Friends also looks for name use only lowercase then u don't get this error
Indeed, juste one google-services.json in MyApp/app/
directory is good, no need for aditional script with com.google.gms:google-services:3.0.0
. But be careful to delete the file google-services.json
from the app directory MyApp/app/src/flavor1/res/
to avoid the error type Execution failed for task ':app:processDebugGoogleServices'. > No matching client found for package
Just add the flavours app id and name of the flavour as app in the same project, Download the google-service.json file in the end and it will have all the flavours in the client array[] which will be fine for all flavours.
https://i.stack.imgur.com/Vkyf8.png
You have many flavor, so it mean you will have many difference package id, right? So, just go to the page where you setup/generate your json file and config for each package name. All of it will add to json file.
I'm verry lazy to post picture now, but basically:
go to https://developers.google.com/mobile/add
select platform
select your app
IMPORTANT: type your flavor package name to field "android package name"
... continue to get your configuration file. Download it!
When config the file, you can see that google show you the Server API Key + Sender ID. And it is same for all package (flavors)
At the end, you just need only one json file for all flavors.
One more question here that you have to test when you register to get Registration Token, check if is difference for each flavor. I don't touch on it but it think it should be difference. Too late now and i so sleepy :) Hope it help!
I'm currently using two GCM Project Id in the same app package. I put the google-service.json of my first GCM project but I switch from the first to the second one only changing the SENDER_ID:
String token = instanceID.getToken(SENDER_ID,GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
(At this point I think that the google-services.json isn't mandatory )
Inspired by @ahmed_khan_89 answer above. We can directly keep like this in gradle file.
android{
// set build flavor here to get the right Google-services configuration(Google Analytics).
def currentFlavor = "free" //This should match with Build Variant selection. free/paidFull/paidBasic
println "--> $currentFlavor copy!"
copy {
from "src/$currentFlavor/"
include 'google-services.json'
into '.'
}
//other stuff
}
Place your "google-services.json" file under app/src/flavors respectively then in build.gradle of app, under android add below code
gradle.taskGraph.beforeTask { Task task ->
if (task.name ==~ /process.*GoogleServices/) {
android.applicationVariants.all { variant ->
if (task.name ==~ /(?i)process${variant.name}GoogleServices/) {
copy {
from "/src/${variant.flavorName}"
into '.'
include 'google-services.json'
}
}
}
}
}
...
gradle.taskGraph.beforeTask { Task task ->
if (task.name ==~ /process.*GoogleServices/) {
android.applicationVariants.all { variant ->
if (task.name ==~ /(?i)process${variant.flavorName}(Debug|Release)GoogleServices/) {
copy {
from "src/tenants/${variant.flavorName}"
include 'google-services.json'
into '.'
}
}
}
}
}
gradle.taskGraph.afterTask { Task task ->
if (task.name ==~ /process.*GoogleServices/) {
android.applicationVariants.all { variant ->
if (task.name ==~ /(?i)process${variant.flavorName}(Debug|Release)GoogleServices/) {
delete fileTree(".").matching {
include 'google-services.json'
}
}
}
}
}
Success story sharing
File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it.
so I'm going to resort to copying the flavor file into the root folder every time via a build script.