[notifications] Android channel and channel group support

This commit is contained in:
Chris Bianca 2018-03-05 08:28:13 +00:00
parent 7acace4ce6
commit b9df258402
15 changed files with 840 additions and 311 deletions

View File

@ -1,49 +0,0 @@
package io.invertase.firebase.messaging;
import android.app.AlarmManager;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.content.SharedPreferences;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.HttpURLConnection;
public class RNFirebaseLocalMessagingHelper {
private static final long DEFAULT_VIBRATION = 300L;
private static final String TAG = RNFirebaseLocalMessagingHelper.class.getSimpleName();
private final static String PREFERENCES_KEY = "ReactNativeSystemNotification";
private static boolean mIsForeground = false; //this is a hack
private Context mContext;
private SharedPreferences sharedPreferences = null;
public RNFirebaseLocalMessagingHelper(Application context) {
mContext = context;
sharedPreferences = mContext.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
}
public void setApplicationForeground(boolean foreground){
mIsForeground = foreground;
}
}

View File

@ -1,16 +1,12 @@
package io.invertase.firebase.messaging; package io.invertase.firebase.messaging;
import android.app.Activity;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
@ -18,32 +14,20 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator; import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.RemoteMessage.Notification;
import io.invertase.firebase.Utils; import io.invertase.firebase.Utils;
import me.leolin.shortcutbadger.ShortcutBadger;
import java.util.Map; import java.util.Map;
public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements ActivityEventListener { public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
private static final String BADGE_FILE = "BadgeCountFile";
private static final String BADGE_KEY = "BadgeCount";
private static final String TAG = "RNFirebaseMessaging"; private static final String TAG = "RNFirebaseMessaging";
private SharedPreferences sharedPreferences = null;
public RNFirebaseMessaging(ReactApplicationContext context) { public RNFirebaseMessaging(ReactApplicationContext context) {
super(context); super(context);
context.addActivityEventListener(this);
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
// Subscribe to message events // Subscribe to message events
@ -73,24 +57,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
} }
// Non Web SDK methods // Non Web SDK methods
@ReactMethod
public void getBadge(Promise promise) {
int badge = sharedPreferences.getInt(BADGE_KEY, 0);
Log.d(TAG, "Got badge count: " + badge);
promise.resolve(badge);
}
@ReactMethod
public void getInitialMessage(Promise promise) {
if (getCurrentActivity() == null) {
promise.resolve(null);
} else {
WritableMap messageMap = parseIntentForMessage(getCurrentActivity().getIntent());
promise.resolve(messageMap);
}
}
@ReactMethod @ReactMethod
public void hasPermission(Promise promise) { public void hasPermission(Promise promise) {
promise.resolve(true); promise.resolve(true);
@ -132,19 +98,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
promise.resolve(null); promise.resolve(null);
} }
@ReactMethod
public void setBadge(int badge) {
// Store the badge count for later retrieval
sharedPreferences.edit().putInt(BADGE_KEY, badge).apply();
if (badge == 0) {
Log.d(TAG, "Remove badge count");
ShortcutBadger.removeCount(this.getReactApplicationContext());
} else {
Log.d(TAG, "Apply badge count: " + badge);
ShortcutBadger.applyCount(this.getReactApplicationContext(), badge);
}
}
@ReactMethod @ReactMethod
public void subscribeToTopic(String topic) { public void subscribeToTopic(String topic) {
FirebaseMessaging.getInstance().subscribeToTopic(topic); FirebaseMessaging.getInstance().subscribeToTopic(topic);
@ -155,60 +108,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic); FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
} }
//////////////////////////////////////////////////////////////////////
// Start ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// FCM functionality does not need this function
}
@Override
public void onNewIntent(Intent intent) {
WritableMap messageMap = parseIntentForMessage(intent);
if (messageMap != null) {
Log.d(TAG, "onNewIntent called with new FCM message");
Utils.sendEvent(getReactApplicationContext(), "messaging_message_received", messageMap);
}
}
//////////////////////////////////////////////////////////////////////
// End ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
private WritableMap parseIntentForMessage(Intent intent) {
// Check if FCM data exists
if (intent.getExtras() == null || !intent.hasExtra("google.message_id")) {
return null;
}
Bundle extras = intent.getExtras();
WritableMap messageMap = Arguments.createMap();
WritableMap dataMap = Arguments.createMap();
for (String key : extras.keySet()) {
if (key.equals("collapse_key")) {
messageMap.putString("collapseKey", extras.getString("collapse_key"));
} else if (key.equals("from")) {
messageMap.putString("from", extras.getString("from"));
} else if (key.equals("google.message_id")) {
messageMap.putString("messageId", extras.getString("google.message_id"));
} else if (key.equals("google.sent_time")) {
messageMap.putDouble("sentTime", extras.getLong("google.sent_time"));
} else if (key.equals("google.ttl")) {
messageMap.putDouble("ttl", extras.getDouble("google.ttl"));
} else if (key.equals("_fbSourceApplicationHasBeenSet")) {
// ignore known unneeded fields
} else {
dataMap.putString(key, extras.getString(key));
}
}
messageMap.putMap("data", dataMap);
messageMap.putBoolean("openedFromTray", true);
return messageMap;
}
private class MessageReceiver extends BroadcastReceiver { private class MessageReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -246,67 +145,10 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
if (message.getMessageType() != null) { if (message.getMessageType() != null) {
messageMap.putString("messageType", message.getMessageType()); messageMap.putString("messageType", message.getMessageType());
} }
if (message.getNotification() != null) {
Notification notification = message.getNotification();
WritableMap notificationMap = Arguments.createMap();
if (notification.getBody() != null) {
notificationMap.putString("body", notification.getBody());
}
if (notification.getBodyLocalizationArgs() != null) {
WritableArray bodyArgs = Arguments.createArray();
for (String arg : notification.getBodyLocalizationArgs()) {
bodyArgs.pushString(arg);
}
notificationMap.putArray("bodyLocalizationArgs", bodyArgs);
}
if (notification.getBodyLocalizationKey() != null) {
notificationMap.putString("bodyLocalizationKey", notification.getBodyLocalizationKey());
}
if (notification.getClickAction() != null) {
notificationMap.putString("clickAction", notification.getClickAction());
}
if (notification.getColor() != null) {
notificationMap.putString("color", notification.getColor());
}
if (notification.getIcon() != null) {
notificationMap.putString("icon", notification.getIcon());
}
if (notification.getLink() != null) {
notificationMap.putString("link", notification.getLink().toString());
}
if (notification.getSound() != null) {
notificationMap.putString("sound", notification.getSound());
}
if (notification.getTag() != null) {
notificationMap.putString("tag", notification.getTag());
}
if (notification.getTitle() != null) {
notificationMap.putString("title", notification.getTitle());
}
if (notification.getTitleLocalizationArgs() != null) {
WritableArray titleArgs = Arguments.createArray();
for (String arg : notification.getTitleLocalizationArgs()) {
titleArgs.pushString(arg);
}
notificationMap.putArray("titleLocalizationArgs", titleArgs);
}
if (notification.getTitleLocalizationKey() != null) {
notificationMap.putString("titleLocalizationKey", notification.getTitleLocalizationKey());
}
messageMap.putMap("notification", notificationMap);
}
messageMap.putBoolean("openedFromTray", false);
messageMap.putDouble("sentTime", message.getSentTime()); messageMap.putDouble("sentTime", message.getSentTime());
if (message.getTo() != null) { if (message.getTo() != null) {
messageMap.putString("to", message.getTo()); messageMap.putString("to", message.getTo());
} }
messageMap.putDouble("ttl", message.getTtl()); messageMap.putDouble("ttl", message.getTtl());
return messageMap; return messageMap;

View File

@ -1,7 +1,6 @@
package io.invertase.firebase.messaging; package io.invertase.firebase.messaging;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.UIManagerModule;

View File

@ -10,16 +10,25 @@ import com.google.firebase.messaging.RemoteMessage;
public class RNFirebaseMessagingService extends FirebaseMessagingService { public class RNFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "RNFMessagingService"; private static final String TAG = "RNFMessagingService";
public static final String MESSAGE_EVENT = "messaging-message"; public static final String MESSAGE_EVENT = "messaging-message";
public static final String REMOTE_NOTIFICATION_EVENT = "notifications-remote-notification";
@Override @Override
public void onMessageReceived(RemoteMessage message) { public void onMessageReceived(RemoteMessage message) {
Log.d(TAG, "onMessageReceived event received"); Log.d(TAG, "onMessageReceived event received");
// Build an Intent to pass the token to the RN Application Intent event;
Intent messageEvent = new Intent(MESSAGE_EVENT);
messageEvent.putExtra("message", message); if (message.getNotification() != null) {
// It's a notification, pass to the notification module
event = new Intent(REMOTE_NOTIFICATION_EVENT);
event.putExtra("notification", message);
} else {
// It's a data message, pass to the messaging module
event = new Intent(MESSAGE_EVENT);
event.putExtra("message", message);
}
// Broadcast it so it is only available to the RN Application // Broadcast it so it is only available to the RN Application
LocalBroadcastManager.getInstance(this).sendBroadcast(messageEvent); LocalBroadcastManager.getInstance(this).sendBroadcast(event);
} }
} }

View File

@ -3,6 +3,8 @@ package io.invertase.firebase.notifications;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
@ -21,6 +23,7 @@ import android.util.Log;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import org.json.JSONException; import org.json.JSONException;
@ -30,6 +33,7 @@ import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import io.invertase.firebase.messaging.BundleJSONConverter; import io.invertase.firebase.messaging.BundleJSONConverter;
@ -71,6 +75,42 @@ public class RNFirebaseNotificationManager {
preferences.edit().remove(notificationId).apply(); preferences.edit().remove(notificationId).apply();
} }
public void createChannel(ReadableMap channelMap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = parseChannelMap(channelMap);
notificationManager.createNotificationChannel(channel);
}
}
public void createChannelGroup(ReadableMap channelGroupMap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupMap);
notificationManager.createNotificationChannelGroup(channelGroup);
}
}
public void createChannelGroups(ReadableArray channelGroupsArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
List<NotificationChannelGroup> channelGroups = new ArrayList<>();
for (int i = 0; i < channelGroupsArray.size(); i++) {
NotificationChannelGroup channelGroup = parseChannelGroupMap(channelGroupsArray.getMap(i));
channelGroups.add(channelGroup);
}
notificationManager.createNotificationChannelGroups(channelGroups);
}
}
public void createChannels(ReadableArray channelsArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
List<NotificationChannel> channels = new ArrayList<>();
for (int i = 0; i < channelsArray.size(); i++) {
NotificationChannel channel = parseChannelMap(channelsArray.getMap(i));
channels.add(channel);
}
notificationManager.createNotificationChannels(channels);
}
}
public void displayNotification(ReadableMap notification, Promise promise) { public void displayNotification(ReadableMap notification, Promise promise) {
Bundle notificationBundle = Arguments.toBundle(notification); Bundle notificationBundle = Arguments.toBundle(notification);
displayNotification(notificationBundle, promise); displayNotification(notificationBundle, promise);
@ -155,12 +195,14 @@ public class RNFirebaseNotificationManager {
return; return;
} }
String channelId = notification.getString("channelId"); Bundle android = notification.getBundle("android");
String channelId = android.getString("channelId");
String notificationId = notification.getString("notificationId"); String notificationId = notification.getString("notificationId");
NotificationCompat.Builder nb; NotificationCompat.Builder nb;
// TODO: Change 27 to 'Build.VERSION_CODES.O_MR1' when using appsupport v27 // TODO: Change 27 to 'Build.VERSION_CODES.O_MR1' when using appsupport v27
if (Build.VERSION.SDK_INT >= 27) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
nb = new NotificationCompat.Builder(context, channelId); nb = new NotificationCompat.Builder(context, channelId);
} else { } else {
nb = new NotificationCompat.Builder(context); nb = new NotificationCompat.Builder(context);
@ -173,16 +215,8 @@ public class RNFirebaseNotificationManager {
nb = nb.setExtras(notification.getBundle("data")); nb = nb.setExtras(notification.getBundle("data"));
} }
if (notification.containsKey("sound")) { if (notification.containsKey("sound")) {
String sound = notification.getString("sound"); Uri sound = getSound(notification.getString("sound"));
if (sound.equalsIgnoreCase("default")) { nb = nb.setSound(sound);
nb = nb.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
} else {
int soundResourceId = getResourceId("raw", sound);
if (soundResourceId == 0) {
soundResourceId = getResourceId("raw", sound.substring(0, sound.lastIndexOf('.')));
}
nb = nb.setSound(Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId));
}
} }
if (notification.containsKey("subtitle")) { if (notification.containsKey("subtitle")) {
nb = nb.setSubText(notification.getString("subtitle")); nb = nb.setSubText(notification.getString("subtitle"));
@ -191,125 +225,138 @@ public class RNFirebaseNotificationManager {
nb = nb.setContentTitle(notification.getString("title")); nb = nb.setContentTitle(notification.getString("title"));
} }
if (notification.containsKey("autoCancel")) { if (android.containsKey("autoCancel")) {
nb = nb.setAutoCancel(notification.getBoolean("autoCancel")); nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
} }
if (notification.containsKey("badgeIconType")) { if (android.containsKey("badgeIconType")) {
nb = nb.setBadgeIconType(notification.getInt("badgeIconType")); Double badgeIconType = android.getDouble("badgeIconType");
nb = nb.setBadgeIconType(badgeIconType.intValue());
} }
if (notification.containsKey("category")) { if (android.containsKey("category")) {
nb = nb.setCategory(notification.getString("category")); nb = nb.setCategory(android.getString("category"));
} }
if (notification.containsKey("color")) { if (android.containsKey("color")) {
String color = notification.getString("color"); String color = android.getString("color");
nb = nb.setColor(Color.parseColor(color)); nb = nb.setColor(Color.parseColor(color));
} }
if (notification.containsKey("colorized")) { if (android.containsKey("colorized")) {
nb = nb.setColorized(notification.getBoolean("colorized")); nb = nb.setColorized(android.getBoolean("colorized"));
} }
if (notification.containsKey("contentInfo")) { if (android.containsKey("contentInfo")) {
nb = nb.setContentInfo(notification.getString("contentInfo")); nb = nb.setContentInfo(android.getString("contentInfo"));
} }
if (notification.containsKey("defaults")) { if (notification.containsKey("defaults")) {
int[] defaultsArray = notification.getIntArray("defaults"); double[] defaultsArray = android.getDoubleArray("defaults");
int defaults = 0; int defaults = 0;
for (int d : defaultsArray) { for (Double d : defaultsArray) {
defaults |= d; defaults |= d.intValue();
} }
nb = nb.setDefaults(defaults); nb = nb.setDefaults(defaults);
} }
if (notification.containsKey("group")) { if (android.containsKey("group")) {
nb = nb.setGroup(notification.getString("group")); nb = nb.setGroup(android.getString("group"));
} }
if (notification.containsKey("groupAlertBehaviour")) { if (android.containsKey("groupAlertBehaviour")) {
nb = nb.setGroupAlertBehavior(notification.getInt("groupAlertBehaviour")); Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
} }
if (notification.containsKey("groupSummary")) { if (android.containsKey("groupSummary")) {
nb = nb.setGroupSummary(notification.getBoolean("groupSummary")); nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
} }
if (notification.containsKey("largeIcon")) { if (android.containsKey("largeIcon")) {
Bitmap largeIcon = getBitmap(notification.getString("largeIcon")); Bitmap largeIcon = getBitmap(android.getString("largeIcon"));
if (largeIcon != null) { if (largeIcon != null) {
nb = nb.setLargeIcon(largeIcon); nb = nb.setLargeIcon(largeIcon);
} }
} }
if (notification.containsKey("lights")) { if (android.containsKey("lights")) {
Bundle lights = notification.getBundle("lights"); Bundle lights = android.getBundle("lights");
nb = nb.setLights(lights.getInt("argb"), lights.getInt("onMs"), lights.getInt("offMs")); Double argb = lights.getDouble("argb");
Double onMs = lights.getDouble("onMs");
Double offMs = lights.getDouble("offMs");
nb = nb.setLights(argb.intValue(), onMs.intValue(), offMs.intValue());
} }
if (notification.containsKey("localOnly")) { if (android.containsKey("localOnly")) {
nb = nb.setLocalOnly(notification.getBoolean("localOnly")); nb = nb.setLocalOnly(android.getBoolean("localOnly"));
} }
if (notification.containsKey("number")) { if (android.containsKey("number")) {
nb = nb.setNumber(notification.getInt("number")); Double number = android.getDouble("number");
nb = nb.setNumber(number.intValue());
} }
if (notification.containsKey("ongoing")) { if (android.containsKey("ongoing")) {
nb = nb.setOngoing(notification.getBoolean("ongoing")); nb = nb.setOngoing(android.getBoolean("ongoing"));
} }
if (notification.containsKey("onlyAlertOnce")) { if (android.containsKey("onlyAlertOnce")) {
nb = nb.setOngoing(notification.getBoolean("onlyAlertOnce")); nb = nb.setOngoing(android.getBoolean("onlyAlertOnce"));
} }
if (notification.containsKey("people")) { if (android.containsKey("people")) {
String[] people = notification.getStringArray("people"); String[] people = android.getStringArray("people");
if (people != null) {
for (String person : people) { for (String person : people) {
nb = nb.addPerson(person); nb = nb.addPerson(person);
} }
} }
if (notification.containsKey("priority")) {
nb = nb.setPriority(notification.getInt("priority"));
} }
if (notification.containsKey("progress")) { if (android.containsKey("priority")) {
Bundle progress = notification.getBundle("lights"); Double priority = android.getDouble("priority");
nb = nb.setProgress(progress.getInt("max"), progress.getInt("progress"), progress.getBoolean("indeterminate")); nb = nb.setPriority(priority.intValue());
}
if (android.containsKey("progress")) {
Bundle progress = android.getBundle("lights");
Double max = progress.getDouble("max");
Double progressI = progress.getDouble("progress");
nb = nb.setProgress(max.intValue(), progressI.intValue(), progress.getBoolean("indeterminate"));
} }
// TODO: Public version of notification // TODO: Public version of notification
/* if (notification.containsKey("publicVersion")) { /* if (android.containsKey("publicVersion")) {
nb = nb.setPublicVersion(); nb = nb.setPublicVersion();
} */ } */
if (notification.containsKey("remoteInputHistory")) { if (android.containsKey("remoteInputHistory")) {
nb = nb.setRemoteInputHistory(notification.getStringArray("remoteInputHistory")); nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
} }
if (notification.containsKey("shortcutId")) { if (android.containsKey("shortcutId")) {
nb = nb.setShortcutId(notification.getString("shortcutId")); nb = nb.setShortcutId(android.getString("shortcutId"));
} }
if (notification.containsKey("showWhen")) { if (android.containsKey("showWhen")) {
nb = nb.setShowWhen(notification.getBoolean("showWhen")); nb = nb.setShowWhen(android.getBoolean("showWhen"));
} }
if (notification.containsKey("smallIcon")) { if (android.containsKey("smallIcon")) {
Bundle smallIcon = notification.getBundle("smallIcon"); Bundle smallIcon = android.getBundle("smallIcon");
int smallIconResourceId = getResourceId("mipmap", smallIcon.getString("icon")); int smallIconResourceId = getResourceId("mipmap", smallIcon.getString("icon"));
if (smallIconResourceId == 0) { if (smallIconResourceId == 0) {
smallIconResourceId = getResourceId("drawable", smallIcon.getString("icon")); smallIconResourceId = getResourceId("drawable", smallIcon.getString("icon"));
} }
if (smallIconResourceId != 0) { if (smallIconResourceId != 0) {
if (smallIcon.containsKey("level")) { if (smallIcon.containsKey("level")) {
nb = nb.setSmallIcon(smallIconResourceId, smallIcon.getInt("level")); Double level = smallIcon.getDouble("level");
nb = nb.setSmallIcon(smallIconResourceId, level.intValue());
} else { } else {
nb = nb.setSmallIcon(smallIconResourceId); nb = nb.setSmallIcon(smallIconResourceId);
} }
} }
} }
if (notification.containsKey("sortKey")) { if (android.containsKey("sortKey")) {
nb = nb.setSortKey(notification.getString("sortKey")); nb = nb.setSortKey(android.getString("sortKey"));
} }
if (notification.containsKey("ticker")) { if (android.containsKey("ticker")) {
nb = nb.setTicker(notification.getString("ticker")); nb = nb.setTicker(android.getString("ticker"));
} }
if (notification.containsKey("timeoutAfter")) { if (android.containsKey("timeoutAfter")) {
nb = nb.setTimeoutAfter(notification.getLong("timeoutAfter")); nb = nb.setTimeoutAfter(android.getLong("timeoutAfter"));
} }
if (notification.containsKey("usesChronometer")) { if (android.containsKey("usesChronometer")) {
nb = nb.setUsesChronometer(notification.getBoolean("usesChronometer")); nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
} }
if (notification.containsKey("vibrate")) { if (android.containsKey("vibrate")) {
nb = nb.setVibrate(notification.getLongArray("vibrate")); nb = nb.setVibrate(android.getLongArray("vibrate"));
} }
if (notification.containsKey("visibility")) { if (android.containsKey("visibility")) {
nb = nb.setVisibility(notification.getInt("visibility")); Double visibility = android.getDouble("visibility");
nb = nb.setVisibility(visibility.intValue());
} }
if (notification.containsKey("when")) { if (android.containsKey("when")) {
nb = nb.setWhen(notification.getLong("when")); nb = nb.setWhen(android.getLong("when"));
} }
// TODO: Big text / Big picture // TODO: Big text / Big picture
@ -335,8 +382,8 @@ public class RNFirebaseNotificationManager {
Intent intent = new Intent(context, intentClass); Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(notification); intent.putExtras(notification);
if (notification.containsKey("clickAction")) { if (android.containsKey("clickAction")) {
intent.setAction(notification.getString("clickAction")); intent.setAction(android.getString("clickAction"));
} }
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationId.hashCode(), intent, PendingIntent contentIntent = PendingIntent.getActivity(context, notificationId.hashCode(), intent,
@ -391,6 +438,71 @@ public class RNFirebaseNotificationManager {
return context.getResources().getIdentifier(image, type, context.getPackageName()); return context.getResources().getIdentifier(image, type, context.getPackageName());
} }
private Uri getSound(String sound) {
if (sound.equalsIgnoreCase("default")) {
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
} else {
int soundResourceId = getResourceId("raw", sound);
if (soundResourceId == 0) {
soundResourceId = getResourceId("raw", sound.substring(0, sound.lastIndexOf('.')));
}
return Uri.parse("android.resource://" + context.getPackageName() + "/" + soundResourceId);
}
}
private NotificationChannelGroup parseChannelGroupMap(ReadableMap channelGroupMap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String groupId = channelGroupMap.getString("groupId");
String name = channelGroupMap.getString("name");
return new NotificationChannelGroup(groupId, name);
}
return null;
}
private NotificationChannel parseChannelMap(ReadableMap channelMap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = channelMap.getString("channelId");
String name = channelMap.getString("name");
int importance = channelMap.getInt("importance");
NotificationChannel channel = new NotificationChannel(channelId, name, importance);
if (channelMap.hasKey("bypassDnd")) {
channel.setBypassDnd(channelMap.getBoolean("bypassDnd"));
}
if (channelMap.hasKey("description")) {
channel.setDescription(channelMap.getString("description"));
}
if (channelMap.hasKey("group")) {
channel.setGroup(channelMap.getString("group"));
}
if (channelMap.hasKey("lightColor")) {
String lightColor = channelMap.getString("lightColor");
channel.setLightColor(Color.parseColor(lightColor));
}
if (channelMap.hasKey("lockScreenVisibility")) {
channel.setLockscreenVisibility(channelMap.getInt("lockScreenVisibility"));
}
if (channelMap.hasKey("showBadge")) {
channel.setShowBadge(channelMap.getBoolean("showBadge"));
}
if (channelMap.hasKey("sound")) {
Uri sound = getSound(channelMap.getString("sound"));
channel.setSound(sound, null);
}
if (channelMap.hasKey("vibrationPattern")) {
ReadableArray vibrationArray = channelMap.getArray("vibrationPattern");
long[] vibration = new long[]{};
for (int i = 0; i < vibrationArray.size(); i++) {
vibration[i] = (long) vibrationArray.getDouble(i);
}
channel.setVibrationPattern(vibration);
}
return channel;
}
return null;
}
private void scheduleNotification(Bundle notification, Promise promise) { private void scheduleNotification(Bundle notification, Promise promise) {
if (!notification.containsKey("notificationId")) { if (!notification.containsKey("notificationId")) {
if (promise == null) { if (promise == null) {

View File

@ -1,38 +1,57 @@
package io.invertase.firebase.notifications; package io.invertase.firebase.notifications;
import android.app.Activity;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.google.firebase.messaging.RemoteMessage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map;
import io.invertase.firebase.Utils; import io.invertase.firebase.Utils;
import io.invertase.firebase.messaging.RNFirebaseMessagingService;
import me.leolin.shortcutbadger.ShortcutBadger;
public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements LifecycleEventListener { public class RNFirebaseNotifications extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private static final String BADGE_FILE = "BadgeCountFile";
private static final String BADGE_KEY = "BadgeCount";
private static final String TAG = "RNFirebaseNotifications"; private static final String TAG = "RNFirebaseNotifications";
private SharedPreferences sharedPreferences = null;
private RNFirebaseNotificationManager notificationManager; private RNFirebaseNotificationManager notificationManager;
public RNFirebaseNotifications(ReactApplicationContext context) { public RNFirebaseNotifications(ReactApplicationContext context) {
super(context); super(context);
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext()); context.addActivityEventListener(this);
context.addLifecycleEventListener(this); context.addLifecycleEventListener(this);
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
// Subscribe to remote notification events
localBroadcastManager.registerReceiver(new RemoteNotificationReceiver(),
new IntentFilter(RNFirebaseMessagingService.REMOTE_NOTIFICATION_EVENT));
// Subscribe to scheduled notification events // Subscribe to scheduled notification events
localBroadcastManager.registerReceiver(new ScheduledNotificationReceiver(), localBroadcastManager.registerReceiver(new ScheduledNotificationReceiver(),
new IntentFilter(RNFirebaseNotificationManager.SCHEDULED_NOTIFICATION_EVENT)); new IntentFilter(RNFirebaseNotificationManager.SCHEDULED_NOTIFICATION_EVENT));
@ -58,9 +77,22 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
notificationManager.displayNotification(notification, promise); notificationManager.displayNotification(notification, promise);
} }
@ReactMethod
public void getBadge(Promise promise) {
int badge = sharedPreferences.getInt(BADGE_KEY, 0);
Log.d(TAG, "Got badge count: " + badge);
promise.resolve(badge);
}
@ReactMethod @ReactMethod
public void getInitialNotification(Promise promise) { public void getInitialNotification(Promise promise) {
// TODO // TODO
if (getCurrentActivity() == null) {
promise.resolve(null);
} else {
WritableMap notificationOpenedMap = parseIntentForRemoteNotification(getCurrentActivity().getIntent());
promise.resolve(notificationOpenedMap);
}
} }
@ReactMethod @ReactMethod
@ -83,11 +115,74 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
notificationManager.removeDeliveredNotification(notificationId); notificationManager.removeDeliveredNotification(notificationId);
} }
@ReactMethod
public void setBadge(int badge) {
// Store the badge count for later retrieval
sharedPreferences.edit().putInt(BADGE_KEY, badge).apply();
if (badge == 0) {
Log.d(TAG, "Remove badge count");
ShortcutBadger.removeCount(this.getReactApplicationContext());
} else {
Log.d(TAG, "Apply badge count: " + badge);
ShortcutBadger.applyCount(this.getReactApplicationContext(), badge);
}
}
@ReactMethod @ReactMethod
public void scheduleNotification(ReadableMap notification, Promise promise) { public void scheduleNotification(ReadableMap notification, Promise promise) {
notificationManager.scheduleNotification(notification, promise); notificationManager.scheduleNotification(notification, promise);
} }
//////////////////////////////////////////////////////////////////////
// Start Android specific methods
//////////////////////////////////////////////////////////////////////
@ReactMethod
public void createChannel(ReadableMap channelMap, Promise promise) {
notificationManager.createChannel(channelMap);
promise.resolve(null);
}
@ReactMethod
public void createChannelGroup(ReadableMap channelGroupMap, Promise promise) {
notificationManager.createChannelGroup(channelGroupMap);
promise.resolve(null);
}
@ReactMethod
public void createChannelGroup(ReadableArray channelGroupsArray, Promise promise) {
notificationManager.createChannelGroups(channelGroupsArray);
promise.resolve(null);
}
@ReactMethod
public void createChannels(ReadableArray channelsArray, Promise promise) {
notificationManager.createChannels(channelsArray);
promise.resolve(null);
}
//////////////////////////////////////////////////////////////////////
// End Android specific methods
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Start ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// FCM functionality does not need this function
}
@Override
public void onNewIntent(Intent intent) {
WritableMap notificationOpenedMap = parseIntentForRemoteNotification(intent);
if (notificationOpenedMap != null) {
Log.d(TAG, "onNewIntent called with new remote notification");
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenedMap);
}
}
//////////////////////////////////////////////////////////////////////
// End ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Start LifecycleEventListener methods // Start LifecycleEventListener methods
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -109,10 +204,102 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
// End LifecycleEventListener methods // End LifecycleEventListener methods
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
private WritableMap parseIntentForRemoteNotification(Intent intent) {
// Check if FCM data exists
if (intent.getExtras() == null || !intent.hasExtra("google.message_id")) {
return null;
}
Bundle extras = intent.getExtras();
WritableMap notificationMap = Arguments.createMap();
WritableMap dataMap = Arguments.createMap();
for (String key : extras.keySet()) {
if (key.equals("google.message_id")) {
notificationMap.putString("notificationId", extras.getString(key));
} else if (key.equals("collapse_key")
|| key.equals("from")
|| key.equals("google.sent_time")
|| key.equals("google.ttl")
|| key.equals("_fbSourceApplicationHasBeenSet")) {
// ignore known unneeded fields
} else {
dataMap.putString(key, extras.getString(key));
}
}
notificationMap.putMap("data", dataMap);
WritableMap notificationOpenedMap = Arguments.createMap();
notificationOpenedMap.putString("action", intent.getAction());
notificationOpenedMap.putMap("notification", notificationMap);
return notificationOpenedMap;
}
private WritableMap parseNotificationBundle(Bundle notification) { private WritableMap parseNotificationBundle(Bundle notification) {
return Arguments.makeNativeMap(notification); return Arguments.makeNativeMap(notification);
} }
private WritableMap parseRemoteMessage(RemoteMessage message) {
RemoteMessage.Notification notification = message.getNotification();
WritableMap notificationMap = Arguments.createMap();
WritableMap dataMap = Arguments.createMap();
// Cross platform notification properties
notificationMap.putString("body", notification.getBody());
if (message.getData() != null) {
for (Map.Entry<String, String> e : message.getData().entrySet()) {
dataMap.putString(e.getKey(), e.getValue());
}
}
notificationMap.putMap("data", dataMap);
if (message.getMessageId() != null) {
notificationMap.putString("notificationId", message.getMessageId());
}
if (notification.getSound() != null) {
notificationMap.putString("sound", notification.getSound());
}
if (notification.getTitle() != null) {
notificationMap.putString("title", notification.getTitle());
}
// Android specific notification properties
WritableMap androidMap = Arguments.createMap();
if (notification.getClickAction() != null) {
androidMap.putString("clickAction", notification.getClickAction());
}
if (notification.getColor() != null) {
androidMap.putString("color", notification.getColor());
}
if (notification.getIcon() != null) {
WritableMap iconMap = Arguments.createMap();
iconMap.putString("icon", notification.getIcon());
androidMap.putMap("smallIcon", iconMap);
}
if (notification.getTag() != null) {
androidMap.putString("group", notification.getTag());
}
return notificationMap;
}
private class RemoteNotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (getReactApplicationContext().hasActiveCatalystInstance()) {
Log.d(TAG, "Received new remote notification");
RemoteMessage message = intent.getParcelableExtra("notification");
WritableMap messageMap = parseRemoteMessage(message);
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_received", messageMap);
}
}
}
private class ScheduledNotificationReceiver extends BroadcastReceiver { private class ScheduledNotificationReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {

View File

@ -25,6 +25,7 @@ export default class RemoteMessage {
this._data = inboundMessage.data; this._data = inboundMessage.data;
this._from = inboundMessage.from; this._from = inboundMessage.from;
this._messageId = inboundMessage.messageId; this._messageId = inboundMessage.messageId;
this._messageType = inboundMessage.messageType;
this._sentTime = inboundMessage.sentTime; this._sentTime = inboundMessage.sentTime;
} }
// defaults // defaults
@ -50,6 +51,10 @@ export default class RemoteMessage {
return this._messageId; return this._messageId;
} }
get messageType(): ?string {
return this._messageType;
}
get sentTime(): ?number { get sentTime(): ?number {
return this._sentTime; return this._sentTime;
} }

View File

@ -22,6 +22,7 @@ export type NativeInboundRemoteMessage = {
data: { [string]: string }, data: { [string]: string },
from?: string, from?: string,
messageId: string, messageId: string,
messageType?: string,
sentTime?: number, sentTime?: number,
to?: string, to?: string,
ttl?: number, ttl?: number,

View File

@ -0,0 +1,213 @@
/**
* @flow
* AndroidChannel representation wrapper
*/
import type { ImportanceType, VisibilityType } from './types';
type NativeAndroidChannel = {|
bypassDnd?: boolean,
channelId: string,
description?: string,
group?: string,
importance: ImportanceType,
lightColor?: string,
lockScreenVisibility?: VisibilityType,
name: string,
showBadge?: boolean,
sound?: string,
vibrationPattern?: number[],
|};
export default class AndroidChannel {
_bypassDnd: boolean | void;
_channelId: string;
_description: string | void;
_group: string | void;
_importance: ImportanceType;
_lightColor: string | void;
_lockScreenVisibility: VisibilityType;
_name: string;
_showBadge: boolean | void;
_sound: string | void;
_vibrationPattern: number[] | void;
get bypassDnd(): ?boolean {
return this._bypassDnd;
}
get channelId(): string {
return this._channelId;
}
get description(): ?string {
return this._description;
}
get group(): ?string {
return this._group;
}
get importance(): ImportanceType {
return this._importance;
}
get lightColor(): ?string {
return this._lightColor;
}
get lockScreenVisibility(): ?VisibilityType {
return this._lockScreenVisibility;
}
get name(): string {
return this._name;
}
get showBadge(): ?boolean {
return this._showBadge;
}
get sound(): ?string {
return this._sound;
}
get vibrationPattern(): ?(number[]) {
return this._vibrationPattern;
}
/**
*
* @param bypassDnd
* @returns {AndroidChannel}
*/
setBypassDnd(bypassDnd: boolean): AndroidChannel {
this._bypassDnd = bypassDnd;
return this;
}
/**
*
* @param channelId
* @returns {AndroidChannel}
*/
setChannelId(channelId: string): AndroidChannel {
this._channelId = channelId;
return this;
}
/**
*
* @param description
* @returns {AndroidChannel}
*/
setDescription(description: string): AndroidChannel {
this._description = description;
return this;
}
/**
*
* @param group
* @returns {AndroidChannel}
*/
setGroup(groupId: string): AndroidChannel {
this._group = groupId;
return this;
}
/**
*
* @param importance
* @returns {AndroidChannel}
*/
setImportance(importance: ImportanceType): AndroidChannel {
this._importance = importance;
return this;
}
/**
*
* @param lightColor
* @returns {AndroidChannel}
*/
setLightColor(lightColor: string): AndroidChannel {
this._lightColor = lightColor;
return this;
}
/**
*
* @param lockScreenVisibility
* @returns {AndroidChannel}
*/
setLockScreenVisibility(
lockScreenVisibility: VisibilityType
): AndroidChannel {
this._lockScreenVisibility = lockScreenVisibility;
return this;
}
/**
*
* @param name
* @returns {AndroidChannel}
*/
setName(name: string): AndroidChannel {
this._name = name;
return this;
}
/**
*
* @param showBadge
* @returns {AndroidChannel}
*/
setShowBadge(showBadge: boolean): AndroidChannel {
this._showBadge = showBadge;
return this;
}
/**
*
* @param sound
* @returns {AndroidChannel}
*/
setSound(sound: string): AndroidChannel {
this._sound = sound;
return this;
}
/**
*
* @param vibrationPattern
* @returns {AndroidChannel}
*/
setVibrationPattern(vibrationPattern: number[]): AndroidChannel {
this._vibrationPattern = vibrationPattern;
return this;
}
build(): NativeAndroidChannel {
if (!this._channelId) {
throw new Error('AndroidChannel: Missing required `channelId` property');
} else if (!this._importance) {
throw new Error('AndroidChannel: Missing required `importance` property');
} else if (!this._name) {
throw new Error('AndroidChannel: Missing required `name` property');
}
return {
bypassDnd: this._bypassDnd,
channelId: this._channelId,
description: this._description,
group: this._group,
importance: this._importance,
lightColor: this._lightColor,
lockScreenVisibility: this._lockScreenVisibility,
name: this._name,
showBadge: this._showBadge,
sound: this._sound,
vibrationPattern: this._vibrationPattern,
};
}
}

View File

@ -0,0 +1,57 @@
/**
* @flow
* AndroidChannelGroup representation wrapper
*/
type NativeAndroidChannelGroup = {|
groupId: string,
name: string,
|};
export default class AndroidChannel {
_groupId: string;
_name: string;
get groupId(): string {
return this._groupId;
}
get name(): string {
return this._name;
}
/**
*
* @param groupId
* @returns {AndroidChannel}
*/
setGroupId(groupId: string): AndroidChannel {
this._groupId = groupId;
return this;
}
/**
*
* @param name
* @returns {AndroidChannel}
*/
setName(name: string): AndroidChannel {
this._name = name;
return this;
}
build(): NativeAndroidChannelGroup {
if (!this._groupId) {
throw new Error(
'AndroidChannelGroup: Missing required `groupId` property'
);
} else if (!this._name) {
throw new Error('AndroidChannelGroup: Missing required `name` property');
}
return {
groupId: this._groupId,
name: this._name,
};
}
}

View File

@ -0,0 +1,94 @@
/**
* @flow
* AndroidNotifications representation wrapper
*/
import { Platform } from 'react-native';
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import { getNativeModule } from '../../utils/native';
import type Notifications from './';
export default class AndroidNotifications {
_notifications: Notifications;
constructor(notifications: Notifications) {
this._notifications = notifications;
}
createChannel(channel: AndroidChannel): Promise<void> {
if (Platform.OS === 'android') {
if (!(channel instanceof AndroidChannel)) {
throw new Error(
`AndroidNotifications:createChannel expects an 'AndroidChannel' but got type ${typeof channel}`
);
}
return getNativeModule(this._notifications).createChannel(
channel.build()
);
}
return Promise.resolve();
}
createChannelGroup(channelGroup: AndroidChannelGroup): Promise<void> {
if (Platform.OS === 'android') {
if (!(channelGroup instanceof AndroidChannelGroup)) {
throw new Error(
`AndroidNotifications:createChannelGroup expects an 'AndroidChannelGroup' but got type ${typeof channelGroup}`
);
}
return getNativeModule(this._notifications).createChannelGroup(
channelGroup.build()
);
}
return Promise.resolve();
}
createChannelGroups(channelGroups: AndroidChannelGroup[]): Promise<void> {
if (Platform.OS === 'android') {
if (!Array.isArray(channelGroups)) {
throw new Error(
`AndroidNotifications:createChannelGroups expects an 'Array' but got type ${typeof channelGroups}`
);
}
const nativeChannelGroups = [];
for (let i = 0; i < channelGroups.length; i++) {
const channelGroup = channelGroups[i];
if (!(channelGroup instanceof AndroidChannelGroup)) {
throw new Error(
`AndroidNotifications:createChannelGroups expects array items of type 'AndroidChannelGroup' but got type ${typeof channelGroup}`
);
}
nativeChannelGroups.push(channelGroup.build());
}
return getNativeModule(this._notifications).createChannelGroups(
nativeChannelGroups
);
}
return Promise.resolve();
}
createChannels(channels: AndroidChannel[]): Promise<void> {
if (Platform.OS === 'android') {
if (!Array.isArray(channels)) {
throw new Error(
`AndroidNotifications:createChannels expects an 'Array' but got type ${typeof channels}`
);
}
const nativeChannels = [];
for (let i = 0; i < channels.length; i++) {
const channel = channels[i];
if (!(channel instanceof AndroidChannel)) {
throw new Error(
`AndroidNotifications:createChannels expects array items of type 'AndroidChannel' but got type ${typeof channel}`
);
}
nativeChannels.push(channel.build());
}
return getNativeModule(this._notifications).createChannels(
nativeChannels
);
}
return Promise.resolve();
}
}

View File

@ -1,18 +1,22 @@
/** /**
* @flow * @flow
* Messaging (FCM) representation wrapper * Notifications representation wrapper
*/ */
import { SharedEventEmitter } from '../../utils/events'; import { SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log'; import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase'; import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native'; import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils'; import { isFunction, isObject } from '../../utils';
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import AndroidNotifications from './AndroidNotifications';
import Notification from './Notification'; import Notification from './Notification';
import { import {
BadgeIconType, BadgeIconType,
Category, Category,
Defaults, Defaults,
GroupAlert, GroupAlert,
Importance,
Priority, Priority,
Visibility, Visibility,
} from './types'; } from './types';
@ -64,6 +68,8 @@ export const NAMESPACE = 'notifications';
* @class Notifications * @class Notifications
*/ */
export default class Notifications extends ModuleBase { export default class Notifications extends ModuleBase {
_android: AndroidNotifications;
constructor(app: App) { constructor(app: App) {
super(app, { super(app, {
events: NATIVE_EVENTS, events: NATIVE_EVENTS,
@ -71,6 +77,7 @@ export default class Notifications extends ModuleBase {
multiApp: false, multiApp: false,
namespace: NAMESPACE, namespace: NAMESPACE,
}); });
this._android = new AndroidNotifications(this);
SharedEventEmitter.addListener( SharedEventEmitter.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
@ -89,7 +96,7 @@ export default class Notifications extends ModuleBase {
// public event name: onNotificationOpened // public event name: onNotificationOpened
'notifications_notification_opened', 'notifications_notification_opened',
(notificationOpened: NativeNotificationOpened) => { (notificationOpened: NativeNotificationOpened) => {
SharedEventEmitter.emit('OnNotificationOpened', { SharedEventEmitter.emit('onNotificationOpened', {
action: notificationOpened.action, action: notificationOpened.action,
notification: new Notification(notificationOpened.notification), notification: new Notification(notificationOpened.notification),
}); });
@ -109,6 +116,10 @@ export default class Notifications extends ModuleBase {
); );
} }
get android(): AndroidNotifications {
return this._android;
}
/** /**
* Cancel all notifications * Cancel all notifications
*/ */
@ -148,9 +159,11 @@ export default class Notifications extends ModuleBase {
} }
getInitialNotification(): Promise<Object> { getInitialNotification(): Promise<Object> {
return getNativeModule(this).getInitialNotification(); return getNativeModule(this)
// TODO .getInitialNotification()
// .then(notification => (notification ? new Notification(this, notification) : null)); .then(
notification => (notification ? new Notification(notification) : null)
);
} }
/** /**
@ -278,8 +291,11 @@ export const statics = {
Android: { Android: {
BadgeIconType, BadgeIconType,
Category, Category,
Channel: AndroidChannel,
ChannelGroup: AndroidChannelGroup,
Defaults, Defaults,
GroupAlert, GroupAlert,
Importance,
Priority, Priority,
Visibility, Visibility,
}, },

View File

@ -39,6 +39,16 @@ export const GroupAlert = {
Summary: 1, Summary: 1,
}; };
export const Importance = {
Default: 3,
High: 4,
Low: 2,
Max: 5,
Min: 1,
None: 3,
Unspecified: -1000,
};
export const Priority = { export const Priority = {
Default: 0, Default: 0,
High: 1, High: 1,
@ -57,6 +67,7 @@ export type BadgeIconTypeType = $Values<typeof BadgeIconType>;
export type CategoryType = $Values<typeof Category>; export type CategoryType = $Values<typeof Category>;
export type DefaultsType = $Values<typeof Defaults>; export type DefaultsType = $Values<typeof Defaults>;
export type GroupAlertType = $Values<typeof GroupAlert>; export type GroupAlertType = $Values<typeof GroupAlert>;
export type ImportanceType = $Values<typeof Importance>;
export type PriorityType = $Values<typeof Priority>; export type PriorityType = $Values<typeof Priority>;
export type VisibilityType = $Values<typeof Visibility>; export type VisibilityType = $Values<typeof Visibility>;

View File

@ -15,6 +15,7 @@ import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage; import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage;
import io.invertase.firebase.links.RNFirebaseLinksPackage; import io.invertase.firebase.links.RNFirebaseLinksPackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
import io.invertase.firebase.perf.RNFirebasePerformancePackage; import io.invertase.firebase.perf.RNFirebasePerformancePackage;
import io.invertase.firebase.storage.RNFirebaseStoragePackage; import io.invertase.firebase.storage.RNFirebaseStoragePackage;
import com.oblador.vectoricons.VectorIconsPackage; import com.oblador.vectoricons.VectorIconsPackage;
@ -51,6 +52,7 @@ public class MainApplication extends Application implements ReactApplication {
new RNFirebaseInstanceIdPackage(), new RNFirebaseInstanceIdPackage(),
new RNFirebaseLinksPackage(), new RNFirebaseLinksPackage(),
new RNFirebaseMessagingPackage(), new RNFirebaseMessagingPackage(),
new RNFirebaseNotificationsPackage(),
new RNFirebasePerformancePackage(), new RNFirebasePerformancePackage(),
new RNFirebaseStoragePackage() new RNFirebaseStoragePackage()
); );

View File

@ -7,14 +7,6 @@ import DatabaseContents from './tests/support/DatabaseContents';
RNfirebase.database.enableLogging(true); RNfirebase.database.enableLogging(true);
RNfirebase.firestore.enableLogging(true); RNfirebase.firestore.enableLogging(true);
RNfirebase.messaging().onMessage(message => {
console.log('got new message: ', message);
});
RNfirebase.messaging().onTokenRefresh(token => {
console.log('got new token: ', token);
});
const init = async () => { const init = async () => {
try { try {
await RNfirebase.messaging().requestPermission(); await RNfirebase.messaging().requestPermission();
@ -22,9 +14,47 @@ const init = async () => {
console.log('instanceid: ', instanceid); console.log('instanceid: ', instanceid);
const token = await RNfirebase.messaging().getToken(); const token = await RNfirebase.messaging().getToken();
console.log('token: ', token); console.log('token: ', token);
const initialMessage = await RNfirebase.messaging().getInitialMessage(); const initialNotification = await RNfirebase.notifications().getInitialNotification();
console.log('initial message: ', initialMessage); console.log('initialNotification: ', initialNotification);
RNfirebase.messaging().onMessage(message => {
console.log('onMessage: ', message);
});
RNfirebase.messaging().onTokenRefresh(deviceToken => {
dispatch(fcmTokenReceived(deviceToken));
});
RNfirebase.notifications().onNotification(notification => {
console.log('onNotification: ', notification);
});
RNfirebase.notifications().onNotificationOpened(notification => {
console.log('onNotificationOpened: ', notification);
});
RNfirebase.notifications().onNotificationDisplayed(notification => {
console.log('onNotificationDisplayed: ', notification);
});
// RNfirebase.instanceid().delete(); // RNfirebase.instanceid().delete();
const channel = new RNfirebase.notifications.Android.Channel();
channel
.setChannelId('test')
.setName('test')
.setImportance(RNfirebase.notifications.Android.Importance.Max)
.setDescription('test channel');
RNfirebase.notifications().android.createChannel(channel);
const notification = new RNfirebase.notifications.Notification();
notification
.setTitle('Test title')
.setBody('Test body')
.android.setChannelId('test')
.android.setPriority(RNfirebase.notifications.Android.Priority.Max);
const date = new Date();
date.setMinutes(date.getMinutes() + 1);
setTimeout(() => {
RNfirebase.notifications().displayNotification(notification);
}, 5);
RNfirebase.notifications().scheduleNotification(notification, {
fireDate: date.getTime(),
});
} catch (error) { } catch (error) {
console.error('messaging init error:', error); console.error('messaging init error:', error);
} }