feature(messaging): android messaging upgrade (#3309)

* messaging receiver updates / initial notification handling

* Attach Notification to RemoteMessage

Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com>
This commit is contained in:
Elliot Hesp
2020-03-17 11:51:36 +00:00
committed by GitHub
parent 3b129dcc00
commit cf0bbdcaf0
12 changed files with 712 additions and 125 deletions

View File

@@ -38,18 +38,27 @@ project.ext {
}
apply from: file("./../../app/android/firebase-json.gradle")
def autoInitEnabled = "true"
def defaultNotificationChannelId = ""
def defaultNotificationColor = ""
if (rootProject.ext && rootProject.ext.firebaseJson) {
if (rootProject.ext.firebaseJson.isFlagEnabled("messaging_auto_init_enabled") == false) {
autoInitEnabled = "false"
}
defaultNotificationChannelId = rootProject.ext.firebaseJson.getStringValue("messaging_android_notification_channel_id", "")
defaultNotificationColor = rootProject.ext.firebaseJson.getStringValue("messaging_android_notification_color", "")
}
android {
defaultConfig {
multiDexEnabled true
manifestPlaceholders = [
firebaseJsonAutoInitEnabled: autoInitEnabled
firebaseJsonAutoInitEnabled: autoInitEnabled,
firebaseJsonNotificationChannelId: defaultNotificationChannelId,
firebaseJsonNotificationColor: defaultNotificationColor
]
}
lintOptions {

View File

@@ -7,33 +7,30 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<service android:name="io.invertase.firebase.messaging.ReactNativeFirebaseMessagingHeadlessService" />
<service android:name="io.invertase.firebase.messaging.ReactNativeFirebaseMessagingService"
<service android:name=".ReactNativeFirebaseMessagingHeadlessService" />
<service android:name=".ReactNativeFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<receiver
android:name=".ReactNativeFirebaseMessagingReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="${firebaseJsonAutoInitEnabled}"/>
<!-- TODO move below TODOs to Notifcations module -->
<!-- TODO firebase.json -->
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. -->
<!-- <meta-data-->
<!-- android:name="com.google.firebase.messaging.default_notification_icon"-->
<!-- android:resource="@drawable/ic_stat_ic_notification"/>-->
<!-- TODO firebase.json -->
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<!-- <meta-data-->
<!-- android:name="com.google.firebase.messaging.default_notification_color"-->
<!-- android:resource="@color/colorAccent"/>-->
<!-- TODO firebase.json -->
<!-- <meta-data-->
<!-- android:name="com.google.firebase.messaging.default_notification_channel_id"-->
<!-- android:value="@string/default_notification_channel_id"/>-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="${firebaseJsonNotificationChannelId}" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="${firebaseJsonNotificationColor}" />
</application>
</manifest>

View File

@@ -25,7 +25,9 @@ public class ReactNativeFirebaseMessagingHeadlessService extends HeadlessJsTaskS
TASK_KEY,
ReactNativeFirebaseMessagingSerializer.remoteMessageToWritableMap(remoteMessage),
ReactNativeFirebaseJSON.getSharedInstance().getLongValue(TIMEOUT_JSON_KEY, TIMEOUT_DEFAULT),
true /* allow to run foreground */
// Prevents race condition where the user opens the app at the same time as a notification
// is delivered, causing a crash.
true
);
}
}

View File

@@ -17,7 +17,12 @@ package io.invertase.firebase.messaging;
*
*/
import android.app.Activity;
import android.content.Intent;
import androidx.core.app.NotificationManagerCompat;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
@@ -25,16 +30,52 @@ import com.facebook.react.bridge.ReadableMap;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import io.invertase.firebase.common.ReactNativeFirebaseModule;
import com.google.firebase.messaging.RemoteMessage;
import java.util.HashMap;
import java.util.Map;
public class ReactNativeFirebaseMessagingModule extends ReactNativeFirebaseModule {
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
import io.invertase.firebase.common.ReactNativeFirebaseModule;
public class ReactNativeFirebaseMessagingModule extends ReactNativeFirebaseModule implements ActivityEventListener {
private static final String TAG = "Messaging";
RemoteMessage initialNotification = null;
private HashMap<String, Boolean> initialNotificationMap = new HashMap<>();
ReactNativeFirebaseMessagingModule(ReactApplicationContext reactContext) {
super(reactContext, TAG);
reactContext.addActivityEventListener(this);
}
@ReactMethod
public void getInitialNotification(Promise promise) {
if (initialNotification != null) {
promise.resolve(ReactNativeFirebaseMessagingSerializer.remoteMessageToWritableMap(initialNotification));
initialNotification = null;
return;
} else {
Intent intent = getCurrentActivity().getIntent();
if (intent != null && intent.getExtras() != null) {
// messageId can be either one...
String messageId = intent.getExtras().getString("google.message_id");
if (messageId == null) messageId = intent.getExtras().getString("message_id");
// only handle non-consumed initial notifications
if (messageId != null && initialNotificationMap.get(messageId) == null) {
RemoteMessage remoteMessage = ReactNativeFirebaseMessagingReceiver.notifications.get(messageId);
if (remoteMessage != null) {
promise.resolve(ReactNativeFirebaseMessagingSerializer.remoteMessageToWritableMap(remoteMessage));
initialNotificationMap.put(messageId, true);
return;
}
}
}
}
promise.resolve(null);
}
@ReactMethod
@@ -146,4 +187,29 @@ public class ReactNativeFirebaseMessagingModule extends ReactNativeFirebaseModul
);
return constants;
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// noop
}
@Override
public void onNewIntent(Intent intent) {
if (intent != null && intent.getExtras() != null) {
String messageId = intent.getExtras().getString("google.message_id");
if (messageId == null) messageId = intent.getExtras().getString("message_id");
if (messageId != null) {
RemoteMessage remoteMessage = ReactNativeFirebaseMessagingReceiver.notifications.get(messageId);
if (remoteMessage != null) {
initialNotification = remoteMessage;
ReactNativeFirebaseMessagingReceiver.notifications.remove(messageId);
ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();
emitter.sendEvent(ReactNativeFirebaseMessagingSerializer.remoteMessageToEvent(remoteMessage, true));
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
package io.invertase.firebase.messaging;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.facebook.react.HeadlessJsTaskService;
import com.google.firebase.messaging.RemoteMessage;
import java.util.HashMap;
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
import io.invertase.firebase.common.SharedUtils;
public class ReactNativeFirebaseMessagingReceiver extends BroadcastReceiver {
private static final String TAG = "RNFirebaseMsgReceiver";
static HashMap<String, RemoteMessage> notifications = new HashMap<>();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "broadcast received for message");
RemoteMessage remoteMessage = new RemoteMessage(intent.getExtras());
ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();
// |-> ---------------------
// App in Foreground
// ------------------------
if (SharedUtils.isAppInForeground(context)) {
emitter.sendEvent(ReactNativeFirebaseMessagingSerializer.remoteMessageToEvent(remoteMessage, false));
return;
}
// |-> ---------------------
// App in Background/Quit
// ------------------------
// Add a RemoteMessage if the message contains a notification payload
if (remoteMessage.getNotification() != null) {
notifications.put(remoteMessage.getMessageId(), remoteMessage);
}
try {
Intent backgroundIntent = new Intent(context, ReactNativeFirebaseMessagingHeadlessService.class);
backgroundIntent.putExtra("message", remoteMessage);
ComponentName name = context.startService(backgroundIntent);
if (name != null) {
HeadlessJsTaskService.acquireWakeLockNow(context);
}
} catch (IllegalStateException ex) {
// By default, data only messages are "default" priority and cannot trigger Headless tasks
Log.e(
TAG,
"Background messages only work if the message priority is set to 'high'",
ex
);
}
}
}

View File

@@ -25,6 +25,7 @@ class ReactNativeFirebaseMessagingSerializer {
private static final String EVENT_MESSAGE_SENT = "messaging_message_sent";
private static final String EVENT_MESSAGES_DELETED = "messaging_message_deleted";
private static final String EVENT_MESSAGE_RECEIVED = "messaging_message_received";
private static final String EVENT_NOTIFICATION_OPENED = "messaging_notification_opened";
private static final String EVENT_MESSAGE_SEND_ERROR = "messaging_message_send_error";
private static final String EVENT_NEW_TOKEN = "messaging_token_refresh";
@@ -45,8 +46,8 @@ class ReactNativeFirebaseMessagingSerializer {
return new ReactNativeFirebaseEvent(EVENT_MESSAGE_SEND_ERROR, eventBody);
}
static ReactNativeFirebaseEvent remoteMessageToEvent(RemoteMessage remoteMessage) {
return new ReactNativeFirebaseEvent(EVENT_MESSAGE_RECEIVED, remoteMessageToWritableMap(remoteMessage));
static ReactNativeFirebaseEvent remoteMessageToEvent(RemoteMessage remoteMessage, Boolean openEvent) {
return new ReactNativeFirebaseEvent(openEvent ? EVENT_NOTIFICATION_OPENED : EVENT_MESSAGE_RECEIVED, remoteMessageToWritableMap(remoteMessage));
}
static ReactNativeFirebaseEvent newTokenToTokenEvent(String newToken) {
@@ -89,9 +90,74 @@ class ReactNativeFirebaseMessagingSerializer {
messageMap.putMap(KEY_DATA, dataMap);
messageMap.putDouble(KEY_TTL, remoteMessage.getTtl());
messageMap.putDouble(KEY_SENT_TIME, remoteMessage.getSentTime());
if (remoteMessage.getNotification() != null) {
messageMap.putMap("notification", remoteMessageNotificationToWritableMap(remoteMessage.getNotification()));
}
return messageMap;
}
static WritableMap remoteMessageNotificationToWritableMap(RemoteMessage.Notification notification) {
WritableMap notificationMap = Arguments.createMap();
WritableMap androidNotificationMap = Arguments.createMap();
if (notification.getTitle() != null) {
notificationMap.putString("title", notification.getTitle());
}
if (notification.getBody() != null) {
notificationMap.putString("body", notification.getBody());
}
if (notification.getChannelId() != null) {
androidNotificationMap.putString("channelId", notification.getChannelId());
}
if (notification.getClickAction() != null) {
androidNotificationMap.putString("clickAction", notification.getClickAction());
}
if (notification.getColor() != null) {
androidNotificationMap.putString("color", notification.getColor());
}
if (notification.getIcon() != null) {
androidNotificationMap.putString("smallIcon", notification.getIcon());
}
if (notification.getImageUrl() != null) {
androidNotificationMap.putString("imageUrl", notification.getImageUrl().toString());
}
if (notification.getLink() != null) {
androidNotificationMap.putString("link", notification.getLink().toString());
}
if (notification.getNotificationCount() != null) {
androidNotificationMap.putInt("count", notification.getNotificationCount());
}
if (notification.getNotificationPriority() != null) {
androidNotificationMap.putInt("priority", notification.getNotificationPriority());
}
if (notification.getSound() != null) {
androidNotificationMap.putString("sound", notification.getSound());
}
if (notification.getTicker() != null) {
androidNotificationMap.putString("ticker", notification.getTicker());
}
if (notification.getVisibility() != null) {
androidNotificationMap.putInt("visibility", notification.getVisibility());
}
notificationMap.putMap("android", androidNotificationMap);
return notificationMap;
}
static RemoteMessage remoteMessageFromReadableMap(ReadableMap readableMap) {
RemoteMessage.Builder builder = new RemoteMessage.Builder(readableMap.getString(KEY_TO));

View File

@@ -1,17 +1,11 @@
package io.invertase.firebase.messaging;
import android.content.ComponentName;
import android.content.Intent;
import android.util.Log;
import com.facebook.react.HeadlessJsTaskService;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
import io.invertase.firebase.common.SharedUtils;
public class ReactNativeFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "RNFirebaseMsgService";
@Override
public void onSendError(String messageId, Exception sendError) {
ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();
@@ -38,47 +32,6 @@ public class ReactNativeFirebaseMessagingService extends FirebaseMessagingServic
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived");
ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();
// ----------------------
// NOTIFICATION Message
// --------------------\/
// with no data
if (remoteMessage.getNotification() != null && remoteMessage.getData().size() == 0) {
// TODO broadcast intent when notifications module ready
return;
}
// ----------------------
// DATA Message
// --------------------\/
// |-> ---------------------
// App in Foreground
// ------------------------
if (SharedUtils.isAppInForeground(getApplicationContext())) {
emitter.sendEvent(ReactNativeFirebaseMessagingSerializer.remoteMessageToEvent(remoteMessage));
return;
}
// |-> ---------------------
// App in Background/Quit
// ------------------------
try {
Intent intent = new Intent(getApplicationContext(), ReactNativeFirebaseMessagingHeadlessService.class);
intent.putExtra("message", remoteMessage);
ComponentName name = getApplicationContext().startService(intent);
if (name != null) {
HeadlessJsTaskService.acquireWakeLockNow(getApplicationContext());
}
} catch (IllegalStateException ex) {
Log.e(
TAG,
"Background messages only work if the message priority is set to 'high'",
ex
);
}
// noop - handled in receiver
}
}

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="ivory">#FFFFF0</color>
<color name="lightyellow">#FFFFE0</color>
<color name="yellow">#FFFF00</color>
<color name="snow">#FFFAFA</color>
<color name="floralwhite">#FFFAF0</color>
<color name="lemonchiffon">#FFFACD</color>
<color name="cornsilk">#FFF8DC</color>
<color name="seashell">#FFF5EE</color>
<color name="lavenderblush">#FFF0F5</color>
<color name="papayawhip">#FFEFD5</color>
<color name="blanchedalmond">#FFEBCD</color>
<color name="mistyrose">#FFE4E1</color>
<color name="bisque">#FFE4C4</color>
<color name="moccasin">#FFE4B5</color>
<color name="navajowhite">#FFDEAD</color>
<color name="peachpuff">#FFDAB9</color>
<color name="gold">#FFD700</color>
<color name="pink">#FFC0CB</color>
<color name="lightpink">#FFB6C1</color>
<color name="orange">#FFA500</color>
<color name="lightsalmon">#FFA07A</color>
<color name="darkorange">#FF8C00</color>
<color name="coral">#FF7F50</color>
<color name="hotpink">#FF69B4</color>
<color name="tomato">#FF6347</color>
<color name="orangered">#FF4500</color>
<color name="deeppink">#FF1493</color>
<color name="fuchsia">#FF00FF</color>
<color name="magenta">#FF00FF</color>
<color name="red">#FF0000</color>
<color name="oldlace">#FDF5E6</color>
<color name="lightgoldenrodyellow">#FAFAD2</color>
<color name="linen">#FAF0E6</color>
<color name="antiquewhite">#FAEBD7</color>
<color name="salmon">#FA8072</color>
<color name="ghostwhite">#F8F8FF</color>
<color name="mintcream">#F5FFFA</color>
<color name="whitesmoke">#F5F5F5</color>
<color name="beige">#F5F5DC</color>
<color name="wheat">#F5DEB3</color>
<color name="sandybrown">#F4A460</color>
<color name="azure">#F0FFFF</color>
<color name="honeydew">#F0FFF0</color>
<color name="aliceblue">#F0F8FF</color>
<color name="khaki">#F0E68C</color>
<color name="lightcoral">#F08080</color>
<color name="palegoldenrod">#EEE8AA</color>
<color name="violet">#EE82EE</color>
<color name="darksalmon">#E9967A</color>
<color name="lavender">#E6E6FA</color>
<color name="lightcyan">#E0FFFF</color>
<color name="burlywood">#DEB887</color>
<color name="plum">#DDA0DD</color>
<color name="gainsboro">#DCDCDC</color>
<color name="crimson">#DC143C</color>
<color name="palevioletred">#DB7093</color>
<color name="goldenrod">#DAA520</color>
<color name="orchid">#DA70D6</color>
<color name="thistle">#D8BFD8</color>
<color name="lightgrey">#D3D3D3</color>
<color name="tan">#D2B48C</color>
<color name="chocolate">#D2691E</color>
<color name="peru">#CD853F</color>
<color name="indianred">#CD5C5C</color>
<color name="mediumvioletred">#C71585</color>
<color name="silver">#C0C0C0</color>
<color name="darkkhaki">#BDB76B</color>
<color name="rosybrown">#BC8F8F</color>
<color name="mediumorchid">#BA55D3</color>
<color name="darkgoldenrod">#B8860B</color>
<color name="firebrick">#B22222</color>
<color name="powderblue">#B0E0E6</color>
<color name="lightsteelblue">#B0C4DE</color>
<color name="paleturquoise">#AFEEEE</color>
<color name="greenyellow">#ADFF2F</color>
<color name="lightblue">#ADD8E6</color>
<color name="darkgray">#A9A9A9</color>
<color name="brown">#A52A2A</color>
<color name="sienna">#A0522D</color>
<color name="yellowgreen">#9ACD32</color>
<color name="darkorchid">#9932CC</color>
<color name="palegreen">#98FB98</color>
<color name="darkviolet">#9400D3</color>
<color name="mediumpurple">#9370DB</color>
<color name="lightgreen">#90EE90</color>
<color name="darkseagreen">#8FBC8F</color>
<color name="saddlebrown">#8B4513</color>
<color name="darkmagenta">#8B008B</color>
<color name="darkred">#8B0000</color>
<color name="blueviolet">#8A2BE2</color>
<color name="lightskyblue">#87CEFA</color>
<color name="skyblue">#87CEEB</color>
<color name="gray">#808080</color>
<color name="olive">#808000</color>
<color name="purple">#800080</color>
<color name="maroon">#800000</color>
<color name="aquamarine">#7FFFD4</color>
<color name="chartreuse">#7FFF00</color>
<color name="lawngreen">#7CFC00</color>
<color name="mediumslateblue">#7B68EE</color>
<color name="lightslategray">#778899</color>
<color name="slategray">#708090</color>
<color name="olivedrab">#6B8E23</color>
<color name="slateblue">#6A5ACD</color>
<color name="dimgray">#696969</color>
<color name="mediumaquamarine">#66CDAA</color>
<color name="cornflowerblue">#6495ED</color>
<color name="cadetblue">#5F9EA0</color>
<color name="darkolivegreen">#556B2F</color>
<color name="indigo">#4B0082</color>
<color name="mediumturquoise">#48D1CC</color>
<color name="darkslateblue">#483D8B</color>
<color name="steelblue">#4682B4</color>
<color name="royalblue">#4169E1</color>
<color name="turquoise">#40E0D0</color>
<color name="mediumseagreen">#3CB371</color>
<color name="limegreen">#32CD32</color>
<color name="darkslategray">#2F4F4F</color>
<color name="seagreen">#2E8B57</color>
<color name="forestgreen">#228B22</color>
<color name="lightseagreen">#20B2AA</color>
<color name="dodgerblue">#1E90FF</color>
<color name="midnightblue">#191970</color>
<color name="aqua">#00FFFF</color>
<color name="cyan">#00FFFF</color>
<color name="springgreen">#00FF7F</color>
<color name="lime">#00FF00</color>
<color name="mediumspringgreen">#00FA9A</color>
<color name="darkturquoise">#00CED1</color>
<color name="deepskyblue">#00BFFF</color>
<color name="darkcyan">#008B8B</color>
<color name="teal">#008080</color>
<color name="green">#008000</color>
<color name="darkgreen">#006400</color>
<color name="blue">#0000FF</color>
<color name="mediumblue">#0000CD</color>
<color name="darkblue">#00008B</color>
<color name="navy">#000080</color>
<color name="black">#000000</color>
</resources>

View File

@@ -99,6 +99,161 @@ export namespace FirebaseMessagingTypes {
* Any additional data sent with the message.
*/
data?: { [key: string]: string };
/**
* Additional Notification data sent with the message
*/
notification?: Notification;
}
export interface Notification {
/**
* The notification title.
*/
title?: string;
/**
* The notification body content.
*/
body?: string;
/**
* Additional Android specific properties set on the notification.
*/
android?: {
/**
* The channel ID set on the notification. If not set, the notification uses the default
* "Miscellaneous" channel set by FCM.
*/
channelId?: string;
/**
* Name of the click action set on the notification.
*/
clickAction?: string;
/**
* The custom color used to tint the notification content.
*/
color?: string;
/**
* The custom small icon used to display on the notification. If not set, uses the default
* application icon defined in the AndroidManifest file.
*/
smallIcon?: string;
/**
* The custom image was provided and displayed in the notification body.
*/
imageUrl?: string;
/**
* Deep link URL provided to the notification.
*/
link?: string;
/**
* The current unread notification count for this application, managed by the device.
*/
count?: number;
/**
* The notification priority.
*
* Note; on devices which have channel support (Android 8.0 (API level 26) +),
* this value will be ignored. Instead, the channel "importance" level is used.
*/
priority?:
| NotificationAndroidPriority.PRIORITY_MIN
| NotificationAndroidPriority.PRIORITY_LOW
| NotificationAndroidPriority.PRIORITY_DEFAULT
| NotificationAndroidPriority.PRIORITY_HIGH
| NotificationAndroidPriority.PRIORITY_MAX;
/**
* The sound played when the notification was delivered on the device (channel settings permitted).
*
* Set as "default" if the default device notification sound was used.
*/
sound?: string;
/**
* Ticker text set on the notification.
*
* Ticker text is used for devices with accessibility enabled (e.g. to read out the message).
*/
ticker?: string;
/**
* The visibility of a notification. This value determines how the notification is shown on the users
* devices (e.g. on the lock-screen).
*/
visibility?:
| NotificationAndroidVisibility.VISIBILITY_SECRET
| NotificationAndroidVisibility.VISIBILITY_PRIVATE
| NotificationAndroidVisibility.VISIBILITY_PUBLIC;
};
}
/**
* The enum representing a notification priority.
*
* Note; on devices which have channel support (Android 8.0 (API level 26) +),
* this value will be ignored. Instead, the channel "importance" level is used.
*/
export enum NotificationAndroidPriority {
/**
The application small icon will not show up in the status bar, or alert the user. The notification
will be in a collapsed state in the notification shade and placed at the bottom of the list.
*/
PRIORITY_MIN = -2,
/**
* The application small icon will show in the device status bar, however the notification will
* not alert the user (no sound or vibration). The notification will show in it's expanded state
* when the notification shade is pulled down.
*/
PRIORITY_LOW = -1,
/**
* When a notification is received, the device smallIcon will appear in the notification shade.
* When the user pulls down the notification shade, the content of the notification will be shown
* in it's expanded state.
*/
PRIORITY_DEFAULT = 0,
/**
* Notifications will appear on-top of applications, allowing direct interaction without pulling
* own the notification shade. This level is used for urgent notifications, such as
* incoming phone calls, messages etc, which require immediate attention.
*/
PRIORITY_HIGH = 1,
/**
* The priority highest level a notification can be set at.
*/
PRIORITY_MAX = 2,
}
/**
* The enum representing the visibility of a notification.
*/
export enum NotificationAndroidVisibility {
/**
* Do not reveal any part of this notification on a secure lock-screen.
*/
VISIBILITY_SECRET = -1,
/**
* Show this notification on all lock-screens, but conceal sensitive or private information on secure lock-screens.
*/
VISIBILITY_PRIVATE = 0,
/**
* Show this notification in its entirety on all lock-screens.
*/
VISIBILITY_PUBLIC = 1,
}
/**
@@ -170,6 +325,16 @@ export namespace FirebaseMessagingTypes {
*/
setAutoInitEnabled(enabled: boolean): Promise<void>;
/**
* When a notification from FCM has triggered the application to open from a quit state,
* this method will return a `RemoteMessage` containing the notification data, or `null` if
* the app was opened via another method.
*
* See `onNotificationOpenedApp` to subscribe to when the notification is opened when the app
* is in a background state.
*/
getInitialNotification(): Promise<RemoteMessage | null>;
/**
* Returns an FCM token for this device. Optionally you can specify a custom authorized entity
* or scope to tailor tokens to your own use-case.
@@ -177,14 +342,14 @@ export namespace FirebaseMessagingTypes {
* It is recommended you call this method on app start and update your backend with the new token.
*
* On iOS you'll need to register for remote notifications before calling this method, you can do
* this by calling `registerForRemoteNotifications` or `requestPermission` as part of your app
* this by calling `registerDeviceForRemoteMessages` or `requestPermission` as part of your app
* startup. If you have not registered and you call this method you will receive an 'unregistered'
* error code.
*
* #### Example - Default token
*
* ```js
* await firebase.messaging().registerForRemoteNotifications();
* await firebase.messaging().registerDeviceForRemoteMessages();
* const fcmToken = await firebase.messaging().getToken();
*
* // Update backend (e.g. Firestore) with our scoped token for the user
@@ -264,6 +429,17 @@ export namespace FirebaseMessagingTypes {
*/
onMessage(listener: (message: RemoteMessage) => any): () => void;
/**
* When the user presses a notification displayed via FCM, this listener will be called if the app
* has opened from a background state.
*
* See `getInitialNotification` to see how to watch for when a notification opens the app from a
* quit state.
*
* @param listener Called with a `RemoteMessage` when a notification press opens the application.
*/
onNotificationOpenedApp(listener: (message: RemoteMessage) => any): void;
/**
* Called when a new registration token is generated for the device. For example, this event can happen when a
* token expires or when the server invalidates the token.
@@ -318,6 +494,14 @@ export namespace FirebaseMessagingTypes {
*/
requestPermission(): Promise<boolean>;
/**
* Deprecated. See `registerDeviceForRemoteMessages` instead.
*
* @platform ios
* @deprecated See registerDeviceForRemoteMessages.
*/
registerForRemoteNotifications(): Promise<void>;
/**
* On iOS, if your app wants to receive remote messages from FCM (via APNS), you must explicitly register
* this request with APNS. For example if you want to display alerts, play sounds
@@ -330,18 +514,24 @@ export namespace FirebaseMessagingTypes {
* #### Example
*
* ```js
* if (!firebase.messaging().isRegisteredForRemoteNotifications) {
* await firebase.messaging().registerForRemoteNotifications();
* if (!firebase.messaging().isDeviceRegisteredForRemoteMessages) {
* await firebase.messaging().registerDeviceForRemoteMessages();
* }
* ```
*
* @ios
*/
registerForRemoteNotifications(): Promise<void>;
registerDeviceForRemoteMessages(): Promise<void>;
/**
* Deprecated. See `isDeviceRegisteredForRemoteMessages` instead.
*
* @platform ios
* @deprecated See isDeviceRegisteredForRemoteMessages
*/
isRegisteredForRemoteNotifications: boolean;
/**
* Returns a boolean value whether the user has registered for remote notifications via
* `registerForRemoteNotifications()`.
* `registerDeviceForRemoteMessages()`.
*
* > You can safely access this property on Android without platform checks. Android returns `true` only.
*
@@ -351,9 +541,17 @@ export namespace FirebaseMessagingTypes {
* const isRegisteredForRemoteNotifications = firebase.messaging().isRegisteredForRemoteNotifications;
* ```
*
* @ios
* @platform ios
*/
isRegisteredForRemoteNotifications: boolean;
isDeviceRegisteredForRemoteMessages: boolean;
/**
* Deprecated. See `unregisterDeviceForRemoteMessages` instead.
*
* @platform ios
* @deprecated See unregisterDeviceForRemoteMessages.
*/
unregisterForRemoteNotifications(): Promise<void>;
/**
* Unregisters the app from receiving remote notifications.
@@ -363,14 +561,14 @@ export namespace FirebaseMessagingTypes {
* #### Example
*
* ```js
* if (firebase.messaging().isRegisteredForRemoteNotifications) {
* await firebase.messaging().unregisterForRemoteNotifications();
* if (firebase.messaging().isDeviceRegisteredForRemoteMessages) {
* await firebase.messaging().unregisterDeviceForRemoteMessages();
* }
* ```
*
* @ios
* @platform ios
*/
unregisterForRemoteNotifications(): Promise<void>;
unregisterDeviceForRemoteMessages(): Promise<void>;
/**
* On iOS, it is possible to get the users APNS token. This may be required if you want to send messages to your
@@ -586,7 +784,9 @@ declare module '@react-native-firebase/app' {
namespace ReactNativeFirebase {
interface FirebaseJsonConfig {
messaging_auto_init_enabled: boolean;
messaging_android_headless_task_timeout: number;
messaging_auto_init_enabled?: boolean;
messaging_android_headless_task_timeout?: number;
messaging_android_notification_channel_id?: string;
messaging_android_notification_color?: string;
}
}

View File

@@ -38,6 +38,8 @@ const namespace = 'messaging';
const nativeModuleName = 'RNFBMessagingModule';
let backgroundMessageHandler;
class FirebaseMessagingModule extends FirebaseModule {
constructor(...args) {
super(...args);
@@ -47,6 +49,16 @@ class FirebaseMessagingModule extends FirebaseModule {
this.native.isRegisteredForRemoteNotifications != null
? this.native.isRegisteredForRemoteNotifications
: true;
AppRegistry.registerHeadlessTask('ReactNativeFirebaseMessagingHeadlessTask', () => {
if (!backgroundMessageHandler) {
console.warn(
'No background message handler has been set. Set a handler via the "setBackgroundMessageHandler" method.',
);
return () => Promise.resolve();
}
return remoteMessage => backgroundMessageHandler(remoteMessage);
});
}
get isAutoInitEnabled() {
@@ -54,15 +66,28 @@ class FirebaseMessagingModule extends FirebaseModule {
}
/**
* @platform ios
* @ios
*/
get isRegisteredForRemoteNotifications() {
get isDeviceRegisteredForRemoteMessages() {
if (isAndroid) {
return true;
}
return this._isRegisteredForRemoteNotifications;
}
/**
* @platform ios
* @deprecated Use isDeviceRegisteredForRemoteMessages.
*/
get isRegisteredForRemoteNotifications() {
// eslint-disable-next-line no-console
console.warn(
'[deprecation] Usage of "isRegisteredForRemoteNotifications" will be deprecated in v7. Use "isDeviceRegisteredForRemoteMessages" instead.',
);
return this.isDeviceRegisteredForRemoteMessages;
}
setAutoInitEnabled(enabled) {
if (!isBoolean(enabled)) {
throw new Error(
@@ -74,6 +99,10 @@ class FirebaseMessagingModule extends FirebaseModule {
return this.native.setAutoInitEnabled(enabled);
}
getInitialNotification() {
return this.native.getInitialNotification();
}
getToken(authorizedEntity, scope) {
if (!isUndefined(authorizedEntity) && !isString(authorizedEntity)) {
throw new Error(
@@ -120,6 +149,20 @@ class FirebaseMessagingModule extends FirebaseModule {
return () => subscription.remove();
}
onNotificationOpenedApp(listener) {
if (!isFunction(listener)) {
throw new Error(
"firebase.messaging().onNotificationOpenedApp(*) 'listener' expected a function.",
);
}
// TODO(salakar) rework internals as without this native module will never be ready (therefore never subscribes)
this.native;
const subscription = this.emitter.addListener('messaging_notification_opened', listener);
return () => subscription.remove();
}
onTokenRefresh(listener) {
if (!isFunction(listener)) {
throw new Error("firebase.messaging().onTokenRefresh(*) 'listener' expected a function.");
@@ -156,10 +199,7 @@ class FirebaseMessagingModule extends FirebaseModule {
return this.native.requestPermission();
}
/**
* @platform ios
*/
registerForRemoteNotifications() {
registerDeviceForRemoteMessages() {
if (isAndroid) {
return Promise.resolve();
}
@@ -169,8 +209,21 @@ class FirebaseMessagingModule extends FirebaseModule {
/**
* @platform ios
* @deprecated
*/
unregisterForRemoteNotifications() {
registerForRemoteNotifications() {
// eslint-disable-next-line no-console
console.warn(
'[deprecation] Usage of "registerForRemoteNotifications" will be removed in v7. Use "registerDeviceForRemoteMessages" instead.',
);
return this.registerDeviceForRemoteMessages();
}
/**
* @platform ios
*/
unregisterDeviceForRemoteMessages() {
if (isAndroid) {
return Promise.resolve();
}
@@ -178,6 +231,19 @@ class FirebaseMessagingModule extends FirebaseModule {
return this.native.unregisterForRemoteNotifications();
}
/**
* @platform ios
* @deprecated
*/
unregisterForRemoteNotifications() {
// eslint-disable-next-line no-console
console.warn(
'[deprecation] Usage of "unregisterForRemoteNotifications" will be removed in v7. Use "unregisterDeviceForRemoteMessages" instead.',
);
return this.unregisterDeviceForRemoteMessages();
}
/**
* @platform ios
*/
@@ -247,7 +313,7 @@ class FirebaseMessagingModule extends FirebaseModule {
return;
}
AppRegistry.registerHeadlessTask('ReactNativeFirebaseMessagingHeadlessTask', () => handler);
backgroundMessageHandler = handler;
}
sendMessage(remoteMessage) {
@@ -324,6 +390,7 @@ export default createModuleNamespace({
'messaging_message_deleted',
'messaging_message_received',
'messaging_message_send_error',
'messaging_notification_opened',
],
hasMultiAppSupport: false,
hasCustomUrlOrRegionSupport: false,

View File

@@ -16,36 +16,54 @@
*
*/
import React, { Component } from 'react';
import { AppRegistry, Image, StyleSheet, View } from 'react-native';
import React, { useEffect, useState } from 'react';
import { AppRegistry, Text, Image, StyleSheet, View } from 'react-native';
import '@react-native-firebase/dynamic-links';
import messaging from '@react-native-firebase/messaging';
class Root extends Component {
constructor(props) {
super(props);
try {
this.runSingleTest().catch(console.error);
} catch (error) {
console.error(error);
}
}
messaging().setBackgroundMessageHandler(async r => {
console.log('setBackgroundMessageHandler', r);
});
async runSingleTest() {}
function Root() {
const [token, setToken] = useState('');
render() {
return (
<View style={[styles.container, styles.horizontal]}>
<Image
source={{
uri:
'https://github.com/invertase/react-native-firebase-starter/raw/master/assets/ReactNativeFirebase.png',
}}
style={[styles.logo]}
/>
</View>
);
}
useEffect(() => {
console.warn('use effect');
messaging()
.getInitialNotification()
.then(n => {
console.warn('initial notification', n);
});
messaging().onNotificationOpenedApp(event => {
console.log('onNotificationOpenedApp', event);
});
messaging().onMessage(msg => {
console.log('onMessage', msg);
});
messaging()
.getToken()
.then(t => {
console.log(t);
setToken(t);
});
}, []);
return (
<View style={[styles.container, styles.horizontal]}>
<Text>{token}</Text>
<Image
source={{
uri:
'https://github.com/invertase/react-native-firebase-starter/raw/master/assets/ReactNativeFirebase.png',
}}
style={[styles.logo]}
/>
</View>
);
}
const styles = StyleSheet.create({

View File

@@ -18,6 +18,9 @@
"messaging_auto_init_enabled": true,
"messaging_android_headless_task_timeout": 30000,
"messaging_android_notification_channel_id": "",
"messaging_android_notification_color": "@color/hotpink",
"ml_vision_label_model": true,
"ml_vision_image_label_model": true,