Daniel Zlotin před 7 roky
rodič
revize
89c165a6d0

+ 3
- 0
lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java Zobrazit soubor

@@ -5,6 +5,7 @@ import android.app.Application;
5 5
 import com.facebook.react.ReactApplication;
6 6
 import com.facebook.react.ReactNativeHost;
7 7
 import com.reactnativenavigation.controllers.ActivityLifecycleDelegate;
8
+import com.reactnativenavigation.controllers.CommandsHandler;
8 9
 import com.reactnativenavigation.react.DevPermissionRequestImpl;
9 10
 import com.reactnativenavigation.react.NavigationReactNativeHost;
10 11
 
@@ -14,6 +15,7 @@ public abstract class NavigationApplication extends Application implements React
14 15
 	public static class Config {
15 16
 		public ReactNativeHost reactNativeHost;
16 17
 		public ActivityLifecycleDelegate activityLifecycleDelegate;
18
+		public CommandsHandler commandsHandler;
17 19
 	}
18 20
 
19 21
 	private Config config;
@@ -45,6 +47,7 @@ public abstract class NavigationApplication extends Application implements React
45 47
 		Config config = new Config();
46 48
 		config.reactNativeHost = new NavigationReactNativeHost(this, isDebug());
47 49
 		config.activityLifecycleDelegate = new ActivityLifecycleDelegate(config.reactNativeHost.getReactInstanceManager(), new DevPermissionRequestImpl());
50
+		config.commandsHandler = new CommandsHandler();
48 51
 		return config;
49 52
 	}
50 53
 }

+ 95
- 0
lib/android/app/src/main/java/com/reactnativenavigation/controllers/CommandsHandler.java Zobrazit soubor

@@ -1,4 +1,99 @@
1 1
 package com.reactnativenavigation.controllers;
2 2
 
3
+import android.os.Bundle;
4
+import android.view.View;
5
+
6
+import com.facebook.react.ReactRootView;
7
+import com.facebook.react.bridge.ReadableArray;
8
+import com.facebook.react.bridge.ReadableMap;
9
+import com.facebook.react.bridge.ReadableMapKeySetIterator;
10
+import com.reactnativenavigation.NavigationActivity;
11
+import com.reactnativenavigation.NavigationApplication;
12
+import com.reactnativenavigation.layout.LayoutFactory;
13
+import com.reactnativenavigation.layout.StackLayout;
14
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
15
+import com.reactnativenavigation.layout.parse.LayoutNode;
16
+import com.reactnativenavigation.utils.UiThread;
17
+
18
+import java.util.ArrayList;
19
+import java.util.HashMap;
20
+import java.util.List;
21
+import java.util.Map;
22
+
3 23
 public class CommandsHandler {
24
+
25
+	public void setRoot(final NavigationActivity activity, final ReadableMap layoutTree) {
26
+		LayoutFactory factory =
27
+				new LayoutFactory(activity, new LayoutFactory.ReactRootViewCreator() {
28
+					@Override
29
+					public View create(String id, String name) {
30
+						ReactRootView rootView = new ReactRootView(activity);
31
+						Bundle opts = new Bundle();
32
+						opts.putString("id", id);
33
+						rootView.startReactApplication(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager(), name, opts);
34
+						return rootView;
35
+					}
36
+				}, new BottomTabsCreator());
37
+
38
+		final LayoutNode layoutTreeRoot = readableMapToLayoutNode(layoutTree);
39
+		final View rootView = factory.create(layoutTreeRoot);
40
+		activity.setContentView(rootView);
41
+	}
42
+
43
+	public void push(final NavigationActivity activity, String onContainerId, final ReadableMap layout) {
44
+		LayoutFactory factory =
45
+				new LayoutFactory(activity, new LayoutFactory.ReactRootViewCreator() {
46
+					@Override
47
+					public View create(String id, String name) {
48
+						ReactRootView rootView = new ReactRootView(activity);
49
+						Bundle opts = new Bundle();
50
+						opts.putString("id", id);
51
+						rootView.startReactApplication(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager(), name, opts);
52
+						return rootView;
53
+					}
54
+				}, new BottomTabsCreator());
55
+		final LayoutNode layoutNode = readableMapToLayoutNode(layout);
56
+		final View rootView = factory.create(layoutNode);
57
+		((StackLayout) activity.getContentView()).push(rootView);
58
+	}
59
+
60
+	public void pop(final NavigationActivity activity, String onContainerId) {
61
+		UiThread.post(new Runnable() {
62
+			@Override
63
+			public void run() {
64
+				((StackLayout) activity.getContentView()).pop();
65
+			}
66
+		});
67
+	}
68
+
69
+	private LayoutNode readableMapToLayoutNode(ReadableMap readableMap) {
70
+		String id = readableMap.getString("id");
71
+		LayoutNode.Type type = LayoutNode.Type.fromString(readableMap.getString("type"));
72
+		Map<String, Object> data = readableMapToJavaMap(readableMap.getMap("data"));
73
+
74
+		ReadableArray childrenNodes = readableMap.getArray("children");
75
+		List<LayoutNode> children = new ArrayList<>(childrenNodes.size());
76
+		for (int i = 0; i < childrenNodes.size(); i++) {
77
+			ReadableMap child = childrenNodes.getMap(i);
78
+			children.add(readableMapToLayoutNode(child));
79
+		}
80
+
81
+		return new LayoutNode(id, type, data, children);
82
+	}
83
+
84
+	private Map<String, Object> readableMapToJavaMap(ReadableMap readableMap) {
85
+		final Map<String, Object> map = new HashMap<>();
86
+		for (ReadableMapKeySetIterator it = readableMap.keySetIterator(); it.hasNextKey(); ) {
87
+			final String key = it.nextKey();
88
+			switch (readableMap.getType(key)) {
89
+				case String:
90
+					map.put(key, readableMap.getString(key));
91
+					break;
92
+				case Map:
93
+					map.put(key, readableMapToJavaMap(readableMap.getMap(key)));
94
+					break;
95
+			}
96
+		}
97
+		return map;
98
+	}
4 99
 }

+ 12
- 12
lib/android/app/src/main/java/com/reactnativenavigation/layout/Container.java Zobrazit soubor

@@ -8,13 +8,13 @@ import com.reactnativenavigation.NavigationApplication;
8 8
 import com.reactnativenavigation.react.NavigationEventEmitter;
9 9
 
10 10
 public class Container extends FrameLayout {
11
-    private static final String TAG = "Container";
12
-    private String id;
11
+	private static final String TAG = "Container";
12
+	private String id;
13 13
 
14
-    public Container(Context context, LayoutFactory.ReactRootViewCreator reactRootViewCreator, String id, String name) {
14
+	public Container(Context context, LayoutFactory.ReactRootViewCreator reactRootViewCreator, String id, String name) {
15 15
 		super(context);
16
-        this.id = id;
17
-        addView(reactRootViewCreator.create(id, name));
16
+		this.id = id;
17
+		addView(reactRootViewCreator.create(id, name));
18 18
 
19 19
 	}
20 20
 
@@ -26,11 +26,11 @@ public class Container extends FrameLayout {
26 26
 //                .containerStart(id);
27 27
 //    }
28 28
 
29
-    @Override
30
-    protected void onDetachedFromWindow() {
31
-        Log.d(TAG, "onDetachedFromWindow: " + id);
32
-        super.onDetachedFromWindow();
33
-        NavigationEventEmitter.emit(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
34
-                .containerStop(id);
35
-    }
29
+	@Override
30
+	protected void onDetachedFromWindow() {
31
+		Log.d(TAG, "onDetachedFromWindow: " + id);
32
+		super.onDetachedFromWindow();
33
+		NavigationEventEmitter.emit(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
34
+				.containerStop(id);
35
+	}
36 36
 }

+ 18
- 18
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationEventEmitter.java Zobrazit soubor

@@ -26,28 +26,28 @@ public class NavigationEventEmitter {
26 26
 	}
27 27
 
28 28
 	private void emit(String eventName) {
29
-        emit(eventName, Arguments.createMap());
30
-    }
29
+		emit(eventName, Arguments.createMap());
30
+	}
31 31
 
32
-    private void emit(String eventName, WritableMap data) {
33
-        emitter.emit(eventName, data);
34
-    }
32
+	private void emit(String eventName, WritableMap data) {
33
+		emitter.emit(eventName, data);
34
+	}
35 35
 
36
-    private void emit(String eventName, String param) {
37
-        emitter.emit(eventName, param);
38
-    }
36
+	private void emit(String eventName, String param) {
37
+		emitter.emit(eventName, param);
38
+	}
39 39
 
40
-    public void containerStop(String id) {
41
-        WritableMap data = Arguments.createMap();
42
-        data.putString("id", id);
40
+	public void containerStop(String id) {
41
+		WritableMap data = Arguments.createMap();
42
+		data.putString("id", id);
43 43
 //        emit(containerStop, data);
44
-        emit(containerStop, id);
45
-    }
44
+		emit(containerStop, id);
45
+	}
46 46
 
47
-    public void containerStart(String id) {
48
-        WritableMap data = Arguments.createMap();
49
-        data.putString("id", id);
47
+	public void containerStart(String id) {
48
+		WritableMap data = Arguments.createMap();
49
+		data.putString("id", id);
50 50
 //        emit(containerStart, data);
51
-        emit(containerStart, id);
52
-    }
51
+		emit(containerStart, id);
52
+	}
53 53
 }

+ 15
- 85
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java Zobrazit soubor

@@ -1,28 +1,13 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.os.Bundle;
4
-import android.view.View;
5
-
6
-import com.facebook.react.ReactRootView;
7 3
 import com.facebook.react.bridge.ReactApplicationContext;
8 4
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
9 5
 import com.facebook.react.bridge.ReactMethod;
10
-import com.facebook.react.bridge.ReadableArray;
11 6
 import com.facebook.react.bridge.ReadableMap;
12
-import com.facebook.react.bridge.ReadableMapKeySetIterator;
13 7
 import com.reactnativenavigation.NavigationActivity;
14 8
 import com.reactnativenavigation.NavigationApplication;
15
-import com.reactnativenavigation.layout.LayoutFactory;
16
-import com.reactnativenavigation.layout.parse.LayoutNode;
17
-import com.reactnativenavigation.layout.StackLayout;
18
-import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
19 9
 import com.reactnativenavigation.utils.UiThread;
20 10
 
21
-import java.util.ArrayList;
22
-import java.util.HashMap;
23
-import java.util.List;
24
-import java.util.Map;
25
-
26 11
 public class NavigationModule extends ReactContextBaseJavaModule {
27 12
 	private static final String NAME = "RNNBridgeModule";
28 13
 
@@ -35,97 +20,42 @@ public class NavigationModule extends ReactContextBaseJavaModule {
35 20
 		return NAME;
36 21
 	}
37 22
 
23
+	public NavigationActivity activity() {
24
+		return (NavigationActivity) getCurrentActivity();
25
+	}
26
+
38 27
 	@ReactMethod
39 28
 	public void setRoot(final ReadableMap layoutTree) {
40
-		if (getCurrentActivity() == null) return;
41
-
42
-		UiThread.post(new Runnable() {
29
+		handle(new Runnable() {
43 30
 			@Override
44 31
 			public void run() {
45
-				LayoutFactory factory =
46
-						new LayoutFactory(getCurrentActivity(), new LayoutFactory.ReactRootViewCreator() {
47
-							@Override
48
-							public View create(String id, String name) {
49
-								ReactRootView rootView = new ReactRootView(getCurrentActivity());
50
-								Bundle opts = new Bundle();
51
-								opts.putString("id", id);
52
-								rootView.startReactApplication(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager(), name, opts);
53
-								return rootView;
54
-							}
55
-						}, new BottomTabsCreator());
56
-
57
-				final LayoutNode layoutTreeRoot = readableMapToLayoutNode(layoutTree);
58
-				final View rootView = factory.create(layoutTreeRoot);
59
-				getCurrentActivity().setContentView(rootView);
32
+				NavigationApplication.instance.getConfig().commandsHandler.setRoot(activity(), layoutTree);
60 33
 			}
61 34
 		});
62 35
 	}
63 36
 
64 37
 	@ReactMethod
65
-	public void push(String onContainerId, final ReadableMap layout) {
66
-		if (getCurrentActivity() == null) return;
67
-
68
-		UiThread.post(new Runnable() {
38
+	public void push(final String onContainerId, final ReadableMap layout) {
39
+		handle(new Runnable() {
69 40
 			@Override
70 41
 			public void run() {
71
-				LayoutFactory factory =
72
-						new LayoutFactory(getCurrentActivity(), new LayoutFactory.ReactRootViewCreator() {
73
-							@Override
74
-							public View create(String id, String name) {
75
-								ReactRootView rootView = new ReactRootView(getCurrentActivity());
76
-								Bundle opts = new Bundle();
77
-								opts.putString("id", id);
78
-								rootView.startReactApplication(NavigationApplication.instance.getReactNativeHost().getReactInstanceManager(), name, opts);
79
-								return rootView;
80
-							}
81
-						}, new BottomTabsCreator());
82
-				final LayoutNode layoutNode = readableMapToLayoutNode(layout);
83
-				final View rootView = factory.create(layoutNode);
84
-				((StackLayout) ((NavigationActivity) getCurrentActivity()).getContentView()).push(rootView);
42
+				NavigationApplication.instance.getConfig().commandsHandler.push(activity(), onContainerId, layout);
85 43
 			}
86 44
 		});
87 45
 	}
88 46
 
89 47
 	@ReactMethod
90
-	public void pop(String onContainerId) {
91
-		if (getCurrentActivity() == null) return;
92
-
93
-		UiThread.post(new Runnable() {
48
+	public void pop(final String onContainerId) {
49
+		handle(new Runnable() {
94 50
 			@Override
95 51
 			public void run() {
96
-				((StackLayout) ((NavigationActivity) getCurrentActivity()).getContentView()).pop();
52
+				NavigationApplication.instance.getConfig().commandsHandler.pop(activity(), onContainerId);
97 53
 			}
98 54
 		});
99 55
 	}
100 56
 
101
-	private LayoutNode readableMapToLayoutNode(ReadableMap readableMap) {
102
-		String id = readableMap.getString("id");
103
-		LayoutNode.Type type = LayoutNode.Type.fromString(readableMap.getString("type"));
104
-		Map<String, Object> data = readableMapToJavaMap(readableMap.getMap("data"));
105
-
106
-		ReadableArray childrenNodes = readableMap.getArray("children");
107
-		List<LayoutNode> children = new ArrayList<>(childrenNodes.size());
108
-		for (int i = 0; i < childrenNodes.size(); i++) {
109
-			ReadableMap child = childrenNodes.getMap(i);
110
-			children.add(readableMapToLayoutNode(child));
111
-		}
112
-
113
-		return new LayoutNode(id, type, data, children);
114
-	}
115
-
116
-	private Map<String, Object> readableMapToJavaMap(ReadableMap readableMap) {
117
-		final Map<String, Object> map = new HashMap<>();
118
-		for (ReadableMapKeySetIterator it = readableMap.keySetIterator(); it.hasNextKey(); ) {
119
-			final String key = it.nextKey();
120
-			switch (readableMap.getType(key)) {
121
-				case String:
122
-					map.put(key, readableMap.getString(key));
123
-					break;
124
-				case Map:
125
-					map.put(key, readableMapToJavaMap(readableMap.getMap(key)));
126
-					break;
127
-			}
128
-		}
129
-		return map;
57
+	private void handle(Runnable task) {
58
+		if (activity() == null) return;
59
+		UiThread.post(task);
130 60
 	}
131 61
 }

+ 9
- 1
lib/android/app/src/test/java/com/reactnativenavigation/NavigationActivityTest.java Zobrazit soubor

@@ -2,6 +2,7 @@ package com.reactnativenavigation;
2 2
 
3 3
 import android.view.View;
4 4
 
5
+import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
5 6
 import com.reactnativenavigation.controllers.ActivityLifecycleDelegate;
6 7
 
7 8
 import org.junit.Test;
@@ -17,7 +18,6 @@ import static org.mockito.Mockito.verifyZeroInteractions;
17 18
 
18 19
 public class NavigationActivityTest extends BaseTest {
19 20
 
20
-
21 21
 	@Test
22 22
 	public void holdsContentView() throws Exception {
23 23
 		NavigationActivity activity = Robolectric.setupActivity(NavigationActivity.class);
@@ -51,4 +51,12 @@ public class NavigationActivityTest extends BaseTest {
51 51
 		verify(activityLifecycleDelegate, times(1)).onActivityDestroyed(activityController.get());
52 52
 		verifyNoMoreInteractions(activityLifecycleDelegate);
53 53
 	}
54
+
55
+	@Test
56
+	public void defaultBackBtnHandler() throws Exception {
57
+		NavigationActivity navigationActivity = Robolectric.setupActivity(NavigationActivity.class);
58
+		assertThat(navigationActivity).isInstanceOf(DefaultHardwareBackBtnHandler.class);
59
+		navigationActivity.invokeDefaultOnBackPressed();
60
+		assertThat(navigationActivity.isFinishing()).isTrue();
61
+	}
54 62
 }

+ 3
- 1
lib/android/app/src/test/java/com/reactnativenavigation/NavigationApplicationTest.java Zobrazit soubor

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import com.facebook.react.ReactApplication;
4
+import com.facebook.react.ReactNativeHost;
4 5
 import com.reactnativenavigation.controllers.ActivityLifecycleDelegate;
5 6
 
6 7
 import org.junit.Test;
@@ -32,7 +33,8 @@ public class NavigationApplicationTest extends BaseTest {
32 33
 	}
33 34
 
34 35
 	@Test
35
-	public void configProvidesActivityLifecycleDelegate() throws Exception {
36
+	public void config() throws Exception {
37
+		assertThat(NavigationApplication.instance.getConfig().reactNativeHost).isInstanceOf(ReactNativeHost.class);
36 38
 		assertThat(NavigationApplication.instance.getConfig().activityLifecycleDelegate).isInstanceOf(ActivityLifecycleDelegate.class);
37 39
 	}
38 40
 }

+ 2
- 0
lib/android/app/src/test/java/com/reactnativenavigation/TestApplication.java Zobrazit soubor

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import com.reactnativenavigation.controllers.ActivityLifecycleDelegate;
4
+import com.reactnativenavigation.controllers.CommandsHandler;
4 5
 import com.reactnativenavigation.mocks.TestDevPermissionRequest;
5 6
 import com.reactnativenavigation.mocks.TestReactNativeHost;
6 7
 
@@ -16,6 +17,7 @@ public class TestApplication extends NavigationApplication {
16 17
 		Config config = new Config();
17 18
 		config.reactNativeHost = new TestReactNativeHost(this, isDebug());
18 19
 		config.activityLifecycleDelegate = new ActivityLifecycleDelegate(config.reactNativeHost.getReactInstanceManager(), new TestDevPermissionRequest());
20
+		config.commandsHandler = new CommandsHandler();
19 21
 		return config;
20 22
 	}
21 23
 }

+ 47
- 0
lib/android/app/src/test/java/com/reactnativenavigation/react/NavigationModuleTest.java Zobrazit soubor

@@ -0,0 +1,47 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import com.facebook.react.bridge.ReactApplicationContext;
4
+import com.facebook.react.bridge.ReactMethod;
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.NavigationApplication;
7
+import com.reactnativenavigation.controllers.CommandsHandler;
8
+
9
+import org.junit.Before;
10
+import org.junit.Test;
11
+
12
+import java.lang.reflect.Method;
13
+
14
+import static org.assertj.core.api.Java6Assertions.assertThat;
15
+import static org.mockito.Mockito.mock;
16
+import static org.mockito.Mockito.verifyZeroInteractions;
17
+
18
+public class NavigationModuleTest extends BaseTest {
19
+
20
+	private NavigationModule uut;
21
+
22
+	@Before
23
+	public void beforeEach() {
24
+		ReactApplicationContext reactApplicationContext = mock(ReactApplicationContext.class);
25
+		uut = new NavigationModule(reactApplicationContext);
26
+	}
27
+
28
+	@Test
29
+	public void bridgeModule() throws Exception {
30
+		assertThat(uut.getName()).isEqualTo("RNNBridgeModule");
31
+	}
32
+
33
+	@Test
34
+	public void allReactMethodsProtectAgainstNullActivity() throws Exception {
35
+		NavigationApplication.instance.getConfig().commandsHandler = mock(CommandsHandler.class);
36
+
37
+		assertThat(uut.activity()).isNull();
38
+
39
+		for (Method method : NavigationModule.class.getDeclaredMethods()) {
40
+			if (method.getAnnotation(ReactMethod.class) != null) {
41
+				Object[] args = new Object[method.getParameterTypes().length];
42
+				method.invoke(uut, args);
43
+				verifyZeroInteractions(NavigationApplication.instance.getConfig().commandsHandler);
44
+			}
45
+		}
46
+	}
47
+}

+ 4
- 4
playground/android/app/src/main/java/com/reactnativenavigation/playground/MainApplication.java Zobrazit soubor

@@ -4,8 +4,8 @@ import com.reactnativenavigation.NavigationApplication;
4 4
 
5 5
 public class MainApplication extends NavigationApplication {
6 6
 
7
-    @Override
8
-    public boolean isDebug() {
9
-        return BuildConfig.DEBUG;
10
-    }
7
+	@Override
8
+	public boolean isDebug() {
9
+		return BuildConfig.DEBUG;
10
+	}
11 11
 }