mirror of
https://github.com/zhigang1992/react-native-notifications.git
synced 2026-06-12 17:18:11 +08:00
Add java-code tests
This commit is contained in:
@@ -16,13 +16,18 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Google's GCM.
|
||||
compile "com.google.android.gms:play-services-gcm:9.4.0"
|
||||
compile 'com.google.android.gms:play-services-gcm:9.4.0'
|
||||
|
||||
compile 'com.facebook.react:react-native:+'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:2.+'
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class AppLaunchHelper {
|
||||
|
||||
private static final String LAUNCH_FLAG_KEY_NAME = "launchedFromNotification";
|
||||
|
||||
public static Intent getLaunchIntent(Context appContext) {
|
||||
public Intent getLaunchIntent(Context appContext) {
|
||||
try {
|
||||
// The desired behavior upon notification opening is as follows:
|
||||
// - If app is in foreground (and possibly has several activities in stack), simply keep it as-is in foreground.
|
||||
@@ -32,14 +32,14 @@ public class AppLaunchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLaunchIntentsActivity(Activity activity) {
|
||||
public boolean isLaunchIntentsActivity(Activity activity) {
|
||||
final Intent helperIntent = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
|
||||
final String activityName = activity.getComponentName().getClassName();
|
||||
final String launchIntentActivityName = helperIntent.getComponent().getClassName();
|
||||
return activityName.equals(launchIntentActivityName);
|
||||
}
|
||||
|
||||
public static boolean isLaunchIntent(Intent intent) {
|
||||
public boolean isLaunchIntent(Intent intent) {
|
||||
return intent.getBooleanExtra(LAUNCH_FLAG_KEY_NAME, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.util.Log;
|
||||
|
||||
import com.wix.reactnativenotifications.core.notification.IPushNotification;
|
||||
import com.wix.reactnativenotifications.core.notification.PushNotification;
|
||||
import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer;
|
||||
import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer;
|
||||
|
||||
public class ProxyService extends IntentService {
|
||||
|
||||
@@ -24,5 +26,8 @@ public class ProxyService extends IntentService {
|
||||
if (pushNotification != null) {
|
||||
pushNotification.onOpened();
|
||||
}
|
||||
|
||||
final IPushNotificationsDrawer pushNotificationDrawer = PushNotificationsDrawer.get(this);
|
||||
pushNotificationDrawer.onNotificationOpened();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.wix.reactnativenotifications.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
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 ReactContextAdapter {
|
||||
public ReactContext getRunningReactContext(Context context) {
|
||||
final ReactNativeHost rnHost = ((ReactApplication) context.getApplicationContext()).getReactNativeHost();
|
||||
if (!rnHost.hasInstance()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ReactInstanceManager instanceManager = rnHost.getReactInstanceManager();
|
||||
final ReactContext reactContext = instanceManager.getCurrentReactContext();
|
||||
if (reactContext == null || !reactContext.hasActiveCatalystInstance()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reactContext;
|
||||
}
|
||||
|
||||
public void sendEventToJS(String eventName, Bundle data, Context context) {
|
||||
final ReactContext reactContext = getRunningReactContext(context);
|
||||
if (reactContext != null) {
|
||||
sendEventToJS(eventName, data, reactContext);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendEventToJS(String eventName, WritableMap data, Context context) {
|
||||
final ReactContext reactContext = getRunningReactContext(context);
|
||||
if (reactContext != null) {
|
||||
sendEventToJS(eventName, data, reactContext);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendEventToJS(String eventName, Bundle data, ReactContext reactContext) {
|
||||
sendEventToJS(eventName, Arguments.fromBundle(data), reactContext);
|
||||
}
|
||||
|
||||
public void sendEventToJS(String eventName, WritableMap data, ReactContext reactContext) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@ 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);
|
||||
IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.wix.reactnativenotifications.core.AppLifecycleFacade.AppVisibilityLis
|
||||
import com.wix.reactnativenotifications.core.InitialNotification;
|
||||
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
import com.wix.reactnativenotifications.core.ProxyService;
|
||||
import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer;
|
||||
import com.wix.reactnativenotifications.core.ReactContextAdapter;
|
||||
|
||||
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME;
|
||||
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
|
||||
@@ -29,6 +29,8 @@ public class PushNotification implements IPushNotification {
|
||||
|
||||
final protected Context mContext;
|
||||
final protected AppLifecycleFacade mAppLifecycleFacade;
|
||||
final protected AppLaunchHelper mAppLaunchHelper;
|
||||
final protected ReactContextAdapter mReactContextAdapter;
|
||||
final protected PushNotificationProps mNotificationProps;
|
||||
final protected AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() {
|
||||
@Override
|
||||
@@ -42,18 +44,24 @@ public class PushNotification implements IPushNotification {
|
||||
}
|
||||
};
|
||||
|
||||
protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade) {
|
||||
mContext = context;
|
||||
mAppLifecycleFacade = appLifecycleFacade;
|
||||
mNotificationProps = createProps(bundle);
|
||||
public static IPushNotification get(Context context, Bundle bundle, AppLifecycleFacade facade) {
|
||||
return PushNotification.get(context, bundle, facade, new AppLaunchHelper());
|
||||
}
|
||||
|
||||
public static IPushNotification get(Context context, Bundle bundle, AppLifecycleFacade facade) {
|
||||
public static IPushNotification get(Context context, Bundle bundle, AppLifecycleFacade facade, AppLaunchHelper appLaunchHelper) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
if (appContext instanceof INotificationsApplication) {
|
||||
return ((INotificationsApplication) appContext).getPushNotification(context, bundle, facade);
|
||||
return ((INotificationsApplication) appContext).getPushNotification(context, bundle, facade, appLaunchHelper);
|
||||
}
|
||||
return new PushNotification(context, bundle, facade);
|
||||
return new PushNotification(context, bundle, facade, appLaunchHelper, new ReactContextAdapter());
|
||||
}
|
||||
|
||||
protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, ReactContextAdapter reactContextAdapter) {
|
||||
mContext = context;
|
||||
mAppLifecycleFacade = appLifecycleFacade;
|
||||
mAppLaunchHelper = appLaunchHelper;
|
||||
mReactContextAdapter = reactContextAdapter;
|
||||
mNotificationProps = createProps(bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +73,6 @@ public class PushNotification implements IPushNotification {
|
||||
@Override
|
||||
public void onOpened() {
|
||||
digestNotification();
|
||||
PushNotificationsDrawer.get(mContext).onNotificationOpened();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +98,7 @@ public class PushNotification implements IPushNotification {
|
||||
return;
|
||||
}
|
||||
|
||||
final ReactContext reactContext = getRunningReactContext();
|
||||
final ReactContext reactContext = mReactContextAdapter.getRunningReactContext(mContext);
|
||||
if (reactContext.getCurrentActivity() == null) {
|
||||
setAsInitialNotification();
|
||||
}
|
||||
@@ -161,46 +168,16 @@ public class PushNotification implements IPushNotification {
|
||||
return (int) System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected ReactContext getRunningReactContext() {
|
||||
final ReactNativeHost rnHost = ((ReactApplication) mContext.getApplicationContext()).getReactNativeHost();
|
||||
if (!rnHost.hasInstance()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ReactInstanceManager instanceManager = rnHost.getReactInstanceManager();
|
||||
final ReactContext reactContext = instanceManager.getCurrentReactContext();
|
||||
if (reactContext == null || !reactContext.hasActiveCatalystInstance()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reactContext;
|
||||
}
|
||||
|
||||
private void notifyReceivedToJS() {
|
||||
notifyJS(NOTIFICATION_RECEIVED_EVENT_NAME, null);
|
||||
mReactContextAdapter.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mContext);
|
||||
}
|
||||
|
||||
private void notifyOpenedToJS() {
|
||||
notifyOpenedToJS(null);
|
||||
}
|
||||
|
||||
private void notifyOpenedToJS(ReactContext reactContext) {
|
||||
notifyJS(NOTIFICATION_OPENED_EVENT_NAME, reactContext);
|
||||
}
|
||||
|
||||
private void notifyJS(String eventName, ReactContext reactContext) {
|
||||
if (reactContext == null) {
|
||||
reactContext = getRunningReactContext();
|
||||
}
|
||||
|
||||
if (reactContext != null) {
|
||||
final WritableMap notificationAsMap = Arguments.fromBundle(mNotificationProps.asBundle());
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, notificationAsMap);
|
||||
}
|
||||
mReactContextAdapter.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mContext);
|
||||
}
|
||||
|
||||
protected void launchOrResumeApp() {
|
||||
final Intent intent = AppLaunchHelper.getLaunchIntent(mContext);
|
||||
final Intent intent = mAppLaunchHelper.getLaunchIntent(mContext);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,18 +10,24 @@ import com.wix.reactnativenotifications.core.InitialNotification;
|
||||
public class PushNotificationsDrawer implements IPushNotificationsDrawer {
|
||||
|
||||
final protected Context mContext;
|
||||
|
||||
public PushNotificationsDrawer(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
final protected AppLaunchHelper mAppLaunchHelper;
|
||||
|
||||
public static IPushNotificationsDrawer get(Context context) {
|
||||
return PushNotificationsDrawer.get(context, new AppLaunchHelper());
|
||||
}
|
||||
|
||||
public static IPushNotificationsDrawer get(Context context, AppLaunchHelper appLaunchHelper) {
|
||||
final Context appContext = context.getApplicationContext();
|
||||
if (appContext instanceof INotificationsDrawerApplication) {
|
||||
return ((INotificationsDrawerApplication) appContext).getPushNotificationsDrawer(context);
|
||||
}
|
||||
|
||||
return new PushNotificationsDrawer(context);
|
||||
return new PushNotificationsDrawer(context, appLaunchHelper);
|
||||
}
|
||||
|
||||
protected PushNotificationsDrawer(Context context, AppLaunchHelper appLaunchHelper) {
|
||||
mContext = context;
|
||||
mAppLaunchHelper = appLaunchHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,8 +42,8 @@ public class PushNotificationsDrawer implements IPushNotificationsDrawer {
|
||||
|
||||
@Override
|
||||
public void onNewActivity(Activity activity) {
|
||||
if (AppLaunchHelper.isLaunchIntentsActivity(activity) &&
|
||||
!AppLaunchHelper.isLaunchIntent(activity.getIntent())) {
|
||||
if (mAppLaunchHelper.isLaunchIntentsActivity(activity) &&
|
||||
!mAppLaunchHelper.isLaunchIntent(activity.getIntent())) {
|
||||
InitialNotification.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.wix.reactnativenotifications.core.notification;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade.AppVisibilityListener;
|
||||
import com.wix.reactnativenotifications.core.ReactContextAdapter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class PushNotificationTest {
|
||||
|
||||
@Mock private ReactContext mReactContext;
|
||||
@Mock private Context mContext;
|
||||
|
||||
@Mock private Bundle mDefaultBundle;
|
||||
@Mock private Intent mLaunchIntent;
|
||||
@Mock private AppLifecycleFacade mAppLifecycleFacade;
|
||||
@Mock private AppLaunchHelper mAppLaunchHelper;
|
||||
@Mock private ReactContextAdapter mReactContextAdapter;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
when(mDefaultBundle.getString(eq("title"))).thenReturn("Notification-title");
|
||||
when(mDefaultBundle.getString(eq("body"))).thenReturn("Notification-body");
|
||||
when(mDefaultBundle.clone()).thenReturn(mDefaultBundle);
|
||||
|
||||
when(mAppLaunchHelper.getLaunchIntent(eq(mContext))).thenReturn(mLaunchIntent);
|
||||
when(mReactContextAdapter.getRunningReactContext(mContext)).thenReturn(mReactContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onOpened_noReactContext_launchApp() throws Exception {
|
||||
when(mAppLifecycleFacade.isReactInitialized()).thenReturn(false);
|
||||
|
||||
final PushNotification uut = createUUT();
|
||||
uut.onOpened();
|
||||
|
||||
verify(mContext).startActivity(eq(mLaunchIntent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onOpened_appInvisible_resumeAppWaitForVisibility() throws Exception {
|
||||
when(mAppLifecycleFacade.isReactInitialized()).thenReturn(true);
|
||||
when(mAppLifecycleFacade.isAppVisible()).thenReturn(false);
|
||||
|
||||
final PushNotification uut = createUUT();
|
||||
uut.onOpened();
|
||||
|
||||
verify(mContext).startActivity(any(Intent.class));
|
||||
verify(mAppLifecycleFacade).addVisibilityListener(any(AppVisibilityListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onOpened_appGoesVisible_resumeAppAndNotifyJs() throws Exception {
|
||||
|
||||
// Arrange
|
||||
|
||||
when(mAppLifecycleFacade.isReactInitialized()).thenReturn(true);
|
||||
when(mAppLifecycleFacade.isAppVisible()).thenReturn(false);
|
||||
|
||||
// Act
|
||||
|
||||
final PushNotification uut = createUUT();
|
||||
uut.onOpened();
|
||||
|
||||
// Hijack and invoke visibility listener
|
||||
ArgumentCaptor<AppVisibilityListener> listenerCaptor = ArgumentCaptor.forClass(AppVisibilityListener.class);
|
||||
verify(mAppLifecycleFacade).addVisibilityListener(listenerCaptor.capture());
|
||||
AppVisibilityListener listener = listenerCaptor.getValue();
|
||||
listener.onAppVisible();
|
||||
|
||||
// Assert
|
||||
|
||||
verify(mReactContextAdapter).sendEventToJS(eq("notificationOpened"), eq(mDefaultBundle), eq(mContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onOpened_appVisible_notifyJS() throws Exception {
|
||||
when(mAppLifecycleFacade.isReactInitialized()).thenReturn(true);
|
||||
when(mAppLifecycleFacade.isAppVisible()).thenReturn(true);
|
||||
|
||||
final PushNotification uut = createUUT();
|
||||
uut.onOpened();
|
||||
|
||||
verify(mContext, never()).startActivity(any(Intent.class));
|
||||
verify(mReactContextAdapter).sendEventToJS(eq("notificationOpened"), eq(mDefaultBundle), eq(mContext));
|
||||
}
|
||||
|
||||
protected PushNotification createUUT() {
|
||||
return new PushNotification(mContext, mDefaultBundle, mAppLifecycleFacade, mAppLaunchHelper, mReactContextAdapter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user