Merge branch 'fcm-rewrite-separate-events' into fcm-rewrite

This commit is contained in:
Chris Bianca 2018-03-07 12:46:40 +00:00
commit aa367e7be8
28 changed files with 1961 additions and 1070 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;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
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.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.RemoteMessage.Notification;
import io.invertase.firebase.Utils;
import me.leolin.shortcutbadger.ShortcutBadger;
import java.util.Map;
public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements ActivityEventListener {
private static final String BADGE_FILE = "BadgeCountFile";
private static final String BADGE_KEY = "BadgeCount";
public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseMessaging";
private SharedPreferences sharedPreferences = null;
public RNFirebaseMessaging(ReactApplicationContext context) {
super(context);
context.addActivityEventListener(this);
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
// Subscribe to message events
@ -73,24 +57,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
}
// 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
public void hasPermission(Promise promise) {
promise.resolve(true);
@ -132,19 +98,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
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
public void subscribeToTopic(String topic) {
FirebaseMessaging.getInstance().subscribeToTopic(topic);
@ -155,60 +108,6 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
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 {
@Override
public void onReceive(Context context, Intent intent) {
@ -246,67 +145,10 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements A
if (message.getMessageType() != null) {
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());
if (message.getTo() != null) {
messageMap.putString("to", message.getTo());
}
messageMap.putDouble("ttl", message.getTtl());
return messageMap;

View File

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

View File

@ -10,16 +10,25 @@ import com.google.firebase.messaging.RemoteMessage;
public class RNFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "RNFMessagingService";
public static final String MESSAGE_EVENT = "messaging-message";
public static final String REMOTE_NOTIFICATION_EVENT = "notifications-remote-notification";
@Override
public void onMessageReceived(RemoteMessage message) {
Log.d(TAG, "onMessageReceived event received");
// Build an Intent to pass the token to the RN Application
Intent messageEvent = new Intent(MESSAGE_EVENT);
messageEvent.putExtra("message", message);
Intent event;
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
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.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@ -21,6 +23,7 @@ import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import org.json.JSONException;
@ -30,6 +33,7 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.invertase.firebase.messaging.BundleJSONConverter;
@ -71,6 +75,42 @@ public class RNFirebaseNotificationManager {
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) {
Bundle notificationBundle = Arguments.toBundle(notification);
displayNotification(notificationBundle, promise);
@ -155,12 +195,14 @@ public class RNFirebaseNotificationManager {
return;
}
String channelId = notification.getString("channelId");
Bundle android = notification.getBundle("android");
String channelId = android.getString("channelId");
String notificationId = notification.getString("notificationId");
NotificationCompat.Builder nb;
// 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);
} else {
nb = new NotificationCompat.Builder(context);
@ -173,16 +215,8 @@ public class RNFirebaseNotificationManager {
nb = nb.setExtras(notification.getBundle("data"));
}
if (notification.containsKey("sound")) {
String sound = notification.getString("sound");
if (sound.equalsIgnoreCase("default")) {
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));
}
Uri sound = getSound(notification.getString("sound"));
nb = nb.setSound(sound);
}
if (notification.containsKey("subtitle")) {
nb = nb.setSubText(notification.getString("subtitle"));
@ -191,125 +225,138 @@ public class RNFirebaseNotificationManager {
nb = nb.setContentTitle(notification.getString("title"));
}
if (notification.containsKey("autoCancel")) {
nb = nb.setAutoCancel(notification.getBoolean("autoCancel"));
if (android.containsKey("autoCancel")) {
nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
}
if (notification.containsKey("badgeIconType")) {
nb = nb.setBadgeIconType(notification.getInt("badgeIconType"));
if (android.containsKey("badgeIconType")) {
Double badgeIconType = android.getDouble("badgeIconType");
nb = nb.setBadgeIconType(badgeIconType.intValue());
}
if (notification.containsKey("category")) {
nb = nb.setCategory(notification.getString("category"));
if (android.containsKey("category")) {
nb = nb.setCategory(android.getString("category"));
}
if (notification.containsKey("color")) {
String color = notification.getString("color");
if (android.containsKey("color")) {
String color = android.getString("color");
nb = nb.setColor(Color.parseColor(color));
}
if (notification.containsKey("colorized")) {
nb = nb.setColorized(notification.getBoolean("colorized"));
if (android.containsKey("colorized")) {
nb = nb.setColorized(android.getBoolean("colorized"));
}
if (notification.containsKey("contentInfo")) {
nb = nb.setContentInfo(notification.getString("contentInfo"));
if (android.containsKey("contentInfo")) {
nb = nb.setContentInfo(android.getString("contentInfo"));
}
if (notification.containsKey("defaults")) {
int[] defaultsArray = notification.getIntArray("defaults");
double[] defaultsArray = android.getDoubleArray("defaults");
int defaults = 0;
for (int d : defaultsArray) {
defaults |= d;
for (Double d : defaultsArray) {
defaults |= d.intValue();
}
nb = nb.setDefaults(defaults);
}
if (notification.containsKey("group")) {
nb = nb.setGroup(notification.getString("group"));
if (android.containsKey("group")) {
nb = nb.setGroup(android.getString("group"));
}
if (notification.containsKey("groupAlertBehaviour")) {
nb = nb.setGroupAlertBehavior(notification.getInt("groupAlertBehaviour"));
if (android.containsKey("groupAlertBehaviour")) {
Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
}
if (notification.containsKey("groupSummary")) {
nb = nb.setGroupSummary(notification.getBoolean("groupSummary"));
if (android.containsKey("groupSummary")) {
nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
}
if (notification.containsKey("largeIcon")) {
Bitmap largeIcon = getBitmap(notification.getString("largeIcon"));
if (android.containsKey("largeIcon")) {
Bitmap largeIcon = getBitmap(android.getString("largeIcon"));
if (largeIcon != null) {
nb = nb.setLargeIcon(largeIcon);
}
}
if (notification.containsKey("lights")) {
Bundle lights = notification.getBundle("lights");
nb = nb.setLights(lights.getInt("argb"), lights.getInt("onMs"), lights.getInt("offMs"));
if (android.containsKey("lights")) {
Bundle lights = android.getBundle("lights");
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")) {
nb = nb.setLocalOnly(notification.getBoolean("localOnly"));
if (android.containsKey("localOnly")) {
nb = nb.setLocalOnly(android.getBoolean("localOnly"));
}
if (notification.containsKey("number")) {
nb = nb.setNumber(notification.getInt("number"));
if (android.containsKey("number")) {
Double number = android.getDouble("number");
nb = nb.setNumber(number.intValue());
}
if (notification.containsKey("ongoing")) {
nb = nb.setOngoing(notification.getBoolean("ongoing"));
if (android.containsKey("ongoing")) {
nb = nb.setOngoing(android.getBoolean("ongoing"));
}
if (notification.containsKey("onlyAlertOnce")) {
nb = nb.setOngoing(notification.getBoolean("onlyAlertOnce"));
if (android.containsKey("onlyAlertOnce")) {
nb = nb.setOngoing(android.getBoolean("onlyAlertOnce"));
}
if (notification.containsKey("people")) {
String[] people = notification.getStringArray("people");
for (String person : people) {
nb = nb.addPerson(person);
if (android.containsKey("people")) {
String[] people = android.getStringArray("people");
if (people != null) {
for (String person : people) {
nb = nb.addPerson(person);
}
}
}
if (notification.containsKey("priority")) {
nb = nb.setPriority(notification.getInt("priority"));
if (android.containsKey("priority")) {
Double priority = android.getDouble("priority");
nb = nb.setPriority(priority.intValue());
}
if (notification.containsKey("progress")) {
Bundle progress = notification.getBundle("lights");
nb = nb.setProgress(progress.getInt("max"), progress.getInt("progress"), progress.getBoolean("indeterminate"));
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
/* if (notification.containsKey("publicVersion")) {
/* if (android.containsKey("publicVersion")) {
nb = nb.setPublicVersion();
} */
if (notification.containsKey("remoteInputHistory")) {
nb = nb.setRemoteInputHistory(notification.getStringArray("remoteInputHistory"));
if (android.containsKey("remoteInputHistory")) {
nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
}
if (notification.containsKey("shortcutId")) {
nb = nb.setShortcutId(notification.getString("shortcutId"));
if (android.containsKey("shortcutId")) {
nb = nb.setShortcutId(android.getString("shortcutId"));
}
if (notification.containsKey("showWhen")) {
nb = nb.setShowWhen(notification.getBoolean("showWhen"));
if (android.containsKey("showWhen")) {
nb = nb.setShowWhen(android.getBoolean("showWhen"));
}
if (notification.containsKey("smallIcon")) {
Bundle smallIcon = notification.getBundle("smallIcon");
if (android.containsKey("smallIcon")) {
Bundle smallIcon = android.getBundle("smallIcon");
int smallIconResourceId = getResourceId("mipmap", smallIcon.getString("icon"));
if (smallIconResourceId == 0) {
smallIconResourceId = getResourceId("drawable", smallIcon.getString("icon"));
}
if (smallIconResourceId != 0) {
if (smallIcon.containsKey("level")) {
nb = nb.setSmallIcon(smallIconResourceId, smallIcon.getInt("level"));
Double level = smallIcon.getDouble("level");
nb = nb.setSmallIcon(smallIconResourceId, level.intValue());
} else {
nb = nb.setSmallIcon(smallIconResourceId);
}
}
}
if (notification.containsKey("sortKey")) {
nb = nb.setSortKey(notification.getString("sortKey"));
if (android.containsKey("sortKey")) {
nb = nb.setSortKey(android.getString("sortKey"));
}
if (notification.containsKey("ticker")) {
nb = nb.setTicker(notification.getString("ticker"));
if (android.containsKey("ticker")) {
nb = nb.setTicker(android.getString("ticker"));
}
if (notification.containsKey("timeoutAfter")) {
nb = nb.setTimeoutAfter(notification.getLong("timeoutAfter"));
if (android.containsKey("timeoutAfter")) {
nb = nb.setTimeoutAfter(android.getLong("timeoutAfter"));
}
if (notification.containsKey("usesChronometer")) {
nb = nb.setUsesChronometer(notification.getBoolean("usesChronometer"));
if (android.containsKey("usesChronometer")) {
nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
}
if (notification.containsKey("vibrate")) {
nb = nb.setVibrate(notification.getLongArray("vibrate"));
if (android.containsKey("vibrate")) {
nb = nb.setVibrate(android.getLongArray("vibrate"));
}
if (notification.containsKey("visibility")) {
nb = nb.setVisibility(notification.getInt("visibility"));
if (android.containsKey("visibility")) {
Double visibility = android.getDouble("visibility");
nb = nb.setVisibility(visibility.intValue());
}
if (notification.containsKey("when")) {
nb = nb.setWhen(notification.getLong("when"));
if (android.containsKey("when")) {
nb = nb.setWhen(android.getLong("when"));
}
// TODO: Big text / Big picture
@ -335,8 +382,8 @@ public class RNFirebaseNotificationManager {
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(notification);
if (notification.containsKey("clickAction")) {
intent.setAction(notification.getString("clickAction"));
if (android.containsKey("clickAction")) {
intent.setAction(android.getString("clickAction"));
}
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationId.hashCode(), intent,
@ -391,6 +438,71 @@ public class RNFirebaseNotificationManager {
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) {
if (!notification.containsKey("notificationId")) {
if (promise == null) {

View File

@ -1,38 +1,57 @@
package io.invertase.firebase.notifications;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.messaging.RemoteMessage;
import java.util.ArrayList;
import java.util.Map;
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 SharedPreferences sharedPreferences = null;
private RNFirebaseNotificationManager notificationManager;
public RNFirebaseNotifications(ReactApplicationContext context) {
super(context);
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
context.addActivityEventListener(this);
context.addLifecycleEventListener(this);
notificationManager = new RNFirebaseNotificationManager(context.getApplicationContext());
sharedPreferences = context.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE);
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
localBroadcastManager.registerReceiver(new ScheduledNotificationReceiver(),
new IntentFilter(RNFirebaseNotificationManager.SCHEDULED_NOTIFICATION_EVENT));
@ -58,9 +77,22 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
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
public void getInitialNotification(Promise promise) {
// TODO
if (getCurrentActivity() == null) {
promise.resolve(null);
} else {
WritableMap notificationOpenMap = parseIntentForRemoteNotification(getCurrentActivity().getIntent());
promise.resolve(notificationOpenMap);
}
}
@ReactMethod
@ -83,11 +115,74 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
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
public void scheduleNotification(ReadableMap notification, Promise 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 notificationOpenMap = parseIntentForRemoteNotification(intent);
if (notificationOpenMap != null) {
Log.d(TAG, "onNewIntent called with new remote notification");
Utils.sendEvent(getReactApplicationContext(), "notifications_notification_opened", notificationOpenMap);
}
}
//////////////////////////////////////////////////////////////////////
// End ActivityEventListener methods
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Start LifecycleEventListener methods
//////////////////////////////////////////////////////////////////////
@ -109,10 +204,102 @@ public class RNFirebaseNotifications extends ReactContextBaseJavaModule implemen
// 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 notificationOpenMap = Arguments.createMap();
notificationOpenMap.putString("action", intent.getAction());
notificationOpenMap.putMap("notification", notificationMap);
return notificationOpenMap;
}
private WritableMap parseNotificationBundle(Bundle 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 {
@Override
public void onReceive(Context context, Intent intent) {

View File

@ -38,6 +38,11 @@ static NSString *const MESSAGING_TOKEN_REFRESHED = @"messaging_token_refreshed";
// TODO: Remove
static NSString *const MESSAGING_NOTIFICATION_RECEIVED = @"messaging_notification_received";
// Notifications
static NSString *const NOTIFICATIONS_NOTIFICATION_DISPLAYED = @"notifications_notification_displayed";
static NSString *const NOTIFICATIONS_NOTIFICATION_OPENED = @"notifications_notification_opened";
static NSString *const NOTIFICATIONS_NOTIFICATION_RECEIVED = @"notifications_notification_received";
// AdMob
static NSString *const ADMOB_INTERSTITIAL_EVENT = @"interstitial_event";
static NSString *const ADMOB_REWARDED_VIDEO_EVENT = @"rewarded_video_event";

View File

@ -16,8 +16,7 @@
#if !TARGET_OS_TV
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo;
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(void (^_Nonnull)(UIBackgroundFetchResult))completionHandler;
- (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
- (void)didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings;
#endif
@end

View File

@ -11,17 +11,9 @@
#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
// For iOS 10 we need to implement UNUserNotificationCenterDelegate to receive display
// notifications via APNS
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@import UserNotifications;
@interface RNFirebaseMessaging () <UNUserNotificationCenterDelegate>
#else
@interface RNFirebaseMessaging ()
#endif
@property (nonatomic, strong) NSMutableDictionary *callbackHandlers;
@end
@implementation RNFirebaseMessaging
@ -48,17 +40,9 @@ RCT_EXPORT_MODULE()
// Establish Firebase managed data channel
[FIRMessaging messaging].shouldEstablishDirectChannel = YES;
// If we're on iOS 10 then we need to set this as a delegate for the UNUserNotificationCenter
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
#endif
// Set static instance for use from AppDelegate
theRNFirebaseMessaging = self;
// Initialise callback handlers dictionary
_callbackHandlers = [NSMutableDictionary dictionary];
}
// *******************************************************
@ -66,25 +50,6 @@ RCT_EXPORT_MODULE()
// ** iOS 8/9 Only
// *******************************************************
// Listen for background messages
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" clickAction:nil openedFromTray:isFromBackground];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
// Listen for background messages
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" clickAction:nil openedFromTray:isFromBackground];
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
// Listen for permission response
- (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
if (notificationSettings.types == UIUserNotificationTypeNone) {
@ -98,50 +63,17 @@ RCT_EXPORT_MODULE()
_permissionResolver = nil;
}
// Listen for FCM data messages that arrive as a remote notification
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
NSDictionary *message = [self parseUserInfo:userInfo];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
// *******************************************************
// ** Finish AppDelegate methods
// *******************************************************
// *******************************************************
// ** Start UNUserNotificationCenterDelegate methods
// ** iOS 10+
// *******************************************************
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
// Handle incoming notification messages while app is in the foreground.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSDictionary *message = [self parseUNNotification:notification messageType:@"PresentNotification" openedFromTray:false];
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
// Handle notification messages after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
#if defined(__IPHONE_11_0)
withCompletionHandler:(void(^)(void))completionHandler {
#else
withCompletionHandler:(void(^)())completionHandler {
#endif
NSDictionary *message = [self parseUNNotification:response.notification messageType:@"NotificationResponse" openedFromTray:true];
[_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
#endif
// *******************************************************
// ** Finish UNUserNotificationCenterDelegate methods
// *******************************************************
// *******************************************************
// ** Start FIRMessagingDelegate methods
// ** iOS 8+
@ -155,8 +87,15 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
// Listen for data messages in the foreground
- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage openedFromTray:false];
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// To enable direct data messages, you can set [Messaging messaging].shouldEstablishDirectChannel to YES.
- (void)messaging:(nonnull FIRMessaging *)messaging
didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
}
@ -204,23 +143,6 @@ RCT_EXPORT_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve rejecter:(RC
}
// Non Web SDK methods
RCT_EXPORT_METHOD(getBadge: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
dispatch_async(dispatch_get_main_queue(), ^{
resolve(@([RCTSharedApplication() applicationIconBadgeNumber]));
});
}
RCT_EXPORT_METHOD(getInitialMessage:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
NSDictionary *notification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
NSDictionary *message = [self parseUserInfo:notification messageType:@"InitialMessage" clickAction:nil openedFromTray:true];
resolve(message);
} else {
resolve(nil);
}
}
RCT_EXPORT_METHOD(hasPermission: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
dispatch_async(dispatch_get_main_queue(), ^{
@ -248,12 +170,9 @@ RCT_EXPORT_METHOD(sendMessage: (NSDictionary *) message
NSDictionary *data = message[@"data"];
[[FIRMessaging messaging] sendMessage:data to:to withMessageID:messageId timeToLive:[ttl intValue]];
}
RCT_EXPORT_METHOD(setBadge: (NSInteger) number) {
dispatch_async(dispatch_get_main_queue(), ^{
[RCTSharedApplication() setApplicationIconBadgeNumber:number];
});
// TODO: Listen for send success / errors
resolve(nil);
}
RCT_EXPORT_METHOD(subscribeToTopic: (NSString*) topic) {
@ -264,64 +183,9 @@ RCT_EXPORT_METHOD(unsubscribeFromTopic: (NSString*) topic) {
[[FIRMessaging messaging] unsubscribeFromTopic:topic];
}
// Response handler methods
RCT_EXPORT_METHOD(completeNotificationResponse: (NSString*) messageId) {
void(^callbackHandler)() = [_callbackHandlers objectForKey:messageId];
if (!callbackHandler) {
NSLog(@"There is no callback handler for messageId: %@", messageId);
return;
}
callbackHandler();
[_callbackHandlers removeObjectForKey:messageId];
}
RCT_EXPORT_METHOD(completePresentNotification: (NSString*) messageId
result: (NSString*) result) {
void(^callbackHandler)(UNNotificationPresentationOptions) = [_callbackHandlers objectForKey:messageId];
if (!callbackHandler) {
NSLog(@"There is no callback handler for messageId: %@", messageId);
return;
}
UNNotificationPresentationOptions options;
if ([result isEqualToString:@"UNNotificationPresentationOptionAll"]) {
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
} else if ([result isEqualToString:@"UNNotificationPresentationOptionNone"]) {
options = UNNotificationPresentationOptionNone;
} else {
NSLog(@"Invalid result for PresentNotification: %@", result);
return;
}
callbackHandler(options);
[_callbackHandlers removeObjectForKey:messageId];
}
RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
result: (NSString*) result) {
void(^callbackHandler)(UIBackgroundFetchResult) = [_callbackHandlers objectForKey:messageId];
if (!callbackHandler) {
NSLog(@"There is no callback handler for messageId: %@", messageId);
return;
}
UIBackgroundFetchResult fetchResult;
if ([result isEqualToString:@"UIBackgroundFetchResultNewData"]) {
fetchResult = UIBackgroundFetchResultNewData;
} else if ([result isEqualToString:@"UIBackgroundFetchResultNoData"]) {
fetchResult = UIBackgroundFetchResultNoData;
} else if ([result isEqualToString:@"UIBackgroundFetchResultFailed"]) {
fetchResult = UIBackgroundFetchResultFailed;
} else {
NSLog(@"Invalid result for PresentNotification: %@", result);
return;
}
callbackHandler(fetchResult);
[_callbackHandlers removeObjectForKey:messageId];
}
// ** Start internals **
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage
openedFromTray:(bool)openedFromTray {
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
NSDictionary *appData = remoteMessage.appData;
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
@ -332,98 +196,24 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
} else if ([k1 isEqualToString:@"from"]) {
message[@"from"] = appData[k1];
} else if ([k1 isEqualToString:@"notification"]) {
NSDictionary *notification = appData[k1];
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
for (id k2 in notification) {
if ([k2 isEqualToString:@"badge"]) {
notif[@"badge"] = notification[k2];
} else if ([k2 isEqualToString:@"body"]) {
notif[@"body"] = notification[k2];
} else if ([k2 isEqualToString:@"body_loc_args"]) {
notif[@"bodyLocalizationArgs"] = notification[k2];
} else if ([k2 isEqualToString:@"body_loc_key"]) {
notif[@"bodyLocalizationKey"] = notification[k2];
} else if ([k2 isEqualToString:@"click_action"]) {
notif[@"clickAction"] = notification[k2];
} else if ([k2 isEqualToString:@"sound"]) {
notif[@"sound"] = notification[k2];
} else if ([k2 isEqualToString:@"subtitle"]) {
notif[@"subtitle"] = notification[k2];
} else if ([k2 isEqualToString:@"title"]) {
notif[@"title"] = notification[k2];
} else if ([k2 isEqualToString:@"title_loc_args"]) {
notif[@"titleLocalizationArgs"] = notification[k2];
} else if ([k2 isEqualToString:@"title_loc_key"]) {
notif[@"titleLocalizationKey"] = notification[k2];
} else {
NSLog(@"Unknown notification key: %@", k2);
}
}
message[@"notification"] = notif;
// Ignore for messages
} else {
// Assume custom data key
data[k1] = appData[k1];
}
}
message[@"messageType"] = @"RemoteMessage";
message[@"data"] = data;
message[@"openedFromTray"] = @(false);
return message;
}
- (NSDictionary*)parseUNNotification:(UNNotification *)notification
messageType:(NSString *)messageType
openedFromTray:(bool)openedFromTray {
NSDictionary *userInfo = notification.request.content.userInfo;
NSString *clickAction = notification.request.content.categoryIdentifier;
return [self parseUserInfo:userInfo messageType:messageType clickAction:clickAction openedFromTray:openedFromTray];
}
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo
messageType:(NSString *) messageType
clickAction:(NSString *) clickAction
openedFromTray:(bool)openedFromTray {
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
for (id k1 in userInfo) {
if ([k1 isEqualToString:@"aps"]) {
NSDictionary *aps = userInfo[k1];
for (id k2 in aps) {
if ([k2 isEqualToString:@"alert"]) {
NSDictionary *alert = aps[k2];
for (id k3 in alert) {
if ([k3 isEqualToString:@"body"]) {
notif[@"body"] = alert[k3];
} else if ([k3 isEqualToString:@"loc-args"]) {
notif[@"bodyLocalizationArgs"] = alert[k3];
} else if ([k3 isEqualToString:@"loc-key"]) {
notif[@"bodyLocalizationKey"] = alert[k3];
} else if ([k3 isEqualToString:@"subtitle"]) {
notif[@"subtitle"] = alert[k3];
} else if ([k3 isEqualToString:@"title"]) {
notif[@"title"] = alert[k3];
} else if ([k3 isEqualToString:@"title-loc-args"]) {
notif[@"titleLocalizationArgs"] = alert[k3];
} else if ([k3 isEqualToString:@"title-loc-key"]) {
notif[@"titleLocalizationKey"] = alert[k3];
} else {
NSLog(@"Unknown alert key: %@", k2);
}
}
} else if ([k2 isEqualToString:@"badge"]) {
notif[@"badge"] = aps[k2];
} else if ([k2 isEqualToString:@"category"]) {
notif[@"clickAction"] = aps[k2];
} else if ([k2 isEqualToString:@"sound"]) {
notif[@"sound"] = aps[k2];
} else {
NSLog(@"Unknown aps key: %@", k2);
}
}
// Ignore notification section
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
message[@"messageId"] = userInfo[k1];
} else if ([k1 isEqualToString:@"google.c.a.ts"]) {
@ -440,22 +230,9 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
data[k1] = userInfo[k1];
}
}
if (!notif[@"clickAction"] && clickAction) {
notif[@"clickAction"] = clickAction;
}
// Generate a message ID if one was not present in the notification
// This is used for resolving click handlers
if (!message[@"messageId"]) {
message[@"messageId"] = [[NSUUID UUID] UUIDString];
}
message[@"messageType"] = messageType;
message[@"data"] = data;
message[@"notification"] = notif;
message[@"openedFromTray"] = @(openedFromTray);
return message;
}
@ -474,3 +251,4 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
@implementation RNFirebaseMessaging
@end
#endif

View File

@ -8,8 +8,12 @@
@interface RNFirebaseNotifications : RCTEventEmitter<RCTBridgeModule>
#if !TARGET_OS_TV
+ (void)configure;
+ (_Nonnull instancetype)instance;
#if !TARGET_OS_TV
- (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification;
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(void (^_Nonnull)(UIBackgroundFetchResult))completionHandler;
#endif
@end
@ -20,3 +24,4 @@
#endif
#endif

View File

@ -2,17 +2,208 @@
#if __has_include(<FirebaseMessaging/FIRMessaging.h>)
#import "RNFirebaseEvents.h"
#import "RNFirebaseMessaging.h"
#import "RNFirebaseUtil.h"
#import <React/RCTUtils.h>
// For iOS 10 we need to implement UNUserNotificationCenterDelegate to receive display
// notifications via APNS
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@import UserNotifications;
@interface RNFirebaseNotifications () <UNUserNotificationCenterDelegate>
#else
@interface RNFirebaseNotifications ()
#endif
@end
@implementation RNFirebaseNotifications
static RNFirebaseNotifications *theRNFirebaseNotifications = nil;
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
// static NSMutableArray *pendingEvents = nil;
static NSDictionary *initialNotification = nil;
+ (nonnull instancetype)instance {
return theRNFirebaseNotifications;
}
+ (void)configure {
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
// pendingEvents = [[NSMutableArray alloc] init];
theRNFirebaseNotifications = [[RNFirebaseNotifications alloc] init];
}
RCT_EXPORT_MODULE();
- (id)init {
self = [super init];
if (self != nil) {
NSLog(@"Setting up RNFirebaseNotifications instance");
[self initialise];
}
return self;
}
- (void)initialise {
// If we're on iOS 10 then we need to set this as a delegate for the UNUserNotificationCenter
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
#endif
// Set static instance for use from AppDelegate
theRNFirebaseNotifications = self;
}
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
// The bridge is initialised after the module is created
// When the bridge is set, check if we have any pending events to send, and send them
/* - (void)setValue:(nullable id)value forKey:(NSString *)key {
[super setValue:value forKey:key];
if ([key isEqualToString:@"bridge"] && value) {
for (NSDictionary* event in pendingEvents) {
[RNFirebaseUtil sendJSEvent:self name:event[@"name"] body:event[@"body"]];
}
[pendingEvents removeAllObjects];
}
} */
// *******************************************************
// ** Start AppDelegate methods
// ** iOS 8/9 Only
// *******************************************************
- (void)didReceiveLocalNotification:(nonnull UILocalNotification *)localNotification {
if ([self isIOS89]) {
NSString *event;
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
} else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
event = NOTIFICATIONS_NOTIFICATION_OPENED;
} else {
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
}
NSDictionary *notification = [self parseUILocalNotification:localNotification];
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
notification = @{
@"action": UNNotificationDefaultActionIdentifier,
@"notification": notification
};
}
[self sendJSEvent:self name:event body:notification];
}
}
// Listen for background messages
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// FCM Data messages come through here if they specify content-available=true
// Pass them over to the RNFirebaseMessaging handler instead
if (userInfo[@"aps"] && ((NSDictionary*)userInfo[@"aps"]).count == 1 && userInfo[@"aps"][@"content-available"]) {
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
return;
}
NSString *event;
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
} else if ([self isIOS89]) {
if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
event = NOTIFICATIONS_NOTIFICATION_OPENED;
} else {
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
}
} else {
// On IOS 10:
// - foreground notifications also go through willPresentNotification
// - background notification presses also go through didReceiveNotificationResponse
// This prevents duplicate messages from hitting the JS app
return;
}
NSDictionary *notification = [self parseUserInfo:userInfo];
// For onOpened events, we set the default action name as iOS 8/9 has no concept of actions
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
notification = @{
@"action": UNNotificationDefaultActionIdentifier,
@"notification": notification
};
}
[self sendJSEvent:self name:event body:notification];
completionHandler(UIBackgroundFetchResultNoData);
}
// *******************************************************
// ** Finish AppDelegate methods
// *******************************************************
// *******************************************************
// ** Start UNUserNotificationCenterDelegate methods
// ** iOS 10+
// *******************************************************
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
// Handle incoming notification messages while app is in the foreground.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
UNNotificationTrigger *trigger = notification.request.trigger;
BOOL isFcm = trigger && [notification.request.trigger class] == [UNPushNotificationTrigger class];
BOOL isScheduled = trigger && [notification.request.trigger class] == [UNCalendarNotificationTrigger class];
NSString *event;
UNNotificationPresentationOptions options;
NSDictionary *message = [self parseUNNotification:notification];
if (isFcm || isScheduled) {
// If app is in the background
if (RCTSharedApplication().applicationState == UIApplicationStateBackground
|| RCTSharedApplication().applicationState == UIApplicationStateInactive) {
// display the notification
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
// notification_displayed
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
} else {
// don't show notification
options = UNNotificationPresentationOptionNone;
// notification_received
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
}
} else {
// Triggered by `notifications().displayNotification(notification)`
// Display the notification
options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
// notification_displayed
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
}
[self sendJSEvent:self name:event body:message];
completionHandler(options);
}
// Handle notification messages after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
#if defined(__IPHONE_11_0)
withCompletionHandler:(void(^)(void))completionHandler {
#else
withCompletionHandler:(void(^)())completionHandler {
#endif
NSDictionary *message = [self parseUNNotificationResponse:response];
[self sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_OPENED body:message];
completionHandler();
}
#endif
// *******************************************************
// ** Finish UNUserNotificationCenterDelegate methods
// *******************************************************
RCT_EXPORT_METHOD(cancelAllNotifications) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
[RCTSharedApplication() cancelAllLocalNotifications];
} else {
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@ -25,7 +216,7 @@ RCT_EXPORT_METHOD(cancelAllNotifications) {
}
RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
NSDictionary *notificationInfo = notification.userInfo;
if ([notificationId isEqualToString:[notificationInfo valueForKey:@"notificationId"]]) {
@ -45,7 +236,7 @@ RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) {
RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:false];
[RCTSharedApplication() presentLocalNotificationNow:notif];
resolve(nil);
@ -55,7 +246,7 @@ RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
resolve(nil);
}else{
} else{
reject(@"notifications/display_notification_error", @"Failed to display notificaton", error);
}
}];
@ -63,19 +254,40 @@ RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification
}
}
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
UILocalNotification *localNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification) {
NSDictionary *notification = [self parseUILocalNotification:localNotification];
resolve(notification);
RCT_EXPORT_METHOD(getBadge: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
dispatch_async(dispatch_get_main_queue(), ^{
resolve(@([RCTSharedApplication() applicationIconBadgeNumber]));
});
}
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if ([self isIOS89]) {
// With iOS 8/9 we rely on the launch options
UILocalNotification *localNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
NSDictionary *notification;
if (localNotification) {
notification = [self parseUILocalNotification:localNotification];
} else {
NSDictionary *remoteNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotification) {
notification = [self parseUserInfo:remoteNotification];
}
}
if (notification) {
resolve(@{@"action": UNNotificationDefaultActionIdentifier, @"notification": notification});
} else {
resolve(nil);
}
} else {
resolve(nil);
// With iOS 10+ we are able to intercept the didReceiveNotificationResponse message
// to get both the notification and the action
resolve(initialNotification);
}
}
RCT_EXPORT_METHOD(getScheduledNotifications:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
NSMutableArray* notifications = [[NSMutableArray alloc] init];
for (UILocalNotification *notif in [RCTSharedApplication() scheduledLocalNotifications]){
NSDictionary *notification = [self parseUILocalNotification:notif];
@ -97,7 +309,7 @@ RCT_EXPORT_METHOD(getScheduledNotifications:(RCTPromiseResolveBlock)resolve
}
RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
// No such functionality on iOS 8/9
} else {
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@ -110,7 +322,7 @@ RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
}
RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
// No such functionality on iOS 8/9
} else {
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@ -125,7 +337,7 @@ RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) {
RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
if ([self isIOS89]) {
UILocalNotification* notif = [self buildUILocalNotification:notification withSchedule:true];
[RCTSharedApplication() scheduleLocalNotification:notif];
resolve(nil);
@ -135,7 +347,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
resolve(nil);
}else{
} else{
reject(@"notification/schedule_notification_error", @"Failed to schedule notificaton", error);
}
}];
@ -143,6 +355,31 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
}
}
RCT_EXPORT_METHOD(setBadge: (NSInteger) number) {
dispatch_async(dispatch_get_main_queue(), ^{
[RCTSharedApplication() setApplicationIconBadgeNumber:number];
});
}
// Because of the time delay between the app starting and the bridge being initialised
// we create a temporary instance of RNFirebaseNotifications.
// With this temporary instance, we cache any events to be sent as soon as the bridge is set on the module
- (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(id)body {
if (emitter.bridge) {
[RNFirebaseUtil sendJSEvent:emitter name:name body:body];
} else {
if ([name isEqualToString:NOTIFICATIONS_NOTIFICATION_OPENED] && !initialNotification) {
initialNotification = body;
}
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
// [pendingEvents addObject:@{@"name":name, @"body":body}];
}
}
- (BOOL)isIOS89 {
return floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max;
}
- (UILocalNotification*) buildUILocalNotification:(NSDictionary *) notification
withSchedule:(BOOL) withSchedule {
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
@ -182,7 +419,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
NSNumber *fireDateNumber = schedule[@"fireDate"];
NSDate *fireDate = [NSDate dateWithTimeIntervalSince1970:([fireDateNumber doubleValue] / 1000.0)];
localNotification.fireDate = fireDate;
NSString *interval = schedule[@"repeatInterval"];
if (interval) {
if ([interval isEqualToString:@"minute"]) {
@ -195,9 +432,9 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
localNotification.repeatInterval = NSCalendarUnitWeekday;
}
}
}
return localNotification;
}
@ -226,10 +463,27 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
for (NSDictionary *a in ios[@"attachments"]) {
NSString *identifier = a[@"identifier"];
NSURL *url = [NSURL URLWithString:a[@"url"]];
NSDictionary *options = a[@"options"];
NSMutableDictionary *attachmentOptions = nil;
if (a[@"options"]) {
NSDictionary *options = a[@"options"];
attachmentOptions = [[NSMutableDictionary alloc] init];
for (id key in options) {
if ([key isEqualToString:@"typeHint"]) {
attachmentOptions[UNNotificationAttachmentOptionsTypeHintKey] = options[key];
} else if ([key isEqualToString:@"thumbnailHidden"]) {
attachmentOptions[UNNotificationAttachmentOptionsThumbnailHiddenKey] = options[key];
} else if ([key isEqualToString:@"thumbnailClippingRect"]) {
attachmentOptions[UNNotificationAttachmentOptionsThumbnailClippingRectKey] = options[key];
} else if ([key isEqualToString:@"thumbnailTime"]) {
attachmentOptions[UNNotificationAttachmentOptionsThumbnailTimeKey] = options[key];
}
}
}
NSError *error;
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:options error:&error];
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:attachmentOptions error:&error];
if (attachment) {
[attachments addObject:attachment];
} else {
@ -252,13 +506,13 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
content.threadIdentifier = ios[@"threadIdentifier"];
}
}
if (withSchedule) {
NSDictionary *schedule = notification[@"schedule"];
NSNumber *fireDateNumber = schedule[@"fireDate"];
NSString *interval = schedule[@"repeatInterval"];
NSDate *fireDate = [NSDate dateWithTimeIntervalSince1970:([fireDateNumber doubleValue] / 1000.0)];
NSCalendarUnit calendarUnit;
if (interval) {
if ([interval isEqualToString:@"minute"]) {
@ -274,7 +528,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
// Needs to match exactly to the secpmd
calendarUnit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
}
NSDateComponents *components = [[NSCalendar currentCalendar] components:calendarUnit fromDate:fireDate];
UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:interval];
return [UNNotificationRequest requestWithIdentifier:notification[@"notificationId"] content:content trigger:trigger];
@ -285,7 +539,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
- (NSDictionary*) parseUILocalNotification:(UILocalNotification *) localNotification {
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
if (localNotification.alertBody) {
notification[@"body"] = localNotification.alertBody;
}
@ -298,7 +552,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
if (localNotification.alertTitle) {
notification[@"title"] = localNotification.alertTitle;
}
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
if (localNotification.alertAction) {
ios[@"alertAction"] = localNotification.alertAction;
@ -316,36 +570,58 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
ios[@"launchImage"] = localNotification.alertLaunchImage;
}
notification[@"ios"] = ios;
return notification;
}
- (NSDictionary*) parseUNNotificationRequest:(UNNotificationRequest *) localNotification {
- (NSDictionary*)parseUNNotificationResponse:(UNNotificationResponse *)response {
NSMutableDictionary *notificationResponse = [[NSMutableDictionary alloc] init];
NSDictionary *notification = [self parseUNNotification:response.notification];
notificationResponse[@"notification"] = notification;
notificationResponse[@"action"] = response.actionIdentifier;
return notificationResponse;
}
- (NSDictionary*)parseUNNotification:(UNNotification *)notification {
return [self parseUNNotificationRequest:notification.request];
}
- (NSDictionary*) parseUNNotificationRequest:(UNNotificationRequest *) notificationRequest {
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
notification[@"notificationId"] = localNotification.identifier;
if (localNotification.content.body) {
notification[@"body"] = localNotification.content.body;
notification[@"notificationId"] = notificationRequest.identifier;
if (notificationRequest.content.body) {
notification[@"body"] = notificationRequest.content.body;
}
if (localNotification.content.userInfo) {
notification[@"data"] = localNotification.content.userInfo;
if (notificationRequest.content.userInfo) {
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
for (id k in notificationRequest.content.userInfo) {
if ([k isEqualToString:@"aps"]
|| [k isEqualToString:@"gcm.message_id"]) {
// ignore as these are handled by the OS
} else {
data[k] = notificationRequest.content.userInfo[k];
}
}
notification[@"data"] = data;
}
if (localNotification.content.sound) {
notification[@"sound"] = localNotification.content.sound;
if (notificationRequest.content.sound) {
notification[@"sound"] = notificationRequest.content.sound;
}
if (localNotification.content.subtitle) {
notification[@"subtitle"] = localNotification.content.subtitle;
if (notificationRequest.content.subtitle) {
notification[@"subtitle"] = notificationRequest.content.subtitle;
}
if (localNotification.content.title) {
notification[@"title"] = localNotification.content.title;
if (notificationRequest.content.title) {
notification[@"title"] = notificationRequest.content.title;
}
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
if (localNotification.content.attachments) {
if (notificationRequest.content.attachments) {
NSMutableArray *attachments = [[NSMutableArray alloc] init];
for (UNNotificationAttachment *a in localNotification.content.attachments) {
for (UNNotificationAttachment *a in notificationRequest.content.attachments) {
NSMutableDictionary *attachment = [[NSMutableDictionary alloc] init];
attachment[@"identifier"] = a.identifier;
attachment[@"type"] = a.type;
@ -354,26 +630,86 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
}
ios[@"attachments"] = attachments;
}
if (localNotification.content.badge) {
ios[@"badge"] = localNotification.content.badge;
if (notificationRequest.content.badge) {
ios[@"badge"] = notificationRequest.content.badge;
}
if (localNotification.content.categoryIdentifier) {
ios[@"category"] = localNotification.content.categoryIdentifier;
if (notificationRequest.content.categoryIdentifier) {
ios[@"category"] = notificationRequest.content.categoryIdentifier;
}
if (localNotification.content.launchImageName) {
ios[@"launchImage"] = localNotification.content.launchImageName;
if (notificationRequest.content.launchImageName) {
ios[@"launchImage"] = notificationRequest.content.launchImageName;
}
if (localNotification.content.threadIdentifier) {
ios[@"threadIdentifier"] = localNotification.content.threadIdentifier;
if (notificationRequest.content.threadIdentifier) {
ios[@"threadIdentifier"] = notificationRequest.content.threadIdentifier;
}
notification[@"ios"] = ios;
return notification;
}
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
for (id k1 in userInfo) {
if ([k1 isEqualToString:@"aps"]) {
NSDictionary *aps = userInfo[k1];
for (id k2 in aps) {
if ([k2 isEqualToString:@"alert"]) {
NSDictionary *alert = aps[k2];
for (id k3 in alert) {
if ([k3 isEqualToString:@"body"]) {
notification[@"body"] = alert[k3];
} else if ([k3 isEqualToString:@"subtitle"]) {
notification[@"subtitle"] = alert[k3];
} else if ([k3 isEqualToString:@"title"]) {
notification[@"title"] = alert[k3];
} else if ([k3 isEqualToString:@"loc-args"]
|| [k3 isEqualToString:@"loc-key"]
|| [k3 isEqualToString:@"title-loc-args"]
|| [k3 isEqualToString:@"title-loc-key"]) {
// Ignore known keys
} else {
NSLog(@"Unknown alert key: %@", k2);
}
}
} else if ([k2 isEqualToString:@"badge"]) {
ios[@"badge"] = aps[k2];
} else if ([k2 isEqualToString:@"category"]) {
ios[@"category"] = aps[k2];
} else if ([k2 isEqualToString:@"sound"]) {
notification[@"sound"] = aps[k2];
} else {
NSLog(@"Unknown aps key: %@", k2);
}
}
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
notification[@"notificationId"] = userInfo[k1];
} else if ([k1 isEqualToString:@"gcm.n.e"]
|| [k1 isEqualToString:@"gcm.notification.sound2"]
|| [k1 isEqualToString:@"google.c.a.c_id"]
|| [k1 isEqualToString:@"google.c.a.c_l"]
|| [k1 isEqualToString:@"google.c.a.e"]
|| [k1 isEqualToString:@"google.c.a.udt"]
|| [k1 isEqualToString:@"google.c.a.ts"]) {
// Ignore known keys
} else {
// Assume custom data
data[k1] = userInfo[k1];
}
}
notification[@"data"] = data;
notification[@"ios"] = ios;
return notification;
}
- (NSArray<NSString *> *)supportedEvents {
return @[NOTIFICATIONS_NOTIFICATION_RECEIVED];
return @[NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_OPENED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
}
+ (BOOL)requiresMainQueueSetup
@ -387,4 +723,3 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
@implementation RNFirebaseNotifications
@end
#endif

View File

@ -68,7 +68,6 @@ export type { default as WriteBatch } from './modules/firestore/WriteBatch';
/*
* Export Messaging types
*/
export type { default as Message } from './modules/messaging/Message';
export type {
default as RemoteMessage,
} from './modules/messaging/RemoteMessage';

View File

@ -1,122 +0,0 @@
/**
* @flow
* Message representation wrapper
*/
import { Platform } from 'react-native';
import { getNativeModule } from '../../utils/native';
import {
MessageType,
PresentNotificationResult,
RemoteNotificationResult,
} from './types';
import type Messaging from './';
import type {
MessageTypeType,
NativeMessage,
Notification,
PresentNotificationResultType,
RemoteNotificationResultType,
} from './types';
/**
* @class Message
*/
export default class Message {
_completed: boolean;
_messaging: Messaging;
_message: NativeMessage;
constructor(messaging: Messaging, message: NativeMessage) {
this._messaging = messaging;
this._message = message;
}
get collapseKey(): ?string {
return this._message.collapseKey;
}
get data(): { [string]: string } {
return this._message.data;
}
get from(): ?string {
return this._message.from;
}
get messageId(): ?string {
return this._message.messageId;
}
get messageType(): ?MessageTypeType {
return this._message.messageType;
}
get openedFromTray(): boolean {
return this._message.openedFromTray;
}
get notification(): ?Notification {
return this._message.notification;
}
get sentTime(): ?number {
return this._message.sentTime;
}
get to(): ?string {
return this._message.to;
}
get ttl(): ?number {
return this._message.ttl;
}
complete(
result?: PresentNotificationResultType | RemoteNotificationResultType
): void {
if (Platform.OS === 'android') {
return;
}
if (!this._completed) {
this._completed = true;
switch (this.messageType) {
case MessageType.NotificationResponse:
getNativeModule(this._messaging).completeNotificationResponse(
this.messageId
);
break;
case MessageType.PresentNotification:
if (
result &&
!Object.values(PresentNotificationResult).includes(result)
) {
throw new Error(`Invalid PresentNotificationResult: ${result}`);
}
getNativeModule(this._messaging).completePresentNotification(
this.messageId,
result || PresentNotificationResult.None
);
break;
case MessageType.RemoteNotificationHandler:
if (
result &&
!Object.values(RemoteNotificationResult).includes(result)
) {
throw new Error(`Invalid RemoteNotificationResult: ${result}`);
}
getNativeModule(this._messaging).completeRemoteNotification(
this.messageId,
result || RemoteNotificationResult.NoData
);
break;
default:
break;
}
}
}
}

View File

@ -4,30 +4,69 @@
*/
import { isObject, generatePushID } from './../../utils';
type NativeRemoteMessage = {
collapseKey?: string,
data: { [string]: string },
messageId: string,
messageType?: string,
to: string,
ttl: number,
};
import type {
NativeInboundRemoteMessage,
NativeOutboundRemoteMessage,
} from './types';
export default class RemoteMessage {
_collapseKey: string | void;
_data: { [string]: string };
_from: string | void;
_messageId: string;
_messageType: string | void;
_sentTime: number | void;
_to: string;
_ttl: number;
constructor() {
this._data = {};
constructor(inboundMessage?: NativeInboundRemoteMessage) {
if (inboundMessage) {
this._collapseKey = inboundMessage.collapseKey;
this._data = inboundMessage.data;
this._from = inboundMessage.from;
this._messageId = inboundMessage.messageId;
this._messageType = inboundMessage.messageType;
this._sentTime = inboundMessage.sentTime;
}
// defaults
this._data = this._data || {};
// TODO: Is this the best way to generate an ID?
this._messageId = generatePushID();
this._messageId = this._messageId || generatePushID();
this._ttl = 3600;
}
get collapseKey(): ?string {
return this._collapseKey;
}
get data(): { [string]: string } {
return this._data;
}
get from(): ?string {
return this._from;
}
get messageId(): ?string {
return this._messageId;
}
get messageType(): ?string {
return this._messageType;
}
get sentTime(): ?number {
return this._sentTime;
}
get to(): ?string {
return this._to;
}
get ttl(): ?number {
return this._ttl;
}
/**
*
* @param collapseKey
@ -73,6 +112,16 @@ export default class RemoteMessage {
return this;
}
/**
*
* @param to
* @returns {RemoteMessage}
*/
setTo(to: string): RemoteMessage {
this._to = to;
return this;
}
/**
*
* @param ttl
@ -83,7 +132,7 @@ export default class RemoteMessage {
return this;
}
build(): NativeRemoteMessage {
build(): NativeOutboundRemoteMessage {
if (!this.data) {
throw new Error('RemoteMessage: Missing required `data` property');
} else if (!this.messageId) {

View File

@ -8,18 +8,12 @@ import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
import Message from './Message';
import RemoteMessage from './RemoteMessage';
import {
MessageType,
PresentNotificationResult,
RemoteNotificationResult,
} from './types';
import type App from '../core/app';
import type { NativeMessage } from './types';
import type { NativeInboundRemoteMessage } from './types';
type OnMessage = Message => any;
type OnMessage = RemoteMessage => any;
type OnMessageObserver = {
next: OnMessage,
@ -55,8 +49,8 @@ export default class Messaging extends ModuleBase {
// sub to internal native event - this fans out to
// public event name: onMessage
'messaging_message_received',
(message: Message) => {
SharedEventEmitter.emit('onMessage', message);
(message: NativeInboundRemoteMessage) => {
SharedEventEmitter.emit('onMessage', new RemoteMessage(message));
}
);
@ -75,7 +69,7 @@ export default class Messaging extends ModuleBase {
}
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
let listener: Message => any;
let listener: RemoteMessage => any;
if (isFunction(nextOrObserver)) {
// $FlowBug: Not coping with the overloaded method signature
listener = nextOrObserver;
@ -89,25 +83,20 @@ export default class Messaging extends ModuleBase {
getLogger(this).info('Creating onMessage listener');
const wrappedListener = async (nativeMessage: NativeMessage) => {
const message = new Message(this, nativeMessage);
await listener(message);
message.complete();
};
SharedEventEmitter.addListener('onMessage', wrappedListener);
SharedEventEmitter.addListener('onMessage', listener);
return () => {
getLogger(this).info('Removing onMessage listener');
SharedEventEmitter.removeListener('onMessage', wrappedListener);
SharedEventEmitter.removeListener('onMessage', listener);
};
}
onTokenRefresh(
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
): () => any {
let listener;
let listener: String => any;
if (isFunction(nextOrObserver)) {
// $FlowBug: Not coping with the overloaded method signature
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
@ -126,7 +115,6 @@ export default class Messaging extends ModuleBase {
};
}
// TODO: Permission structure?
requestPermission(): Promise<void> {
return getNativeModule(this).requestPermission();
}
@ -134,16 +122,6 @@ export default class Messaging extends ModuleBase {
/**
* NON WEB-SDK METHODS
*/
getBadge(): Promise<number> {
return getNativeModule(this).getBadge();
}
getInitialMessage(): Promise<?Message> {
return getNativeModule(this)
.getInitialMessage()
.then(message => (message ? new Message(this, message) : null));
}
hasPermission(): Promise<boolean> {
return getNativeModule(this).hasPermission();
}
@ -157,10 +135,6 @@ export default class Messaging extends ModuleBase {
return getNativeModule(this).send(remoteMessage.build());
}
setBadge(badge: number): void {
getNativeModule(this).setBadge(badge);
}
subscribeToTopic(topic: string): void {
getNativeModule(this).subscribeToTopic(topic);
}
@ -202,8 +176,5 @@ export default class Messaging extends ModuleBase {
}
export const statics = {
MessageType,
PresentNotificationResult,
RemoteMessage,
RemoteNotificationResult,
};

View File

@ -1,35 +1,6 @@
/**
* @flow
*/
export const MessageType = {
InitialMessage: 'InitialMessage',
NotificationResponse: 'NotificationResponse',
PresentNotification: 'PresentNotification',
RemoteMessage: 'RemoteMessage',
RemoteNotification: 'RemoteNotification',
RemoteNotificationHandler: 'RemoteNotificationHandler',
};
export const PresentNotificationResult = {
All: 'UNNotificationPresentationOptionAll',
None: 'UNNotificationPresentationOptionNone',
};
export const RemoteNotificationResult = {
NewData: 'UIBackgroundFetchResultNewData',
NoData: 'UIBackgroundFetchResultNoData',
ResultFailed: 'UIBackgroundFetchResultFailed',
};
export type MessageTypeType = $Values<typeof MessageType>;
export type PresentNotificationResultType = $Values<
typeof PresentNotificationResult
>;
export type RemoteNotificationResultType = $Values<
typeof RemoteNotificationResult
>;
export type Notification = {
body: string,
bodyLocalizationArgs?: string[],
@ -46,15 +17,22 @@ export type Notification = {
titleLocalizationKey?: string,
};
export type NativeMessage = {
export type NativeInboundRemoteMessage = {
collapseKey?: string,
data: { [string]: string },
from?: string,
messageId: string,
messageType?: MessageTypeType,
openedFromTray: boolean,
notification?: Notification,
messageType?: string,
sentTime?: number,
to?: string,
ttl?: number,
};
export type NativeOutboundRemoteMessage = {
collapseKey?: string,
data: { [string]: string },
messageId: string,
messageType?: string,
to: string,
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

@ -2,160 +2,59 @@
* @flow
* AndroidNotification representation wrapper
*/
import { Category } from './types';
import type Notification from './Notification';
type Lights = {
argb: number,
onMs: number,
offMs: number,
};
type Progress = {
max: number,
progress: number,
indeterminate: boolean,
};
type SmallIcon = {
icon: string,
level?: number,
};
export type NativeAndroidNotification = {|
// TODO actions: Action[],
autoCancel: boolean,
badgeIconType: BadgeIconTypeType,
category: CategoryType,
channelId: string,
clickAction?: string,
color: string,
colorized: boolean,
contentInfo: string,
defaults: DefaultsType[],
group: string,
groupAlertBehaviour: GroupAlertType,
groupSummary: boolean,
largeIcon: string,
lights: Lights,
localOnly: boolean,
number: number,
ongoing: boolean,
onlyAlertOnce: boolean,
people: string[],
priority: PriorityType,
progress: Progress,
// publicVersion: Notification,
remoteInputHistory: string[],
shortcutId: string,
showWhen: boolean,
smallIcon: SmallIcon,
sortKey: string,
// TODO: style: Style,
ticker: string,
timeoutAfter: number,
usesChronometer: boolean,
vibrate: number[],
visibility: VisibilityType,
when: number,
|};
export const BadgeIconType = {
Large: 2,
None: 0,
Small: 1,
};
export const Category = {
Alarm: 'alarm',
Call: 'call',
Email: 'email',
Error: 'err',
Event: 'event',
Message: 'msg',
Progress: 'progress',
Promo: 'promo',
Recommendation: 'recommendation',
Reminder: 'reminder',
Service: 'service',
Social: 'social',
Status: 'status',
System: 'system',
Transport: 'transport',
};
export const Defaults = {
All: -1,
Lights: 4,
Sound: 1,
Vibrate: 2,
};
export const GroupAlert = {
All: 0,
Children: 2,
Summary: 1,
};
export const Priority = {
Default: 0,
High: 1,
Low: -1,
Max: 2,
Min: -2,
};
export const Visibility = {
Private: 0,
Public: 1,
Secret: -1,
};
type BadgeIconTypeType = $Values<typeof BadgeIconType>;
type CategoryType = $Values<typeof Category>;
type DefaultsType = $Values<typeof Defaults>;
type GroupAlertType = $Values<typeof GroupAlert>;
type PriorityType = $Values<typeof Priority>;
type VisibilityType = $Values<typeof Visibility>;
import type {
BadgeIconTypeType,
CategoryType,
DefaultsType,
GroupAlertType,
Lights,
NativeAndroidNotification,
PriorityType,
Progress,
SmallIcon,
VisibilityType,
} from './types';
export default class AndroidNotification {
// TODO optional fields
// TODO actions: Action[]; // icon, title, ??pendingIntent??, allowGeneratedReplies, extender, extras, remoteinput (ugh)
_autoCancel: boolean;
_badgeIconType: BadgeIconTypeType;
_category: CategoryType;
_autoCancel: boolean | void;
_badgeIconType: BadgeIconTypeType | void;
_category: CategoryType | void;
_channelId: string;
_clickAction: string;
_color: string;
_colorized: boolean;
_contentInfo: string;
_defaults: DefaultsType[];
_group: string;
_groupAlertBehaviour: GroupAlertType;
_groupSummary: boolean;
_largeIcon: string;
_lights: Lights;
_localOnly: boolean;
_clickAction: string | void;
_color: string | void;
_colorized: boolean | void;
_contentInfo: string | void;
_defaults: DefaultsType[] | void;
_group: string | void;
_groupAlertBehaviour: GroupAlertType | void;
_groupSummary: boolean | void;
_largeIcon: string | void;
_lights: Lights | void;
_localOnly: boolean | void;
_notification: Notification;
_number: number;
_ongoing: boolean;
_onlyAlertOnce: boolean;
_number: number | void;
_ongoing: boolean | void;
_onlyAlertOnce: boolean | void;
_people: string[];
_priority: PriorityType;
_progress: Progress;
_priority: PriorityType | void;
_progress: Progress | void;
// _publicVersion: Notification;
_remoteInputHistory: string[];
_shortcutId: string;
_showWhen: boolean;
_smallIcon: SmallIcon = {
icon: 'ic_launcher',
};
_sortKey: string;
_remoteInputHistory: string[] | void;
_shortcutId: string | void;
_showWhen: boolean | void;
_smallIcon: SmallIcon;
_sortKey: string | void;
// TODO: style: Style; // Need to figure out if this can work
_ticker: string;
_timeoutAfter: number;
_usesChronometer: boolean;
_vibrate: number[];
_visibility: VisibilityType;
_when: number;
_ticker: string | void;
_timeoutAfter: number | void;
_usesChronometer: boolean | void;
_vibrate: number[] | void;
_visibility: VisibilityType | void;
_when: number | void;
// android unsupported
// content: RemoteViews
@ -167,9 +66,178 @@ export default class AndroidNotification {
// fullScreenIntent: PendingIntent
// sound.streamType
constructor(notification: Notification) {
constructor(notification: Notification, data?: NativeAndroidNotification) {
this._notification = notification;
this._people = [];
if (data) {
this._autoCancel = data.autoCancel;
this._badgeIconType = data.badgeIconType;
this._category = data.category;
this._channelId = data.channelId;
this._clickAction = data.clickAction;
this._color = data.color;
this._colorized = data.colorized;
this._contentInfo = data.contentInfo;
this._defaults = data.defaults;
this._group = data.group;
this._groupAlertBehaviour = data.groupAlertBehaviour;
this._groupSummary = data.groupSummary;
this._largeIcon = data.largeIcon;
this._lights = data.lights;
this._localOnly = data.localOnly;
this._number = data.number;
this._ongoing = data.ongoing;
this._onlyAlertOnce = data.onlyAlertOnce;
this._people = data.people;
this._priority = data.priority;
this._progress = data.progress;
// _publicVersion: Notification;
this._remoteInputHistory = data.remoteInputHistory;
this._shortcutId = data.shortcutId;
this._showWhen = data.showWhen;
this._smallIcon = data.smallIcon;
this._sortKey = data.sortKey;
this._ticker = data.ticker;
this._timeoutAfter = data.timeoutAfter;
this._usesChronometer = data.usesChronometer;
this._vibrate = data.vibrate;
this._visibility = data.visibility;
this._when = data.when;
}
// Defaults
this._people = this._people || [];
this._smallIcon = this._smallIcon || {
icon: 'ic_launcher',
};
}
get autoCancel(): ?boolean {
return this._autoCancel;
}
get badgeIconType(): ?BadgeIconTypeType {
return this._badgeIconType;
}
get category(): ?CategoryType {
return this._category;
}
get channelId(): string {
return this._channelId;
}
get clickAction(): ?string {
return this._clickAction;
}
get color(): ?string {
return this._color;
}
get colorized(): ?boolean {
return this._colorized;
}
get contentInfo(): ?string {
return this._contentInfo;
}
get defaults(): ?(DefaultsType[]) {
return this._defaults;
}
get group(): ?string {
return this._group;
}
get groupAlertBehaviour(): ?GroupAlertType {
return this._groupAlertBehaviour;
}
get groupSummary(): ?boolean {
return this._groupSummary;
}
get largeIcon(): ?string {
return this._largeIcon;
}
get lights(): ?Lights {
return this._lights;
}
get localOnly(): ?boolean {
return this._localOnly;
}
get number(): ?number {
return this._number;
}
get ongoing(): ?boolean {
return this._ongoing;
}
get onlyAlertOnce(): ?boolean {
return this._onlyAlertOnce;
}
get people(): string[] {
return this._people;
}
get priority(): ?PriorityType {
return this._priority;
}
get progress(): ?Progress {
return this._progress;
}
get remoteInputHistory(): ?(string[]) {
return this._remoteInputHistory;
}
get shortcutId(): ?string {
return this._shortcutId;
}
get showWhen(): ?boolean {
return this._showWhen;
}
get smallIcon(): SmallIcon {
return this._smallIcon;
}
get sortKey(): ?string {
return this._sortKey;
}
get ticker(): ?string {
return this._ticker;
}
get timeoutAfter(): ?number {
return this._timeoutAfter;
}
get usesChronometer(): ?boolean {
return this._usesChronometer;
}
get vibrate(): ?(number[]) {
return this._vibrate;
}
get visibility(): ?VisibilityType {
return this._visibility;
}
get when(): ?number {
return this._when;
}
/**
@ -506,11 +574,15 @@ export default class AndroidNotification {
}
build(): NativeAndroidNotification {
// TODO: Validation
// TODO: Validation of required fields
if (!this._channelId) {
throw new Error(
'AndroidNotification: Missing required `channelId` property'
);
} else if (!this._smallIcon) {
throw new Error(
'AndroidNotification: Missing required `smallIcon` property'
);
}
return {

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

@ -3,48 +3,65 @@
* IOSNotification representation wrapper
*/
import type Notification from './Notification';
type AttachmentOptions = {|
TypeHint: string,
ThumbnailHidden: boolean,
ThumbnailClippingRect: {
height: number,
width: number,
x: number,
y: number,
},
ThumbnailTime: number,
|};
type Attachment = {|
identifier: string,
options?: AttachmentOptions,
url: string,
|};
export type NativeIOSNotification = {|
alertAction?: string,
attachments: Attachment[],
badge?: number,
category?: string,
hasAction?: boolean,
launchImage?: string,
threadIdentifier?: string,
|};
import type {
IOSAttachment,
IOSAttachmentOptions,
NativeIOSNotification,
} from './types';
export default class IOSNotification {
_alertAction: string; // alertAction | N/A
_attachments: Attachment[]; // N/A | attachments
_badge: number; // applicationIconBadgeNumber | badge
_category: string;
_hasAction: boolean; // hasAction | N/A
_launchImage: string; // alertLaunchImage | launchImageName
_alertAction: string | void; // alertAction | N/A
_attachments: IOSAttachment[]; // N/A | attachments
_badge: number | void; // applicationIconBadgeNumber | badge
_category: string | void;
_hasAction: boolean | void; // hasAction | N/A
_launchImage: string | void; // alertLaunchImage | launchImageName
_notification: Notification;
_threadIdentifier: string; // N/A | threadIdentifier
_threadIdentifier: string | void; // N/A | threadIdentifier
constructor(notification: Notification) {
this._attachments = [];
constructor(notification: Notification, data?: NativeIOSNotification) {
this._notification = notification;
if (data) {
this._alertAction = data.alertAction;
this._attachments = data.attachments;
this._badge = data.badge;
this._category = data.category;
this._hasAction = data.hasAction;
this._launchImage = data.launchImage;
this._threadIdentifier = data.threadIdentifier;
}
// Defaults
this._attachments = this._attachments || [];
}
get alertAction(): ?string {
return this._alertAction;
}
get attachments(): IOSAttachment[] {
return this._attachments;
}
get badge(): ?number {
return this._badge;
}
get category(): ?string {
return this._category;
}
get hasAction(): ?boolean {
return this._hasAction;
}
get launchImage(): ?string {
return this._launchImage;
}
get threadIdentifier(): ?string {
return this._threadIdentifier;
}
/**
@ -57,7 +74,7 @@ export default class IOSNotification {
addAttachment(
identifier: string,
url: string,
options?: AttachmentOptions
options?: IOSAttachmentOptions
): Notification {
this._attachments.push({
identifier,
@ -128,7 +145,7 @@ export default class IOSNotification {
}
build(): NativeIOSNotification {
// TODO: Validation
// TODO: Validation of required fields
return {
alertAction: this._alertAction,

View File

@ -7,21 +7,12 @@ import AndroidNotification from './AndroidNotification';
import IOSNotification from './IOSNotification';
import { generatePushID, isObject } from '../../utils';
import type { NativeAndroidNotification } from './AndroidNotification';
import type { NativeIOSNotification } from './IOSNotification';
import type { Schedule } from './';
import type { NativeNotification } from './types';
type NativeNotification = {|
android?: NativeAndroidNotification,
body: string,
data: { [string]: string },
ios?: NativeIOSNotification,
notificationId: string,
schedule?: Schedule,
sound?: string,
subtitle?: string,
title: string,
|};
export type NotificationOpen = {
action: string,
notification: Notification,
};
export default class Notification {
// iOS 8/9 | 10+ | Android
@ -34,22 +25,57 @@ export default class Notification {
_subtitle: string | void; // N/A | subtitle | subText
_title: string; // alertTitle | title | contentTitle
constructor() {
this._android = new AndroidNotification(this);
this._data = {};
this._ios = new IOSNotification(this);
constructor(data?: NativeNotification) {
this._android = new AndroidNotification(this, data && data.android);
this._ios = new IOSNotification(this, data && data.ios);
if (data) {
this._body = data.body;
this._data = data.data;
this._notificationId = data.notificationId;
this._sound = data.sound;
this._subtitle = data.subtitle;
this._title = data.title;
}
// Defaults
this._data = this._data || {};
// TODO: Is this the best way to generate an ID?
this._notificationId = generatePushID();
this._notificationId = this._notificationId || generatePushID();
}
get android(): AndroidNotification {
return this._android;
}
get body(): string {
return this._body;
}
get data(): { [string]: string } {
return this._data;
}
get ios(): IOSNotification {
return this._ios;
}
get notificationId(): string {
return this._notificationId;
}
get sound(): ?string {
return this._sound;
}
get subtitle(): ?string {
return this._subtitle;
}
get title(): string {
return this._title;
}
/**
*
* @param body

View File

@ -1,38 +1,51 @@
/**
* @flow
* Messaging (FCM) representation wrapper
* Notifications representation wrapper
*/
import { SharedEventEmitter } from '../../utils/events';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
import AndroidChannel from './AndroidChannel';
import AndroidChannelGroup from './AndroidChannelGroup';
import AndroidNotifications from './AndroidNotifications';
import Notification from './Notification';
import {
BadgeIconType,
Category,
Defaults,
GroupAlert,
Importance,
Priority,
Visibility,
} from './AndroidNotification';
} from './types';
import type App from '../core/app';
import type { NotificationOpen } from './Notification';
import type {
NativeNotification,
NativeNotificationOpen,
Schedule,
} from './types';
// TODO: Received notification type will be different from sent notification
type OnNotification = Notification => any;
type OnNotificationObserver = {
next: OnNotification,
};
export type Schedule = {
exact?: boolean,
fireDate: number,
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
type OnNotificationOpened = NotificationOpen => any;
type OnNotificationOpenedObserver = {
next: OnNotificationOpen,
};
const NATIVE_EVENTS = ['notifications_notification_received'];
const NATIVE_EVENTS = [
'notifications_notification_displayed',
'notifications_notification_opened',
'notifications_notification_received',
];
export const MODULE_NAME = 'RNFirebaseNotifications';
export const NAMESPACE = 'notifications';
@ -55,6 +68,8 @@ export const NAMESPACE = 'notifications';
* @class Notifications
*/
export default class Notifications extends ModuleBase {
_android: AndroidNotifications;
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
@ -62,35 +77,71 @@ export default class Notifications extends ModuleBase {
multiApp: false,
namespace: NAMESPACE,
});
this._android = new AndroidNotifications(this);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onMessage
// public event name: onNotificationDisplayed
'notifications_notification_displayed',
(notification: NativeNotification) => {
SharedEventEmitter.emit(
'onNotificationDisplayed',
new Notification(notification)
);
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onNotificationOpened
'notifications_notification_opened',
(notificationOpen: NativeNotificationOpen) => {
SharedEventEmitter.emit('onNotificationOpened', {
action: notificationOpen.action,
notification: new Notification(notificationOpen.notification),
});
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onNotification
'notifications_notification_received',
(notification: Notification) => {
SharedEventEmitter.emit('onNotification', notification);
(notification: NativeNotification) => {
SharedEventEmitter.emit(
'onNotification',
new Notification(notification)
);
}
);
}
cancelAllNotifications(): Promise<void> {
return getNativeModule(this).cancelAllNotifications();
get android(): AndroidNotifications {
return this._android;
}
/**
* Cancel a local notification by id.
* @param id
* @returns {*}
* Cancel all notifications
*/
cancelNotification(notificationId: string): Promise<void> {
if (!notificationId) {
return Promise.reject(new Error('Missing notificationId'));
}
return getNativeModule(this).cancelNotification(notificationId);
cancelAllNotifications(): void {
getNativeModule(this).cancelAllNotifications();
}
/**
* Display a local notification
* Cancel a notification by id.
* @param notificationId
*/
cancelNotification(notificationId: string): void {
if (!notificationId) {
throw new Error(
'Notifications: cancelNotification expects a `notificationId`'
);
}
getNativeModule(this).cancelNotification(notificationId);
}
/**
* Display a notification
* @param notification
* @returns {*}
*/
@ -103,10 +154,16 @@ export default class Notifications extends ModuleBase {
return getNativeModule(this).displayNotification(notification.build());
}
getBadge(): Promise<number> {
return getNativeModule(this).getBadge();
}
getInitialNotification(): Promise<Object> {
return getNativeModule(this).getInitialNotification();
// TODO
// .then(notification => (notification ? new Notification(this, notification) : null));
return getNativeModule(this)
.getInitialNotification()
.then(
notification => (notification ? new Notification(notification) : null)
);
}
/**
@ -131,7 +188,6 @@ export default class Notifications extends ModuleBase {
);
}
// TODO: iOS finish
getLogger(this).info('Creating onNotification listener');
SharedEventEmitter.addListener('onNotification', listener);
@ -141,28 +197,74 @@ export default class Notifications extends ModuleBase {
};
}
onNotificationDisplayed(
nextOrObserver: OnNotification | OnNotificationObserver
): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Notifications.onNotificationDisplayed failed: First argument must be a function or observer object with a `next` function.'
);
}
getLogger(this).info('Creating onNotificationDisplayed listener');
SharedEventEmitter.addListener('onNotificationDisplayed', listener);
return () => {
getLogger(this).info('Removing onNotificationDisplayed listener');
SharedEventEmitter.removeListener('onNotificationDisplayed', listener);
};
}
onNotificationOpened(
nextOrObserver: OnNotificationOpened | OnNotificationOpenedObserver
): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Notifications.onNotificationOpened failed: First argument must be a function or observer object with a `next` function.'
);
}
getLogger(this).info('Creating onNotificationOpened listener');
SharedEventEmitter.addListener('onNotificationOpened', listener);
return () => {
getLogger(this).info('Removing onNotificationOpened listener');
SharedEventEmitter.removeListener('onNotificationOpened', listener);
};
}
/**
* Remove all delivered notifications.
* @returns {*}
*/
removeAllDeliveredNotifications(): Promise<void> {
return getNativeModule(this).removeAllDeliveredNotifications();
removeAllDeliveredNotifications(): void {
getNativeModule(this).removeAllDeliveredNotifications();
}
/**
* Remove a delivered notification.
* @param notificationId
* @returns {*}
*/
removeDeliveredNotification(notificationId: string): Promise<void> {
removeDeliveredNotification(notificationId: string): void {
if (!notificationId) {
return Promise.reject(new Error('Missing notificationId'));
throw new Error(
'Notifications: removeDeliveredNotification expects a `notificationId`'
);
}
return getNativeModule(this).removeDeliveredNotification(notificationId);
getNativeModule(this).removeDeliveredNotification(notificationId);
}
/**
*
* Schedule a notification
* @param notification
* @returns {*}
*/
@ -179,14 +281,21 @@ export default class Notifications extends ModuleBase {
nativeNotification.schedule = schedule;
return getNativeModule(this).scheduleNotification(nativeNotification);
}
setBadge(badge: number): void {
getNativeModule(this).setBadge(badge);
}
}
export const statics = {
Android: {
BadgeIconType,
Category,
Channel: AndroidChannel,
ChannelGroup: AndroidChannelGroup,
Defaults,
GroupAlert,
Importance,
Priority,
Visibility,
},

View File

@ -0,0 +1,178 @@
/**
* @flow
*/
export const BadgeIconType = {
Large: 2,
None: 0,
Small: 1,
};
export const Category = {
Alarm: 'alarm',
Call: 'call',
Email: 'email',
Error: 'err',
Event: 'event',
Message: 'msg',
Progress: 'progress',
Promo: 'promo',
Recommendation: 'recommendation',
Reminder: 'reminder',
Service: 'service',
Social: 'social',
Status: 'status',
System: 'system',
Transport: 'transport',
};
export const Defaults = {
All: -1,
Lights: 4,
Sound: 1,
Vibrate: 2,
};
export const GroupAlert = {
All: 0,
Children: 2,
Summary: 1,
};
export const Importance = {
Default: 3,
High: 4,
Low: 2,
Max: 5,
Min: 1,
None: 3,
Unspecified: -1000,
};
export const Priority = {
Default: 0,
High: 1,
Low: -1,
Max: 2,
Min: -2,
};
export const Visibility = {
Private: 0,
Public: 1,
Secret: -1,
};
export type BadgeIconTypeType = $Values<typeof BadgeIconType>;
export type CategoryType = $Values<typeof Category>;
export type DefaultsType = $Values<typeof Defaults>;
export type GroupAlertType = $Values<typeof GroupAlert>;
export type ImportanceType = $Values<typeof Importance>;
export type PriorityType = $Values<typeof Priority>;
export type VisibilityType = $Values<typeof Visibility>;
export type Lights = {
argb: number,
onMs: number,
offMs: number,
};
export type Progress = {
max: number,
progress: number,
indeterminate: boolean,
};
export type SmallIcon = {
icon: string,
level?: number,
};
export type NativeAndroidNotification = {|
// TODO actions: Action[],
autoCancel?: boolean,
badgeIconType?: BadgeIconTypeType,
category?: CategoryType,
channelId: string,
clickAction?: string,
color?: string,
colorized?: boolean,
contentInfo?: string,
defaults?: DefaultsType[],
group?: string,
groupAlertBehaviour?: GroupAlertType,
groupSummary?: boolean,
largeIcon?: string,
lights?: Lights,
localOnly?: boolean,
number?: number,
ongoing?: boolean,
onlyAlertOnce?: boolean,
people: string[],
priority?: PriorityType,
progress?: Progress,
// publicVersion: Notification,
remoteInputHistory?: string[],
shortcutId?: string,
showWhen?: boolean,
smallIcon: SmallIcon,
sortKey?: string,
// TODO: style: Style,
ticker?: string,
timeoutAfter?: number,
usesChronometer?: boolean,
vibrate?: number[],
visibility?: VisibilityType,
when?: number,
|};
export type IOSAttachmentOptions = {|
typeHint: string,
thumbnailHidden: boolean,
thumbnailClippingRect: {
height: number,
width: number,
x: number,
y: number,
},
thumbnailTime: number,
|};
export type IOSAttachment = {|
identifier: string,
options?: IOSAttachmentOptions,
url: string,
|};
export type NativeIOSNotification = {|
alertAction?: string,
attachments: IOSAttachment[],
badge?: number,
category?: string,
hasAction?: boolean,
launchImage?: string,
threadIdentifier?: string,
|};
export type Schedule = {
exact?: boolean,
fireDate: number,
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
};
export type NativeNotification = {|
android?: NativeAndroidNotification,
body: string,
data: { [string]: string },
ios?: NativeIOSNotification,
notificationId: string,
schedule?: Schedule,
sound?: string,
subtitle?: string,
title: string,
|};
export type NativeNotificationOpen = {|
action: string,
notification: NativeNotification,
|};

View File

@ -18,7 +18,7 @@ import type {
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule } } = {};
const APP_MODULES: { [string]: { [string]: FirebaseModule } } = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
export default {
@ -49,8 +49,8 @@ export default {
InstanceClass: Class<M>
): () => FirebaseModule {
return (): M => {
if (!APP_MODULES[app]) {
APP_MODULES[app] = {};
if (!APP_MODULES[app._name]) {
APP_MODULES[app._name] = {};
}
if (
@ -62,11 +62,11 @@ export default {
app.utils().checkPlayServicesAvailability();
}
if (!APP_MODULES[app][namespace]) {
APP_MODULES[app][namespace] = new InstanceClass(app, app.options);
if (!APP_MODULES[app._name][namespace]) {
APP_MODULES[app._name][namespace] = new InstanceClass(app, app.options);
}
return APP_MODULES[app][namespace];
return APP_MODULES[app._name][namespace];
};
},

View File

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

View File

@ -12,7 +12,7 @@
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import <RNFirebaseMessaging.h>
#import <RNFirebaseNotifications.h>
@implementation AppDelegate
@ -38,13 +38,9 @@
return YES;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
[[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
@end

View File

@ -7,14 +7,6 @@ import DatabaseContents from './tests/support/DatabaseContents';
RNfirebase.database.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 () => {
try {
await RNfirebase.messaging().requestPermission();
@ -22,9 +14,47 @@ const init = async () => {
console.log('instanceid: ', instanceid);
const token = await RNfirebase.messaging().getToken();
console.log('token: ', token);
const initialMessage = await RNfirebase.messaging().getInitialMessage();
console.log('initial message: ', initialMessage);
const initialNotification = await RNfirebase.notifications().getInitialNotification();
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();
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) {
console.error('messaging init error:', error);
}