[android, ios] first draft of full fcm implementation - ios still todo

This commit is contained in:
Salakar 2017-03-17 16:04:39 +00:00
parent b9fd032b6d
commit 856da83a80
11 changed files with 1115 additions and 327 deletions

View File

@ -43,7 +43,9 @@ allprojects {
// END
dependencies {
compile 'com.facebook.react:react-native:0.20.+'
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.facebook.react:react-native:+'
compile 'me.leolin:ShortcutBadger:1.1.10@aar'
compile 'com.google.android.gms:play-services-base:10.2.0'
compile 'com.google.firebase:firebase-core:10.2.0'
compile 'com.google.firebase:firebase-config:10.2.0'

View File

@ -1,29 +0,0 @@
package io.invertase.firebase;
import android.util.Log;
import android.os.Bundle;
import android.content.Intent;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import io.invertase.firebase.messaging.RNFirebaseMessaging;
public class RNFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String TAG = "FSInstanceIdService";
/**
*
*/
@Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_TOKEN);
Bundle bundle = new Bundle();
bundle.putString("token", refreshedToken);
i.putExtras(bundle);
sendBroadcast(i);
}
}

View File

@ -1,74 +0,0 @@
package io.invertase.firebase;
import android.content.Intent;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.SendException;
import io.invertase.firebase.messaging.RNFirebaseMessaging;
public class RNFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FSMessagingService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "Remote message received");
// debug
Log.d(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_NOTIFICATION);
i.putExtra("data", remoteMessage);
sendOrderedBroadcast(i, null);
}
@Override
public void onMessageSent(String msgId) {
// Called when an upstream message has been successfully sent to the GCM connection server.
Log.d(TAG, "upstream message has been successfully sent");
Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_SEND);
i.putExtra("msgId", msgId);
sendOrderedBroadcast(i, null);
}
@Override
public void onSendError(String msgId, Exception exception) {
// Called when there was an error sending an upstream message.
Log.d(TAG, "error sending an upstream message");
Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_SEND);
i.putExtra("msgId", msgId);
i.putExtra("hasError", true);
SendException sendException = (SendException) exception;
i.putExtra("errorCode", sendException.getErrorCode());
switch (sendException.getErrorCode()) {
case SendException.ERROR_INVALID_PARAMETERS:
i.putExtra("errorMessage", "Message was sent with invalid parameters.");
break;
case SendException.ERROR_SIZE:
i.putExtra("errorMessage", "Message exceeded the maximum payload size.");
break;
case SendException.ERROR_TOO_MANY_MESSAGES:
i.putExtra("errorMessage", "App has too many pending messages so this one was dropped.");
break;
case SendException.ERROR_TTL_EXCEEDED:
i.putExtra("errorMessage", "Message time to live (TTL) was exceeded before the message could be sent.");
break;
case SendException.ERROR_UNKNOWN:
default:
i.putExtra("errorMessage", "Unknown error.");
break;
}
sendOrderedBroadcast(i, null);
}
}

View File

@ -0,0 +1,43 @@
package io.invertase.firebase.messaging;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import me.leolin.shortcutbadger.ShortcutBadger;
public class BadgeHelper {
private static final String TAG = "BadgeHelper";
private static final String PREFERENCES_FILE = "BadgeCountFile";
private static final String BADGE_COUNT_KEY = "BadgeCount";
private Context mContext;
private SharedPreferences sharedPreferences = null;
public BadgeHelper(Context context) {
mContext = context;
sharedPreferences = (SharedPreferences) mContext.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
}
public int getBadgeCount() {
return sharedPreferences.getInt(BADGE_COUNT_KEY, 0);
}
public void setBadgeCount(int badgeCount) {
storeBadgeCount(badgeCount);
if (badgeCount == 0) {
ShortcutBadger.removeCount(mContext);
Log.d(TAG, "Remove count");
} else {
ShortcutBadger.applyCount(mContext, badgeCount);
Log.d(TAG, "Apply count: " + badgeCount);
}
}
private void storeBadgeCount(int badgeCount) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt(BADGE_COUNT_KEY, badgeCount);
editor.apply();
}
}

View File

@ -0,0 +1,206 @@
package io.invertase.firebase.messaging;
// taken from https://github.com/facebook/facebook-android-sdk/blob/master/facebook/src/main/java/com/facebook/internal/BundleJSONConverter.java
/*
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
* <p>
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
* copy, modify, and distribute this software in source code or binary form for use
* in connection with the web services and APIs provided by Facebook.
* <p>
* As with any software that integrates with the Facebook platform, your use of
* this software is subject to the Facebook Developer Principles and Policies
* [http://developers.facebook.com/policy/]. This copyright notice shall be
* included in all copies or substantial portions of the software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.*;
/**
* com.facebook.internal is solely for the use of other packages within the Facebook SDK for
* Android. Use of any of the classes in this package is unsupported, and they may be modified or
* removed without warning at any time.
*
* A helper class that can round trip between JSON and Bundle objects that contains the types:
* Boolean, Integer, Long, Double, String
* If other types are found, an IllegalArgumentException is thrown.
*/
public class BundleJSONConverter {
private static final Map<Class<?>, Setter> SETTERS = new HashMap<Class<?>, Setter>();
static {
SETTERS.put(Boolean.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
bundle.putBoolean(key, (Boolean) value);
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
json.put(key, value);
}
});
SETTERS.put(Integer.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
bundle.putInt(key, (Integer) value);
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
json.put(key, value);
}
});
SETTERS.put(Long.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
bundle.putLong(key, (Long) value);
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
json.put(key, value);
}
});
SETTERS.put(Double.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
bundle.putDouble(key, (Double) value);
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
json.put(key, value);
}
});
SETTERS.put(String.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
bundle.putString(key, (String) value);
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
json.put(key, value);
}
});
SETTERS.put(String[].class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
throw new IllegalArgumentException("Unexpected type from JSON");
}
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
JSONArray jsonArray = new JSONArray();
for (String stringValue : (String[]) value) {
jsonArray.put(stringValue);
}
json.put(key, jsonArray);
}
});
SETTERS.put(JSONArray.class, new Setter() {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException {
JSONArray jsonArray = (JSONArray) value;
ArrayList<String> stringArrayList = new ArrayList<String>();
// Empty list, can't even figure out the type, assume an ArrayList<String>
if (jsonArray.length() == 0) {
bundle.putStringArrayList(key, stringArrayList);
return;
}
// Only strings are supported for now
for (int i = 0; i < jsonArray.length(); i++) {
Object current = jsonArray.get(i);
if (current instanceof String) {
stringArrayList.add((String) current);
} else {
throw new IllegalArgumentException("Unexpected type in an array: " + current.getClass());
}
}
bundle.putStringArrayList(key, stringArrayList);
}
@Override
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException {
throw new IllegalArgumentException("JSONArray's are not supported in bundles.");
}
});
}
public interface Setter {
public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException;
public void setOnJSON(JSONObject json, String key, Object value) throws JSONException;
}
public static JSONObject convertToJSON(Bundle bundle) throws JSONException {
JSONObject json = new JSONObject();
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
if (value == null) {
// Null is not supported.
continue;
}
// Special case List<String> as getClass would not work, since List is an interface
if (value instanceof List<?>) {
JSONArray jsonArray = new JSONArray();
@SuppressWarnings("unchecked")
List<String> listValue = (List<String>) value;
for (String stringValue : listValue) {
jsonArray.put(stringValue);
}
json.put(key, jsonArray);
continue;
}
// Special case Bundle as it's one way, on the return it will be JSONObject
if (value instanceof Bundle) {
json.put(key, convertToJSON((Bundle) value));
continue;
}
Setter setter = SETTERS.get(value.getClass());
if (setter == null) {
throw new IllegalArgumentException("Unsupported type: " + value.getClass());
}
setter.setOnJSON(json, key, value);
}
return json;
}
public static Bundle convertToBundle(JSONObject jsonObject) throws JSONException {
Bundle bundle = new Bundle();
@SuppressWarnings("unchecked")
Iterator<String> jsonIterator = jsonObject.keys();
while (jsonIterator.hasNext()) {
String key = jsonIterator.next();
Object value = jsonObject.get(key);
if (value == null || value == JSONObject.NULL) {
// Null is not supported.
continue;
}
// Special case JSONObject as it's one way, on the return it would be Bundle.
if (value instanceof JSONObject) {
bundle.putBundle(key, convertToBundle((JSONObject) value));
continue;
}
Setter setter = SETTERS.get(value.getClass());
if (setter == null) {
throw new IllegalArgumentException("Unsupported type: " + value.getClass());
}
setter.setOnBundle(bundle, key, value);
}
return bundle;
}
}

View File

@ -0,0 +1,34 @@
package io.invertase.firebase.messaging;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class InstanceIdService extends FirebaseInstanceIdService {
private static final String TAG = "InstanceIdService";
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. This call is initiated by the
* InstanceID provider.
*/
// [START refresh_token]
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// Broadcast refreshed token
Intent i = new Intent("io.invertase.firebase.messaging.FCMRefreshToken");
Bundle bundle = new Bundle();
bundle.putString("token", refreshedToken);
i.putExtras(bundle);
sendBroadcast(i);
}
}

View File

@ -0,0 +1,64 @@
package io.invertase.firebase.messaging;
import java.util.Map;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import org.json.JSONException;
import org.json.JSONObject;
public class MessagingService extends FirebaseMessagingService {
private static final String TAG = "MessagingService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "Remote message received");
Intent i = new Intent("io.invertase.firebase.messaging.ReceiveNotification");
i.putExtra("data", remoteMessage);
handleBadge(remoteMessage);
buildLocalNotification(remoteMessage);
sendOrderedBroadcast(i, null);
}
public void handleBadge(RemoteMessage remoteMessage) {
BadgeHelper badgeHelper = new BadgeHelper(this);
if (remoteMessage.getData() == null) {
return;
}
Map data = remoteMessage.getData();
if (data.get("badge") == null) {
return;
}
try {
int badgeCount = Integer.parseInt((String)data.get("badge"));
badgeHelper.setBadgeCount(badgeCount);
} catch (Exception e) {
Log.e(TAG, "Badge count needs to be an integer", e);
}
}
public void buildLocalNotification(RemoteMessage remoteMessage) {
if(remoteMessage.getData() == null){
return;
}
Map<String, String> data = remoteMessage.getData();
String customNotification = data.get("custom_notification");
if(customNotification != null){
try {
Bundle bundle = BundleJSONConverter.convertToBundle(new JSONObject(customNotification));
RNFirebaseLocalMessagingHelper helper = new RNFirebaseLocalMessagingHelper(this.getApplication());
helper.sendNotification(bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,332 @@
package io.invertase.firebase.messaging;
import android.app.*;
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.util.Patterns;
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 = (SharedPreferences) mContext.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
}
public Class getMainActivityClass() {
String packageName = mContext.getPackageName();
Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
private AlarmManager getAlarmManager() {
return (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}
public void sendNotification(Bundle bundle) {
try {
Class intentClass = getMainActivityClass();
if (intentClass == null) {
return;
}
if (bundle.getString("body") == null) {
return;
}
Resources res = mContext.getResources();
String packageName = mContext.getPackageName();
String title = bundle.getString("title");
if (title == null) {
ApplicationInfo appInfo = mContext.getApplicationInfo();
title = mContext.getPackageManager().getApplicationLabel(appInfo).toString();
}
NotificationCompat.Builder notification = new NotificationCompat.Builder(mContext)
.setContentTitle(title)
.setContentText(bundle.getString("body"))
.setTicker(bundle.getString("ticker"))
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
.setAutoCancel(bundle.getBoolean("auto_cancel", true))
.setNumber(bundle.getInt("number"))
.setSubText(bundle.getString("sub_text"))
.setGroup(bundle.getString("group"))
.setVibrate(new long[]{0, DEFAULT_VIBRATION})
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setExtras(bundle.getBundle("data"));
//priority
String priority = bundle.getString("priority", "");
switch(priority) {
case "min":
notification.setPriority(NotificationCompat.PRIORITY_MIN);
break;
case "high":
notification.setPriority(NotificationCompat.PRIORITY_HIGH);
break;
case "max":
notification.setPriority(NotificationCompat.PRIORITY_MAX);
break;
default:
notification.setPriority(NotificationCompat.PRIORITY_DEFAULT);
}
//icon
String smallIcon = bundle.getString("icon", "ic_launcher");
int smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
notification.setSmallIcon(smallIconResId);
//large icon
String largeIcon = bundle.getString("large_icon");
if(largeIcon != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
if (largeIcon.startsWith("http://") || largeIcon.startsWith("https://")) {
Bitmap bitmap = getBitmapFromURL(largeIcon);
notification.setLargeIcon(bitmap);
} else {
int largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
if (largeIconResId != 0) {
notification.setLargeIcon(largeIconBitmap);
}
}
}
//big text
String bigText = bundle.getString("big_text");
if(bigText != null){
notification.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
}
//sound
String soundName = bundle.getString("sound", "default");
if (!soundName.equalsIgnoreCase("default")) {
int soundResourceId = res.getIdentifier(soundName, "raw", packageName);
if(soundResourceId == 0){
soundName = soundName.substring(0, soundName.lastIndexOf('.'));
soundResourceId = res.getIdentifier(soundName, "raw", packageName);
}
notification.setSound(Uri.parse("android.resource://" + packageName + "/" + soundResourceId));
}
//color
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notification.setCategory(NotificationCompat.CATEGORY_CALL);
String color = bundle.getString("color");
if (color != null) {
notification.setColor(Color.parseColor(color));
}
}
//vibrate
if(bundle.containsKey("vibrate")){
long vibrate = bundle.getLong("vibrate", Math.round(bundle.getDouble("vibrate", bundle.getInt("vibrate"))));
if(vibrate > 0){
notification.setVibrate(new long[]{0, vibrate});
}else{
notification.setVibrate(null);
}
}
//lights
if (bundle.getBoolean("lights")) {
notification.setDefaults(NotificationCompat.DEFAULT_LIGHTS);
}
Log.d(TAG, "broadcast intent before showing notification");
Intent i = new Intent("io.invertase.firebase.messaging.ReceiveLocalNotification");
i.putExtras(bundle);
mContext.sendOrderedBroadcast(i, null);
if(!mIsForeground || bundle.getBoolean("show_in_foreground")){
Intent intent = new Intent(mContext, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(bundle);
intent.setAction(bundle.getString("click_action"));
int notificationID = bundle.containsKey("id") ? bundle.getString("id", "").hashCode() : (int) System.currentTimeMillis();
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, notificationID, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notification.setContentIntent(pendingIntent);
Notification info = notification.build();
if (bundle.containsKey("tag")) {
String tag = bundle.getString("tag");
notificationManager.notify(tag, notificationID, info);
} else {
notificationManager.notify(notificationID, info);
}
}
//clear out one time scheduled notification once fired
if(!bundle.containsKey("repeat_interval") && bundle.containsKey("fire_date")) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(bundle.getString("id"));
editor.apply();
}
} catch (Exception e) {
Log.e(TAG, "failed to send local notification", e);
}
}
public void sendNotificationScheduled(Bundle bundle) {
Class intentClass = getMainActivityClass();
if (intentClass == null) {
return;
}
String notificationId = bundle.getString("id");
if(notificationId == null){
Log.e(TAG, "failed to schedule notification because id is missing");
return;
}
Long fireDate = bundle.getLong("fire_date", Math.round(bundle.getDouble("fire_date")));
if (fireDate == 0) {
Log.e(TAG, "failed to schedule notification because fire date is missing");
return;
}
Intent notificationIntent = new Intent(mContext, RNFirebaseLocalMessagingPublisher.class);
notificationIntent.putExtras(bundle);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Long interval = null;
switch (bundle.getString("repeat_interval", "")) {
case "minute":
interval = (long) 60000;
break;
case "hour":
interval = AlarmManager.INTERVAL_HOUR;
break;
case "day":
interval = AlarmManager.INTERVAL_DAY;
break;
case "week":
interval = AlarmManager.INTERVAL_DAY * 7;
break;
}
if(interval != null){
getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, fireDate, interval, pendingIntent);
} else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
getAlarmManager().setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
}else {
getAlarmManager().set(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
}
//store intent
SharedPreferences.Editor editor = sharedPreferences.edit();
try {
JSONObject json = BundleJSONConverter.convertToJSON(bundle);
editor.putString(notificationId, json.toString());
editor.apply();
} catch (JSONException e) {
e.printStackTrace();
}
}
public void cancelLocalNotification(String notificationId) {
cancelAlarm(notificationId);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(notificationId);
editor.apply();
}
public void cancelAllLocalNotifications() {
java.util.Map<String, ?> keyMap = sharedPreferences.getAll();
SharedPreferences.Editor editor = sharedPreferences.edit();
for(java.util.Map.Entry<String, ?> entry:keyMap.entrySet()){
cancelAlarm(entry.getKey());
}
editor.clear();
editor.apply();
}
public void removeDeliveredNotification(String notificationId){
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId.hashCode());
}
public void removeAllDeliveredNotifications(){
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
}
public void cancelAlarm(String notificationId) {
Intent notificationIntent = new Intent(mContext, RNFirebaseLocalMessagingPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, notificationId.hashCode(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
getAlarmManager().cancel(pendingIntent);
}
public ArrayList<Bundle> getScheduledLocalNotifications(){
ArrayList<Bundle> array = new ArrayList<Bundle>();
java.util.Map<String, ?> keyMap = sharedPreferences.getAll();
for(java.util.Map.Entry<String, ?> entry:keyMap.entrySet()){
try {
JSONObject json = new JSONObject((String)entry.getValue());
Bundle bundle = BundleJSONConverter.convertToBundle(json);
array.add(bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
return array;
}
public void setApplicationForeground(boolean foreground){
mIsForeground = foreground;
}
public Bitmap getBitmapFromURL(String strURL) {
try {
URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,14 @@
package io.invertase.firebase.messaging;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class RNFirebaseLocalMessagingPublisher extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
new RNFirebaseLocalMessagingHelper((Application) context.getApplicationContext()).sendNotification(intent.getExtras());
}
}

View File

@ -1,222 +1,285 @@
package io.invertase.firebase.messaging;
import java.util.Map;
import android.content.Context;
import android.content.IntentFilter;
import android.content.Intent;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.util.Log;
import android.content.Intent;
import android.content.IntentFilter;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
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.ReadableType;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
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 android.app.Application;
import android.os.Bundle;
import android.util.Log;
public class RNFirebaseMessaging extends ReactContextBaseJavaModule {
import android.content.Context;
private static final String TAG = "RNFirebaseMessaging";
private static final String EVENT_NAME_TOKEN = "RNFirebaseRefreshToken";
private static final String EVENT_NAME_NOTIFICATION = "RNFirebaseReceiveNotification";
private static final String EVENT_NAME_SEND = "RNFirebaseUpstreamSend";
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public static final String INTENT_NAME_TOKEN = "io.invertase.firebase.refreshToken";
public static final String INTENT_NAME_NOTIFICATION = "io.invertase.firebase.ReceiveNotification";
public static final String INTENT_NAME_SEND = "io.invertase.firebase.Upstream";
private IntentFilter mRefreshTokenIntentFilter;
private IntentFilter mReceiveNotificationIntentFilter;
private IntentFilter mReceiveSendIntentFilter;
private BroadcastReceiver mBroadcastReceiver;
public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements LifecycleEventListener, ActivityEventListener {
private final static String TAG = RNFirebaseMessaging.class.getCanonicalName();
private RNFirebaseLocalMessagingHelper mRNFirebaseLocalMessagingHelper;
private BadgeHelper mBadgeHelper;
public RNFirebaseMessaging(ReactApplicationContext reactContext) {
super(reactContext);
mRefreshTokenIntentFilter = new IntentFilter(INTENT_NAME_TOKEN);
mReceiveNotificationIntentFilter = new IntentFilter(INTENT_NAME_NOTIFICATION);
mReceiveSendIntentFilter = new IntentFilter(INTENT_NAME_SEND);
initRefreshTokenHandler();
initMessageHandler();
initSendHandler();
Log.d(TAG, "New instance");
mRNFirebaseLocalMessagingHelper = new RNFirebaseLocalMessagingHelper((Application) reactContext.getApplicationContext());
mBadgeHelper = new BadgeHelper(reactContext.getApplicationContext());
getReactApplicationContext().addLifecycleEventListener(this);
getReactApplicationContext().addActivityEventListener(this);
registerTokenRefreshHandler();
registerMessageHandler();
registerLocalMessageHandler();
}
@Override
public String getName() {
return TAG;
return "RNFirebaseMessaging";
}
private void initMessageHandler() {
Log.d(TAG, "RNFirebase initMessageHandler called");
if (mBroadcastReceiver == null) {
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
RemoteMessage remoteMessage = intent.getParcelableExtra("data");
Log.d(TAG, "Firebase onReceive: " + remoteMessage);
WritableMap params = Arguments.createMap();
params.putNull("data");
params.putNull("notification");
params.putString("id", remoteMessage.getMessageId());
params.putString("messageId", remoteMessage.getMessageId());
if (remoteMessage.getData().size() != 0) {
WritableMap dataMap = Arguments.createMap();
Map<String, String> data = remoteMessage.getData();
for (String key : data.keySet()) {
dataMap.putString(key, data.get(key));
}
params.putMap("data", dataMap);
}
if (remoteMessage.getNotification() != null) {
WritableMap notificationMap = Arguments.createMap();
RemoteMessage.Notification notification = remoteMessage.getNotification();
notificationMap.putString("title", notification.getTitle());
notificationMap.putString("body", notification.getBody());
notificationMap.putString("icon", notification.getIcon());
notificationMap.putString("sound", notification.getSound());
notificationMap.putString("tag", notification.getTag());
params.putMap("notification", notificationMap);
}
ReactContext ctx = getReactApplicationContext();
Utils.sendEvent(ctx, EVENT_NAME_NOTIFICATION, params);
}
};
@ReactMethod
public void getInitialNotification(Promise promise) {
Activity activity = getCurrentActivity();
if (activity == null) {
promise.resolve(null);
return;
}
getReactApplicationContext().registerReceiver(mBroadcastReceiver, mReceiveNotificationIntentFilter);
promise.resolve(parseIntent(getCurrentActivity().getIntent()));
}
/**
*
*/
private void initRefreshTokenHandler() {
@ReactMethod
public void requestPermissions() {
}
@ReactMethod
public void getToken(Promise promise) {
Log.d(TAG, "Firebase token: " + FirebaseInstanceId.getInstance().getToken());
promise.resolve(FirebaseInstanceId.getInstance().getToken());
}
@ReactMethod
public void createLocalNotification(ReadableMap details) {
Bundle bundle = Arguments.toBundle(details);
mRNFirebaseLocalMessagingHelper.sendNotification(bundle);
}
@ReactMethod
public void scheduleLocalNotification(ReadableMap details) {
Bundle bundle = Arguments.toBundle(details);
mRNFirebaseLocalMessagingHelper.sendNotificationScheduled(bundle);
}
@ReactMethod
public void cancelLocalNotification(String notificationID) {
mRNFirebaseLocalMessagingHelper.cancelLocalNotification(notificationID);
}
@ReactMethod
public void cancelAllLocalNotifications() {
mRNFirebaseLocalMessagingHelper.cancelAllLocalNotifications();
}
@ReactMethod
public void removeDeliveredNotification(String notificationID) {
mRNFirebaseLocalMessagingHelper.removeDeliveredNotification(notificationID);
}
@ReactMethod
public void removeAllDeliveredNotifications() {
mRNFirebaseLocalMessagingHelper.removeAllDeliveredNotifications();
}
@ReactMethod
public void subscribeToTopic(String topic) {
FirebaseMessaging.getInstance().subscribeToTopic(topic);
}
@ReactMethod
public void unsubscribeFromTopic(String topic) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
}
@ReactMethod
public void getScheduledLocalNotifications(Promise promise) {
ArrayList<Bundle> bundles = mRNFirebaseLocalMessagingHelper.getScheduledLocalNotifications();
WritableArray array = Arguments.createArray();
for (Bundle bundle : bundles) {
array.pushMap(Arguments.fromBundle(bundle));
}
promise.resolve(array);
}
@ReactMethod
public void setBadgeNumber(int badgeNumber) {
mBadgeHelper.setBadgeCount(badgeNumber);
}
@ReactMethod
public void getBadgeNumber(Promise promise) {
promise.resolve(mBadgeHelper.getBadgeCount());
}
private void sendEvent(String eventName, Object params) {
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
private void registerTokenRefreshHandler() {
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.FCMRefreshToken");
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
WritableMap params = Arguments.createMap();
params.putString("token", intent.getStringExtra("token"));
ReactContext ctx = getReactApplicationContext();
Log.d(TAG, "initRefreshTokenHandler received event " + EVENT_NAME_TOKEN);
Utils.sendEvent(ctx, EVENT_NAME_TOKEN, params);
if (getReactApplicationContext().hasActiveCatalystInstance()) {
String token = intent.getStringExtra("token");
sendEvent("FCMTokenRefreshed", token);
}
}
;
}, mRefreshTokenIntentFilter);
}, intentFilter);
}
@ReactMethod
public void subscribeToTopic(String topic, final Callback callback) {
try {
FirebaseMessaging.getInstance().subscribeToTopic(topic);
callback.invoke(null, topic);
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "Firebase token: " + e);
WritableMap error = Arguments.createMap();
error.putString("message", e.getMessage());
callback.invoke(error);
}
}
@ReactMethod
public void getToken(final Promise promise) {
try {
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Firebase token: " + token);
promise.resolve(token);
} catch (Exception e) {
promise.reject("messaging/unknown", e.getMessage(), e);
}
}
@ReactMethod
public void unsubscribeFromTopic(String topic, final Callback callback) {
try {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
callback.invoke(null, topic);
} catch (Exception e) {
WritableMap error = Arguments.createMap();
error.putString("message", e.getMessage());
callback.invoke(error);
}
}
// String senderId, String messageId, String messageType,
@ReactMethod
public void send(ReadableMap params, final Promise promise) {
ReadableMap data = params.getMap("data");
public void send(String senderId, ReadableMap payload) throws Exception {
FirebaseMessaging fm = FirebaseMessaging.getInstance();
RemoteMessage.Builder remoteMessage = new RemoteMessage.Builder(params.getString("sender"));
remoteMessage.setMessageId(params.getString("id"));
remoteMessage.setMessageType(params.getString("type"));
if (params.hasKey("ttl")) {
remoteMessage.setTtl(params.getInt("ttl"));
}
if (params.hasKey("collapseKey")) {
remoteMessage.setCollapseKey(params.getString("collapseKey"));
}
ReadableMapKeySetIterator iterator = data.keySetIterator();
RemoteMessage.Builder message = new RemoteMessage.Builder(senderId + "@gcm.googleapis.com")
.setMessageId(UUID.randomUUID().toString());
ReadableMapKeySetIterator iterator = payload.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ReadableType type = data.getType(key);
if (type == ReadableType.String) {
remoteMessage.addData(key, data.getString(key));
}
String value = getStringFromReadableMap(payload, key);
message.addData(key, value);
}
fm.send(message.build());
}
try {
fm.send(remoteMessage.build());
WritableMap res = Arguments.createMap();
promise.resolve(null);
} catch (Exception e) {
Log.e(TAG, "send: error sending message", e);
promise.reject("messaging/unknown", e.getMessage(), e);
private String getStringFromReadableMap(ReadableMap map, String key) throws Exception {
switch (map.getType(key)) {
case String:
return map.getString(key);
case Number:
try {
return String.valueOf(map.getInt(key));
} catch (Exception e) {
return String.valueOf(map.getDouble(key));
}
case Boolean:
return String.valueOf(map.getBoolean(key));
default:
throw new Exception("Unknown data type: " + map.getType(key).name() + " for message key " + key);
}
}
private void initSendHandler() {
private void registerMessageHandler() {
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveNotification");
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
WritableMap params = Arguments.createMap();
if (intent.getBooleanExtra("hasError", false)) {
WritableMap error = Arguments.createMap();
error.putInt("code", intent.getIntExtra("errCode", 0));
error.putString("message", intent.getStringExtra("errorMessage"));
params.putMap("err", error);
} else {
params.putNull("err");
if (getReactApplicationContext().hasActiveCatalystInstance()) {
RemoteMessage message = intent.getParcelableExtra("data");
WritableMap params = Arguments.createMap();
WritableMap fcmData = Arguments.createMap();
if (message.getNotification() != null) {
Notification notification = message.getNotification();
fcmData.putString("title", notification.getTitle());
fcmData.putString("body", notification.getBody());
fcmData.putString("color", notification.getColor());
fcmData.putString("icon", notification.getIcon());
fcmData.putString("tag", notification.getTag());
fcmData.putString("action", notification.getClickAction());
}
params.putMap("fcm", fcmData);
if (message.getData() != null) {
Map<String, String> data = message.getData();
Set<String> keysIterator = data.keySet();
for (String key : keysIterator) {
params.putString(key, data.get(key));
}
}
sendEvent("FCMNotificationReceived", params);
}
ReactContext ctx = getReactApplicationContext();
Utils.sendEvent(ctx, EVENT_NAME_SEND, params);
}
}, mReceiveSendIntentFilter);
}, intentFilter);
}
private void registerLocalMessageHandler() {
IntentFilter intentFilter = new IntentFilter("io.invertase.firebase.messaging.ReceiveLocalNotification");
getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (getReactApplicationContext().hasActiveCatalystInstance()) {
sendEvent("FCMNotificationReceived", Arguments.fromBundle(intent.getExtras()));
}
}
}, intentFilter);
}
private WritableMap parseIntent(Intent intent) {
WritableMap params;
Bundle extras = intent.getExtras();
if (extras != null) {
try {
params = Arguments.fromBundle(extras);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
params = Arguments.createMap();
}
} else {
params = Arguments.createMap();
}
WritableMap fcm = Arguments.createMap();
fcm.putString("action", intent.getAction());
params.putMap("fcm", fcm);
params.putBoolean("opened_from_tray", true);
return params;
}
@Override
public void onHostResume() {
mRNFirebaseLocalMessagingHelper.setApplicationForeground(true);
}
@Override
public void onHostPause() {
mRNFirebaseLocalMessagingHelper.setApplicationForeground(false);
}
@Override
public void onHostDestroy() {
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// todo hmm?
}
@Override
public void onNewIntent(Intent intent) {
// todo hmm?
sendEvent("FCMNotificationReceived", parseIntent(intent));
}
}

View File

@ -1,9 +1,30 @@
import { NativeModules, NativeEventEmitter } from 'react-native';
import { NativeModules, DeviceEventEmitter, Platform } from 'react-native';
import { Base } from './../base';
import { promisify } from './../../utils';
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
const FirebaseMessagingEvt = new NativeEventEmitter(FirebaseMessaging);
export const EVENT_TYPE = {
RefreshToken: 'FCMTokenRefreshed',
Notification: 'FCMNotificationReceived',
};
export const NOTIFICATION_TYPE = {
Remote: 'remote_notification',
NotificationResponse: 'notification_response',
WillPresent: 'will_present_notification',
Local: 'local_notification',
};
export const REMOTE_NOTIFICATION_RESULT = {
NewData: 'UIBackgroundFetchResultNewData',
NoData: 'UIBackgroundFetchResultNoData',
ResultFailed: 'UIBackgroundFetchResultFailed',
};
export const WILL_PRESENT_RESULT = {
All: 'UNNotificationPresentationOptionAll',
None: 'UNNotificationPresentationOptionNone',
};
type RemoteMessage = {
id: string,
@ -14,6 +35,48 @@ type RemoteMessage = {
data: Object,
};
/**
* IOS only finish function
* @param data
*/
function finish(data) {
if (Platform.OS !== 'ios') {
return;
}
if (!this._finishCalled && this._completionHandlerId) {
let result = Object.assign({}, data);
this._finishCalled = true;
switch (this._notificationType) {
case NOTIFICATION_TYPE.Remote:
result = result || REMOTE_NOTIFICATION_RESULT.NoData;
if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) {
throw new Error('Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT');
}
FirebaseMessaging.finishRemoteNotification(this._completionHandlerId, result);
return;
case NOTIFICATION_TYPE.NotificationResponse:
FirebaseMessaging.finishNotificationResponse(this._completionHandlerId);
return;
case NOTIFICATION_TYPE.WillPresent:
result = result || (this.show_in_foreground ? WILL_PRESENT_RESULT.All : WILL_PRESENT_RESULT.None);
if (!Object.values(WILL_PRESENT_RESULT).includes(result)) {
throw new Error('Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT');
}
FirebaseMessaging.finishWillPresentNotification(this._completionHandlerId, result);
return;
default:
return;
}
}
}
/**
* @class Messaging
*/
@ -23,80 +86,150 @@ export default class Messaging extends Base {
this.namespace = 'firebase:messaging';
}
/*
* WEB API
get EVENT_TYPE() {
return EVENT_TYPE;
}
get NOTIFICATION_TYPE() {
return NOTIFICATION_TYPE;
}
get REMOTE_NOTIFICATION_RESULT() {
return REMOTE_NOTIFICATION_RESULT;
}
get WILL_PRESENT_RESULT() {
return WILL_PRESENT_RESULT;
}
/**
* Returns the notification that triggered application open
* @returns {*}
*/
// TODO move to new event emitter logic
onMessage(callback) {
this.log.info('Setting up onMessage callback');
const sub = this._on('RNFirebaseReceiveNotification', callback, FirebaseMessagingEvt);
return promisify(() => sub, FirebaseMessaging)(sub);
}
// TODO this is wrong - also there is no 'off' onMessage should return the unsubscribe function?
offMessage() {
this.log.info('Unlistening from onMessage (offMessage)');
this._off('FirebaseReceiveNotification');
}
offMessageReceived(...args) {
return this.offMessage(...args);
getInitialNotification() {
return FirebaseMessaging.getInitialNotification();
}
/**
* Returns the fcm token for the current device
* @returns {*|Promise.<String>}
*/
getToken() {
return FirebaseMessaging.getToken();
}
send(remoteMessage: RemoteMessage) {
if (!remoteMessage || !remoteMessage.data) return Promise.reject(new Error('Invalid remote message format provided.'));
return FirebaseMessaging.send(remoteMessage);
/**
* Create and display a local notification
* @param notification
* @returns {*}
*/
createLocalNotification(notification: Object) {
const _notification = Object.assign({}, notification);
_notification.id = _notification.id || new Date().getTime().toString();
_notification.local_notification = true;
return FirebaseMessaging.createLocalNotification(_notification);
}
//
listenForTokenRefresh(callback) {
this.log.info('Setting up listenForTokenRefresh callback');
const sub = this._on('RNFirebaseRefreshToken', callback, FirebaseMessagingEvt);
return promisify(() => sub, FirebaseMessaging)(sub);
/**
*
* @param notification
* @returns {*}
*/
scheduleLocalNotification(notification: Object) {
const _notification = Object.assign({}, notification);
if (!notification.id) return Promise.reject(new Error('An id is required to schedule a local notification.'));
_notification.local_notification = true;
return FirebaseMessaging.scheduleLocalNotification(_notification);
}
unlistenForTokenRefresh() {
this.log.info('Unlistening for TokenRefresh');
this._off('RNFirebaseRefreshToken');
/**
* Returns an array of all scheduled notifications
* @returns {Promise.<Array>}
*/
getScheduledLocalNotifications() {
return FirebaseMessaging.getScheduledLocalNotifications();
}
subscribeToTopic(topic) {
this.log.info(`subscribeToTopic ${topic}`);
const finalTopic = `/topics/${topic}`;
return promisify('subscribeToTopic', FirebaseMessaging)(finalTopic);
/**
* Cancel a local notification by id - using '*' will cancel
* all local notifications.
* @param id
* @returns {*}
*/
cancelLocalNotification(id: string) {
if (!id) return null;
if (id === '*') return FirebaseMessaging.cancelAllLocalNotifications();
return FirebaseMessaging.cancelLocalNotification(id);
}
unsubscribeFromTopic(topic) {
this.log.info(`unsubscribeFromTopic ${topic}`);
const finalTopic = `/topics/${topic}`;
return promisify('unsubscribeFromTopic', FirebaseMessaging)(finalTopic);
/**
* Remove a delivered notification - using '*' will remove
* all delivered notifications.
* @param id
* @returns {*}
*/
removeDeliveredNotification(id: string) {
if (!id) return null;
if (id === '*') return FirebaseMessaging.removeAllDeliveredNotifications();
return FirebaseMessaging.removeDeliveredNotification(id);
}
// New api
onRemoteMessage(callback) {
this.log.info('On remote message callback');
const sub = this._on('messaging_remote_event_received', callback, FirebaseMessagingEvt);
return promisify(() => sub, FirebaseMessaging)(sub);
/**
* Set notification count badge number
*/
setBadgeNumber() {
FirebaseMessaging.setBadgeNumber();
}
onLocalMessage(callback) {
this.log.info('on local callback');
const sub = this._on('messaging_local_event_received', callback, FirebaseMessagingEvt);
return promisify(() => sub, FirebaseMessaging)(sub);
/**
* set notification count badge number
* @returns {Promise.<Number>}
*/
getBadgeNumber() {
return FirebaseMessaging.getBadgeNumber();
}
listenForReceiveUpstreamSend(callback) {
this.log.info('Setting up send callback');
const sub = this._on('FirebaseUpstreamSend', callback, FirebaseMessagingEvt);
return promisify(() => sub, FirebaseMessaging)(sub);
/**
* Subscribe to messages / notifications
* @param listener
* @returns {*}
*/
onMessageReceived(listener: Function) {
return DeviceEventEmitter.addListener(EVENT_TYPE.Notification, async(event) => {
const data = Object.assign({}, event);
data.finish = finish;
await listener(data);
if (!data._finishCalled) {
data.finish();
}
});
}
unlistenForReceiveUpstreamSend() {
this.log.info('Unlistening for send');
this._off('FirebaseUpstreamSend');
/**
* Subscribe to token refresh events
* @param listener
* @returns {*}
*/
onTokenRefresh(listener: Function) {
return DeviceEventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
}
/**
* Subscribe to a topic
* @param topic
*/
subscribeToTopic(topic: String) {
FirebaseMessaging.subscribeToTopic(topic);
}
/**
* Unsubscribe from a topic
* @param topic
*/
unsubscribeFromTopic(topic: String) {
FirebaseMessaging.unsubscribeFromTopic(topic);
}
}