blob: de0e92aa9800e35b20a65f4d6131e58b03655f60 [file] [log] [blame]
package org.wordpress.android;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;
import com.google.android.gms.gcm.GcmListenerService;
import com.simperium.client.BucketObjectMissingException;
import org.apache.commons.lang.StringEscapeUtils;
import org.wordpress.android.analytics.AnalyticsTracker;
import org.wordpress.android.analytics.AnalyticsTracker.Stat;
import org.wordpress.android.analytics.AnalyticsTrackerMixpanel;
import org.wordpress.android.models.AccountHelper;
import org.wordpress.android.models.CommentStatus;
import org.wordpress.android.models.Note;
import org.wordpress.android.ui.main.WPMainActivity;
import org.wordpress.android.ui.notifications.NotificationDismissBroadcastReceiver;
import org.wordpress.android.ui.notifications.NotificationEvents;
import org.wordpress.android.ui.notifications.NotificationsListFragment;
import org.wordpress.android.ui.notifications.utils.NotificationsUtils;
import org.wordpress.android.ui.notifications.utils.SimperiumUtils;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.HelpshiftHelper;
import org.wordpress.android.util.ImageUtils;
import org.wordpress.android.util.PhotonUtils;
import org.wordpress.android.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import de.greenrobot.event.EventBus;
public class GCMMessageService extends GcmListenerService {
private static final ArrayMap<Integer, Bundle> sActiveNotificationsMap = new ArrayMap<>();
private static String sPreviousNoteId = null;
private static long sPreviousNoteTime = 0L;
private static final String NOTIFICATION_GROUP_KEY = "notification_group_key";
private static final int PUSH_NOTIFICATION_ID = 10000;
private static final int AUTH_PUSH_NOTIFICATION_ID = 20000;
public static final int GROUP_NOTIFICATION_ID = 30000;
private static final int MAX_INBOX_ITEMS = 5;
private static final String PUSH_ARG_USER = "user";
private static final String PUSH_ARG_TYPE = "type";
private static final String PUSH_ARG_TITLE = "title";
private static final String PUSH_ARG_MSG = "msg";
private static final String PUSH_ARG_NOTE_ID = "note_id";
private static final String PUSH_TYPE_COMMENT = "c";
private static final String PUSH_TYPE_LIKE = "like";
private static final String PUSH_TYPE_COMMENT_LIKE = "comment_like";
private static final String PUSH_TYPE_AUTOMATTCHER = "automattcher";
private static final String PUSH_TYPE_FOLLOW = "follow";
private static final String PUSH_TYPE_REBLOG = "reblog";
private static final String PUSH_TYPE_PUSH_AUTH = "push_auth";
private static final String PUSH_TYPE_BADGE_RESET = "badge-reset";
private static final String KEY_CATEGORY_COMMENT_LIKE = "comment-like";
private static final String KEY_CATEGORY_COMMENT_REPLY = "comment-reply";
private static final String KEY_CATEGORY_COMMENT_MODERATE = "comment-moderate";
// Add to the analytics properties map a subset of the push notification payload.
private static String[] propertiesToCopyIntoAnalytics = {PUSH_ARG_NOTE_ID, PUSH_ARG_TYPE, "blog_id", "post_id",
"comment_id"};
private void synchronizedHandleDefaultPush(String from, @NonNull Bundle data) {
// sActiveNotificationsMap being static, we can't just synchronize the method
synchronized (GCMMessageService.class) {
handleDefaultPush(from, data);
}
}
private void handleDefaultPush(String from, @NonNull Bundle data) {
// Ensure Simperium is running so that notes sync
SimperiumUtils.configureSimperium(this, AccountHelper.getDefaultAccount().getAccessToken());
long wpcomUserId = AccountHelper.getDefaultAccount().getUserId();
String pushUserId = data.getString(PUSH_ARG_USER);
// pushUserId is always set server side, but better to double check it here.
if (!String.valueOf(wpcomUserId).equals(pushUserId)) {
AppLog.e(T.NOTIFS, "wpcom userId found in the app doesn't match with the ID in the PN. Aborting.");
return;
}
String noteType = StringUtils.notNullStr(data.getString(PUSH_ARG_TYPE));
// Check for wpcom auth push, if so we will process this push differently
if (noteType.equals(PUSH_TYPE_PUSH_AUTH)) {
handlePushAuth(from, data);
return;
}
if (noteType.equals(PUSH_TYPE_BADGE_RESET)) {
handleBadgeResetPN(data);
return;
}
buildAndShowNotificationFromNoteData(data);
EventBus.getDefault().post(new NotificationEvents.NotificationsChanged());
}
private void buildAndShowNotificationFromNoteData(Bundle data) {
if (data == null)
return;
String noteType = StringUtils.notNullStr(data.getString(PUSH_ARG_TYPE));
String title = StringEscapeUtils.unescapeHtml(data.getString(PUSH_ARG_TITLE));
if (title == null) {
title = getString(R.string.app_name);
}
String message = StringEscapeUtils.unescapeHtml(data.getString(PUSH_ARG_MSG));
String noteId = data.getString(PUSH_ARG_NOTE_ID, "");
/*
* if this has the same note_id as the previous notification, and the previous notification
* was received within the last second, then skip showing it - this handles duplicate
* notifications being shown due to the device being registered multiple times with different tokens.
* (still investigating how this could happen - 21-Oct-13)
*
* this also handles the (rare) case where the user receives rapid-fire sub-second like notifications
* due to sudden popularity (post gets added to FP and is liked by many people all at once, etc.),
* which we also want to avoid since it would drain the battery and annoy the user
*
* NOTE: different comments on the same post will have a different note_id, but different likes
* on the same post will have the same note_id, so don't assume that the note_id is unique
*/
long thisTime = System.currentTimeMillis();
if (sPreviousNoteId != null && sPreviousNoteId.equals(noteId)) {
long seconds = TimeUnit.MILLISECONDS.toSeconds(thisTime - sPreviousNoteTime);
if (seconds <= 1) {
AppLog.w(T.NOTIFS, "skipped potential duplicate notification");
return;
}
}
sPreviousNoteId = noteId;
sPreviousNoteTime = thisTime;
// Update notification content for the same noteId if it is already showing
int pushId = 0;
for (Integer id : sActiveNotificationsMap.keySet()) {
if (id == null) {
continue;
}
Bundle noteBundle = sActiveNotificationsMap.get(id);
if (noteBundle != null && noteBundle.getString(PUSH_ARG_NOTE_ID, "").equals(noteId)) {
pushId = id;
sActiveNotificationsMap.put(pushId, data);
break;
}
}
if (pushId == 0) {
pushId = PUSH_NOTIFICATION_ID + sActiveNotificationsMap.size();
sActiveNotificationsMap.put(pushId, data);
}
// Bump Analytics for PNs if "Show notifications" setting is checked (default). Skip otherwise.
if (NotificationsUtils.isNotificationsEnabled(this)) {
Map<String, Object> properties = new HashMap<>();
if (!TextUtils.isEmpty(noteType)) {
// 'comment' and 'comment_pingback' types are sent in PN as type = "c"
if (noteType.equals(PUSH_TYPE_COMMENT)) {
properties.put("notification_type", "comment");
} else {
properties.put("notification_type", noteType);
}
}
bumpPushNotificationsAnalytics(Stat.PUSH_NOTIFICATION_RECEIVED, data, properties);
AnalyticsTracker.flush();
}
showGroupNotificationForActiveNotificationsMap(pushId, noteId, noteType, data.getString("icon"), title, message);
}
private void showGroupNotificationForActiveNotificationsMap(int pushId, String noteId, String noteType,
String largeIconUri, String title, String message) {
// Build the new notification, add group to support wearable stacking
NotificationCompat.Builder builder = getNotificationBuilder(title, message);
Bitmap largeIconBitmap = getLargeIconBitmap(largeIconUri, shouldCircularizeNoteIcon(noteType));
if (largeIconBitmap != null) {
builder.setLargeIcon(largeIconBitmap);
}
showIndividualNotificationForBuilder(builder, noteType, noteId, pushId);
// Also add a group summary notification, which is required for non-wearable devices
showGroupNotificationForBuilder(builder, message);
}
private void addActionsForCommentNotification(NotificationCompat.Builder builder, String noteId) {
// Add some actions if this is a comment notification
boolean areActionsSet = false;
if (SimperiumUtils.getNotesBucket() != null) {
try {
Note note = SimperiumUtils.getNotesBucket().get(noteId);
if (note != null) {
//if note can be replied to, we'll always add this action first
if (note.canReply()) {
addCommentReplyActionForCommentNotification(builder, noteId);
}
// if the comment is lacking approval, offer moderation actions
if (note.getCommentStatus().equals(CommentStatus.UNAPPROVED)) {
if (note.canModerate()) {
addCommentApproveActionForCommentNotification(builder, noteId);
}
} else {
//else offer REPLY / LIKE actions
if (note.canLike()) {
addCommentLikeActionForCommentNotification(builder, noteId);
}
}
}
areActionsSet = true;
} catch (BucketObjectMissingException e) {
e.printStackTrace();
}
}
// if we could not set the actions, set the default ones REPLY / LIKE
if (!areActionsSet) {
addCommentReplyActionForCommentNotification(builder, noteId);
addCommentLikeActionForCommentNotification(builder, noteId);
}
}
private void addCommentReplyActionForCommentNotification(NotificationCompat.Builder builder, String noteId) {
// adding comment reply action
Intent commentReplyIntent = getCommentActionIntent();
commentReplyIntent.addCategory(KEY_CATEGORY_COMMENT_REPLY);
commentReplyIntent.putExtra(NotificationsListFragment.NOTE_INSTANT_REPLY_EXTRA, true);
if (noteId != null) {
commentReplyIntent.putExtra(NotificationsListFragment.NOTE_ID_EXTRA, noteId);
}
PendingIntent commentReplyPendingIntent = PendingIntent.getActivity(this, 0, commentReplyIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(R.drawable.ic_reply_white_24dp, getText(R.string.reply),
commentReplyPendingIntent);
}
private void addCommentLikeActionForCommentNotification(NotificationCompat.Builder builder, String noteId) {
// adding comment like action
Intent commentLikeIntent = getCommentActionIntent();
commentLikeIntent.addCategory(KEY_CATEGORY_COMMENT_LIKE);
commentLikeIntent.putExtra(NotificationsListFragment.NOTE_INSTANT_LIKE_EXTRA, true);
if (noteId != null) {
commentLikeIntent.putExtra(NotificationsListFragment.NOTE_ID_EXTRA, noteId);
}
PendingIntent commentLikePendingIntent = PendingIntent.getActivity(this, 0, commentLikeIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(R.drawable.ic_action_like, getText(R.string.like),
commentLikePendingIntent);
}
private void addCommentApproveActionForCommentNotification(NotificationCompat.Builder builder, String noteId) {
// adding comment approve action
Intent commentApproveIntent = getCommentActionIntent();
commentApproveIntent.addCategory(KEY_CATEGORY_COMMENT_MODERATE);
commentApproveIntent.putExtra(NotificationsListFragment.NOTE_INSTANT_APPROVE_EXTRA, true);
if (noteId != null) {
commentApproveIntent.putExtra(NotificationsListFragment.NOTE_ID_EXTRA, noteId);
}
PendingIntent commentApprovePendingIntent = PendingIntent.getActivity(this, 0, commentApproveIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(R.drawable.ic_action_approve, getText(R.string.approve),
commentApprovePendingIntent);
}
private Intent getCommentActionIntent(){
Intent intent = new Intent(this, WPMainActivity.class);
intent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
return intent;
}
private Bitmap getLargeIconBitmap(String iconUrl, boolean shouldCircularizeIcon){
Bitmap largeIconBitmap = null;
if (iconUrl != null) {
try {
iconUrl = URLDecoder.decode(iconUrl, "UTF-8");
int largeIconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_height);
String resizedUrl = PhotonUtils.getPhotonImageUrl(iconUrl, largeIconSize, largeIconSize);
largeIconBitmap = ImageUtils.downloadBitmap(resizedUrl);
if (largeIconBitmap != null && shouldCircularizeIcon) {
largeIconBitmap = ImageUtils.getCircularBitmap(largeIconBitmap);
}
} catch (UnsupportedEncodingException e) {
AppLog.e(T.NOTIFS, e);
}
}
return largeIconBitmap;
}
private NotificationCompat.Builder getNotificationBuilder(String title, String message){
// Build the new notification, add group to support wearable stacking
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setColor(getResources().getColor(R.color.blue_wordpress))
.setContentTitle(title)
.setContentText(message)
.setTicker(message)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setGroup(NOTIFICATION_GROUP_KEY);
return builder;
}
private void showGroupNotificationForBuilder(NotificationCompat.Builder builder, String message) {
if (builder == null) {
return;
}
if (sActiveNotificationsMap.size() > 1) {
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
int noteCtr = 1;
for (Bundle pushBundle : sActiveNotificationsMap.values()) {
// InboxStyle notification is limited to 5 lines
if (noteCtr > MAX_INBOX_ITEMS) {
break;
}
if (pushBundle == null || pushBundle.getString(PUSH_ARG_MSG) == null) {
continue;
}
if (pushBundle.getString(PUSH_ARG_TYPE, "").equals(PUSH_TYPE_COMMENT)) {
String pnTitle = StringEscapeUtils.unescapeHtml((pushBundle.getString(PUSH_ARG_TITLE)));
String pnMessage = StringEscapeUtils.unescapeHtml((pushBundle.getString(PUSH_ARG_MSG)));
inboxStyle.addLine(pnTitle + ": " + pnMessage);
} else {
String pnMessage = StringEscapeUtils.unescapeHtml((pushBundle.getString(PUSH_ARG_MSG)));
inboxStyle.addLine(pnMessage);
}
noteCtr++;
}
if (sActiveNotificationsMap.size() > MAX_INBOX_ITEMS) {
inboxStyle.setSummaryText(String.format(getString(R.string.more_notifications),
sActiveNotificationsMap.size() - MAX_INBOX_ITEMS));
}
String subject = String.format(getString(R.string.new_notifications), sActiveNotificationsMap.size());
NotificationCompat.Builder groupBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setColor(getResources().getColor(R.color.blue_wordpress))
.setGroup(NOTIFICATION_GROUP_KEY)
.setGroupSummary(true)
.setAutoCancel(true)
.setTicker(message)
.setContentTitle(getString(R.string.app_name))
.setContentText(subject)
.setStyle(inboxStyle);
showNotificationForBuilder(groupBuilder, this, GROUP_NOTIFICATION_ID);
} else {
// Set the individual notification we've already built as the group summary
builder.setGroupSummary(true);
showNotificationForBuilder(builder, this, GROUP_NOTIFICATION_ID);
}
}
private void showIndividualNotificationForBuilder(NotificationCompat.Builder builder, String noteType, String noteId, int pushId) {
if (builder == null) {
return;
}
if (noteType.equals(PUSH_TYPE_COMMENT)) {
addActionsForCommentNotification(builder, noteId);
}
showNotificationForBuilder(builder, this, pushId);
}
// Displays a notification to the user
private void showNotificationForBuilder(NotificationCompat.Builder builder, Context context, int notificationId) {
if (builder == null || context == null) {
return;
}
Intent resultIntent = new Intent(this, WPMainActivity.class);
resultIntent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
resultIntent.setAction("android.intent.action.MAIN");
resultIntent.addCategory("android.intent.category.LAUNCHER");
if (sPreviousNoteId != null) {
resultIntent.putExtra(NotificationsListFragment.NOTE_ID_EXTRA, sPreviousNoteId);
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean shouldPlaySound = prefs.getBoolean("wp_pref_notification_sound", false);
boolean shouldVibrate = prefs.getBoolean("wp_pref_notification_vibrate", false);
boolean shouldBlinkLight = prefs.getBoolean("wp_pref_notification_light", false);
String notificationSound = prefs.getString("wp_pref_custom_notification_sound", null); //"" if None is selected
// use default sound if the legacy sound preference was ON but the custom sound was not selected (null)
if (shouldPlaySound && notificationSound == null) {
builder.setSound(Uri.parse("content://settings/system/notification_sound"));
} else if (!TextUtils.isEmpty(notificationSound)) {
builder.setSound(Uri.parse(notificationSound));
}
if (shouldVibrate) {
builder.setVibrate(new long[]{500, 500, 500});
}
if (shouldBlinkLight) {
builder.setLights(0xff0000ff, 1000, 5000);
}
// Call broadcast receiver when notification is dismissed
Intent notificationDeletedIntent = new Intent(this, NotificationDismissBroadcastReceiver.class);
notificationDeletedIntent.putExtra("notificationId", notificationId);
notificationDeletedIntent.setAction(String.valueOf(notificationId));
PendingIntent pendingDeleteIntent =
PendingIntent.getBroadcast(context, notificationId, notificationDeletedIntent, 0);
builder.setDeleteIntent(pendingDeleteIntent);
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, resultIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(notificationId, builder.build());
}
private void rebuildAndUpdateNotificationsOnSystemBar(Bundle data) {
Bitmap largeIconBitmap = null;
// here notify the existing group notification by eliminating the line that is now gone
String title = getNotificationTitleOrAppNameFromBundle(data);
String message = StringEscapeUtils.unescapeHtml(data.getString(PUSH_ARG_MSG));
NotificationCompat.Builder builder = null;
if (sActiveNotificationsMap.size() == 1) {
//only one notification remains, so get the proper message for it and re-instate in the system dashboard
Bundle remainingNote = sActiveNotificationsMap.values().iterator().next();
if (remainingNote != null) {
String remainingNoteTitle = StringEscapeUtils.unescapeHtml(remainingNote.getString(PUSH_ARG_TITLE));
if (!TextUtils.isEmpty(remainingNoteTitle)) {
title = remainingNoteTitle;
}
String remainingNoteMessage = StringEscapeUtils.unescapeHtml(remainingNote.getString(PUSH_ARG_MSG));
if (!TextUtils.isEmpty(remainingNoteMessage)) {
message = remainingNoteMessage;
}
largeIconBitmap = getLargeIconBitmap(remainingNote.getString("icon"),
shouldCircularizeNoteIcon(remainingNote.getString(PUSH_ARG_TYPE)));
builder = getNotificationBuilder(title, message);
String noteType = StringUtils.notNullStr(remainingNote.getString(PUSH_ARG_TYPE));
String noteId = remainingNote.getString(PUSH_ARG_NOTE_ID, "");
if (!sActiveNotificationsMap.isEmpty()) {
showIndividualNotificationForBuilder(builder, noteType, noteId, sActiveNotificationsMap.keyAt(0));
}
}
}
if (builder == null) {
builder = getNotificationBuilder(title, message);
}
if (largeIconBitmap == null) {
largeIconBitmap = getLargeIconBitmap(data.getString("icon"), shouldCircularizeNoteIcon(PUSH_TYPE_BADGE_RESET));
}
if (largeIconBitmap != null) {
builder.setLargeIcon(largeIconBitmap);
}
showGroupNotificationForBuilder(builder, message);
}
private String getNotificationTitleOrAppNameFromBundle(Bundle data){
String title = StringEscapeUtils.unescapeHtml(data.getString(PUSH_ARG_TITLE));
if (title == null) {
title = getString(R.string.app_name);
}
return title;
}
// Clear all notifications
private void handleBadgeResetPN(Bundle data) {
if (data == null || !data.containsKey(PUSH_ARG_NOTE_ID)) {
// ignore the reset-badge PN if it's a global one
return;
}
removeNotificationWithNoteIdFromSystemBar(this, data.getString(PUSH_ARG_NOTE_ID, ""));
//now that we cleared the specific notif, we can check and make any visual updates
if (sActiveNotificationsMap.size() > 0) {
rebuildAndUpdateNotificationsOnSystemBar(data);
}
EventBus.getDefault().post(new NotificationEvents.NotificationsChanged());
}
// Show a notification for two-step auth users who sign in from a web browser
private void handlePushAuth(String from, Bundle data) {
if (data == null) {
return;
}
String pushAuthToken = data.getString("push_auth_token", "");
String title = data.getString("title", "");
String message = data.getString("msg", "");
long expirationTimestamp = Long.valueOf(data.getString("expires", "0"));
// No strings, no service
if (TextUtils.isEmpty(pushAuthToken) || TextUtils.isEmpty(title) || TextUtils.isEmpty(message)) {
return;
}
// Show authorization intent
Intent pushAuthIntent = new Intent(this, WPMainActivity.class);
pushAuthIntent.putExtra(WPMainActivity.ARG_OPENED_FROM_PUSH, true);
pushAuthIntent.putExtra(NotificationsUtils.ARG_PUSH_AUTH_TOKEN, pushAuthToken);
pushAuthIntent.putExtra(NotificationsUtils.ARG_PUSH_AUTH_TITLE, title);
pushAuthIntent.putExtra(NotificationsUtils.ARG_PUSH_AUTH_MESSAGE, message);
pushAuthIntent.putExtra(NotificationsUtils.ARG_PUSH_AUTH_EXPIRES, expirationTimestamp);
pushAuthIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
pushAuthIntent.setAction("android.intent.action.MAIN");
pushAuthIntent.addCategory("android.intent.category.LAUNCHER");
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setColor(getResources().getColor(R.color.blue_wordpress))
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setPriority(NotificationCompat.PRIORITY_MAX);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, pushAuthIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(AUTH_PUSH_NOTIFICATION_ID, builder.build());
}
@Override
public void onMessageReceived(String from, Bundle data) {
AppLog.v(T.NOTIFS, "Received Message");
if (data == null) {
AppLog.v(T.NOTIFS, "No notification message content received. Aborting.");
return;
}
// Handle helpshift PNs
if (TextUtils.equals(data.getString("origin"), "helpshift")) {
HelpshiftHelper.getInstance().handlePush(this, new Intent().putExtras(data));
return;
}
// Handle mixpanel PNs
if (data.containsKey("mp_message")) {
String mpMessage = data.getString("mp_message");
String title = getString(R.string.app_name);
Intent resultIntent = new Intent(this, WPMainActivity.class);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AnalyticsTrackerMixpanel.showNotification(this, pendingIntent,
R.drawable.notification_icon, title, mpMessage);
return;
}
if (!AccountHelper.isSignedInWordPressDotCom()) {
return;
}
synchronizedHandleDefaultPush(from, data);
}
// Returns true if the note type is known to have a gravatar
public boolean shouldCircularizeNoteIcon(String noteType) {
if (TextUtils.isEmpty(noteType)) {
return false;
}
switch (noteType) {
case PUSH_TYPE_COMMENT:
case PUSH_TYPE_LIKE:
case PUSH_TYPE_COMMENT_LIKE:
case PUSH_TYPE_AUTOMATTCHER:
case PUSH_TYPE_FOLLOW:
case PUSH_TYPE_REBLOG:
return true;
default:
return false;
}
}
public static synchronized void clearNotifications() {
sActiveNotificationsMap.clear();
}
public static synchronized int getNotificationsCount() {
return sActiveNotificationsMap.size();
}
public static synchronized boolean hasNotifications() {
return !sActiveNotificationsMap.isEmpty();
}
// Removes a specific notification from the internal map - only use this when we know
// the user has dismissed the app by swiping it off the screen
public static synchronized void removeNotification(int notificationId) {
sActiveNotificationsMap.remove(notificationId);
}
// Removes a specific notification from the system bar
public static synchronized void removeNotificationWithNoteIdFromSystemBar(Context context, String noteID) {
if (context == null || TextUtils.isEmpty(noteID) || !hasNotifications()) {
return;
}
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
// here we loop with an Iterator as there might be several Notifications with the same Note ID (i.e. likes on the same Note)
// so we need to keep cancelling them and removing them from our activeNotificationsMap as we find it suitable
for(Iterator<Map.Entry<Integer, Bundle>> it = sActiveNotificationsMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<Integer, Bundle> row = it.next();
Integer pushId = row.getKey();
Bundle noteBundle = row.getValue();
if (noteBundle.getString(PUSH_ARG_NOTE_ID, "").equals(noteID)) {
notificationManager.cancel(pushId);
it.remove();
}
}
if (sActiveNotificationsMap.size() == 0) {
notificationManager.cancel(GCMMessageService.GROUP_NOTIFICATION_ID);
}
}
// Removes all app notifications from the system bar
public static synchronized void removeAllNotifications(Context context) {
if (context == null || !hasNotifications()) {
return;
}
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
for (Integer pushId : sActiveNotificationsMap.keySet()) {
notificationManager.cancel(pushId);
}
notificationManager.cancel(GCMMessageService.GROUP_NOTIFICATION_ID);
clearNotifications();
}
// NoteID is the ID if the note in WordPress
public static synchronized void bumpPushNotificationsTappedAnalytics(String noteID) {
for (int id : sActiveNotificationsMap.keySet()) {
Bundle noteBundle = sActiveNotificationsMap.get(id);
if (noteBundle.getString(PUSH_ARG_NOTE_ID, "").equals(noteID)) {
bumpPushNotificationsAnalytics(Stat.PUSH_NOTIFICATION_TAPPED, noteBundle, null);
AnalyticsTracker.flush();
return;
}
}
}
// Mark all notifications as tapped
public static synchronized void bumpPushNotificationsTappedAllAnalytics() {
for (int id : sActiveNotificationsMap.keySet()) {
Bundle noteBundle = sActiveNotificationsMap.get(id);
bumpPushNotificationsAnalytics(Stat.PUSH_NOTIFICATION_TAPPED, noteBundle, null);
}
AnalyticsTracker.flush();
}
private static void bumpPushNotificationsAnalytics(Stat stat, Bundle noteBundle,
Map<String, Object> properties) {
// Bump Analytics for PNs if "Show notifications" setting is checked (default). Skip otherwise.
if (!NotificationsUtils.isNotificationsEnabled(WordPress.getContext())) {
return;
}
if (properties == null) {
properties = new HashMap<>();
}
String notificationID = noteBundle.getString(PUSH_ARG_NOTE_ID, "");
if (!TextUtils.isEmpty(notificationID)) {
for (String currentPropertyToCopy : propertiesToCopyIntoAnalytics) {
if (noteBundle.containsKey(currentPropertyToCopy)) {
properties.put("push_notification_" + currentPropertyToCopy, noteBundle.get(currentPropertyToCopy));
}
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(WordPress.getContext());
String lastRegisteredGCMToken = preferences.getString(NotificationsUtils.WPCOM_PUSH_DEVICE_TOKEN, null);
properties.put("push_notification_token", lastRegisteredGCMToken);
AnalyticsTracker.track(stat, properties);
}
}
}