diff --git a/android/build.gradle b/android/build.gradle
index af712657..460a890b 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -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'
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java b/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java
deleted file mode 100644
index 4a92b9af..00000000
--- a/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java b/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java
deleted file mode 100644
index c315b212..00000000
--- a/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java b/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java
new file mode 100644
index 00000000..ba88bc7e
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/BadgeHelper.java
@@ -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();
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/BundleJSONConverter.java b/android/src/main/java/io/invertase/firebase/messaging/BundleJSONConverter.java
new file mode 100644
index 00000000..70d31515
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/BundleJSONConverter.java
@@ -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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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, Setter> SETTERS = new HashMap, 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 stringArrayList = new ArrayList();
+ // Empty list, can't even figure out the type, assume an ArrayList
+ 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 as getClass would not work, since List is an interface
+ if (value instanceof List>) {
+ JSONArray jsonArray = new JSONArray();
+ @SuppressWarnings("unchecked")
+ List listValue = (List) 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 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;
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java b/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java
new file mode 100644
index 00000000..1b09d540
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/InstanceIdService.java
@@ -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);
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java b/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java
new file mode 100644
index 00000000..cd5af92a
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/MessagingService.java
@@ -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 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();
+ }
+
+ }
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingHelper.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingHelper.java
new file mode 100644
index 00000000..642ef6e7
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingHelper.java
@@ -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 keyMap = sharedPreferences.getAll();
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ for(java.util.Map.Entry 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 getScheduledLocalNotifications(){
+ ArrayList array = new ArrayList();
+ java.util.Map keyMap = sharedPreferences.getAll();
+ for(java.util.Map.Entry 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;
+ }
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingPublisher.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingPublisher.java
new file mode 100644
index 00000000..103b3168
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseLocalMessagingPublisher.java
@@ -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());
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java
index eeae6740..148b0e7d 100644
--- a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java
+++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java
@@ -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 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 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 data = message.getData();
+ Set 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));
}
}
diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js
index 2ee14fed..1f904f23 100644
--- a/lib/modules/messaging/index.js
+++ b/lib/modules/messaging/index.js
@@ -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.}
+ */
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.}
+ */
+ 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.}
+ */
+ 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);
}
}