diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotification.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotification.java new file mode 100644 index 0000000000000000000000000000000000000000..4b02ad23fb74f0bf613af6a0abd94d9b2ee3e226 --- /dev/null +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotification.java @@ -0,0 +1,16 @@ +package com.wix.reactnativenotifications.core; + +import android.support.annotation.Nullable; + +public class InitialNotification { + private static PushNotificationProps sNotification; + + public static void set(PushNotificationProps pushNotificationProps) { + sNotification = pushNotificationProps; + } + + @Nullable + public static PushNotificationProps get() { + return sNotification; + } +} diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationStore.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationStore.java deleted file mode 100644 index e7563c130b351d1fdc1c0e11638d6df073fb90e4..0000000000000000000000000000000000000000 --- a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/InitialNotificationStore.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.wix.reactnativenotifications.core; - -public class InitialNotificationStore { - private static PushNotificationProps sInitialNotif; - - public static void setInitialNotification(PushNotificationProps pushNotificationProps) { - sInitialNotif = pushNotificationProps; - } - - public static PushNotificationProps getInitialNotification() { - return sInitialNotif; - } -} diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/PushNotification.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/PushNotification.java index 29e31dd2d279c0c4bed26025913693668791fe48..47f5d5bd5592730b0939af78be9d15c7cd7c237a 100644 --- a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/PushNotification.java +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/PushNotification.java @@ -63,7 +63,7 @@ public class PushNotification { launchOrResumeApp(activity); notifyOpenedToJS(reactContext); } else { - InitialNotificationStore.setInitialNotification(mNotificationProps); + InitialNotification.set(mNotificationProps); launchOrResumeApp(activity); } } diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/RNNotificationsModule.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/RNNotificationsModule.java index 35578a3d009cbad12f14ca54a295f0b81a08c827..94995d88bd6ae31ad2d9a7a7be7f0c2cb95d6ead 100644 --- a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/RNNotificationsModule.java +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/core/RNNotificationsModule.java @@ -1,7 +1,5 @@ package com.wix.reactnativenotifications.core; -import android.content.Context; -import android.content.Intent; import android.util.Log; import com.facebook.react.bridge.Arguments; @@ -9,7 +7,8 @@ import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.wix.reactnativenotifications.gcm.GcmInstanceIdRefreshHandlerService; +import com.wix.reactnativenotifications.gcm.GcmToken; +import com.wix.reactnativenotifications.gcm.IGcmToken; import static com.wix.reactnativenotifications.Defs.LOGTAG; @@ -26,10 +25,9 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule { @Override public void initialize() { - Log.d(LOGTAG, "native module init"); - final Context appContext = getReactApplicationContext().getApplicationContext(); - final Intent tokenFetchIntent = new Intent(appContext, GcmInstanceIdRefreshHandlerService.class); - appContext.startService(tokenFetchIntent); + Log.d(LOGTAG, "Native module init"); + IGcmToken gcmToken = GcmToken.get(getReactApplicationContext().getApplicationContext()); + gcmToken.onAppReady(); } @ReactMethod @@ -37,7 +35,7 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule { Object result = null; try { - final PushNotificationProps notification = InitialNotificationStore.getInitialNotification(); + final PushNotificationProps notification = InitialNotification.get(); if (notification == null) { return; } diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java index 887f3b08d5ec29dbbb72883bfa85dc54e40d9357..f3b96e482aa585911c7b29b80ef121d69378ebd8 100644 --- a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java @@ -2,20 +2,6 @@ package com.wix.reactnativenotifications.gcm; import android.app.IntentService; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -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 GcmInstanceIdRefreshHandlerService extends IntentService { @@ -23,57 +9,11 @@ public class GcmInstanceIdRefreshHandlerService extends IntentService { super(GcmInstanceIdRefreshHandlerService.class.getSimpleName()); } - public GcmInstanceIdRefreshHandlerService(String name) { - super(name); - } - @Override protected void onHandleIntent(Intent intent) { - final InstanceID instanceId = InstanceID.getInstance(getApplicationContext()); - Log.d(LOGTAG, "GCM is refreshing token... instanceId=" + instanceId.getId()); - - // TODO why is this needed? - GoogleCloudMessaging.getInstance(getApplicationContext()).close(); - - final String registrationToken; - try { - registrationToken = instanceId.getToken(getSenderId(), GoogleCloudMessaging.INSTANCE_ID_SCOPE); - Log.i(LOGTAG, "GCM has a new token: instanceId=" + instanceId.getId() + ", token=" + registrationToken); - } catch (Exception e) { - Log.e(LOGTAG, "FATAL: Failed to fetch a fresh new token, instanceId=" + instanceId.getId(), e); - return; - } - - notifyTokenEvent(registrationToken); - } - - 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 = getPackageManager().getApplicationInfo(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 notifyTokenEvent(String registrationToken) { - final ReactInstanceManager instanceManager = ((ReactApplication) getApplication()).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, registrationToken); + IGcmToken gcmToken = GcmToken.get(this); + if (gcmToken != null) { + gcmToken.onNewTokenReady(); } } } diff --git a/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java new file mode 100644 index 0000000000000000000000000000000000000000..402c546f00ade474f5653c5432049489e9b7b952 --- /dev/null +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java @@ -0,0 +1,132 @@ +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; + + public 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/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java new file mode 100644 index 0000000000000000000000000000000000000000..f324a591f64f0324be8132d41243c18b7b12f60f --- /dev/null +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java @@ -0,0 +1,21 @@ +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/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..36f59f71cf2e4682d409c1fd21ac07a333d66a11 --- /dev/null +++ b/example/android/reactnativenotification/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java @@ -0,0 +1,7 @@ +package com.wix.reactnativenotifications.gcm; + +import android.content.Context; + +public interface INotificationsGcmApplication { + IGcmToken getGcmToken(Context context); +}