Skip to content

Commit

Permalink
Group the notification into one showing all the notifications
Browse files Browse the repository at this point in the history
Adds an action to clear all the notifications
  • Loading branch information
kunall17 committed Jun 24, 2017
1 parent 3387037 commit 8aa9eee
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 21 deletions.
27 changes: 21 additions & 6 deletions android/app/src/main/java/com/zulipmobile/MainApplication.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
package com.zulipmobile;

import android.app.Application;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;

import com.facebook.react.ReactApplication;
import com.zmxv.RNSound.RNSoundPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.smixx.fabric.FabricPackage;
import com.crashlytics.android.Crashlytics;
import io.fabric.sdk.android.Fabric;
import com.wix.reactnativenotifications.RNNotificationsPackage;
import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.notification.INotificationsApplication;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.zmxv.RNSound.RNSoundPackage;
import com.zulipmobile.notifications.GCMPushNotifications;
import com.zulipmobile.notifications.PushNotificationsProp;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;

import com.learnium.RNDeviceInfo.RNDeviceInfo;
import static com.zulipmobile.notifications.GCMPushNotifications.ACTION_NOTIFICATIONS_DISMISS;
import static com.zulipmobile.notifications.NotificationHelper.addConversationToMap;
import static com.zulipmobile.notifications.NotificationHelper.clearConversations;

public class MainApplication extends Application implements ReactApplication, INotificationsApplication {
private LinkedHashMap<String, Pair<String, Integer>> conversations;

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
Expand Down Expand Up @@ -59,10 +64,20 @@ public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
//Fabric.with(this, new Crashlytics());
conversations = new LinkedHashMap<>();
}

@Override
public IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper) {
return new GCMPushNotifications(context, bundle, defaultFacade, defaultAppLaunchHelper, new JsIOHelper());
if (ACTION_NOTIFICATIONS_DISMISS.equals(bundle.getString(ACTION_NOTIFICATIONS_DISMISS))) {
clearConversations(conversations);
NotificationManager nMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nMgr.cancelAll();
return null;
} else {
PushNotificationsProp prop = new PushNotificationsProp(bundle);
addConversationToMap(prop, conversations);
return new GCMPushNotifications(context, bundle, defaultFacade, defaultAppLaunchHelper, new JsIOHelper(), conversations);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,45 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.ProxyService;
import com.wix.reactnativenotifications.core.notification.PushNotification;
import com.zulipmobile.R;

import java.io.IOException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Locale;

import static com.zulipmobile.notifications.NotificationHelper.buildNotificationContent;
import static com.zulipmobile.notifications.NotificationHelper.clearConversations;
import static com.zulipmobile.notifications.NotificationHelper.extractNames;
import static com.zulipmobile.notifications.NotificationHelper.extractTotalMessagesCount;

public class GCMPushNotifications extends PushNotification {

private static final int NOTIFICATION_ID = 1;
private static final int NOTIFICATION_ID = 435;
public static final String ACTION_NOTIFICATIONS_DISMISS = "ACTION_NOTIFICATIONS_DISMISS";
private LinkedHashMap<String, Pair<String, Integer>> conversations;

/**
* Same as {@link com.wix.reactnativenotifications.core.NotificationIntentAdapter#PUSH_NOTIFICATION_EXTRA_NAME}
*/
private static final String PUSH_NOTIFICATION_EXTRA_NAME = "pushNotification";

public GCMPushNotifications(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
public GCMPushNotifications(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper, LinkedHashMap<String, Pair<String, Integer>> conversations) {
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
this.conversations = conversations;
}

@Override
Expand All @@ -33,6 +53,14 @@ protected PushNotificationsProp getProps() {
return (PushNotificationsProp) mNotificationProps;
}


@Override
public void onOpened() {
super.onOpened();
clearConversations(conversations);
}


@Override
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
// First, get a builder initialized with defaults from the core class.
Expand All @@ -48,31 +76,55 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
String baseURL = getProps().getBaseURL();

builder.setSmallIcon(R.drawable.zulip_notification);
builder.setContentTitle(title);
builder.setAutoCancel(true);
builder.setContentText(content);

if (type.equals("stream")) {
if (android.os.Build.VERSION.SDK_INT >= 16) {
String displayTopic = stream + " > "
+ topic;
builder.setSubText("Mention on " + displayTopic);
if (conversations.size() == 1) {
//Only one 1 notification therefore no using of big view styles
builder.setContentTitle(title);
if (type.equals("stream")) {
if (Build.VERSION.SDK_INT >= 16) {
String displayTopic = stream + " > "
+ topic;
builder.setSubText("Mention on " + displayTopic);
}
}
}
if (avatarURL != null && avatarURL.startsWith("http")) {
Bitmap avatar = fetchAvatar(NotificationHelper.sizedURL(mContext,
avatarURL, 64, baseURL));
if (avatar != null) {
builder.setLargeIcon(avatar);
if (avatarURL != null && avatarURL.startsWith("http")) {
Bitmap avatar = fetchAvatar(NotificationHelper.sizedURL(mContext,
avatarURL, 64, baseURL));
if (avatar != null) {
builder.setLargeIcon(avatar);
}
}
} else {
builder.setContentTitle(String.format(Locale.ENGLISH, "New %d messages in %d conversations", extractTotalMessagesCount(conversations), conversations.size()));
builder.setStyle(new Notification.BigTextStyle().bigText(buildNotificationContent(conversations)));
builder.setContentText("Messages from " + TextUtils.join(",", extractNames(conversations)));
}
if (time != null) {
long timStamp = Long.parseLong(getProps().getTime()) * 1000;
builder.setWhen(timStamp);
}

long[] vPattern = {0, 100, 200, 100};
builder.setVibrate(vPattern);


/**
* Ideally, actions are sent using dismissIntent.setAction(String),
* But here {@link com.wix.reactnativenotifications.core.NotificationIntentAdapter#extractPendingNotificationDataFromIntent(Intent)}
* it checks in the bundle hence, An empty bundle is sent and checked in
* {@link com.zulipmobile.MainApplication#getPushNotification} for this string and then dismissed
*
**/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Intent dismissIntent = new Intent(mContext, ProxyService.class);
Bundle bundle = new Bundle();
bundle.putString(ACTION_NOTIFICATIONS_DISMISS, ACTION_NOTIFICATIONS_DISMISS);
dismissIntent.putExtra(PUSH_NOTIFICATION_EXTRA_NAME, bundle);
PendingIntent piDismiss = PendingIntent.getService(mContext, 0, dismissIntent, 0);
Notification.Action action = new Notification.Action(android.R.drawable.ic_notification_clear_all, "Clear", piDismiss);
builder.addAction(action);
}
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedHashMap;
import java.util.Map;

public class NotificationHelper {

Expand Down Expand Up @@ -49,4 +52,61 @@ public static String addHost(String url, String baseURL) {
}
return url;
}


public static String extractName(String key) {
return key.split(":")[0];
}

public static String buildNotificationContent(LinkedHashMap<String, Pair<String, Integer>> conversations) {
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, Pair<String, Integer>> map : conversations.entrySet()) {
stringBuilder.append(extractName(map.getKey())).append(" (").append(map.getValue().second).append("): ").append(map.getValue().first).append("\n");
}
return stringBuilder.toString();
}

public static int extractTotalMessagesCount(LinkedHashMap<String, Pair<String, Integer>> conversations) {
int totalNumber = 0;
for (Map.Entry<String, Pair<String, Integer>> map : conversations.entrySet()) {
totalNumber += map.getValue().second;
}
return totalNumber;
}

/**
* Formats -
* private message - fullName:Email
* stream message - fullName:Email:stream
*/
public static String buildKeyString(PushNotificationsProp prop) {
if (prop.getRecipientType() == "stream")
return prop.getSenderFullName() + ":" + prop.getEmail();
else
return String.format("%s:%s:stream", prop.getSenderFullName(), prop.getEmail());
}

public static String[] extractNames(LinkedHashMap<String, Pair<String, Integer>> conversations) {
String[] names = new String[conversations.size()];
int index = 0;
for (Map.Entry<String, Pair<String, Integer>> map : conversations.entrySet()) {
names[index++] = map.getKey().split(":")[0];
}
return names;
}

public static void addConversationToMap(PushNotificationsProp prop, LinkedHashMap<String, Pair<String, Integer>> conversations) {
String key = buildKeyString(prop);
Pair<String, Integer> messages = conversations.get(key);
if (messages != null) {
conversations.put(key, new Pair<>(prop.getContent(), messages.second + 1));
} else {
conversations.put(key, new Pair<>(prop.getContent(), 1));
}
}


public static void clearConversations(LinkedHashMap<String, Pair<String, Integer>> conversations) {
conversations.clear();
}
}

0 comments on commit 8aa9eee

Please sign in to comment.