Skip to content

Commit

Permalink
Add subscribe method to subscribe to a GCM topic
Browse files Browse the repository at this point in the history
Add stripped down google-play-services jar as a dependency for GcmPubSub
Add example for subscribing to a topic
Documentation updates
Add Apache License to all Java files
  • Loading branch information
morinel committed Sep 19, 2015
1 parent 23c99f1 commit 6277560
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 113 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -2,4 +2,6 @@

A Titanium module for registering a device with Google Cloud Messaging and handling push notifications sent to the device.

[![gitTio](http://gitt.io/badge.png)](http://gitt.io/component/nl.vanvianen.android.gcm)

Read the [documentation](https://github.com/morinel/gcmpush/blob/master/documentation/index.md).
87 changes: 76 additions & 11 deletions documentation/index.md
@@ -1,15 +1,27 @@
# Titanium Module for Google Cloud Messaging Push Notifications for Android #

A Titanium module for registering a device with Google Cloud Messaging and handling push notifications sent to the device.
[![gitTio](http://gitt.io/badge.png)](http://gitt.io/component/nl.vanvianen.android.gcm)

1. Install the module as usual in Titanium Studio by downloading the [zip file](https://github.com/morinel/gcmpush/releases/download/1.1/nl.vanvianen.android.gcm-android-1.1.zip) or use ```gittio install nl.vanvianen.android.gcm```
1. Refer to the example for possibilities
A Titanium module for registering a device with Google Cloud Messaging and handling push notifications sent to the device. Both push notifications and topic subscriptions are supported.

1. Install the module as usual in Titanium Studio by downloading the [zip file](https://github.com/morinel/gcmpush/releases/download/1.2/nl.vanvianen.android.gcm-android-1.2.zip) or use ```gittio install nl.vanvianen.android.gcm```
1. Refer to the examples for possibilities.
1. Send a server push notification with your preferred server-side technology to the registrationId returned while registering your device.
1. The callback you specified will then be called
1. The callback you specified will then be called.

This module does not require any tiapp.xml properties, all configuration is done in Javascript.

## Example server-side code ##
## Example server-side code to send a push notification ##

Use the following dependency:

```xml
<dependency>
<groupId>com.google.android.gcm</groupId>
<artifactId>gcm-server</artifactId>
<version>1.0.2</version>
</dependency>
```

```java
import com.google.android.gcm.server.Message;
Expand All @@ -22,8 +34,8 @@ import com.google.android.gcm.server.Sender;
public void sendPush() {
Sender sender = new Sender(APIKEY);
Message msg = new Message.Builder()
.addData("title", title)
.addData("message", message)
.addData("title", "Lorem ipsum dolor sit amet")
.addData("message", "Lorem ipsum dolor sit amet")
.addData("sound", "mysound.mp3")
.addData("vibrate", "true")
.addData("insistent", "true")
Expand All @@ -32,6 +44,7 @@ public void sendPush() {
.addData("group", "mygroup")
.build();
try {
/* Use the registrationIds returned in the success handler in the apps registerPush() call. */
List<String> list = new ArrayList<>(REGISTRATION_IDS);
MulticastResult result = sender.send(msg, list, 1);
log.info("Total = " + result.getTotal() + ", success = " + result.getSuccess() + ", failure = " + result.getFailure());
Expand All @@ -41,11 +54,15 @@ public void sendPush() {
}
```

## Notification settings ##

See the [example](https://github.com/morinel/gcmpush/blob/master/example/app.js) for an overview of how to specify the below settings.
## Register your app for push notifications ##

See [this example](https://github.com/morinel/gcmpush/blob/master/example/app.js).


## Notification settings for push notifications ##

The following settings can be specified:
See the [example](https://github.com/morinel/gcmpush/blob/master/example/app.js) for an overview of how to specify the below settings:

1. **smallIcon**: the tiny icon shown at the top of the screen, see this [stackoverflow question](http://stackoverflow.com/questions/28387602/notification-bar-icon-turns-white-in-android-5-lollipop) for details. The file should be placed in the ```platform/android/res/drawable``` directory.
1. **largeIcon**: the large icon shown in the notification bar. If not specified your appicon will be used. The file should be placed in the ```platform/android/res/drawable``` directory.
Expand All @@ -54,8 +71,56 @@ The following settings can be specified:
1. **insistent** (true / false): whether the notification should be [insistent](http://developer.android.com/reference/android/app/Notification.html#FLAG_INSISTENT), default false.
1. **group**: name of group to group similar notifications together, default null.
1. **localOnly** (true / false): whether this notification should be bridged to other devices (false) or is only relevant to this device (true), default true.
1. **priority**: specifies the priority of the notification, should be between [PRIORITY_MIN](http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html#PRIORITY_MIN) and [PRIORITY_MAX](http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html#PRIORITY_MAX), default 0.
1. **priority**: (integer) specifies the priority of the notification, should be between [PRIORITY_MIN](http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html#PRIORITY_MIN) and [PRIORITY_MAX](http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html#PRIORITY_MAX), default 0.

The settings sound, vibrate, insistent, group, localOnly and priority can also be set as data in the push message being received (see the server-side example above).

If the app is not active when the notification is received, use gcm.getLastData() to retrieve the contents of the notification and act accordingly to start or resume the app in a suitable way. If you're done, call gcm.clearLastData(), otherwise the same logic will happen when resuming the app again, see the [example](https://github.com/morinel/gcmpush/blob/master/example/app.js).



## Example server-side code to send a message to a topic ##

```java
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;

...

public void sendTopic(e) throws Exception {
// Prepare JSON containing the GCM message content. What to send and where to send.
JSONObject json = new JSONObject();
JSONObject data = new JSONObject();
data.put("message", "Lorem ipsum dolor sit amet");
/* Add any other notification settings here, see the push notification server-side example */

json.put("to", "/topics/mytopic");
json.put("data", data);

// Create connection to send GCM Message request.
URL url = new URL("https://android.googleapis.com/gcm/send");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "key=" + APIKEY);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestMethod("POST");
conn.setDoOutput(true);

// Send GCM message content.
String content = json.toString();
System.out.println(content);
OutputStream outputStream = conn.getOutputStream();
outputStream.write(content.getBytes());

// Read GCM response.
InputStream inputStream = conn.getInputStream();
System.out.println(IOUtils.toString(inputStream));
}
```


## Subscribe your app to a topic ##

See [this example](https://github.com/morinel/gcmpush/blob/master/example/topic.js).

The same notification settings apply as for regular push notifications.
2 changes: 1 addition & 1 deletion example/app.js
Expand Up @@ -36,7 +36,7 @@ gcm.registerPush({
var dialog = Ti.UI.createAlertDialog({
title: 'Push received',
message: JSON.stringify(event.data),
buttonNames: ['View'],
buttonNames: ['View','Cancel'],
cancel: 1
});
dialog.addEventListener("click", function(event) {
Expand Down
37 changes: 37 additions & 0 deletions example/topic.js
@@ -0,0 +1,37 @@
var gcm = require("nl.vanvianen.android.gcm");

gcm.subscribe({
/* The Sender ID from Google Developers Console, see https://console.developers.google.com/project/XXXXXXXX/apiui/credential */
/* It's the same as your project id */
senderId: 'XXXXXXXX',
/* Should start with "/topics/" */
topic: '/topics/mytopic',
notificationSettings: {
vibrate: true /* Whether the phone should vibrate, see the push notification example for more settings */
},
success: function (event) {
Ti.API.info("Topic registration success: " + JSON.stringify(event));
},
error: function (event) {
Ti.API.info("Topic registration error: " + JSON.stringify(event));
alert(event.error);
},
callback: function (event) {
Ti.API.info("Topic callback = " + JSON.stringify(event));
/* Called when a notification is received and the app is in the foreground */

var dialog = Ti.UI.createAlertDialog({
title: 'Topic received',
message: JSON.stringify(event.data),
buttonNames: ['View','Cancel'],
cancel: 1
});
dialog.addEventListener("click", function(event) {
dialog.hide();
if (event.index == 0) {
/* Do stuff to view the notification */
}
});
dialog.show();
}
});
Binary file added lib/google-play-services-gms.jar
Binary file not shown.
Binary file removed lib/google-play-services.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions manifest
Expand Up @@ -2,8 +2,8 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 1.1
apiversion: 2
version: 1.2
apiversion: 3
description: Google Cloud Push for Titanium
author: Jeroen van Vianen <jeroen@vanvianen.nl>
license: Apache License, Version 2.0
Expand Down
16 changes: 16 additions & 0 deletions src/nl/vanvianen/android/gcm/GCMBroadcastReceiver.java
@@ -1,3 +1,19 @@
/**
* Copyright 2015 Jeroen van Vianen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package nl.vanvianen.android.gcm;

import android.content.Context;
Expand Down
50 changes: 40 additions & 10 deletions src/nl/vanvianen/android/gcm/GCMIntentService.java
@@ -1,9 +1,24 @@
/**
* Copyright 2015 Jeroen van Vianen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package nl.vanvianen.android.gcm;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
Expand Down Expand Up @@ -67,9 +82,16 @@ private int getResource(String type, String name) {
protected void onMessage(Context context, Intent intent) {
Log.d(LCAT, "Push notification received");

boolean isTopic = false;

HashMap<String, Object> data = new HashMap<String, Object>();
for (String key : intent.getExtras().keySet()) {
Log.d(LCAT, "Message key: " + key + " value: " + intent.getExtras().getString(key));
String value = intent.getExtras().getString(key);
Log.d(LCAT, "Message key: " + key + " value: " + value);

if (key.equals("from") && value != null && value.startsWith("/topics/")) {
isTopic = true;
}

String eventKey = key.startsWith("data.") ? key.substring(5) : key;
data.put(eventKey, intent.getExtras().getString(key));
Expand Down Expand Up @@ -147,8 +169,10 @@ protected void onMessage(Context context, Intent intent) {
if (notificationSettings.get("priority") != null) {
if (notificationSettings.get("priority") instanceof Integer) {
priority = (Integer) notificationSettings.get("priority");
} else if (notificationSettings.get("priority") instanceof Double) {
priority = ((Double) notificationSettings.get("priority")).intValue();
} else {
Log.e(LCAT, "Invalid setting priority, should be int, between PRIORITY_MIN (" + NotificationCompat.PRIORITY_MIN + ") and PRIORITY_MAX (" + NotificationCompat.PRIORITY_MAX + ")");
Log.e(LCAT, "Invalid setting priority, should be an integer, between PRIORITY_MIN (" + NotificationCompat.PRIORITY_MIN + ") and PRIORITY_MAX (" + NotificationCompat.PRIORITY_MAX + ")");
}
}

Expand All @@ -172,14 +196,16 @@ protected void onMessage(Context context, Intent intent) {
launcherIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launcherIntent, PendingIntent.FLAG_ONE_SHOT);

String title = (String) data.get("title");
String message = (String) data.get("message");
String ticker = (String) data.get("ticker");

Log.i(LCAT, "Title: " + title);
Log.i(LCAT, "Message: " + message);
Log.i(LCAT, "Ticker: " + ticker);

if (message == null) {
Log.d(LCAT, "Message received but no message so will make this silent");
Log.d(LCAT, "Message received but no 'message' specified in push notification payload, so will make this silent");
} else {
Log.d(LCAT, "Creating notification...");

Expand All @@ -192,7 +218,7 @@ protected void onMessage(Context context, Intent intent) {
.setContentTitle(title)
.setContentText(message)
.setTicker(ticker)
.setContentIntent(contentIntent)
.setContentIntent(PendingIntent.getActivity(this, 0, launcherIntent, PendingIntent.FLAG_ONE_SHOT))
.setSmallIcon(smallIcon)
.setLargeIcon(bitmap);

Expand All @@ -201,15 +227,15 @@ protected void onMessage(Context context, Intent intent) {
group = (String) data.get("group");
}
if (group != null) {
builder = builder.setGroup(group);
builder.setGroup(group);
}
Log.i(LCAT, "Group: " + group);

/* Whether notification should be for this device only or bridged to other devices, can also be set in the push notification payload */
if (data.get("localOnly") != null) {
localOnly = Boolean.getBoolean((String) data.get("localOnly"));
}
builder = builder.setLocalOnly(localOnly);
builder.setLocalOnly(localOnly);
Log.i(LCAT, "LocalOnly: " + localOnly);

/* Specify notification priority, can also be set in the push notification payload */
Expand Down Expand Up @@ -264,7 +290,11 @@ protected void onMessage(Context context, Intent intent) {
}

if (GCMModule.getInstance() != null) {
GCMModule.getInstance().sendMessage(data);
if (isTopic) {
GCMModule.getInstance().sendTopicMessage(data);
} else {
GCMModule.getInstance().sendMessage(data);
}
}
}

Expand Down

0 comments on commit 6277560

Please sign in to comment.