diff --git a/android/build.gradle b/android/build.gradle index 6f05c1b10..dc7614327 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,7 +20,7 @@ android { dependencies { // Google's GCM. - compile 'com.google.android.gms:play-services-gcm:10.0.1' + compile "com.google.firebase:firebase-messaging:10.2.6" compile 'com.facebook.react:react-native:+' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 143347523..e31d5a40b 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -3,14 +3,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="com.wix.reactnativenotifications"> - - - - @@ -21,39 +13,22 @@ --> - - - - - - - - + android:name="com.wix.reactnativenotifications.fcm.FcmMessageHandlerService"> - + + android:name=".fcm.FcmInstanceIdListenerService"> - + diff --git a/android/src/main/java/com/wix/reactnativenotifications/Defs.java b/android/src/main/java/com/wix/reactnativenotifications/Defs.java index fff51742c..5256baa94 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/Defs.java +++ b/android/src/main/java/com/wix/reactnativenotifications/Defs.java @@ -2,7 +2,6 @@ public interface Defs { String LOGTAG = "ReactNativeNotifs"; - String GCM_SENDER_ID_ATTR_NAME = "com.wix.reactnativenotifications.gcmSenderId"; String TOKEN_RECEIVED_EVENT_NAME = "remoteNotificationsRegistered"; diff --git a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java index 1f5535e62..58e67faef 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java +++ b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.Nullable; import android.util.Log; import com.facebook.react.bridge.Arguments; @@ -17,12 +18,12 @@ import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder; import com.wix.reactnativenotifications.core.InitialNotificationHolder; import com.wix.reactnativenotifications.core.ReactAppLifecycleFacade; -import com.wix.reactnativenotifications.core.notification.IPushNotification; -import com.wix.reactnativenotifications.core.notification.PushNotification; -import com.wix.reactnativenotifications.core.notification.PushNotificationProps; -import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer; -import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer; -import com.wix.reactnativenotifications.gcm.GcmInstanceIdRefreshHandlerService; +import com.wix.reactnativenotifications.core.notificationdrawer.INotificationDrawer; +import com.wix.reactnativenotifications.core.notificationdrawer.NotificationDrawer; +import com.wix.reactnativenotifications.core.notifications.ILocalNotification; +import com.wix.reactnativenotifications.core.notifications.LocalNotification; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; +import com.wix.reactnativenotifications.fcm.FcmTokenService; import static com.wix.reactnativenotifications.Defs.LOGTAG; @@ -46,16 +47,14 @@ public String getName() { @Override public void initialize() { Log.d(LOGTAG, "Native module init"); - startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_IS_APP_INIT); - - final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); + final INotificationDrawer notificationsDrawer = NotificationDrawer.get(getReactApplicationContext().getApplicationContext()); notificationsDrawer.onAppInit(); } @ReactMethod public void refreshToken() { Log.d(LOGTAG, "Native method invocation: refreshToken()"); - startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_MANUAL_REFRESH); + startTokenService(FcmTokenService.ACTION_REFRESH_TOKEN); } @ReactMethod @@ -64,7 +63,7 @@ public void getInitialNotification(final Promise promise) { Object result = null; try { - final PushNotificationProps notification = InitialNotificationHolder.getInstance().get(); + final NotificationProps notification = InitialNotificationHolder.getInstance().get(); if (notification == null) { return; } @@ -76,22 +75,29 @@ public void getInitialNotification(final Promise promise) { } @ReactMethod - public void postLocalNotification(ReadableMap notificationPropsMap, int notificationId) { + public void postLocalNotification(ReadableMap propsMap, int notificationId) { Log.d(LOGTAG, "Native method invocation: postLocalNotification"); - final Bundle notificationProps = Arguments.toBundle(notificationPropsMap); - final IPushNotification pushNotification = PushNotification.get(getReactApplicationContext().getApplicationContext(), notificationProps); - pushNotification.onPostRequest(notificationId); + final Context context = getReactApplicationContext().getApplicationContext(); + final NotificationProps localNotificationProps = NotificationProps.fromBundle(context, Arguments.toBundle(propsMap)); + final ILocalNotification notification = LocalNotification.get(context, localNotificationProps); + notification.post(notificationId); + } + + @ReactMethod + public void cancelLocalNotification(int notificationId, @Nullable String notificationTag) { + INotificationDrawer notificationsDrawer = NotificationDrawer.get(getReactApplicationContext().getApplicationContext()); + notificationsDrawer.onCancelLocalNotification(notificationTag, notificationId); } @ReactMethod - public void cancelLocalNotification(int notificationId) { - IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); - notificationsDrawer.onNotificationClearRequest(notificationId); + public void cancelAllLocalNotifications() { + INotificationDrawer notificationDrawer = NotificationDrawer.get(getReactApplicationContext().getApplicationContext()); + notificationDrawer.onCancelAllLocalNotifications(); } @Override public void onAppVisible() { - final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); + final INotificationDrawer notificationsDrawer = NotificationDrawer.get(getReactApplicationContext().getApplicationContext()); notificationsDrawer.onAppVisible(); } @@ -101,7 +107,7 @@ public void onAppNotVisible() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); + final INotificationDrawer notificationsDrawer = NotificationDrawer.get(getReactApplicationContext().getApplicationContext()); notificationsDrawer.onNewActivity(activity); } @@ -129,10 +135,10 @@ public void onActivitySaveInstanceState(Activity activity, Bundle outState) { public void onActivityDestroyed(Activity activity) { } - protected void startGcmIntentService(String extraFlag) { + protected void startTokenService(String action) { final Context appContext = getReactApplicationContext().getApplicationContext(); - final Intent tokenFetchIntent = new Intent(appContext, GcmInstanceIdRefreshHandlerService.class); - tokenFetchIntent.putExtra(extraFlag, true); - appContext.startService(tokenFetchIntent); + final Intent intent = new Intent(appContext, FcmTokenService.class); + intent.setAction(action); + appContext.startService(intent); } } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationHolder.java b/android/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationHolder.java index 3636f41f7..b257f9a2d 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationHolder.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationHolder.java @@ -2,13 +2,13 @@ import android.support.annotation.Nullable; -import com.wix.reactnativenotifications.core.notification.PushNotificationProps; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; public class InitialNotificationHolder { private static InitialNotificationHolder sInstance; - private PushNotificationProps mNotification; + private NotificationProps mNotification; public static void setInstance(InitialNotificationHolder instance) { sInstance = instance; @@ -24,8 +24,8 @@ public static InitialNotificationHolder getInstance() { return sInstance; } - public void set(PushNotificationProps pushNotificationProps) { - mNotification = pushNotificationProps; + public void set(NotificationProps notificationProps) { + mNotification = notificationProps; } public void clear() { @@ -33,7 +33,7 @@ public void clear() { } @Nullable - public PushNotificationProps get() { + public NotificationProps get() { return mNotification; } } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java b/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java index 4d8f4d1d5..ba49f96d0 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java @@ -1,26 +1,38 @@ package com.wix.reactnativenotifications.core; +import android.content.Context; import android.os.Bundle; +import com.facebook.react.ReactApplication; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; public class JsIOHelper { - public boolean sendEventToJS(String eventName, Bundle data, ReactContext reactContext) { - if (reactContext != null) { - sendEventToJS(eventName, Arguments.fromBundle(data), reactContext); - return true; - } - return false; + + private final Context mContext; + + public JsIOHelper(Context context) { + mContext = context; } - public boolean sendEventToJS(String eventName, WritableMap data, ReactContext reactContext) { - if (reactContext != null) { - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data); + public boolean sendEventToJS(String eventName, Bundle bundle) { + return sendEventToJS(eventName, Arguments.fromBundle(bundle)); + } + + public boolean sendEventToJS(String eventName, String string) { + return sendEventToJS(eventName, (Object) string); + } + + boolean sendEventToJS(String eventName, Object object) { + final ReactContext reactContext = ((ReactApplication) mContext.getApplicationContext()).getReactNativeHost().getReactInstanceManager().getCurrentReactContext(); + + if (reactContext != null && reactContext.hasActiveCatalystInstance()) { + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, object); return true; } + return false; } } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java b/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java index 0413e2f86..e00b92a95 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java @@ -5,13 +5,13 @@ import android.content.Intent; import android.os.Bundle; -import com.wix.reactnativenotifications.core.notification.PushNotificationProps; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; public class NotificationIntentAdapter { private static final int PENDING_INTENT_CODE = 0; private static final String PUSH_NOTIFICATION_EXTRA_NAME = "pushNotification"; - public static PendingIntent createPendingNotificationIntent(Context appContext, Intent intent, PushNotificationProps notification) { + public static PendingIntent createPendingNotificationIntent(Context appContext, Intent intent, NotificationProps notification) { intent.putExtra(PUSH_NOTIFICATION_EXTRA_NAME, notification.asBundle()); return PendingIntent.getService(appContext, PENDING_INTENT_CODE, intent, PendingIntent.FLAG_ONE_SHOT); } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/ProxyService.java b/android/src/main/java/com/wix/reactnativenotifications/core/ProxyService.java index f1abbb2ce..3985c4dfb 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/ProxyService.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/ProxyService.java @@ -5,8 +5,9 @@ import android.os.Bundle; import android.util.Log; -import com.wix.reactnativenotifications.core.notification.IPushNotification; -import com.wix.reactnativenotifications.core.notification.PushNotification; +import com.wix.reactnativenotifications.core.notifications.ILocalNotification; +import com.wix.reactnativenotifications.core.notifications.LocalNotification; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; public class ProxyService extends IntentService { @@ -19,10 +20,12 @@ public ProxyService() { @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "New intent: "+intent); - final Bundle notificationData = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent); - final IPushNotification pushNotification = PushNotification.get(this, notificationData); - if (pushNotification != null) { - pushNotification.onOpened(); + final Bundle notificationBundle = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent); + + if (notificationBundle != null) { + final NotificationProps notificationProps = NotificationProps.fromBundle(this, notificationBundle); + final ILocalNotification localNotification = LocalNotification.get(this, notificationProps); + localNotification.onOpened(); } } } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notification/INotificationsApplication.java b/android/src/main/java/com/wix/reactnativenotifications/core/notification/INotificationsApplication.java deleted file mode 100644 index 25cf21b9f..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notification/INotificationsApplication.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wix.reactnativenotifications.core.notification; - -import android.content.Context; -import android.os.Bundle; - -import com.wix.reactnativenotifications.core.AppLaunchHelper; -import com.wix.reactnativenotifications.core.AppLifecycleFacade; - -public interface INotificationsApplication { - IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade facade, AppLaunchHelper defaultAppLaunchHelper); -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java b/android/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java deleted file mode 100644 index 0d70024ac..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.wix.reactnativenotifications.core.notification; - -public interface IPushNotification { - class InvalidNotificationException extends Exception { - public InvalidNotificationException(String detailMessage) { - super(detailMessage); - } - } - - /** - * Handle an event where notification has just been received. - * @throws InvalidNotificationException - */ - void onReceived() throws InvalidNotificationException; - - /** - * Handle an event where notification has already been dispatched and is not being opened by the device user. - */ - void onOpened(); - - /** - * Handle a request to post this notification. - * - * @param notificationId (optional) The specific ID to associated with the notification. - * @return The ID effectively assigned to the notification (Auto-assigned if not specified as a parameter). - */ - int onPostRequest(Integer notificationId); - - PushNotificationProps asProps(); -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationProps.java b/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationProps.java deleted file mode 100644 index b155dfa03..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationProps.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.wix.reactnativenotifications.core.notification; - -import android.os.Bundle; - -public class PushNotificationProps { - - protected Bundle mBundle; - - public PushNotificationProps() { - mBundle = new Bundle(); - } - - public PushNotificationProps(String title, String body) { - mBundle = new Bundle(); - mBundle.putString("title", title); - mBundle.putString("body", body); - } - - public PushNotificationProps(Bundle bundle) { - mBundle = bundle; - } - - public String getTitle() { - return mBundle.getString("title"); - } - - public String getBody() { - return mBundle.getString("body"); - } - - public Bundle asBundle() { - return (Bundle) mBundle.clone(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(1024); - for (String key : mBundle.keySet()) { - sb.append(key).append("=").append(mBundle.get(key)).append(", "); - } - return sb.toString(); - } - - protected PushNotificationProps copy() { - return new PushNotificationProps((Bundle) mBundle.clone()); - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawer.java similarity index 53% rename from android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java rename to android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawer.java index 3be3dc1e5..0df866489 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawer.java @@ -1,12 +1,15 @@ package com.wix.reactnativenotifications.core.notificationdrawer; import android.app.Activity; +import android.support.annotation.Nullable; -public interface IPushNotificationsDrawer { +public interface INotificationDrawer { void onAppInit(); void onAppVisible(); void onNewActivity(Activity activity); void onNotificationOpened(); - void onNotificationClearRequest(int id); + + void onCancelLocalNotification(@Nullable String tag, int id); + void onCancelAllLocalNotifications(); } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationsDrawerApplication.java b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawerApplication.java similarity index 50% rename from android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationsDrawerApplication.java rename to android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawerApplication.java index 77d83d60e..2007a3727 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationsDrawerApplication.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/INotificationDrawerApplication.java @@ -4,6 +4,6 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper; -public interface INotificationsDrawerApplication { - IPushNotificationsDrawer getPushNotificationsDrawer(Context context, AppLaunchHelper defaultAppLaunchHelper); +public interface INotificationDrawerApplication { + INotificationDrawer getNotificationsDrawer(Context context, AppLaunchHelper defaultAppLaunchHelper); } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationDrawer.java similarity index 58% rename from android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java rename to android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationDrawer.java index 7b320e16d..bb4e26070 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationDrawer.java @@ -3,29 +3,32 @@ import android.app.Activity; import android.app.NotificationManager; import android.content.Context; +import android.content.Intent; +import android.support.annotation.Nullable; import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.InitialNotificationHolder; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; -public class PushNotificationsDrawer implements IPushNotificationsDrawer { +public class NotificationDrawer implements INotificationDrawer { - final protected Context mContext; - final protected AppLaunchHelper mAppLaunchHelper; + protected final Context mContext; + protected final AppLaunchHelper mAppLaunchHelper; - public static IPushNotificationsDrawer get(Context context) { - return PushNotificationsDrawer.get(context, new AppLaunchHelper()); + public static INotificationDrawer get(Context context) { + return NotificationDrawer.get(context, new AppLaunchHelper()); } - public static IPushNotificationsDrawer get(Context context, AppLaunchHelper appLaunchHelper) { + public static INotificationDrawer get(Context context, AppLaunchHelper appLaunchHelper) { final Context appContext = context.getApplicationContext(); - if (appContext instanceof INotificationsDrawerApplication) { - return ((INotificationsDrawerApplication) appContext).getPushNotificationsDrawer(context, appLaunchHelper); + if (appContext instanceof INotificationDrawerApplication) { + return ((INotificationDrawerApplication) appContext).getNotificationsDrawer(context, appLaunchHelper); } - return new PushNotificationsDrawer(context, appLaunchHelper); + return new NotificationDrawer(context, appLaunchHelper); } - protected PushNotificationsDrawer(Context context, AppLaunchHelper appLaunchHelper) { + protected NotificationDrawer(Context context, AppLaunchHelper appLaunchHelper) { mContext = context; mAppLaunchHelper = appLaunchHelper; } @@ -55,9 +58,14 @@ public void onNotificationOpened() { } @Override - public void onNotificationClearRequest(int id) { + public void onCancelLocalNotification(@Nullable String tag, int id) { final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(id); + notificationManager.cancel(tag, id); + } + + @Override + public void onCancelAllLocalNotifications() { + clearAll(); } protected void clearAll() { diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notifications/ILocalNotification.java b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/ILocalNotification.java new file mode 100644 index 000000000..a8013ccfa --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/ILocalNotification.java @@ -0,0 +1,19 @@ +package com.wix.reactnativenotifications.core.notifications; + +import android.support.annotation.Nullable; + +public interface ILocalNotification { + + /** + * Post this notification. + * + * @param notificationId (optional) The specific ID to associated with the notification. + * @return The ID effectively assigned to the notification (Auto-assigned if not specified as a parameter). + */ + int post(@Nullable Integer notificationId); + + /** + * Handle an event where notification has already been dispatched and is not being opened by the device user. + */ + void onOpened(); +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notifications/INotificationsApplication.java b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/INotificationsApplication.java new file mode 100644 index 000000000..78d0817d7 --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/INotificationsApplication.java @@ -0,0 +1,10 @@ +package com.wix.reactnativenotifications.core.notifications; + +import android.content.Context; + +import com.wix.reactnativenotifications.core.AppLaunchHelper; +import com.wix.reactnativenotifications.core.AppLifecycleFacade; + +public interface INotificationsApplication { + ILocalNotification getLocalNotification(Context context, NotificationProps notificationProps, AppLifecycleFacade facade, AppLaunchHelper defaultAppLaunchHelper); +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/LocalNotification.java similarity index 57% rename from android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java rename to android/src/main/java/com/wix/reactnativenotifications/core/notifications/LocalNotification.java index 9fec1de6b..cea627ea9 100644 --- a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/LocalNotification.java @@ -1,11 +1,15 @@ -package com.wix.reactnativenotifications.core.notification; +package com.wix.reactnativenotifications.core.notifications; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.os.Bundle; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.util.Log; import com.facebook.react.bridge.ReactContext; import com.wix.reactnativenotifications.core.AppLaunchHelper; @@ -17,17 +21,18 @@ import com.wix.reactnativenotifications.core.NotificationIntentAdapter; import com.wix.reactnativenotifications.core.ProxyService; +import static com.wix.reactnativenotifications.Defs.LOGTAG; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME; -import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; -public class PushNotification implements IPushNotification { +public class LocalNotification implements ILocalNotification { + + private final Context mContext; + private final NotificationProps mNotificationProps; + private final AppLifecycleFacade mAppLifecycleFacade; + private final AppLaunchHelper mAppLaunchHelper; + private final JsIOHelper mJsIOHelper; + private final AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() { - final protected Context mContext; - final protected AppLifecycleFacade mAppLifecycleFacade; - final protected AppLaunchHelper mAppLaunchHelper; - final protected JsIOHelper mJsIOHelper; - final protected PushNotificationProps mNotificationProps; - final protected AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() { @Override public void onAppVisible() { mAppLifecycleFacade.removeVisibilityListener(this); @@ -39,26 +44,36 @@ public void onAppNotVisible() { } }; - public static IPushNotification get(Context context, Bundle bundle) { - Context appContext = context.getApplicationContext(); + public static ILocalNotification get(Context context, NotificationProps localNotificationProps) { + final AppLifecycleFacade appLifecycleFacade = AppLifecycleFacadeHolder.get(); + final AppLaunchHelper appLaunchHelper = new AppLaunchHelper(); + final Context appContext = context.getApplicationContext(); + if (appContext instanceof INotificationsApplication) { - return ((INotificationsApplication) appContext).getPushNotification(context, bundle, AppLifecycleFacadeHolder.get(), new AppLaunchHelper()); + return ((INotificationsApplication) appContext).getLocalNotification(context, localNotificationProps, AppLifecycleFacadeHolder.get(), new AppLaunchHelper()); } - return new PushNotification(context, bundle, AppLifecycleFacadeHolder.get(), new AppLaunchHelper(), new JsIOHelper()); + + return new LocalNotification(context, localNotificationProps, appLifecycleFacade, appLaunchHelper); } - protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper JsIOHelper) { + protected LocalNotification(Context context, NotificationProps localNotificationProps, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIOHelper) { mContext = context; + mNotificationProps = localNotificationProps; mAppLifecycleFacade = appLifecycleFacade; mAppLaunchHelper = appLaunchHelper; - mJsIOHelper = JsIOHelper; - mNotificationProps = createProps(bundle); + mJsIOHelper = jsIOHelper; + } + + protected LocalNotification(Context context, NotificationProps localNotificationProps, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper) { + this(context, localNotificationProps, appLifecycleFacade, appLaunchHelper, new JsIOHelper(context)); } @Override - public void onReceived() throws InvalidNotificationException { - postNotification(null); - notifyReceivedToJS(); + public int post(Integer notificationId) { + final PendingIntent pendingIntent = getCTAPendingIntent(); + final int id = notificationId != null ? notificationId : createNotificationId(); + postNotification(id, getNotificationBuilder(pendingIntent).build()); + return id; } @Override @@ -67,22 +82,6 @@ public void onOpened() { clearAllNotifications(); } - @Override - public int onPostRequest(Integer notificationId) { - return postNotification(notificationId); - } - - @Override - public PushNotificationProps asProps() { - return mNotificationProps.copy(); - } - - protected int postNotification(Integer notificationId) { - final PendingIntent pendingIntent = getCTAPendingIntent(); - final Notification notification = buildNotification(pendingIntent); - return postNotification(notification, notificationId); - } - protected void digestNotification() { if (!mAppLifecycleFacade.isReactInitialized()) { setAsInitialNotification(); @@ -102,16 +101,12 @@ protected void digestNotification() { } } - protected PushNotificationProps createProps(Bundle bundle) { - return new PushNotificationProps(bundle); - } - protected void setAsInitialNotification() { InitialNotificationHolder.getInstance().set(mNotificationProps); } protected void dispatchImmediately() { - notifyOpenedToJS(); + sendOpenedEvent(); } protected void dispatchUponVisibility() { @@ -131,29 +126,29 @@ protected PendingIntent getCTAPendingIntent() { return NotificationIntentAdapter.createPendingNotificationIntent(mContext, cta, mNotificationProps); } - protected Notification buildNotification(PendingIntent intent) { - return getNotificationBuilder(intent).build(); - } - protected Notification.Builder getNotificationBuilder(PendingIntent intent) { - return new Notification.Builder(mContext) + final Integer icon = mNotificationProps.getIcon(); + final Integer color = mNotificationProps.getColor(); + + final Notification.Builder builder = new Notification.Builder(mContext) .setContentTitle(mNotificationProps.getTitle()) .setContentText(mNotificationProps.getBody()) - .setSmallIcon(mContext.getApplicationInfo().icon) + .setSmallIcon(icon != null ? icon : mContext.getApplicationContext().getApplicationInfo().icon) + .setSound(mNotificationProps.getSound()) .setContentIntent(intent) .setDefaults(Notification.DEFAULT_ALL) .setAutoCancel(true); - } - protected int postNotification(Notification notification, Integer notificationId) { - int id = notificationId != null ? notificationId : createNotificationId(notification); - postNotification(id, notification); - return id; + if (color != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.setColor(color); + } + + return builder; } protected void postNotification(int id, Notification notification) { final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.notify(id, notification); + notificationManager.notify(mNotificationProps.getTag(), id, notification); } protected void clearAllNotifications() { @@ -161,20 +156,16 @@ protected void clearAllNotifications() { notificationManager.cancelAll(); } - protected int createNotificationId(Notification notification) { - return (int) System.nanoTime(); - } - - private void notifyReceivedToJS() { - mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext()); - } - - private void notifyOpenedToJS() { - mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext()); + protected int createNotificationId() { + return mNotificationProps.getTag() != null ? 0 : (int) System.nanoTime(); } protected void launchOrResumeApp() { final Intent intent = mAppLaunchHelper.getLaunchIntent(mContext); mContext.startActivity(intent); } + + private void sendOpenedEvent() { + mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle()); + } } diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notifications/NotificationProps.java b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/NotificationProps.java new file mode 100644 index 000000000..5361a6af6 --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/NotificationProps.java @@ -0,0 +1,146 @@ +package com.wix.reactnativenotifications.core.notifications; + +import android.content.Context; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; + +import com.google.firebase.messaging.RemoteMessage; + +import java.util.Map; + +public class NotificationProps { + + // Local & remote (Firebase) support + + private static final String TITLE = "title"; + private static final String BODY = "body"; + private static final String ICON = "icon"; + private static final String SOUND = "sound"; + private static final String TAG = "tag"; + private static final String COLOR = "color"; + + private static final String DATA = "data"; + + // Local-only support + + public static NotificationProps fromRemoteMessage(Context context, RemoteMessage remoteMessage) { + final Bundle properties = new Bundle(); + final RemoteMessage.Notification notification = remoteMessage.getNotification(); + + if (notification != null) { + properties.putString(TITLE, notification.getTitle()); + properties.putString(BODY, notification.getBody()); + properties.putString(ICON, notification.getIcon()); + properties.putString(SOUND, notification.getSound()); + properties.putString(TAG, notification.getTag()); + properties.putString(COLOR, notification.getColor()); + } + + final Map data = remoteMessage.getData(); + + if (data != null) { + final Bundle dataBundle = new Bundle(); + + for (final Map.Entry entry : data.entrySet()) { + dataBundle.putString(entry.getKey(), entry.getValue()); + } + + properties.putBundle(DATA, dataBundle); + } + + return new NotificationProps(context, properties); + } + + public static NotificationProps fromBundle(Context context, Bundle bundle) { + return new NotificationProps(context, new Bundle(bundle)); + } + + private Context mContext; + private Bundle mProperties; + + protected NotificationProps(Context context, Bundle properties) { + mContext = context; + mProperties = properties; + } + + @Nullable + public String getTitle() { + return mProperties.getString(TITLE); + } + + @Nullable + public String getBody() { + return mProperties.getString(BODY); + } + + @Nullable + public Integer getIcon() { + return drawableIdFromString(mProperties.getString(ICON)); + } + + @Nullable + public Uri getSound() { + return rawResourceUriFromString(mProperties.getString(SOUND)); + } + + @Nullable + public String getTag() { + return mProperties.getString(TAG); + } + + @Nullable + public Integer getColor() { + return colorFromString(mProperties.getString(COLOR)); + } + + @Nullable + public Bundle getData() { + return mProperties.getBundle(DATA); + } + + public Bundle asBundle() { + return new Bundle(mProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(1024); + for (String key : mProperties.keySet()) { + sb.append(key).append("=").append(mProperties.get(key)).append(", "); + } + return sb.toString(); + } + + @Nullable + private Integer colorFromString(String string) { + if (string != null) { + try { + return Color.parseColor(string); + } catch (IllegalArgumentException e) { + // Move on + } + } + + return null; + } + + @Nullable + private Integer drawableIdFromString(String string) { + if (string != null) { + int id = mContext.getResources().getIdentifier(string, "drawable", mContext.getPackageName()); + + if (id != 0) { + return id; + } + } + + return null; + } + + @Nullable + private Uri rawResourceUriFromString(String string) { + return string != null ? Uri.parse("android.resource://" + mContext.getPackageName() + "/raw/" + string) : null; + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notifications/RemoteNotification.java b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/RemoteNotification.java new file mode 100644 index 000000000..75d780324 --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/core/notifications/RemoteNotification.java @@ -0,0 +1,30 @@ +package com.wix.reactnativenotifications.core.notifications; + +import android.content.Context; + +import com.wix.reactnativenotifications.core.JsIOHelper; + +import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; + +public class RemoteNotification { + + private final NotificationProps mNotificationProps; + private final JsIOHelper mJsIOHelper; + + protected RemoteNotification(NotificationProps notificationProps, JsIOHelper jsIOHelper) { + mNotificationProps = notificationProps; + mJsIOHelper = jsIOHelper; + } + + public RemoteNotification(Context context, NotificationProps notificationProps) { + this(notificationProps, new JsIOHelper(context)); + } + + public void onReceived() { + sendReceivedEvent(); + } + + private void sendReceivedEvent() { + mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle()); + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmInstanceIdListenerService.java b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmInstanceIdListenerService.java new file mode 100644 index 000000000..df158ab0a --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmInstanceIdListenerService.java @@ -0,0 +1,15 @@ +package com.wix.reactnativenotifications.fcm; + +import android.content.Intent; + +import com.google.firebase.iid.FirebaseInstanceIdService; + +public class FcmInstanceIdListenerService extends FirebaseInstanceIdService { + + @Override + public void onTokenRefresh() { + final Intent intent = new Intent(this, FcmTokenService.class); + intent.setAction(FcmTokenService.ACTION_REFRESH_TOKEN); + startService(intent); + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmMessageHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmMessageHandlerService.java new file mode 100644 index 000000000..90151bfea --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmMessageHandlerService.java @@ -0,0 +1,20 @@ +package com.wix.reactnativenotifications.fcm; + +import android.util.Log; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; +import com.wix.reactnativenotifications.core.notifications.RemoteNotification; + +import static com.wix.reactnativenotifications.Defs.LOGTAG; + +public class FcmMessageHandlerService extends FirebaseMessagingService { + + @Override + public void onMessageReceived(RemoteMessage message) { + Log.d(LOGTAG, "New message from firebase"); + final NotificationProps notificationProps = NotificationProps.fromRemoteMessage(this, message); + new RemoteNotification(this, notificationProps).onReceived(); + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenBridge.java b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenBridge.java new file mode 100644 index 000000000..17c959c2e --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenBridge.java @@ -0,0 +1,39 @@ +package com.wix.reactnativenotifications.fcm; + +import android.content.Context; +import android.util.Log; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.wix.reactnativenotifications.core.JsIOHelper; + +import static com.wix.reactnativenotifications.Defs.LOGTAG; +import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME; + +public class FcmTokenBridge { + + private final FirebaseInstanceId mFirebaseInstanceId; + private final JsIOHelper mJsIOHelper; + + protected FcmTokenBridge(FirebaseInstanceId firebaseInstanceId, JsIOHelper jsIOHelper) { + mFirebaseInstanceId = firebaseInstanceId; + mJsIOHelper = jsIOHelper; + } + + public FcmTokenBridge(Context context) { + this(FirebaseInstanceId.getInstance(), new JsIOHelper(context)); + } + + public void refreshToken() { + try { + sendReceivedEvent(); + } catch (Exception e) { + Log.e(LOGTAG, "Failed to refresh FCM token", e); + } + } + + private void sendReceivedEvent() { + final String token = mFirebaseInstanceId.getToken(); + Log.i(LOGTAG, "Firebase has a new token: FirebaseInstanceId=" + mFirebaseInstanceId.getId() + ", token=" + token); + mJsIOHelper.sendEventToJS(TOKEN_RECEIVED_EVENT_NAME, token); + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenService.java b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenService.java new file mode 100644 index 000000000..e541dfb1d --- /dev/null +++ b/android/src/main/java/com/wix/reactnativenotifications/fcm/FcmTokenService.java @@ -0,0 +1,26 @@ +package com.wix.reactnativenotifications.fcm; + +import android.app.IntentService; +import android.content.Intent; + +public class FcmTokenService extends IntentService { + + public static final String ACTION_REFRESH_TOKEN = "com.wix.reactnativenotifications.fcm.ACTION_REFRESH_TOKEN"; + + private FcmTokenBridge fcmTokenBridge; + + public FcmTokenService() { + super(FcmTokenService.class.getSimpleName()); + + fcmTokenBridge = new FcmTokenBridge(this); + } + + @Override + protected void onHandleIntent(Intent intent) { + final String action = intent.getAction(); + + if (ACTION_REFRESH_TOKEN.equals(action)) { + fcmTokenBridge.refreshToken(); + } + } +} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java deleted file mode 100644 index 933415f5c..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Intent; - -import com.google.android.gms.iid.InstanceIDListenerService; - -/** - * Instance-ID + token refreshing handling service. Contacts the GCM to fetch the updated token. - * - * @author amitd - */ -public class GcmInstanceIdListenerService extends InstanceIDListenerService { - - @Override - public void onTokenRefresh() { - // Fetch updated Instance ID token and notify our app's server of any changes (if applicable). - // Google recommends running this from an intent service. - Intent intent = new Intent(this, GcmInstanceIdRefreshHandlerService.class); - startService(intent); - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java deleted file mode 100644 index 3aa7aa9dc..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.app.IntentService; -import android.content.Intent; - -public class GcmInstanceIdRefreshHandlerService extends IntentService { - - public static String EXTRA_IS_APP_INIT = "isAppInit"; - public static String EXTRA_MANUAL_REFRESH = "doManualRefresh"; - - public GcmInstanceIdRefreshHandlerService() { - super(GcmInstanceIdRefreshHandlerService.class.getSimpleName()); - } - - @Override - protected void onHandleIntent(Intent intent) { - IGcmToken gcmToken = GcmToken.get(this); - if (gcmToken == null) { - return; - } - - if (intent.getBooleanExtra(EXTRA_IS_APP_INIT, false)) { - gcmToken.onAppReady(); - } else if (intent.getBooleanExtra(EXTRA_MANUAL_REFRESH, false)) { - gcmToken.onManualRefresh(); - } else { - gcmToken.onNewTokenReady(); - } - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java deleted file mode 100644 index f59665529..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.os.Bundle; -import android.util.Log; - -import com.google.android.gms.gcm.GcmListenerService; -import com.wix.reactnativenotifications.core.notification.IPushNotification; -import com.wix.reactnativenotifications.core.notification.PushNotification; - -import static com.wix.reactnativenotifications.Defs.LOGTAG; - -public class GcmMessageHandlerService extends GcmListenerService { - - @Override - public void onMessageReceived(String s, Bundle bundle) { - Log.d(LOGTAG, "New message from GCM: " + bundle); - - try { - final IPushNotification notification = PushNotification.get(getApplicationContext(), bundle); - notification.onReceived(); - } catch (IPushNotification.InvalidNotificationException e) { - // A GCM message, yes - but not the kind we know how to work with. - Log.v(LOGTAG, "GCM message handling aborted", e); - } - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java deleted file mode 100644 index b11a6b5f4..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.support.annotation.NonNull; -import android.util.Log; - -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.google.android.gms.gcm.GoogleCloudMessaging; -import com.google.android.gms.iid.InstanceID; - -import static com.wix.reactnativenotifications.Defs.GCM_SENDER_ID_ATTR_NAME; -import static com.wix.reactnativenotifications.Defs.LOGTAG; -import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME; - -public class GcmToken implements IGcmToken { - - final protected Context mAppContext; - - protected static String sToken; - - protected GcmToken(Context appContext) { - if (!(appContext instanceof ReactApplication)) { - throw new IllegalStateException("Application instance isn't a react-application"); - } - mAppContext = appContext; - } - - public static IGcmToken get(Context context) { - Context appContext = context.getApplicationContext(); - if (appContext instanceof INotificationsGcmApplication) { - return ((INotificationsGcmApplication) appContext).getGcmToken(context); - } - return new GcmToken(appContext); - } - - @Override - public void onNewTokenReady() { - synchronized (mAppContext) { - refreshToken(); - } - } - - @Override - public void onManualRefresh() { - synchronized (mAppContext) { - if (sToken == null) { - Log.i(LOGTAG, "Manual token refresh => asking for new token"); - refreshToken(); - } else { - Log.i(LOGTAG, "Manual token refresh => publishing existing token ("+sToken+")"); - sendTokenToJS(); - } - } - } - - @Override - public void onAppReady() { - synchronized (mAppContext) { - if (sToken == null) { - Log.i(LOGTAG, "App initialized => asking for new token"); - refreshToken(); - } else { - // Except for first run, this should be the case. - Log.i(LOGTAG, "App initialized => publishing existing token ("+sToken+")"); - sendTokenToJS(); - } - } - } - - protected void refreshToken() { - try { - sToken = getNewToken(); - } catch (Exception e) { - Log.e(LOGTAG, "Failed to retrieve new token", e); - return; - } - - sendTokenToJS(); - } - - @NonNull - protected String getNewToken() throws Exception { - final InstanceID instanceId = InstanceID.getInstance(mAppContext); - Log.d(LOGTAG, "GCM is refreshing token... instanceId=" + instanceId.getId()); - - // TODO why is this needed? - GoogleCloudMessaging.getInstance(mAppContext).close(); - - try { - final String registrationToken = instanceId.getToken(getSenderId(), GoogleCloudMessaging.INSTANCE_ID_SCOPE); - Log.i(LOGTAG, "GCM has a new token: instanceId=" + instanceId.getId() + ", token=" + registrationToken); - return registrationToken; - } catch (Exception e) { - throw new Exception("FATAL: Failed to fetch a fresh new token, instanceId=" + instanceId.getId(), e); - } - } - - protected String getSenderId() { - final String senderId = getSenderIdFromManifest(); - if (senderId == null) { - throw new IllegalStateException("Sender ID not found in manifest. Did you forget to add it as the value of a '"+GCM_SENDER_ID_ATTR_NAME+"' meta-data field?"); - } - return senderId; - } - - protected String getSenderIdFromManifest() { - final ApplicationInfo appInfo; - try { - appInfo = mAppContext.getPackageManager().getApplicationInfo(mAppContext.getPackageName(), PackageManager.GET_META_DATA); - return appInfo.metaData.getString(GCM_SENDER_ID_ATTR_NAME); - } catch (PackageManager.NameNotFoundException e) { - // Should REALLY never happen cause we're querying for our own package. - Log.e(LOGTAG, "Failed to resolve sender ID from manifest", e); - return null; - } - } - - protected void sendTokenToJS() { - final ReactInstanceManager instanceManager = ((ReactApplication) mAppContext).getReactNativeHost().getReactInstanceManager(); - final ReactContext reactContext = instanceManager.getCurrentReactContext(); - - // Note: Cannot assume react-context exists cause this is an async dispatched service. - if (reactContext != null && reactContext.hasActiveCatalystInstance()) { - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(TOKEN_RECEIVED_EVENT_NAME, sToken); - } - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java deleted file mode 100644 index f324a591f..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -public interface IGcmToken { - - /** - * Handle an event where we've been notified of a that a fresh token is now available from Google. - */ - void onNewTokenReady(); - - /** - * Handle an event where application is ready; typically used for sending token to JS. - */ - void onAppReady(); - - /** - * Handle a request to actively refresh the token on demand. - * This is in essence a workaround so as to allow apps to handle end-cases of token refreshing. It - * shouldn't be used by standard apps, as the token management is self sufficient. - */ - void onManualRefresh(); -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java deleted file mode 100644 index 36f59f71c..000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Context; - -public interface INotificationsGcmApplication { - IGcmToken getGcmToken(Context context); -} diff --git a/android/src/test/java/com/wix/reactnativenotifications/core/InitialNotificationHolderTest.java b/android/src/test/java/com/wix/reactnativenotifications/core/InitialNotificationHolderTest.java index 23fb7499a..56d6b281b 100644 --- a/android/src/test/java/com/wix/reactnativenotifications/core/InitialNotificationHolderTest.java +++ b/android/src/test/java/com/wix/reactnativenotifications/core/InitialNotificationHolderTest.java @@ -1,6 +1,6 @@ package com.wix.reactnativenotifications.core; -import com.wix.reactnativenotifications.core.notification.PushNotificationProps; +import com.wix.reactnativenotifications.core.notifications.NotificationProps; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,7 +20,7 @@ public void initialState() throws Exception { @Test public void setsInitialNotification() throws Exception { - PushNotificationProps props = mock(PushNotificationProps.class); + NotificationProps props = mock(NotificationProps.class); final InitialNotificationHolder uut = createUUT(); uut.set(props); assertEquals(props, uut.get()); @@ -28,7 +28,7 @@ public void setsInitialNotification() throws Exception { @Test public void clearsInitialNotification() throws Exception { - PushNotificationProps props = mock(PushNotificationProps.class); + NotificationProps props = mock(NotificationProps.class); final InitialNotificationHolder uut = createUUT(); uut.set(props); uut.clear(); @@ -37,8 +37,8 @@ public void clearsInitialNotification() throws Exception { @Test public void replacesInitialNotification() throws Exception { - PushNotificationProps props1 = mock(PushNotificationProps.class); - PushNotificationProps props2 = mock(PushNotificationProps.class); + NotificationProps props1 = mock(NotificationProps.class); + NotificationProps props2 = mock(NotificationProps.class); final InitialNotificationHolder uut = createUUT(); uut.set(props1); uut.set(props2); @@ -56,4 +56,4 @@ public void isALazySingleton() throws Exception { private InitialNotificationHolder createUUT() { return new InitialNotificationHolder(); } -} \ No newline at end of file +} diff --git a/android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawerTest.java b/android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationsDrawerTest.java similarity index 91% rename from android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawerTest.java rename to android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationsDrawerTest.java index d65b0d763..637173cfa 100644 --- a/android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawerTest.java +++ b/android/src/test/java/com/wix/reactnativenotifications/core/notificationdrawer/NotificationsDrawerTest.java @@ -18,13 +18,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) -public class PushNotificationsDrawerTest { +public class NotificationsDrawerTest { @Mock private ReactContext mReactContext; @Mock private Context mContext; @@ -53,8 +54,8 @@ public void onAppVisible_clearAllNotifications() throws Exception { @Test public void onNotificationClearRequest_clearSpecificNotification() throws Exception { - createUUT().onNotificationClearRequest(666); - verify(mNotificationManager).cancel(eq(666)); + createUUT().onCancelLocalNotification(null, 666); + verify(mNotificationManager).cancel((String) isNull(), eq(666)); verify(mNotificationManager, never()).cancelAll(); } @@ -99,7 +100,7 @@ public void onNewActivity_activityLaunchedFromPushNotification_dontClearInitialN verify(InitialNotificationHolder.getInstance(), never()).clear(); } - protected PushNotificationsDrawer createUUT() { - return new PushNotificationsDrawer(mContext, mAppLaunchHelper); + protected NotificationDrawer createUUT() { + return new NotificationDrawer(mContext, mAppLaunchHelper); } } diff --git a/android/src/test/java/com/wix/reactnativenotifications/core/notification/PushNotificationTest.java b/android/src/test/java/com/wix/reactnativenotifications/core/notifications/LocalNotificationTest.java similarity index 71% rename from android/src/test/java/com/wix/reactnativenotifications/core/notification/PushNotificationTest.java rename to android/src/test/java/com/wix/reactnativenotifications/core/notifications/LocalNotificationTest.java index 5da598626..86d80c1c5 100644 --- a/android/src/test/java/com/wix/reactnativenotifications/core/notification/PushNotificationTest.java +++ b/android/src/test/java/com/wix/reactnativenotifications/core/notifications/LocalNotificationTest.java @@ -1,4 +1,4 @@ -package com.wix.reactnativenotifications.core.notification; +package com.wix.reactnativenotifications.core.notifications; import android.app.Activity; import android.app.Notification; @@ -25,6 +25,8 @@ import org.robolectric.Shadows; import org.robolectric.shadows.ShadowNotification; +import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME; +import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.mockito.ArgumentMatchers.any; @@ -37,10 +39,7 @@ import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) -public class PushNotificationTest { - - private static final String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened"; - private static final String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived"; +public class LocalNotificationTest { private static final String DEFAULT_NOTIFICATION_TITLE = "Notification-title"; private static final String DEFAULT_NOTIFICATION_BODY = "Notification-body"; @@ -76,7 +75,7 @@ public void setup() throws Exception { public void onOpened_noReactContext_launchApp() throws Exception { when(mAppLifecycleFacade.isReactInitialized()).thenReturn(false); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); verify(mContext).startActivity(eq(mLaunchIntent)); @@ -93,17 +92,17 @@ public void onOpened_noReactContext_setAsInitialNotification() throws Exception Activity currentActivity = mock(Activity.class); when(mReactContext.getCurrentActivity()).thenReturn(currentActivity); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); - verify(InitialNotificationHolder.getInstance()).set(any(PushNotificationProps.class)); + verify(InitialNotificationHolder.getInstance()).set(any(NotificationProps.class)); } @Test public void onOpened_appInvisible_resumeAppWaitForVisibility() throws Exception { setUpBackgroundApp(); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); verify(mContext).startActivity(any(Intent.class)); @@ -116,10 +115,10 @@ public void onOpened_appInvisible_dontSetInitialNotification() throws Exception Activity currentActivity = mock(Activity.class); when(mReactContext.getCurrentActivity()).thenReturn(currentActivity); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); - verify(InitialNotificationHolder.getInstance(), never()).set(any(PushNotificationProps.class)); + verify(InitialNotificationHolder.getInstance(), never()).set(any(NotificationProps.class)); } @Test @@ -131,7 +130,7 @@ public void onOpened_appGoesVisible_resumeAppAndNotifyJs() throws Exception { // Act - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); // Hijack and invoke visibility listener @@ -142,18 +141,18 @@ public void onOpened_appGoesVisible_resumeAppAndNotifyJs() throws Exception { // Assert - verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle), eq(mReactContext)); + verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle)); } @Test public void onOpened_appVisible_notifyJS() throws Exception { setUpForegroundApp(); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); verify(mContext, never()).startActivity(any(Intent.class)); - verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle), eq(mReactContext)); + verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle)); } @Test @@ -161,7 +160,7 @@ public void onOpened_appVisible_clearNotificationsDrawer() throws Exception { verify(mNotificationManager, never()).cancelAll(); setUpForegroundApp(); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); verify(mNotificationManager).cancelAll(); @@ -173,10 +172,10 @@ public void onOpened_appVisible_dontSetInitialNotification() throws Exception { Activity currentActivity = mock(Activity.class); when(mReactContext.getCurrentActivity()).thenReturn(currentActivity); - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); - verify(InitialNotificationHolder.getInstance(), never()).set(any(PushNotificationProps.class)); + verify(InitialNotificationHolder.getInstance(), never()).set(any(NotificationProps.class)); } @Test @@ -184,62 +183,10 @@ public void onOpened_reactInitializedWithNoActivities_setAsInitialNotification() setUpBackgroundApp(); when(mReactContext.getCurrentActivity()).thenReturn(null); // Just for clarity - final PushNotification uut = createUUT(); + final LocalNotification uut = createUUT(); uut.onOpened(); - verify(InitialNotificationHolder.getInstance()).set(any(PushNotificationProps.class)); - } - - @Test - public void onReceived_validData_postNotificationAndNotifyJS() throws Exception { - // Arrange - - setUpForegroundApp(); - - // Act - - final PushNotification uut = createUUT(); - uut.onReceived(); - - // Assert - - ArgumentCaptor notificationCaptor = ArgumentCaptor.forClass(Notification.class); - verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture()); - verifyNotification(notificationCaptor.getValue()); - - verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), eq(mDefaultBundle), eq(mReactContext)); - } - - @Test - public void onReceived_validDataForBackgroundApp_postNotificationAndNotifyJs() throws Exception { - // Arrange - - setUpForegroundApp(); - - // Act - - final PushNotification uut = createUUT(); - uut.onReceived(); - - // Assert - - ArgumentCaptor notificationCaptor = ArgumentCaptor.forClass(Notification.class); - verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture()); - verifyNotification(notificationCaptor.getValue()); - - verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), eq(mDefaultBundle), eq(mReactContext)); - } - - @Test - public void onReceived_validDataForDeadApp_postNotificationDontNotifyJS() throws Exception { - final PushNotification uut = createUUT(); - uut.onReceived(); - - ArgumentCaptor notificationCaptor = ArgumentCaptor.forClass(Notification.class); - verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture()); - verifyNotification(notificationCaptor.getValue()); - - verify(mJsIOHelper, never()).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), any(Bundle.class), any(ReactContext.class)); + verify(InitialNotificationHolder.getInstance()).set(any(NotificationProps.class)); } @Test @@ -251,8 +198,8 @@ public void onPostRequest_withValidDataButNoId_postNotifications() throws Except // Act - final PushNotification uut = createUUT(); - uut.onPostRequest(null); + final LocalNotification uut = createUUT(); + uut.post(null); // Assert @@ -261,13 +208,13 @@ public void onPostRequest_withValidDataButNoId_postNotifications() throws Except verifyNotification(notificationCaptor.getValue()); // Shouldn't notify an event on an explicit call to notification posting - verify(mJsIOHelper, never()).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), any(Bundle.class), any(ReactContext.class)); + verify(mJsIOHelper, never()).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), any(Bundle.class)); } @Test public void onPostRequest_withValidDataButNoId_idsShouldBeUnique() throws Exception { - createUUT().onPostRequest(null); - createUUT().onPostRequest(null); + createUUT().post(null); + createUUT().post(null); ArgumentCaptor idsCaptor = ArgumentCaptor.forClass(Integer.class); verify(mNotificationManager, times(2)).notify(idsCaptor.capture(), any(Notification.class)); @@ -278,26 +225,27 @@ public void onPostRequest_withValidDataButNoId_idsShouldBeUnique() throws Except public void onPostRequest_withValidDataAndExplicitId_postNotification() throws Exception { final int id = 666; - final PushNotification uut = createUUT(); - uut.onPostRequest(id); + final LocalNotification uut = createUUT(); + uut.post(id); verify(mNotificationManager).notify(eq(id), any(Notification.class)); } @Test public void onPostRequest_emptyData_postNotification() throws Exception { - PushNotification uut = createUUT(new Bundle()); - uut.onPostRequest(null); + LocalNotification uut = createUUT(new Bundle()); + uut.post(null); verify(mNotificationManager).notify(anyInt(), any(Notification.class)); } - protected PushNotification createUUT() { + protected LocalNotification createUUT() { return createUUT(mDefaultBundle); } - protected PushNotification createUUT(Bundle bundle) { - return new PushNotification(mContext, bundle, mAppLifecycleFacade, mAppLaunchHelper, mJsIOHelper); + protected LocalNotification createUUT(Bundle bundle) { + final NotificationProps localNotificationProps = new NotificationProps(mContext, bundle); + return new LocalNotification(mContext, localNotificationProps, mAppLifecycleFacade, mAppLaunchHelper, mJsIOHelper); } protected void setUpBackgroundApp() { diff --git a/android/src/test/java/com/wix/reactnativenotifications/core/notifications/RemoteNotificationTest.java b/android/src/test/java/com/wix/reactnativenotifications/core/notifications/RemoteNotificationTest.java new file mode 100644 index 000000000..c2377d334 --- /dev/null +++ b/android/src/test/java/com/wix/reactnativenotifications/core/notifications/RemoteNotificationTest.java @@ -0,0 +1,60 @@ +package com.wix.reactnativenotifications.core.notifications; + +import android.content.Context; +import android.os.Bundle; + +import com.wix.reactnativenotifications.core.JsIOHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +public class RemoteNotificationTest { + + private static final String DEFAULT_NOTIFICATION_TITLE = "Notification-title"; + private static final String DEFAULT_NOTIFICATION_BODY = "Notification-body"; + + @Mock private Context mContext; + + @Mock private Bundle mDefaultBundle; + @Mock private JsIOHelper mJsIOHelper; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mDefaultBundle.getString(eq("title"))).thenReturn(DEFAULT_NOTIFICATION_TITLE); + when(mDefaultBundle.getString(eq("body"))).thenReturn(DEFAULT_NOTIFICATION_BODY); + when(mDefaultBundle.clone()).thenReturn(mDefaultBundle); + } + + @Test + public void onReceived_validData_postNotificationAndNotifyJS() throws Exception { + // Act + + final RemoteNotification uut = createUUT(); + uut.onReceived(); + + // Assert + + verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), eq(mDefaultBundle)); + } + + protected RemoteNotification createUUT() { + return createUUT(mDefaultBundle); + } + + protected RemoteNotification createUUT(Bundle bundle) { + final NotificationProps notificationProps = new NotificationProps(mContext, bundle); + return new RemoteNotification(notificationProps, mJsIOHelper); + } +} diff --git a/android/src/test/java/com/wix/reactnativenotifications/fcm/FcmTokenBridgeTest.java b/android/src/test/java/com/wix/reactnativenotifications/fcm/FcmTokenBridgeTest.java new file mode 100644 index 000000000..b3f687218 --- /dev/null +++ b/android/src/test/java/com/wix/reactnativenotifications/fcm/FcmTokenBridgeTest.java @@ -0,0 +1,57 @@ +package com.wix.reactnativenotifications.fcm; + +import android.content.Context; +import android.os.Bundle; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.wix.reactnativenotifications.core.JsIOHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +public class FcmTokenBridgeTest { + + private static final String DEFAULT_NOTIFICATION_TITLE = "Notification-title"; + private static final String DEFAULT_NOTIFICATION_BODY = "Notification-body"; + + @Mock private Context mContext; + + @Mock private Bundle mDefaultBundle; + @Mock private FirebaseInstanceId mFirebaseInstanceId; + @Mock private JsIOHelper mJsIOHelper; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mDefaultBundle.getString(eq("title"))).thenReturn(DEFAULT_NOTIFICATION_TITLE); + when(mDefaultBundle.getString(eq("body"))).thenReturn(DEFAULT_NOTIFICATION_BODY); + when(mDefaultBundle.clone()).thenReturn(mDefaultBundle); + } + + @Test + public void refreshToken_notifyJs() throws Exception { + // Act + + final FcmTokenBridge uut = createUUT(); + uut.refreshToken(); + + // Assert + + verify(mJsIOHelper).sendEventToJS(eq(TOKEN_RECEIVED_EVENT_NAME), eq(mDefaultBundle)); + } + + protected FcmTokenBridge createUUT() { + return new FcmTokenBridge(mFirebaseInstanceId, mJsIOHelper); + } +} diff --git a/example/index.android.js b/example/index.android.js index 5681d187c..585cf8523 100644 --- a/example/index.android.js +++ b/example/index.android.js @@ -117,7 +117,7 @@ class MainComponent extends Component { Wix React Native Notifications {this.state.initialNotification ? 'Opened from notification' : ''} - Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"} + Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.getBody() + ` (opened at ''${this.state.notificationRxTime})` : "N/A"} Time elapsed: {this.state.elapsed} {"\n\n"} this.onPostNotification()}> diff --git a/index.android.js b/index.android.js index 03fbe0d45..8d2d604c1 100644 --- a/index.android.js +++ b/index.android.js @@ -45,14 +45,23 @@ export class NotificationsAndroid { RNNotifications.refreshToken(); } - static localNotification(notification: Object) { - const id = Math.random() * 100000000 | 0; // Bitwise-OR forces value onto a 32bit limit - RNNotifications.postLocalNotification(notification, id); + static localNotification(notification, id) { + const notificationProperties = notification instanceof NotificationAndroid ? notification.properties : notification; + + if (!id && id !== 0) { + id = notificationProperties.tag ? 0 : Math.random() * 100000000 | 0; // Bitwise-OR forces value onto a 32bit limit + } + + RNNotifications.postLocalNotification(notificationProperties, id); return id; } - static cancelLocalNotification(id) { - RNNotifications.cancelLocalNotification(id); + static cancelLocalNotification(id, tag) { + RNNotifications.cancelLocalNotification(id, tag); + } + + static cancelAllLocalNotifications() { + RNNotifications.cancelAllLocalNotifications(); } } diff --git a/notification.android.js b/notification.android.js index bb2ace85e..1aed2dab8 100644 --- a/notification.android.js +++ b/notification.android.js @@ -1,20 +1,46 @@ -/** A wrapper to align Android with iOS in terms on notification structure. */ +/** A wrapper to align Android with iOS in terms on notification structure and provide convenience methods. */ export default class NotificationAndroid { - constructor(notification) { - this.data = notification; + constructor(properties) { + this.properties = properties; } + isDataOnly() { + return this.getData() && Object.keys(this.properties).length === 1; + } + + // Convenience accessors + getData() { - return this.data; + return this.properties.data; } getTitle() { - return this.data.title; + return this.properties.title; } + getBody() { + return this.properties.body; + } + + // Alias for getBody() getMessage() { - return this.data.body; + return this.getBody(); + } + + getIcon() { + return this.properties.icon; + } + + getSound() { + return this.properties.sound; } -} + getTag() { + return this.properties.tag; + } + + getColor() { + return this.properties.color; + } +} diff --git a/test/index.android.spec.js b/test/index.android.spec.js index a41255b81..96149b405 100644 --- a/test/index.android.spec.js +++ b/test/index.android.spec.js @@ -10,6 +10,7 @@ describe("Notifications-Android > ", () => { let getInitialNotificationStub; let postLocalNotificationStub; let cancelLocalNotificationStub; + let cancelAllLocalNotificationsStub; let deviceEventEmitterListenerStub; let libUnderTest; beforeEach(() => { @@ -17,6 +18,7 @@ describe("Notifications-Android > ", () => { getInitialNotificationStub = sinon.stub(); postLocalNotificationStub = sinon.stub(); cancelLocalNotificationStub = sinon.stub(); + cancelAllLocalNotificationsStub = sinon.stub(); deviceEventEmitterListenerStub = sinon.stub(); libUnderTest = proxyquire("../index.android", { @@ -26,7 +28,8 @@ describe("Notifications-Android > ", () => { refreshToken: refreshTokenStub, getInitialNotification: getInitialNotificationStub, postLocalNotification: postLocalNotificationStub, - cancelLocalNotification: cancelLocalNotificationStub + cancelLocalNotification: cancelLocalNotificationStub, + cancelAllLocalNotifications: cancelAllLocalNotificationsStub } }, DeviceEventEmitter: { @@ -83,14 +86,15 @@ describe("Notifications-Android > ", () => { it("should assign a wrapper-callback upon registration", () => { expect(deviceEventEmitterListenerStub).to.not.have.been.called; const userListenerStub = sinon.stub(); - const notification = { foo: "bar" }; + const data = {foo: "bar"}; + const notification = {data}; libUnderTest.NotificationsAndroid.setNotificationOpenedListener(userListenerStub); expect(userListenerStub).to.not.have.been.called; deviceEventEmitterListenerStub.args[0][1](notification); expect(userListenerStub).to.have.been.calledOnce; - expect(userListenerStub.args[0][0].getData()).to.equal(notification); + expect(userListenerStub.args[0][0].getData()).to.equal(data); }); it("should clear native event listener upon listener deregister", () => { @@ -128,14 +132,15 @@ describe("Notifications-Android > ", () => { it("should assign a wrapper-callback upon registration", () => { expect(deviceEventEmitterListenerStub).to.not.have.been.called; const userListenerStub = sinon.stub(); - const notification = { foo: "bar" }; + const data = {foo: "bar"}; + const notification = {data}; libUnderTest.NotificationsAndroid.setNotificationReceivedListener(userListenerStub); expect(userListenerStub).to.not.have.been.called; deviceEventEmitterListenerStub.args[0][1](notification); expect(userListenerStub).to.have.been.calledOnce; - expect(userListenerStub.args[0][0].getData()).to.equal(notification); + expect(userListenerStub.args[0][0].getData()).to.equal(data); }); it("should clear native event listener upon listener deregister", () => { @@ -170,12 +175,13 @@ describe("Notifications-Android > ", () => { describe("Initial notification API", () => { it("should return initial notification data if available", (done) => { expect(getInitialNotificationStub).to.not.have.been.called; - const rawNotification = {foo: "bar"}; + const data = {foo: "bar"}; + const rawNotification = {data}; getInitialNotificationStub.returns(Promise.resolve(rawNotification)); libUnderTest.PendingNotifications.getInitialNotification() .then((notification) => { - expect(notification.getData()).to.equal(rawNotification); + expect(notification.getData()).to.equal(data); done(); }) .catch((err) => done(err)); @@ -195,7 +201,7 @@ describe("Notifications-Android > ", () => { }); - describe("Local notification", () => { + describe("Local notifications", () => { const notification = { title: "notification-title", body: "notification-body" @@ -209,7 +215,7 @@ describe("Notifications-Android > ", () => { expect(postLocalNotificationStub).to.have.been.calledWith(notification, id); }); - it("should be called with a unique ID", () => { + it("should be published with a unique ID", () => { expect(postLocalNotificationStub).to.not.have.been.called; const id = libUnderTest.NotificationsAndroid.localNotification(notification); @@ -226,6 +232,14 @@ describe("Notifications-Android > ", () => { expect(cancelLocalNotificationStub).to.have.been.calledWith(666); }); + + it("should be cancellable", () => { + expect(cancelLocalNotificationStub).to.not.have.been.called; + + libUnderTest.NotificationsAndroid.cancelAllLocalNotifications(); + + expect(cancelAllLocalNotificationsStub).to.have.been.called; + }); }); });