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?
This question is related to
android
google-cloud-messaging
google-play-services
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 )
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'
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
.
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 :
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 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.
google-services.json
from your project.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"
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);
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
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:
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.
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>
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
}
Hey Friends also looks for name use only lowercase then u don't get this error
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.
That is it!..
Short answer:
google-services.json
to app dir.For other flavor, copy google-services.json
to app/src/{flavor-name} dir
...
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'
}
}
}
}
}
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.
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'
}
}
}
}
}
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.
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:
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/).
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.
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.
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:
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!
UPDATED:
In terms of Firebase setup with build variants, please refer to this blog which has detailed instructions.
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.
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
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 "."
}
}
}
There's a caveat to this approach that is you need to have google-service.json
file in each of your variants folder here's an example.
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"
}
]
}
Source: Stackoverflow.com