瀏覽代碼

Support headlessJs (#1444)

Currently startApp is called from global context which results in the
app being launched when headlessJs tasks run in the background.
In order to support this use case, while not breaking existing users,
this commit adds two mechanisms which should help users detrmine if
the native Activity is running and startApp can be called safely.

1. RNN.appLaunched event is emitted is SplashActivity starts and react
   context has already been created. This is the use case of openeing
   the app while headless js task was started or has just finished and
   react context is in a "pasued" state.

2. Navigation.isAppLaunched() convenience method has been added.
   It returns a promise which when resolved, indicates if the app
   is launched and we should show the ui or not.

Usage

import {Navigation, NativeEventsReceiver} from 'react-native-navigation';

Promise.resolve(Navigation.isAppLaunched())
  .then(appLaunched => {
    if (appLaunched) {
      startApp();
    } else {
      new NativeEventsReceiver().appLaunched(startApp);
    }
  });

function startApp() {
  Navigation.startTabBasedApp({ ... });
}
Guy Carmeli 7 年之前
父節點
當前提交
8acfe41f04

+ 7
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/EventEmitter.java 查看文件

@@ -52,4 +52,11 @@ public class EventEmitter {
52 52
         }
53 53
         reactGateway.getReactEventEmitter().sendEvent(eventId, Arguments.createMap());
54 54
     }
55
+
56
+    public void sendAppLaunchedEvent() {
57
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
58
+            return;
59
+        }
60
+        reactGateway.getReactEventEmitter().sendEvent("RNN.appLaunched", Arguments.createMap());
61
+    }
55 62
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java 查看文件

@@ -258,4 +258,9 @@ public class NavigationReactModule extends ReactContextBaseJavaModule {
258 258
     public void getOrientation(Promise promise) {
259 259
         NavigationCommandsHandler.getOrientation(promise);
260 260
     }
261
+
262
+    @ReactMethod
263
+    public void isAppLaunched(Promise promise) {
264
+        NavigationCommandsHandler.isAppLaunched(promise);
265
+    }
261 266
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java 查看文件

@@ -535,4 +535,9 @@ public class NavigationCommandsHandler {
535 535
         }
536 536
         promise.resolve(OrientationHelper.getOrientation(currentActivity));
537 537
     }
538
+
539
+    public static void isAppLaunched(Promise promise) {
540
+        final boolean isAppLaunched = SplashActivity.isResumed || NavigationActivity.currentActivity != null;
541
+        promise.resolve(isAppLaunched);
542
+    }
538 543
 }

+ 10
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/SplashActivity.java 查看文件

@@ -11,6 +11,7 @@ import com.reactnativenavigation.NavigationApplication;
11 11
 import com.reactnativenavigation.react.ReactDevPermission;
12 12
 
13 13
 public abstract class SplashActivity extends AppCompatActivity {
14
+    public static boolean isResumed = false;
14 15
 
15 16
     @Override
16 17
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -22,8 +23,11 @@ public abstract class SplashActivity extends AppCompatActivity {
22 23
     @Override
23 24
     protected void onResume() {
24 25
         super.onResume();
26
+        isResumed = true;
25 27
 
26 28
         if (NavigationApplication.instance.getReactGateway().hasStartedCreatingContext()) {
29
+            NavigationApplication.instance.getEventEmitter().sendAppLaunchedEvent();
30
+            overridePendingTransition(0, 0);
27 31
             finish();
28 32
             return;
29 33
         }
@@ -42,6 +46,12 @@ public abstract class SplashActivity extends AppCompatActivity {
42 46
         NavigationApplication.instance.startReactContextOnceInBackgroundAndExecuteJS();
43 47
     }
44 48
 
49
+    @Override
50
+    protected void onPause() {
51
+        super.onPause();
52
+        isResumed = false;
53
+    }
54
+
45 55
     private void setSplashLayout() {
46 56
         final int splashLayout = getSplashLayout();
47 57
         if (splashLayout > 0) {

+ 14
- 0
src/NativeEventsReceiver.js 查看文件

@@ -0,0 +1,14 @@
1
+import {
2
+  NativeAppEventEmitter,
3
+  DeviceEventEmitter,
4
+  Platform
5
+} from 'react-native';
6
+export default class NativeEventsReceiver {
7
+  constructor() {
8
+    this.emitter = Platform.OS === 'android' ? DeviceEventEmitter : NativeAppEventEmitter;
9
+  }
10
+
11
+  appLaunched(callback) {
12
+    this.emitter.addListener('RNN.appLaunched', callback);
13
+  }
14
+}

+ 6
- 1
src/Navigation.js 查看文件

@@ -162,6 +162,10 @@ function handleDeepLink(params = {}) {
162 162
   }
163 163
 }
164 164
 
165
+async function isAppLaunched() {
166
+  return await platformSpecific.isAppLaunched();
167
+}
168
+
165 169
 export default {
166 170
   getRegisteredScreen,
167 171
   registerComponent,
@@ -177,5 +181,6 @@ export default {
177 181
   startSingleScreenApp: startSingleScreenApp,
178 182
   setEventHandler: setEventHandler,
179 183
   clearEventHandler: clearEventHandler,
180
-  handleDeepLink: handleDeepLink
184
+  handleDeepLink: handleDeepLink,
185
+  isAppLaunched: isAppLaunched
181 186
 };

+ 3
- 2
src/deprecated/indexDeprecated.android.js 查看文件

@@ -1,8 +1,9 @@
1 1
 import Navigation from './../Navigation';
2 2
 import SharedElementTransition from './../views/sharedElementTransition';
3
+import NativeEventsReceiver from './../NativeEventsReceiver';
3 4
 
4 5
 module.exports = {
5 6
   Navigation,
6
-  SharedElementTransition
7
+  SharedElementTransition,
8
+  NativeEventsReceiver
7 9
 };
8
-

+ 3
- 1
src/deprecated/indexDeprecated.ios.js 查看文件

@@ -1,9 +1,11 @@
1 1
 import Navigation from './../Navigation';
2 2
 import {NavigationToolBarIOS} from './controllers';
3 3
 import SharedElementTransition from '../views/sharedElementTransition';
4
+import NativeEventsReceiver from './../NativeEventsReceiver';
4 5
 
5 6
 module.exports = {
6 7
   Navigation,
7 8
   NavigationToolBarIOS,
8
-  SharedElementTransition
9
+  SharedElementTransition,
10
+  NativeEventsReceiver
9 11
 };

+ 6
- 1
src/deprecated/platformSpecificDeprecated.android.js 查看文件

@@ -675,6 +675,10 @@ function dismissContextualMenu() {
675 675
   newPlatformSpecific.dismissContextualMenu();
676 676
 }
677 677
 
678
+async function isAppLaunched() {
679
+  return await newPlatformSpecific.isAppLaunched();
680
+}
681
+
678 682
 export default {
679 683
   startTabBasedApp,
680 684
   startSingleScreenApp,
@@ -704,5 +708,6 @@ export default {
704 708
   showSnackbar,
705 709
   dismissSnackbar,
706 710
   showContextualMenu,
707
-  dismissContextualMenu
711
+  dismissContextualMenu,
712
+  isAppLaunched
708 713
 };

+ 6
- 1
src/platformSpecific.android.js 查看文件

@@ -177,6 +177,10 @@ function setScreenStyle(screenInstanceId, style) {
177 177
   NativeReactModule.setScreenStyle(screenInstanceId, style);
178 178
 }
179 179
 
180
+async function isAppLaunched() {
181
+  return await NativeReactModule.isAppLaunched();
182
+}
183
+
180 184
 module.exports = {
181 185
   startApp,
182 186
   push,
@@ -210,5 +214,6 @@ module.exports = {
210 214
   dismissSnackbar,
211 215
   showContextualMenu,
212 216
   dismissContextualMenu,
213
-  setScreenStyle
217
+  setScreenStyle,
218
+  isAppLaunched
214 219
 };