Daniel Zlotin 7 years ago
parent
commit
5e74cc72c6

+ 2
- 0
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/ApplicationLifecycleTest.java View File

3
 import android.support.test.uiautomator.By;
3
 import android.support.test.uiautomator.By;
4
 
4
 
5
 import org.junit.FixMethodOrder;
5
 import org.junit.FixMethodOrder;
6
+import org.junit.Ignore;
6
 import org.junit.Test;
7
 import org.junit.Test;
7
 import org.junit.runners.MethodSorters;
8
 import org.junit.runners.MethodSorters;
8
 
9
 
9
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
10
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
11
+@Ignore
10
 public class ApplicationLifecycleTest extends BaseTest {
12
 public class ApplicationLifecycleTest extends BaseTest {
11
 
13
 
12
 	@Test
14
 	@Test

+ 8
- 7
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/TopLevelApiTest.java View File

31
 		launchTheApp();
31
 		launchTheApp();
32
 		assertMainShown();
32
 		assertMainShown();
33
 		elementByText("PUSH LIFECYCLE SCREEN").click();
33
 		elementByText("PUSH LIFECYCLE SCREEN").click();
34
-		elementByText("onStart!");
34
+		assertExists(By.text("onStart"));
35
 		elementByText("PUSH TO TEST ONSTOP").click();
35
 		elementByText("PUSH TO TEST ONSTOP").click();
36
-		elementByText("Alert");
37
-		elementByText("onStop").click();
36
+		assertExists(By.text("onStop"));
38
 	}
37
 	}
39
 
38
 
40
 	@Test
39
 	@Test
42
 	public void unmountIsCalledOnPop() throws Exception {
41
 	public void unmountIsCalledOnPop() throws Exception {
43
 		launchTheApp();
42
 		launchTheApp();
44
 		assertMainShown();
43
 		assertMainShown();
45
-		elementByText("Push lifecycle screen").click();
46
-		elementByText("onStart!");
47
-		elementByText("BACK").click();
48
-		elementByText("componentWillUnmount");
44
+		elementByText("PUSH LIFECYCLE SCREEN").click();
45
+		elementByText("onStart");
46
+		device().pressBack();
47
+		assertMainShown();
48
+		assertExists(By.text("onStop"));
49
+		assertExists(By.text("componentWillUnmount"));
49
 	}
50
 	}
50
 }
51
 }

+ 2
- 2
e2e/app.test.js View File

21
 
21
 
22
   it('screen lifecycle', async () => {
22
   it('screen lifecycle', async () => {
23
     await elementByLabel('Push lifecycle screen').tap();
23
     await elementByLabel('Push lifecycle screen').tap();
24
-    await expect(elementByLabel('onStart!')).toBeVisible();
24
+    await expect(elementByLabel('onStart')).toBeVisible();
25
     await elementByLabel('Push to test onStop').tap();
25
     await elementByLabel('Push to test onStop').tap();
26
     await expect(elementByLabel('Alert')).toBeVisible();
26
     await expect(elementByLabel('Alert')).toBeVisible();
27
     await expect(elementByLabel('onStop')).toBeVisible();
27
     await expect(elementByLabel('onStop')).toBeVisible();
29
 
29
 
30
   it('unmount is called on pop', async () => {
30
   it('unmount is called on pop', async () => {
31
     await elementByLabel('Push lifecycle screen').tap();
31
     await elementByLabel('Push lifecycle screen').tap();
32
-    await expect(elementByLabel('onStart!')).toBeVisible();
32
+    await expect(elementByLabel('onStart')).toBeVisible();
33
     await element(by.traits(['button']).and(by.label('Back'))).tap();
33
     await element(by.traits(['button']).and(by.label('Back'))).tap();
34
     await expect(elementByLabel('onStop')).toBeVisible();
34
     await expect(elementByLabel('onStop')).toBeVisible();
35
     await expect(elementByLabel('componentWillUnmount')).toBeVisible();
35
     await expect(elementByLabel('componentWillUnmount')).toBeVisible();

+ 8
- 0
lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java View File

6
 import android.view.View;
6
 import android.view.View;
7
 
7
 
8
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
8
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
9
+import com.reactnativenavigation.layout.StackLayout;
9
 
10
 
10
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
11
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
11
 	private View contentView;
12
 	private View contentView;
50
 		onBackPressed();
51
 		onBackPressed();
51
 	}
52
 	}
52
 
53
 
54
+	@Override
55
+	public void onBackPressed() {
56
+		if (!(contentView instanceof StackLayout) || !((StackLayout) contentView).onBackPressed()) {
57
+			super.onBackPressed();
58
+		}
59
+	}
60
+
53
 	private NavigationApplication app() {
61
 	private NavigationApplication app() {
54
 		return ((NavigationApplication) getApplication());
62
 		return ((NavigationApplication) getApplication());
55
 	}
63
 	}

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/controllers/CommandsHandler.java View File

7
 import com.reactnativenavigation.layout.LayoutFactory;
7
 import com.reactnativenavigation.layout.LayoutFactory;
8
 import com.reactnativenavigation.layout.LayoutNode;
8
 import com.reactnativenavigation.layout.LayoutNode;
9
 import com.reactnativenavigation.layout.StackLayout;
9
 import com.reactnativenavigation.layout.StackLayout;
10
+import com.reactnativenavigation.layout.containers.Container;
10
 
11
 
11
 import org.json.JSONObject;
12
 import org.json.JSONObject;
12
 
13
 
29
 		final LayoutNode layoutNode = LayoutNode.parse(layoutTree);
30
 		final LayoutNode layoutNode = LayoutNode.parse(layoutTree);
30
 		LayoutFactory factory = new LayoutFactory(activity, application.getReactNativeHost());
31
 		LayoutFactory factory = new LayoutFactory(activity, application.getReactNativeHost());
31
 		final View rootView = factory.create(layoutNode);
32
 		final View rootView = factory.create(layoutNode);
32
-		((StackLayout) activity.getContentView()).push(rootView);
33
+		((StackLayout) activity.getContentView()).push((Container) rootView);
33
 	}
34
 	}
34
 
35
 
35
 	public void pop(final NavigationActivity activity, String onContainerId) {
36
 	public void pop(final NavigationActivity activity, String onContainerId) {

+ 4
- 2
lib/android/app/src/main/java/com/reactnativenavigation/layout/StackLayout.java View File

1
 package com.reactnativenavigation.layout;
1
 package com.reactnativenavigation.layout;
2
 
2
 
3
-import android.view.View;
3
+import com.reactnativenavigation.layout.containers.Container;
4
 
4
 
5
 public interface StackLayout {
5
 public interface StackLayout {
6
-	void push(View view);
6
+	void push(Container view);
7
 
7
 
8
 	void pop();
8
 	void pop();
9
+
10
+	boolean onBackPressed();
9
 }
11
 }

+ 12
- 7
lib/android/app/src/main/java/com/reactnativenavigation/layout/containers/Container.java View File

15
 	private final ReactNativeHost reactNativeHost;
15
 	private final ReactNativeHost reactNativeHost;
16
 	private final String id;
16
 	private final String id;
17
 	private final String name;
17
 	private final String name;
18
+	private ReactRootView rootView;
18
 
19
 
19
 	public Container(Activity activity, ReactNativeHost reactNativeHost, String id, String name) {
20
 	public Container(Activity activity, ReactNativeHost reactNativeHost, String id, String name) {
20
 		super(activity);
21
 		super(activity);
24
 		addView(createReactRootView());
25
 		addView(createReactRootView());
25
 	}
26
 	}
26
 
27
 
28
+	@Override
29
+	protected void onDetachedFromWindow() {
30
+		super.onDetachedFromWindow();
31
+		onStop();
32
+	}
33
+
34
+	public void destroy() {
35
+		rootView.unmountReactApplication();
36
+	}
37
+
27
 	private View createReactRootView() {
38
 	private View createReactRootView() {
28
-		final ReactRootView rootView = new ReactRootView(getContext());
39
+		rootView = new ReactRootView(getContext());
29
 		Bundle opts = new Bundle();
40
 		Bundle opts = new Bundle();
30
 		opts.putString("id", id);
41
 		opts.putString("id", id);
31
 		rootView.startReactApplication(reactNativeHost.getReactInstanceManager(), name, opts);
42
 		rootView.startReactApplication(reactNativeHost.getReactInstanceManager(), name, opts);
39
 		return rootView;
50
 		return rootView;
40
 	}
51
 	}
41
 
52
 
42
-	@Override
43
-	protected void onDetachedFromWindow() {
44
-		super.onDetachedFromWindow();
45
-		onStop();
46
-	}
47
-
48
 	private void onStart() {
53
 	private void onStart() {
49
 		new NavigationEventEmitter(reactContext()).containerStart(id);
54
 		new NavigationEventEmitter(reactContext()).containerStart(id);
50
 	}
55
 	}

+ 29
- 7
lib/android/app/src/main/java/com/reactnativenavigation/layout/containers/ContainerStack.java View File

1
 package com.reactnativenavigation.layout.containers;
1
 package com.reactnativenavigation.layout.containers;
2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
-import android.view.View;
5
 import android.widget.FrameLayout;
4
 import android.widget.FrameLayout;
6
 
5
 
7
 import com.reactnativenavigation.layout.StackLayout;
6
 import com.reactnativenavigation.layout.StackLayout;
10
 
9
 
11
 public class ContainerStack extends FrameLayout implements StackLayout {
10
 public class ContainerStack extends FrameLayout implements StackLayout {
12
 
11
 
13
-	private Stack<View> stack = new Stack<>();
12
+	private Stack<Container> stack = new Stack<>();
14
 
13
 
15
 	public ContainerStack(Context context) {
14
 	public ContainerStack(Context context) {
16
 		super(context);
15
 		super(context);
17
 	}
16
 	}
18
 
17
 
18
+
19
 	@Override
19
 	@Override
20
-	public void push(View view) {
20
+	public void push(Container view) {
21
+		stack.push(view);
21
 		addView(view);
22
 		addView(view);
22
-		stack.push(getChildAt(0));
23
-		removeView(getChildAt(0));
23
+		if (stack.size() > 1) {
24
+			Container previousTop = stack.elementAt(stack.size() - 2);
25
+			removeView(previousTop);
26
+		}
24
 	}
27
 	}
25
 
28
 
26
 	@Override
29
 	@Override
27
 	public void pop() {
30
 	public void pop() {
28
-		addView(stack.pop());
29
-		removeView(getChildAt(0));
31
+		Container top = stack.pop();
32
+		removeView(top);
33
+		top.destroy();
34
+		if (!stack.isEmpty()) {
35
+			Container previousTop = stack.peek();
36
+			addView(previousTop);
37
+		}
38
+	}
39
+
40
+	@Override
41
+	public boolean onBackPressed() {
42
+		if (stack.isEmpty()) {
43
+			return false;
44
+		} else {
45
+			pop();
46
+			return true;
47
+		}
48
+	}
49
+
50
+	public boolean isEmpty() {
51
+		return stack.isEmpty();
30
 	}
52
 	}
31
 }
53
 }

+ 41
- 0
lib/android/app/src/test/java/com/reactnativenavigation/layout/ContainerStackTest.java View File

1
+package com.reactnativenavigation.layout;
2
+
3
+import android.support.annotation.NonNull;
4
+
5
+import com.facebook.react.ReactInstanceManager;
6
+import com.facebook.react.ReactNativeHost;
7
+import com.reactnativenavigation.BaseTest;
8
+import com.reactnativenavigation.NavigationActivity;
9
+import com.reactnativenavigation.layout.containers.Container;
10
+import com.reactnativenavigation.layout.containers.ContainerStack;
11
+
12
+import org.junit.Test;
13
+import org.robolectric.Robolectric;
14
+
15
+import static org.assertj.core.api.Java6Assertions.assertThat;
16
+import static org.mockito.Mockito.mock;
17
+import static org.mockito.Mockito.when;
18
+
19
+public class ContainerStackTest extends BaseTest {
20
+	@Test
21
+	public void push() throws Exception {
22
+		NavigationActivity context = Robolectric.setupActivity(NavigationActivity.class);
23
+		ContainerStack uut = new ContainerStack(context);
24
+		assertThat(uut.isEmpty()).isTrue();
25
+		assertThat(uut.getChildCount()).isZero();
26
+
27
+		Container container = createContainer(context);
28
+		uut.push(container);
29
+		assertThat(uut.isEmpty()).isFalse();
30
+
31
+		assertThat(uut.getChildCount()).isEqualTo(1);
32
+
33
+	}
34
+
35
+	@NonNull
36
+	private Container createContainer(final NavigationActivity context) {
37
+		ReactNativeHost reactNativeHost = mock(ReactNativeHost.class);
38
+		when(reactNativeHost.getReactInstanceManager()).thenReturn(mock(ReactInstanceManager.class));
39
+		return new Container(context, reactNativeHost, "id", "name");
40
+	}
41
+}

+ 1
- 1
playground/src/containers/LifecycleScreen.js View File

13
   }
13
   }
14
 
14
 
15
   onStart() {
15
   onStart() {
16
-    this.setState({ text: 'onStart!' });
16
+    this.setState({ text: 'onStart' });
17
   }
17
   }
18
 
18
 
19
   onStop() {
19
   onStop() {