Skip to content

Commit

Permalink
Merge pull request flutter#2182 from flutter/gcm
Browse files Browse the repository at this point in the history
First steps toward GCM support
  • Loading branch information
mpcomplete committed Jan 7, 2016
2 parents ebe379e + 6ecb8aa commit 93a9d52
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 0 deletions.
46 changes: 46 additions & 0 deletions sky/services/gcm/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# TODO(mpcomplete): move this to a separate repo. Doesn't belong in the main
# Flutter repo.

import("//mojo/public/tools/bindings/mojom.gni")

group("gcm") {
deps = [
":interfaces",
]

if (is_android) {
deps += [ ":gcm_lib" ]
}
}

mojom("interfaces") {
sources = [
"gcm.mojom",
]
}

if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")

android_library("gcm_lib") {
java_files = [
"src/org/domokit/gcm/GcmListenerService.java",
"src/org/domokit/gcm/InstanceIDListenerService.java",
"src/org/domokit/gcm/RegistrationIntentService.java",
]

deps = [
"//base:base_java",
"//mojo/java",
"//mojo/public/java:bindings",
"//mojo/public/java:system",
":interfaces_java",
google_play_services_library,
]
}
}
21 changes: 21 additions & 0 deletions sky/services/gcm/gcm.mojom
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[DartPackage="sky_services"]
module gcm;

[ServiceName="gcm::GcmListener"]
interface GcmListener {
OnMessageReceived(string from, string jsonMessage);
};

[ServiceName="gcm::GcmService"]
interface GcmService {
// senderId is the project_number from google-services.json.
// token should be saved and remembered - you only need to register your app
// once. I don't understand how this token is used, though.
Register(string senderId, GcmListener listener) => (string token);
SubscribeTopics(string token, array<string> topics);
UnsubscribeTopics(string token, array<string> topics);
};
32 changes: 32 additions & 0 deletions sky/services/gcm/src/org/domokit/gcm/GcmListenerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.domokit.gcm;

import android.os.Bundle;
import android.util.Log;

import java.util.Set;
import org.json.JSONObject;
import org.json.JSONException;

public class GcmListenerService extends com.google.android.gms.gcm.GcmListenerService {
private static final String TAG = "GcmListenerService";

@Override
public void onMessageReceived(String from, Bundle data) {
// Convert the data Bundle to JSON.
JSONObject json = new JSONObject();
Set<String> keys = data.keySet();
for (String key : keys) {
try {
json.put(key, JSONObject.wrap(data.get(key)));
} catch(JSONException e) {
Log.d(TAG, "Failed to convert GCM message to JSON: " + e);
}
}

RegistrationIntentService.notifyMessageReceived(from, json.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.domokit.gcm;

import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;

import com.google.android.gms.iid.InstanceID;

public class InstanceIDListenerService extends com.google.android.gms.iid.InstanceIDListenerService {
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. This call is initiated by the
* InstanceID provider.
*/
@Override
public void onTokenRefresh() {
// TODO(mpcomplete): need to notify Dart app?
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
}
149 changes: 149 additions & 0 deletions sky/services/gcm/src/org/domokit/gcm/RegistrationIntentService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.domokit.gcm;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.google.android.gms.gcm.GcmPubSub;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;

import java.io.IOException;

import org.chromium.base.ThreadUtils;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojom.gcm.GcmListener;
import org.chromium.mojom.gcm.GcmService;

public class RegistrationIntentService extends IntentService {
private static final String TAG = "RegistrationIntentService";
private static final String REGISTER_EXTRA = "register";
private static final String TOKEN_EXTRA = "token";
private static final String SUBSCRIBE_EXTRA = "subscribe";
private static final String UNSUBSCRIBE_EXTRA = "unsubscribe";

private static GcmListener sListener;
private static GcmService.RegisterResponse sRegisterResponse;

public static class MojoService implements GcmService {
private Context context;

public MojoService(Context context) {
this.context = context;
}

@Override
public void close() {}

@Override
public void onConnectionError(MojoException e) {}

@Override
public void register(String senderId, GcmListener listener, RegisterResponse response) {
if (checkPlayServices()) {
// Start IntentService to register this application with GCM.
RegistrationIntentService.sListener = listener;
RegistrationIntentService.sRegisterResponse = response;
Intent intent = new Intent(context, RegistrationIntentService.class);
intent.putExtra(REGISTER_EXTRA, senderId);
context.startService(intent);
}
}

public void subscribeTopics(String token, String[] topics) {
Intent intent = new Intent(context, RegistrationIntentService.class);
intent.putExtra(TOKEN_EXTRA, token);
intent.putExtra(SUBSCRIBE_EXTRA, topics);
context.startService(intent);
}

public void unsubscribeTopics(String token, String[] topics) {
Intent intent = new Intent(context, RegistrationIntentService.class);
intent.putExtra(TOKEN_EXTRA, token);
intent.putExtra(UNSUBSCRIBE_EXTRA, topics);
context.startService(intent);
}

private boolean checkPlayServices() {
// TODO(mpcomplete): implement? This would check if the user has the Google Play Services
// library installed, and if not, prompt them to download it.
return true;
}
}

public RegistrationIntentService() {
super(TAG);
}

public static void notifyMessageReceived(final String from, final String message) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
if (sListener != null)
sListener.onMessageReceived(from, message);
}
});
}

public static void notifyRegistered(final String token) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
if (sRegisterResponse != null)
sRegisterResponse.call(token);
}
});
}

@Override
protected void onHandleIntent(Intent intent) {
try {
if (intent.hasExtra(REGISTER_EXTRA)) {
register(intent.getStringExtra(REGISTER_EXTRA));
} else if (intent.hasExtra(SUBSCRIBE_EXTRA)) {
subscribeTopics(
intent.getStringExtra(TOKEN_EXTRA),
intent.getStringArrayExtra(SUBSCRIBE_EXTRA));
} else if (intent.hasExtra(UNSUBSCRIBE_EXTRA)) {
unsubscribeTopics(
intent.getStringExtra(TOKEN_EXTRA),
intent.getStringArrayExtra(UNSUBSCRIBE_EXTRA));
} else {
Log.d(TAG, "Unexpected intent.");
}
} catch (Exception e) {
Log.d(TAG, "Failed to process GCM request", e);
}
}

private void register(String senderId) {
try {
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(senderId,
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
notifyRegistered(token);
} catch (Exception e) {
Log.d(TAG, "Failed to complete token refresh", e);
// TODO(mpcomplete): callback error code.
}
}

private void subscribeTopics(String token, String[] topics) throws IOException {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
for (String topic : topics) {
pubSub.subscribe(token, "/topics/" + topic, null);
}
}

private void unsubscribeTopics(String token, String[] topics) throws IOException {
GcmPubSub pubSub = GcmPubSub.getInstance(this);
for (String topic : topics) {
pubSub.unsubscribe(token, "/topics/" + topic);
}
}
}

0 comments on commit 93a9d52

Please sign in to comment.