From 7e72e5a12fd436c74e6a55fd7723e69825e9755b Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 16 Apr 2019 11:18:02 +0200 Subject: [PATCH] :sparkles: Support messaging style for notifications --- docs/PAYLOAD.md | 154 +++++++++++++++++- .../com/adobe/phonegap/push/FCMService.java | 64 +++++++- .../adobe/phonegap/push/PushConstants.java | 2 + 3 files changed, 217 insertions(+), 3 deletions(-) diff --git a/docs/PAYLOAD.md b/docs/PAYLOAD.md index d06085340..812caaebc 100644 --- a/docs/PAYLOAD.md +++ b/docs/PAYLOAD.md @@ -10,6 +10,7 @@ - [Sound](#sound) - [Stacking](#stacking) - [Inbox Stacking](#inbox-stacking) + - [Messaging Stacking](#messaging-stacking) - [Action Buttons](#action-buttons) - [In Line Replies](#in-line-replies) - [Led in Notifications](#led-in-notifications) @@ -966,6 +967,155 @@ You will get an inbox view so you can display multiple notifications in a single If you use `%n%` in the `summaryText` of the JSON coming down from FCM it will be replaced by the number of messages that are currently in the queue. +## Messaging Stacking + +When developing messaging apps, a better alternative to stacking your notifications is to use the messaging style. The style is similar to the inbox stacking, but it also defines a sender name to be displayed next to each message. If the sender name is empty or not provided, no sender will be displayed (this is useful for private conversations). In this If you send the following JSON from FCM: + +```json +{ + "registration_ids": ["my device id"], + "data": { + "title": "John Smith", + "message": "My first message", + "style": "messaging", + "image": "https://randomuser.me/api/portraits/men/79.jpg", + "image-type": "circle" + } +} +``` + +Here is an example using fcm-node that sends the above JSON: + +```javascript +const FCM = require('fcm-node'); +// Replace these with your own values. +const apiKey = 'replace with API key'; +const deviceID = 'my device id'; +const fcm = new FCM(apiKey); + +const message = { + to: deviceID, + data: { + title: 'John Smith', + message: 'My first message', + style: 'messaging', + image: 'https://randomuser.me/api/portraits/men/79.jpg', + 'image-type': 'circle' + } +}; + +fcm.send(message, (err, response) => { + if (err) { + console.log(err); + console.log('Something has gone wrong!'); + } else { + console.log('Successfully sent with response: ', response); + } +}); +``` + +It will produce a notification for an individual conversation: + +![2019-04-16 11 15 00](https://user-images.githubusercontent.com/7590321/56197310-d933da80-6038-11e9-887c-edeea100fcc5.png) + +The result looks similar to inbox stacking, with the difference that messages won't be truncated to a single line. + +If you follow it up with subsequent notifications like: + +```json +{ + "registration_ids": ["my device id"], + "data": { + "title": "John Smith", + "message": "My second message", + "style": "messaging", + "image": "https://randomuser.me/api/portraits/men/79.jpg", + "image-type": "circle" + } +} +``` + +Here is an example using fcm-node that sends the above JSON: + +```javascript +const FCM = require('fcm-node'); +// Replace these with your own values. +const apiKey = 'replace with API key'; +const deviceID = 'my device id'; +const fcm = new FCM(apiKey); + +const message = { + to: deviceID, + data: { + title: 'John Smith', + message: 'My second message', + style: 'messaging', + image: 'https://randomuser.me/api/portraits/men/79.jpg', + 'image-type': 'circle' + } +}; + +fcm.send(message, (err, response) => { + if (err) { + console.log(err); + console.log('Something has gone wrong!'); + } else { + console.log('Successfully sent with response: ', response); + } +}); +``` + +The new message will be added to the current notification. + +![2019-04-16 11 15 00](https://user-images.githubusercontent.com/7590321/56197352-f1a3f500-6038-11e9-8616-77f499126914.png) + +If you also specify the name of the user that sent the message, it will be displayed next to the message. This is useful for group conversations. If you send the following JSON from FCM: + +```json +{ + "registration_ids": ["my device id"], + "data": { + "title": "My Group", + "message": "My first message", + "style": "messaging", + "sender": "John Smith" + } +} +``` + +Here is an example using fcm-node that sends the above JSON: + +```javascript +const FCM = require('fcm-node'); +// Replace these with your own values. +const apiKey = 'replace with API key'; +const deviceID = 'my device id'; +const fcm = new FCM(apiKey); + +const message = { + to: deviceID, + data: { + title: 'My Group', + message: 'My first message', + style: 'messaging', + sender: 'John Smith' + } +}; + +fcm.send(message, (err, response) => { + if (err) { + console.log(err); + console.log('Something has gone wrong!'); + } else { + console.log('Successfully sent with response: ', response); + } +}); +``` + +It will produce a notification for a group conversation: + +![2019-04-16 11 15 00](https://user-images.githubusercontent.com/7590321/56197398-08e2e280-6039-11e9-8119-7be98822bf44.png) + ## Action Buttons Your notification can include a maximum of three action buttons. You register the event callback name for each of your actions, then when a user clicks on one of notification's buttons, the event corresponding to that button is fired and the listener you have registered is invoked. For instance, here is a setup with two actions `emailGuests` and `snooze`. @@ -2128,12 +2278,12 @@ On iOS, using the FCM app server protocol, if you are trying to send a silent pu "custom_var_2:" "custom value here" /* Retrieved on app as data.additionalData.custom_var_2 */ }, /* Forces FCM silent push notifications to be triggered in the foreground of your iOS device. */ - "content_available": true + "content_available": true } ``` *Doc modification came in response to @andreszs - Issue [#2449](https://github.com/phonegap/phonegap-plugin-push/issues/2449). -** IMPORTANT: When using the content_available field, Android payload issues may occur. [Read here](../docs/PAYLOAD.md#user-content-use-of-content_available-true) Make sure you separate your Android/iOS server payloads to mitigate any problems that may arise. +** IMPORTANT: When using the content_available field, Android payload issues may occur. [Read here](../docs/PAYLOAD.md#user-content-use-of-content_available-true) Make sure you separate your Android/iOS server payloads to mitigate any problems that may arise. More information on how to send push notifications using the FCM HTTP protocol and payload details can be found here: diff --git a/src/android/com/adobe/phonegap/push/FCMService.java b/src/android/com/adobe/phonegap/push/FCMService.java index db1738a72..15c654f0a 100644 --- a/src/android/com/adobe/phonegap/push/FCMService.java +++ b/src/android/com/adobe/phonegap/push/FCMService.java @@ -29,6 +29,7 @@ import android.text.Html; import android.text.Spanned; import android.util.Log; +import android.service.notification.StatusBarNotification; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; @@ -660,7 +661,47 @@ private void setNotificationOngoing(Bundle extras, NotificationCompat.Builder mB private void setNotificationMessage(int notId, Bundle extras, NotificationCompat.Builder mBuilder) { String message = extras.getString(MESSAGE); String style = extras.getString(STYLE, STYLE_TEXT); - if (STYLE_INBOX.equals(style)) { + + if (STYLE_MESSAGING.equals(style)) { + NotificationCompat.MessagingStyle msgStyle; + String title = extras.getString(TITLE); + + // Find if there is a notification already displayed with this ID. + Notification notification = findActiveNotification(getApplicationContext(), notId); + + if (notification) { + // Notification already displayed. Extract the MessagingStyle to add the message. + msgStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification); + } else { + // There is no notification, create a new style. + msgStyle = new NotificationCompat.MessagingStyle(""); + } + + // Add the new message to the style. + msgStyle.addMessage(message, System.currentTimeMillis(), extras.getString(SENDER, "")); + + // Add the count of messages to the title if there is more than 1. + Integer sizeList = msgStyle.getMessages().size(); + + if (sizeList > 1) { + String stacking = "(" + sizeList + ")"; // Default value. + + if (extras.getString(SUMMARY_TEXT) != null) { + stacking = extras.getString(SUMMARY_TEXT); + stacking = stacking.replace("%n%", sizeList); + } + + if (!stacking.trim().equals("")) { + title += " " + stacking; + } + } + + msgStyle.setConversationTitle(title); + + // Use the style. + mBuilder.setStyle(msgStyle); + + } else if (STYLE_INBOX.equals(style)) { setNotification(notId, message); mBuilder.setContentText(fromHtml(message)); @@ -728,6 +769,27 @@ private void setNotificationMessage(int notId, Bundle extras, NotificationCompat } } + /** + * Find an active notification with a certain ID. + * + * @param context Context. + * @param notId Notification ID to find. + * @return Notification The active notification, null if not found. + */ + private Notification findActiveNotification(Context context, Integer notId) { + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + StatusBarNotification[] notifications = mNotificationManager.getActiveNotifications(); + + // Find the notification. + for (int i = 0; i < notifications.length; i++) { + if (notifications[i].getId() == notId) { + return notifications[i].getNotification(); + } + } + + return null; + } + private void setNotificationSound(Context context, Bundle extras, NotificationCompat.Builder mBuilder) { String soundname = extras.getString(SOUNDNAME); if (soundname == null) { diff --git a/src/android/com/adobe/phonegap/push/PushConstants.java b/src/android/com/adobe/phonegap/push/PushConstants.java index 9b4656a00..84972855d 100644 --- a/src/android/com/adobe/phonegap/push/PushConstants.java +++ b/src/android/com/adobe/phonegap/push/PushConstants.java @@ -31,6 +31,7 @@ public interface PushConstants { public static final String ALERT = "alert"; public static final String MESSAGE = "message"; public static final String BODY = "body"; + public static final String SENDER = "sender"; public static final String SOUNDNAME = "soundname"; public static final String COLOR = "color"; public static final String LED_COLOR = "ledColor"; @@ -39,6 +40,7 @@ public interface PushConstants { public static final String STYLE_INBOX = "inbox"; public static final String STYLE_PICTURE = "picture"; public static final String STYLE_TEXT = "text"; + public static final String STYLE_MESSAGING = "messaging"; public static final String BADGE = "badge"; public static final String INITIALIZE = "init"; public static final String SUBSCRIBE = "subscribe";