Browse Source

android: splash, removed singleton, dependency injection, fixed e2e

Daniel Zlotin 7 years ago
parent
commit
93f49300c2

+ 11
- 6
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java View File

@@ -11,10 +11,10 @@ import com.facebook.react.ReactPackage;
11 11
 import com.facebook.react.bridge.ReactContext;
12 12
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
13 13
 import com.facebook.react.shell.MainReactPackage;
14
-import com.reactnativenavigation.R;
15 14
 import com.reactnativenavigation.react.NavigationEventEmitter;
16 15
 import com.reactnativenavigation.react.NavigationPackage;
17 16
 import com.reactnativenavigation.react.ReactDevPermission;
17
+import com.reactnativenavigation.views.NavigationSplashView;
18 18
 
19 19
 import java.util.Arrays;
20 20
 import java.util.List;
@@ -26,7 +26,8 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
26 26
     @Override
27 27
     public void onCreate(Bundle savedInstanceState) {
28 28
         super.onCreate(savedInstanceState);
29
-        setContentView(R.layout.splash);
29
+        setContentView(new NavigationSplashView(this));
30
+
30 31
         host = new ReactNativeHost(getApplication()) {
31 32
             @Override
32 33
             protected boolean getUseDeveloperSupport() {
@@ -37,7 +38,7 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
37 38
             protected List<ReactPackage> getPackages() {
38 39
                 return Arrays.asList(
39 40
                         new MainReactPackage(),
40
-                        new NavigationPackage()
41
+                        new NavigationPackage(NavigationActivity.this)
41 42
                 );
42 43
             }
43 44
         };
@@ -69,20 +70,24 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
69 70
             ReactDevPermission.askPermission(this);
70 71
         } else {
71 72
             host.getReactInstanceManager().createReactContextInBackground();
73
+            host.getReactInstanceManager().onHostResume(this, this);
72 74
         }
73
-        host.getReactInstanceManager().onHostResume(this, this);
74 75
     }
75 76
 
76 77
     @Override
77 78
     protected void onPause() {
78 79
         super.onPause();
79
-        host.getReactInstanceManager().onHostPause(this);
80
+        if (host.getReactInstanceManager().hasStartedCreatingInitialContext()) {
81
+            host.getReactInstanceManager().onHostPause(this);
82
+        }
80 83
     }
81 84
 
82 85
     @Override
83 86
     protected void onDestroy() {
84 87
         super.onDestroy();
85
-        host.getReactInstanceManager().onHostDestroy(this);
88
+        if (host.getReactInstanceManager().hasStartedCreatingInitialContext()) {
89
+            host.getReactInstanceManager().onHostDestroy(this);
90
+        }
86 91
     }
87 92
 
88 93
     @Override

+ 8
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationApplication.java View File

@@ -1,14 +1,22 @@
1 1
 package com.reactnativenavigation.controllers;
2 2
 
3 3
 import android.app.Application;
4
+import android.os.Handler;
5
+import android.os.Looper;
4 6
 
5 7
 public abstract class NavigationApplication extends Application {
6 8
     public static NavigationApplication instance;
9
+    private Handler handler;
7 10
 
8 11
     @Override
9 12
     public void onCreate() {
10 13
         super.onCreate();
11 14
         instance = this;
15
+        handler = new Handler(Looper.getMainLooper());
16
+    }
17
+
18
+    public void runOnUiThread(Runnable runnable) {
19
+        handler.post(runnable);
12 20
     }
13 21
 
14 22
     public abstract boolean isDebug();

+ 28
- 20
android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

@@ -11,6 +11,7 @@ import com.facebook.react.bridge.ReadableArray;
11 11
 import com.facebook.react.bridge.ReadableMap;
12 12
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
13 13
 import com.reactnativenavigation.controllers.NavigationActivity;
14
+import com.reactnativenavigation.controllers.NavigationApplication;
14 15
 import com.reactnativenavigation.layout.LayoutFactory;
15 16
 import com.reactnativenavigation.layout.LayoutNode;
16 17
 import com.reactnativenavigation.layout.StackLayout;
@@ -21,10 +22,12 @@ import java.util.HashMap;
21 22
 import java.util.Map;
22 23
 
23 24
 public class NavigationModule extends ReactContextBaseJavaModule {
24
-    public static final String NAME = "RNNBridgeModule";
25
+    private static final String NAME = "RNNBridgeModule";
26
+    private final NavigationActivity activity;
25 27
 
26
-    public NavigationModule(ReactApplicationContext reactContext) {
27
-        super(reactContext);
28
+    public NavigationModule(ReactApplicationContext context, NavigationActivity activity) {
29
+        super(context);
30
+        this.activity = activity;
28 31
     }
29 32
 
30 33
     @Override
@@ -34,30 +37,35 @@ public class NavigationModule extends ReactContextBaseJavaModule {
34 37
 
35 38
     @ReactMethod
36 39
     public void setRoot(final ReadableMap layoutTree) {
37
-        getActivity().runOnUiThread(new Runnable() {
40
+        NavigationApplication.instance.runOnUiThread(new Runnable() {
38 41
             @Override
39 42
             public void run() {
40
-                LayoutFactory factory =
41
-                        new LayoutFactory(getActivity(), new LayoutFactory.ReactRootViewCreator() {
42
-                            @Override
43
-                            public View create(String id, String name) {
44
-                                ReactRootView rootView = new ReactRootView(getActivity());
45
-                                Bundle opts = new Bundle();
46
-                                opts.putString("id", id);
47
-                                rootView.startReactApplication(getActivity().getHost().getReactInstanceManager(), name, opts);
48
-                                return rootView;
49
-                            }
50
-                        }, new BottomTabsCreator());
51
-
52
-                final LayoutNode layoutTreeRoot = readableMapToLayoutNode(layoutTree);
53
-                final View rootView = factory.create(layoutTreeRoot);
54
-                getActivity().setContentView(rootView);
43
+                getActivity().runOnUiThread(new Runnable() {
44
+                    @Override
45
+                    public void run() {
46
+                        LayoutFactory factory =
47
+                                new LayoutFactory(getActivity(), new LayoutFactory.ReactRootViewCreator() {
48
+                                    @Override
49
+                                    public View create(String id, String name) {
50
+                                        ReactRootView rootView = new ReactRootView(getActivity());
51
+                                        Bundle opts = new Bundle();
52
+                                        opts.putString("id", id);
53
+                                        rootView.startReactApplication(getActivity().getHost().getReactInstanceManager(), name, opts);
54
+                                        return rootView;
55
+                                    }
56
+                                }, new BottomTabsCreator());
57
+
58
+                        final LayoutNode layoutTreeRoot = readableMapToLayoutNode(layoutTree);
59
+                        final View rootView = factory.create(layoutTreeRoot);
60
+                        getActivity().setContentView(rootView);
61
+                    }
62
+                });
55 63
             }
56 64
         });
57 65
     }
58 66
 
59 67
     private NavigationActivity getActivity() {
60
-        return (NavigationActivity) getCurrentActivity();
68
+        return activity;
61 69
     }
62 70
 
63 71
     @ReactMethod

+ 8
- 1
android/app/src/main/java/com/reactnativenavigation/react/NavigationPackage.java View File

@@ -5,6 +5,7 @@ import com.facebook.react.bridge.JavaScriptModule;
5 5
 import com.facebook.react.bridge.NativeModule;
6 6
 import com.facebook.react.bridge.ReactApplicationContext;
7 7
 import com.facebook.react.uimanager.ViewManager;
8
+import com.reactnativenavigation.controllers.NavigationActivity;
8 9
 
9 10
 import java.util.Arrays;
10 11
 import java.util.Collections;
@@ -12,10 +13,16 @@ import java.util.List;
12 13
 
13 14
 public class NavigationPackage implements ReactPackage {
14 15
 
16
+    private final NavigationActivity activity;
17
+
18
+    public NavigationPackage(NavigationActivity activity) {
19
+        this.activity = activity;
20
+    }
21
+
15 22
     @Override
16 23
     public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
17 24
         return Arrays.<NativeModule>asList(
18
-                new NavigationModule(reactContext)
25
+                new NavigationModule(reactContext, activity)
19 26
         );
20 27
     }
21 28
 

+ 4
- 1
android/app/src/main/java/com/reactnativenavigation/react/ReactDevPermission.java View File

@@ -13,8 +13,10 @@ import com.reactnativenavigation.controllers.NavigationApplication;
13 13
 
14 14
 public class ReactDevPermission {
15 15
 
16
+    private static boolean hasRequestedPermissions = false;
17
+
16 18
     public static boolean shouldAskPermission() {
17
-        return NavigationApplication.instance.isDebug() &&
19
+        return !hasRequestedPermissions && NavigationApplication.instance.isDebug() &&
18 20
                 Build.VERSION.SDK_INT >= 23 &&
19 21
                 !Settings.canDrawOverlays(NavigationApplication.instance);
20 22
     }
@@ -22,6 +24,7 @@ public class ReactDevPermission {
22 24
     @TargetApi(23)
23 25
     public static void askPermission(Context context) {
24 26
         if (shouldAskPermission()) {
27
+            hasRequestedPermissions = true;
25 28
             Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
26 29
             context.startActivity(serviceIntent);
27 30
             String msg = "Overlay permissions needs to be granted in order for react native apps to run in dev mode";

+ 16
- 0
android/app/src/main/java/com/reactnativenavigation/views/NavigationSplashView.java View File

@@ -0,0 +1,16 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.widget.FrameLayout;
5
+import android.widget.ImageView;
6
+
7
+import com.reactnativenavigation.R;
8
+
9
+public class NavigationSplashView extends FrameLayout {
10
+    public NavigationSplashView(Context context) {
11
+        super(context);
12
+        ImageView image = new ImageView(context);
13
+        image.setImageResource(R.drawable.logo);
14
+        addView(image);
15
+    }
16
+}

BIN
android/app/src/main/res/drawable-xxhdpi/logo.png View File


+ 1
- 0
playground/android/app/build.gradle View File

@@ -24,6 +24,7 @@ dependencies {
24 24
     compile project(':react-native-navigation')
25 25
 
26 26
     // native e2e
27
+    androidTestCompile 'org.assertj:assertj-core:2.5.0'
27 28
     androidTestCompile 'com.android.support:support-annotations:25.1.1'
28 29
     androidTestCompile 'com.android.support.test:runner:0.5'
29 30
     androidTestCompile 'com.android.support.test:rules:0.5'

+ 9
- 5
playground/android/app/src/androidTest/java/com/reactnativenavigation/playground/ApplicationLifecycleTest.java View File

@@ -12,6 +12,7 @@ import android.support.test.uiautomator.UiSelector;
12 12
 
13 13
 import com.facebook.react.ReactNativeHost;
14 14
 import com.reactnativenavigation.controllers.NavigationActivity;
15
+import com.reactnativenavigation.views.NavigationSplashView;
15 16
 
16 17
 import org.junit.Rule;
17 18
 import org.junit.Test;
@@ -22,6 +23,7 @@ import static android.support.test.espresso.Espresso.onView;
22 23
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
23 24
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
24 25
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
26
+import static org.assertj.core.api.Java6Assertions.assertThat;
25 27
 import static org.junit.Assume.assumeFalse;
26 28
 import static org.junit.Assume.assumeTrue;
27 29
 
@@ -78,7 +80,7 @@ public class ApplicationLifecycleTest {
78 80
         rule.launchActivity(null);
79 81
     }
80 82
 
81
-    private void acceptPermissionIfNeeded() throws Exception {
83
+    private void acceptOverlayPermissionIfNeeded() throws Exception {
82 84
         if (Settings.canDrawOverlays(getInstrumentation().getContext())) {
83 85
             return;
84 86
         }
@@ -88,12 +90,11 @@ public class ApplicationLifecycleTest {
88 90
         UiDevice.getInstance(getInstrumentation()).pressBack();
89 91
     }
90 92
 
91
-
92 93
     @Test
93
-    public void startApp_HandleOverlayPermissions_LoadsBridge_ThenShowsWelcomeScreen() throws Exception {
94
+    public void startApp_RequiredOverlayPermissions_LoadsBridge_ThenShowsWelcomeScreen() throws Exception {
94 95
         assumeFalse(Settings.canDrawOverlays(getInstrumentation().getContext()));
95 96
         launchActivity();
96
-        acceptPermissionIfNeeded();
97
+        acceptOverlayPermissionIfNeeded();
97 98
         onView(withText("React Native Navigation!")).check(matches(isDisplayed()));
98 99
     }
99 100
 
@@ -105,6 +106,9 @@ public class ApplicationLifecycleTest {
105 106
     }
106 107
 
107 108
     @Test
108
-    public void () {
109
+    public void showsSplashOnStartup() throws Exception {
110
+        launchActivity();
111
+        assertThat(rule.getActivity().getContentView()).isNotNull().isInstanceOf(NavigationSplashView.class);
112
+        acceptOverlayPermissionIfNeeded();
109 113
     }
110 114
 }