Sfoglia il codice sorgente

Further work on TopTabs and some refactoring (#2362)

* Further work on TopTabs and some refactoring

* OptionsPresenter receives views as params
* Remove TopBar and style logic from ViewControllers, these are the OptionsPresenters concern
* StackAnimator isn't coupled to LinearLayout
* Convert Runnable to lambda
* ContainerLayout now implements ReactContainer interface
* Enable desugaring in playground app

* TopTabs unit tests
Guy Carmeli 6 anni fa
parent
commit
138391502f
No account linked to committer's email address
32 ha cambiato i file con 622 aggiunte e 242 eliminazioni
  1. 4
    0
      lib/android/app/build.gradle
  2. 17
    23
      lib/android/app/src/main/java/com/reactnativenavigation/anim/StackAnimator.java
  3. 22
    10
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  4. 5
    5
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNode.java
  5. 22
    24
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  6. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/Task.java
  7. 17
    46
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ContainerViewController.java
  8. 11
    30
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  9. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  10. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  11. 36
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  12. 72
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabController.java
  13. 22
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java
  14. 33
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  15. 12
    31
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsViewPager.java
  16. 7
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/Container.java
  17. 17
    12
      lib/android/app/src/main/java/com/reactnativenavigation/views/ContainerLayout.java
  18. 6
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/ReactContainer.java
  19. 4
    10
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTab.java
  20. 37
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java
  21. 16
    9
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestContainerLayout.java
  22. 38
    0
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopTabLayoutMock.java
  23. 7
    7
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  24. 122
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  25. 3
    3
      lib/src/commands/LayoutTreeParser.js
  26. 1
    1
      lib/src/commands/LayoutTreeParser.test.js
  27. 1
    1
      lib/src/commands/LayoutTypes.js
  28. 5
    0
      playground/android/app/build.gradle
  29. 52
    0
      playground/src/containers/TopTabOptionsScreen.js
  30. 20
    0
      playground/src/containers/TopTabScreen.js
  31. 4
    1
      playground/src/containers/WelcomeScreen.js
  32. 2
    0
      playground/src/containers/index.js

+ 4
- 0
lib/android/app/build.gradle Vedi File

@@ -42,6 +42,10 @@ android {
42 42
             }
43 43
         }
44 44
     }
45
+    compileOptions {
46
+        sourceCompatibility JavaVersion.VERSION_1_8
47
+        targetCompatibility JavaVersion.VERSION_1_8
48
+    }
45 49
 }
46 50
 
47 51
 allprojects { p ->

+ 17
- 23
lib/android/app/src/main/java/com/reactnativenavigation/anim/StackAnimator.java Vedi File

@@ -8,10 +8,10 @@ import android.content.Context;
8 8
 import android.support.annotation.Nullable;
9 9
 import android.util.DisplayMetrics;
10 10
 import android.view.View;
11
+import android.view.ViewGroup;
11 12
 import android.view.WindowManager;
12 13
 import android.view.animation.AccelerateInterpolator;
13 14
 import android.view.animation.DecelerateInterpolator;
14
-import android.widget.LinearLayout;
15 15
 
16 16
 import com.reactnativenavigation.views.TopBar;
17 17
 
@@ -116,15 +116,12 @@ public class StackAnimator {
116 116
 		ValueAnimator containerHeightAnim = ValueAnimator.ofInt(container.getMeasuredHeight(), container.getMeasuredHeight() - topBar.getMeasuredHeight());
117 117
 		containerHeightAnim.setInterpolator(DECELERATE_INTERPOLATOR);
118 118
 		containerHeightAnim.setDuration(DURATION_TOPBAR);
119
-		containerHeightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
120
-			@Override
121
-			public void onAnimationUpdate(ValueAnimator valueAnimator) {
122
-				int val = (Integer) valueAnimator.getAnimatedValue();
123
-				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) container.getLayoutParams();
124
-				layoutParams.height = val;
125
-				container.setLayoutParams(layoutParams);
126
-			}
127
-		});
119
+		containerHeightAnim.addUpdateListener(valueAnimator -> {
120
+            int val = (Integer) valueAnimator.getAnimatedValue();
121
+            ViewGroup.LayoutParams layoutParams = container.getLayoutParams();
122
+            layoutParams.height = val;
123
+            container.setLayoutParams(layoutParams);
124
+        });
128 125
 		ObjectAnimator containerTransitionAnim = ObjectAnimator.ofFloat(container, View.TRANSLATION_Y, -1 * topBar.getMeasuredHeight(), 0);
129 126
 		containerTransitionAnim.setInterpolator(DECELERATE_INTERPOLATOR);
130 127
 		containerTransitionAnim.setDuration(DURATION_TOPBAR);
@@ -142,8 +139,8 @@ public class StackAnimator {
142 139
 
143 140
 			@Override
144 141
 			public void onAnimationEnd(Animator animation) {
145
-				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) container.getLayoutParams();
146
-				layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT;
142
+                ViewGroup.LayoutParams layoutParams = container.getLayoutParams();
143
+				layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
147 144
 				container.setLayoutParams(layoutParams);
148 145
 			}
149 146
 
@@ -165,15 +162,12 @@ public class StackAnimator {
165 162
 		ValueAnimator containerHeightAnim = ValueAnimator.ofInt(container.getMeasuredHeight(), container.getMeasuredHeight() + topBar.getMeasuredHeight());
166 163
 		containerHeightAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
167 164
 		containerHeightAnim.setDuration(DURATION_TOPBAR);
168
-		containerHeightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
169
-			@Override
170
-			public void onAnimationUpdate(ValueAnimator valueAnimator) {
171
-				int val = (Integer) valueAnimator.getAnimatedValue();
172
-				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) container.getLayoutParams();
173
-				layoutParams.height = val;
174
-				container.setLayoutParams(layoutParams);
175
-			}
176
-		});
165
+		containerHeightAnim.addUpdateListener(valueAnimator -> {
166
+            int val = (Integer) valueAnimator.getAnimatedValue();
167
+            ViewGroup.LayoutParams layoutParams = container.getLayoutParams();
168
+            layoutParams.height = val;
169
+            container.setLayoutParams(layoutParams);
170
+        });
177 171
 		ObjectAnimator containerTransitionAnim = ObjectAnimator.ofFloat(container, View.TRANSLATION_Y, 0, -1 * topBar.getMeasuredHeight());
178 172
 		containerTransitionAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
179 173
 		containerTransitionAnim.setDuration(DURATION_TOPBAR);
@@ -190,8 +184,8 @@ public class StackAnimator {
190 184
 
191 185
 			@Override
192 186
 			public void onAnimationEnd(Animator animation) {
193
-				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) container.getLayoutParams();
194
-				layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT;
187
+                ViewGroup.LayoutParams layoutParams = container.getLayoutParams();
188
+				layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
195 189
 				container.setLayoutParams(layoutParams);
196 190
 				container.setTranslationY(0);
197 191
 

+ 22
- 10
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java Vedi File

@@ -10,9 +10,9 @@ import com.reactnativenavigation.viewcontrollers.SideMenuController;
10 10
 import com.reactnativenavigation.viewcontrollers.StackController;
11 11
 import com.reactnativenavigation.viewcontrollers.ViewController;
12 12
 import com.reactnativenavigation.viewcontrollers.overlay.DialogViewController;
13
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
13 14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
14 15
 import com.reactnativenavigation.views.ContainerViewCreator;
15
-import com.reactnativenavigation.views.TopTabCreator;
16 16
 
17 17
 import java.util.ArrayList;
18 18
 import java.util.List;
@@ -32,7 +32,7 @@ public class LayoutFactory {
32 32
 	public ViewController create(final LayoutNode node) {
33 33
 		switch (node.type) {
34 34
 			case Container:
35
-				return createContainer(node, new ContainerViewCreator(reactInstanceManager));
35
+				return createContainer(node);
36 36
 			case ContainerStack:
37 37
 				return createContainerStack(node);
38 38
 			case BottomTabs:
@@ -47,10 +47,10 @@ public class LayoutFactory {
47 47
 				return createSideMenuRight(node);
48 48
 			case CustomDialog:
49 49
 				return createDialogContainer(node);
50
-            case TopTabsContainer:
51
-                return createTopTabsContainer(node);
50
+            case TopTabs:
51
+                return createTopTabs(node);
52 52
             case TopTab:
53
-                return createContainer(node, new TopTabCreator(reactInstanceManager));
53
+                return createTopTab(node);
54 54
 			default:
55 55
 				throw new IllegalArgumentException("Invalid node type: " + node.type);
56 56
 		}
@@ -89,14 +89,14 @@ public class LayoutFactory {
89 89
 		return create(node.children.get(0));
90 90
 	}
91 91
 
92
-	private ViewController createContainer(LayoutNode node, ContainerViewController.ReactViewCreator reactViewCreator) {
92
+	private ViewController createContainer(LayoutNode node) {
93 93
 		String id = node.id;
94 94
 		String name = node.data.optString("name");
95 95
 		NavigationOptions navigationOptions = NavigationOptions.parse(node.data.optJSONObject("navigationOptions"), defaultOptions);
96 96
 		return new ContainerViewController(activity,
97 97
                 id,
98 98
                 name,
99
-                reactViewCreator,
99
+                new ContainerViewCreator(reactInstanceManager),
100 100
                 navigationOptions
101 101
         );
102 102
 	}
@@ -126,11 +126,23 @@ public class LayoutFactory {
126 126
 		return new DialogViewController(activity, id, name, creator);
127 127
 	}
128 128
 
129
-    private ViewController createTopTabsContainer(LayoutNode node) {
130
-        final List<ViewController> tabs = new ArrayList<>();
129
+    private ViewController createTopTabs(LayoutNode node) {
130
+        final List<TopTabController> tabs = new ArrayList<>();
131 131
         for (LayoutNode child : node.children) {
132
-            tabs.add(create(child));
132
+            tabs.add((TopTabController) create(child));
133 133
         }
134 134
         return new TopTabsController(activity, node.id, tabs);
135 135
     }
136
+
137
+    private ViewController createTopTab(LayoutNode node) {
138
+        String id = node.id;
139
+        String name = node.data.optString("name");
140
+        NavigationOptions navigationOptions = NavigationOptions.parse(node.data.optJSONObject("navigationOptions"), defaultOptions);
141
+        return new TopTabController(activity,
142
+                id,
143
+                name,
144
+                new ReactContainerViewCreator(reactInstanceManager),
145
+                navigationOptions
146
+        );
147
+    }
136 148
 }

+ 5
- 5
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNode.java Vedi File

@@ -15,7 +15,7 @@ public class LayoutNode {
15 15
 		SideMenuLeft,
16 16
 		SideMenuRight,
17 17
 		CustomDialog,
18
-        TopTabsContainer,
18
+        TopTabs,
19 19
         TopTab
20 20
 	}
21 21
 
@@ -23,13 +23,13 @@ public class LayoutNode {
23 23
 	public final Type type;
24 24
 	public final JSONObject data;
25 25
 
26
-	public final List<LayoutNode> children;
26
+	final List<LayoutNode> children;
27 27
 
28
-	public LayoutNode(String id, Type type) {
29
-		this(id, type, new JSONObject(), new ArrayList<LayoutNode>());
28
+	LayoutNode(String id, Type type) {
29
+		this(id, type, new JSONObject(), new ArrayList<>());
30 30
 	}
31 31
 
32
-	public LayoutNode(String id, Type type, JSONObject data, List<LayoutNode> children) {
32
+	LayoutNode(String id, Type type, JSONObject data, List<LayoutNode> children) {
33 33
 		this.id = id;
34 34
 		this.type = type;
35 35
 		this.data = data;

+ 22
- 24
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java Vedi File

@@ -7,33 +7,33 @@ import com.reactnativenavigation.parse.NavigationOptions;
7 7
 import com.reactnativenavigation.parse.TopBarOptions;
8 8
 import com.reactnativenavigation.parse.TopTabsOptions;
9 9
 import com.reactnativenavigation.utils.TypefaceLoader;
10
-import com.reactnativenavigation.viewcontrollers.ContainerViewController;
11
-import com.reactnativenavigation.views.ContainerLayout;
10
+import com.reactnativenavigation.views.TopBar;
12 11
 
13 12
 public class OptionsPresenter {
14 13
 
15
-	private ContainerViewController controller;
16 14
 	private final StackAnimator animator;
15
+    private View contentView;
16
+    private TopBar topBar;
17 17
 
18
-	public OptionsPresenter(ContainerViewController controller) {
19
-		this.controller = controller;
20
-		animator = new StackAnimator(controller.getActivity());
21
-	}
18
+    public OptionsPresenter(TopBar topBar, View contentView) {
19
+        this.topBar = topBar;
20
+        this.contentView = contentView;
21
+        animator = new StackAnimator(topBar.getContext());
22
+    }
22 23
 
23 24
 	public void applyOptions(NavigationOptions options) {
24
-		if (controller != null && controller.getTopBar() != null) {
25
-            applyTopBarOptions(options.topBarOptions);
26
-            applyTopTabsOptions(options.topTabsOptions);
27
-		}
25
+        applyTopBarOptions(options.topBarOptions);
26
+        applyTopTabsOptions(options.topTabsOptions);
28 27
 	}
29 28
 
30 29
     private void applyTopBarOptions(TopBarOptions options) {
31
-        controller.setTitle(options.title);
32
-        controller.setBackgroundColor(options.backgroundColor);
33
-        controller.setTitleTextColor(options.textColor);
34
-        controller.setTitleFontSize(options.textFontSize);
30
+        topBar.setTitle(options.title);
31
+        topBar.setBackgroundColor(options.backgroundColor);
32
+        topBar.setTitleTextColor(options.textColor);
33
+        topBar.setTitleFontSize(options.textFontSize);
34
+
35 35
         TypefaceLoader typefaceLoader = new TypefaceLoader();
36
-        controller.getTopBar().setTitleTypeface(typefaceLoader.getTypeFace(controller.getActivity(), options.textFontFamily));
36
+        topBar.setTitleTypeface(typefaceLoader.getTypeFace(topBar.getContext(), options.textFontFamily));
37 37
         if (options.hidden == NavigationOptions.BooleanOptions.True) {
38 38
             hideTopBar(options.animateHide);
39 39
         }
@@ -43,26 +43,24 @@ public class OptionsPresenter {
43 43
     }
44 44
 
45 45
 	private void showTopBar(NavigationOptions.BooleanOptions animated) {
46
-		if (controller.getTopBar().getVisibility() == View.VISIBLE) {
46
+		if (topBar.getVisibility() == View.VISIBLE) {
47 47
 			return;
48 48
 		}
49 49
 		if (animated == NavigationOptions.BooleanOptions.True) {
50
-			ContainerLayout topBarContainerView = (ContainerLayout) controller.getContainerView();
51
-			animator.animateShowTopBar(controller.getTopBar(), topBarContainerView.getReactView().asView());
50
+			animator.animateShowTopBar(topBar, contentView);
52 51
 		} else {
53
-			controller.getTopBar().setVisibility(View.VISIBLE);
52
+			topBar.setVisibility(View.VISIBLE);
54 53
 		}
55 54
 	}
56 55
 
57 56
 	private void hideTopBar(NavigationOptions.BooleanOptions animated) {
58
-		if (controller.getTopBar().getVisibility() == View.GONE) {
57
+		if (topBar.getVisibility() == View.GONE) {
59 58
 			return;
60 59
 		}
61 60
 		if (animated == NavigationOptions.BooleanOptions.True) {
62
-			ContainerLayout topBarContainerView = (ContainerLayout) controller.getContainerView();
63
-			animator.animateHideTopBar(controller.getTopBar(), topBarContainerView.getReactView().asView());
61
+			animator.animateHideTopBar(topBar, contentView);
64 62
 		} else {
65
-			controller.getTopBar().setVisibility(View.GONE);
63
+			topBar.setVisibility(View.GONE);
66 64
 		}
67 65
 	}
68 66
 

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/Task.java Vedi File

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+public interface Task<T> {
4
+    void run(T param);
5
+}

+ 17
- 46
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ContainerViewController.java Vedi File

@@ -2,12 +2,12 @@ package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5
+import android.support.annotation.RestrictTo;
5 6
 import android.view.View;
6 7
 
7 8
 import com.reactnativenavigation.parse.NavigationOptions;
8 9
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
9
-import com.reactnativenavigation.presentation.OptionsPresenter;
10
-import com.reactnativenavigation.views.Container;
10
+import com.reactnativenavigation.views.ReactContainer;
11 11
 import com.reactnativenavigation.views.TopBar;
12 12
 
13 13
 public class ContainerViewController extends ViewController implements NavigationOptionsListener {
@@ -35,25 +35,7 @@ public class ContainerViewController extends ViewController implements Navigatio
35 35
 
36 36
 	private final ReactViewCreator viewCreator;
37 37
 	private NavigationOptions navigationOptions;
38
-	private IReactView containerView;
39
-
40
-	private TopBar topBar;
41
-
42
-    public void setTitle(String title) {
43
-        topBar.setTitle(title);
44
-    }
45
-
46
-    public void setBackgroundColor(int backgroundColor) {
47
-        topBar.setBackgroundColor(backgroundColor);
48
-    }
49
-
50
-    public void setTitleTextColor(int textColor) {
51
-        topBar.setTitleTextColor(textColor);
52
-    }
53
-
54
-    public void setTitleFontSize(float textFontSize) {
55
-        topBar.setTitleFontSize(textFontSize);
56
-    }
38
+	private ReactContainer container;
57 39
 
58 40
 	public ContainerViewController(final Activity activity,
59 41
 								   final String id,
@@ -66,62 +48,51 @@ public class ContainerViewController extends ViewController implements Navigatio
66 48
 		this.navigationOptions = initialNavigationOptions;
67 49
 	}
68 50
 
51
+    @RestrictTo(RestrictTo.Scope.TESTS)
52
+    TopBar getTopBar() {
53
+        return container.getTopBar();
54
+    }
55
+
69 56
 	@Override
70 57
 	public void destroy() {
71 58
 		super.destroy();
72
-		if (containerView != null) containerView.destroy();
73
-		containerView = null;
59
+		if (container != null) container.destroy();
60
+		container = null;
74 61
 	}
75 62
 
76 63
 	@Override
77 64
 	public void onViewAppeared() {
78 65
 		super.onViewAppeared();
79 66
 		ensureViewIsCreated();
80
-		applyOptions();
81
-		containerView.sendContainerStart();
67
+		container.applyOptions(navigationOptions);
68
+		container.sendContainerStart();
82 69
 	}
83 70
 
84 71
 	@Override
85 72
 	public void onViewDisappear() {
86 73
 		super.onViewDisappear();
87
-		containerView.sendContainerStop();
74
+		container.sendContainerStop();
88 75
 	}
89 76
 
90 77
 	@Override
91 78
 	protected boolean isViewShown() {
92
-		return super.isViewShown() && containerView.isReady();
79
+		return super.isViewShown() && container.isReady();
93 80
 	}
94 81
 
95 82
 	@NonNull
96 83
 	@Override
97 84
 	protected View createView() {
98
-		containerView = viewCreator.create(getActivity(), getId(), containerName);
99
-		if (containerView instanceof Container) {
100
-			topBar = ((Container) containerView).getTopBar();
101
-		}
102
-		return containerView.asView();
85
+		container = (ReactContainer) viewCreator.create(getActivity(), getId(), containerName);
86
+        return container.asView();
103 87
 	}
104 88
 
105 89
 	@Override
106 90
 	public void mergeNavigationOptions(NavigationOptions options) {
107 91
 		navigationOptions.mergeWith(options);
108
-		applyOptions();
92
+        container.applyOptions(options);
109 93
 	}
110 94
 
111 95
 	NavigationOptions getNavigationOptions() {
112 96
 		return navigationOptions;
113 97
 	}
114
-
115
-	private void applyOptions() {
116
-		OptionsPresenter presenter = new OptionsPresenter(this);
117
-		presenter.applyOptions(navigationOptions);
118
-	}
119
-
120
-	public TopBar getTopBar() {
121
-		return topBar;
122
-	}
123
-
124
-	public IReactView getContainerView() {
125
-		return containerView;
126
-	}
127 98
 }

+ 11
- 30
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java Vedi File

@@ -53,7 +53,7 @@ public class Navigator extends ParentController {
53 53
 	 * Navigation methods
54 54
 	 */
55 55
 
56
-	public void setRoot(final ViewController viewController) {
56
+	void setRoot(final ViewController viewController) {
57 57
 		setRoot(viewController, null);
58 58
 	}
59 59
 
@@ -94,72 +94,53 @@ public class Navigator extends ParentController {
94 94
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
95 95
 		ViewController from = findControllerById(fromId);
96 96
 		if (from != null) {
97
-			StackController parentStackController = from.getParentStackController();
98
-			if (parentStackController != null) {
99
-				parentStackController.push(viewController, promise);
100
-			}
97
+		    from.performOnParentStack(stack -> stack.push(viewController, promise));
101 98
 		}
102 99
 	}
103 100
 
104
-	public void pop(final String fromId) {
101
+	void pop(final String fromId) {
105 102
 		pop(fromId, null);
106 103
 	}
107 104
 
108
-	public void pop(final String fromId, Promise promise) {
105
+	void pop(final String fromId, Promise promise) {
109 106
 		ViewController from = findControllerById(fromId);
110 107
 		if (from != null) {
111
-			StackController parentStackController = from.getParentStackController();
112
-			if (parentStackController != null) {
113
-				parentStackController.pop(promise);
114
-			}
108
+		    from.performOnParentStack(stack -> stack.pop(promise));
115 109
 		}
116 110
 	}
117 111
 
118
-	public void popSpecific(final String id) {
112
+	void popSpecific(final String id) {
119 113
 		popSpecific(id, null);
120 114
 	}
121 115
 
122 116
 	public void popSpecific(final String id, Promise promise) {
123 117
 		ViewController from = findControllerById(id);
124 118
 		if (from != null) {
125
-			StackController parentStackController = from.getParentStackController();
126
-			if (parentStackController != null) {
127
-				parentStackController.popSpecific(from, promise);
128
-			} else {
129
-				rejectPromise(promise);
130
-			}
119
+		    from.performOnParentStack(stack -> stack.popSpecific(from, promise), () -> rejectPromise(promise));
131 120
 		} else {
132 121
 			rejectPromise(promise);
133 122
 		}
134 123
 	}
135 124
 
136
-	public void popToRoot(final String id) {
125
+	void popToRoot(final String id) {
137 126
 		popToRoot(id, null);
138 127
 	}
139 128
 
140 129
 	public void popToRoot(final String id, Promise promise) {
141 130
 		ViewController from = findControllerById(id);
142 131
 		if (from != null) {
143
-			StackController parentStackController = from.getParentStackController();
144
-			if (parentStackController != null) {
145
-				parentStackController.popToRoot(promise);
146
-			}
132
+		    from.performOnParentStack(stack -> stack.popToRoot(promise));
147 133
 		}
148 134
 	}
149 135
 
150
-	public void popTo(final String containerId) {
136
+	void popTo(final String containerId) {
151 137
 		popTo(containerId, null);
152 138
 	}
153 139
 
154 140
 	public void popTo(final String containerId, Promise promise) {
155 141
 		ViewController target = findControllerById(containerId);
156 142
 		if (target != null) {
157
-			StackController parentStackController = target.getParentStackController();
158
-			if (parentStackController != null) {
159
-				parentStackController.popTo(target, promise);
160
-			} else {
161
-				rejectPromise(promise);
162
-			}
143
+		    target.performOnParentStack(stack -> stack.popTo(target, promise), () -> rejectPromise(promise));
163 144
 		} else {
164 145
 			rejectPromise(promise);
165 146
 		}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java Vedi File

@@ -24,7 +24,7 @@ public abstract class ParentController extends ViewController {
24 24
 	protected abstract ViewGroup createView();
25 25
 
26 26
 	@NonNull
27
-	public abstract Collection<ViewController> getChildControllers();
27
+	public abstract Collection<? extends ViewController> getChildControllers();
28 28
 
29 29
 	@Nullable
30 30
 	@Override

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java Vedi File

@@ -33,7 +33,7 @@ public class StackController extends ParentController {
33 33
 	public void push(final ViewController child, final Promise promise) {
34 34
 		final ViewController previousTop = peek();
35 35
 
36
-		child.setParentStackController(this);
36
+		child.setParentController(this);
37 37
 		stack.push(child.getId(), child);
38 38
 		View enteringView = child.getView();
39 39
 		getView().addView(enteringView);

+ 36
- 10
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java Vedi File

@@ -3,13 +3,16 @@ package com.reactnativenavigation.viewcontrollers;
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5 5
 import android.support.annotation.Nullable;
6
+import android.support.annotation.VisibleForTesting;
6 7
 import android.view.View;
7 8
 import android.view.ViewGroup;
8 9
 import android.view.ViewManager;
9 10
 import android.view.ViewTreeObserver;
10 11
 
12
+import com.reactnativenavigation.parse.NavigationOptions;
11 13
 import com.reactnativenavigation.utils.CompatUtils;
12 14
 import com.reactnativenavigation.utils.StringUtils;
15
+import com.reactnativenavigation.utils.Task;
13 16
 
14 17
 public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutListener {
15 18
 
@@ -17,7 +20,7 @@ public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutL
17 20
 	private final String id;
18 21
 
19 22
 	private View view;
20
-	private StackController parentStackController;
23
+	private ParentController parentController;
21 24
 	private boolean isShown = false;
22 25
 
23 26
 	public ViewController(Activity activity, String id) {
@@ -27,7 +30,8 @@ public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutL
27 30
 
28 31
 	protected abstract View createView();
29 32
 
30
-	void ensureViewIsCreated() {
33
+	@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
34
+	public void ensureViewIsCreated() {
31 35
 		getView();
32 36
 	}
33 37
 
@@ -39,15 +43,33 @@ public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutL
39 43
 		return activity;
40 44
 	}
41 45
 
46
+    protected ViewController getParentController() {
47
+	    return parentController;
48
+    }
49
+
42 50
 	@Nullable
43
-    StackController getParentStackController() {
44
-		return parentStackController;
51
+    ParentController getParentStackController() {
52
+		return parentController;
45 53
 	}
46 54
 
47
-	void setParentStackController(final StackController parentStackController) {
48
-		this.parentStackController = parentStackController;
55
+	public void setParentController(final ParentController parentController) {
56
+		this.parentController = parentController;
49 57
 	}
50 58
 
59
+    void performOnParentStack(Task<StackController> task) {
60
+	    if (parentController instanceof StackController) {
61
+            task.run((StackController) parentController);
62
+        }
63
+    }
64
+
65
+    void performOnParentStack(Task<StackController> accept, Runnable  reject) {
66
+        if (parentController instanceof StackController) {
67
+            accept.run((StackController) parentController);
68
+        } else {
69
+            reject.run();
70
+        }
71
+    }
72
+
51 73
 	@NonNull
52 74
 	public View getView() {
53 75
 		if (view == null) {
@@ -72,12 +94,16 @@ public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutL
72 94
 	}
73 95
 
74 96
 	public void onViewAppeared() {
75
-		//
76
-	}
97
+        isShown = true;
98
+    }
77 99
 
78 100
 	public void onViewDisappear() {
79
-		//
80
-	}
101
+        isShown = false;
102
+    }
103
+
104
+    public void applyOptions(NavigationOptions options) {
105
+
106
+    }
81 107
 
82 108
 	public void destroy() {
83 109
 		if (isShown) {

+ 72
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabController.java Vedi File

@@ -0,0 +1,72 @@
1
+package com.reactnativenavigation.viewcontrollers.toptabs;
2
+
3
+import android.app.Activity;
4
+import android.view.View;
5
+
6
+import com.reactnativenavigation.parse.NavigationOptions;
7
+import com.reactnativenavigation.presentation.NavigationOptionsListener;
8
+import com.reactnativenavigation.viewcontrollers.ContainerViewController;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+import com.reactnativenavigation.views.TopTab;
11
+
12
+public class TopTabController extends ViewController implements NavigationOptionsListener {
13
+
14
+    private final String containerName;
15
+    private ContainerViewController.ReactViewCreator viewCreator;
16
+    private final NavigationOptions options;
17
+    private TopTab topTab;
18
+    private boolean isSelectedTab;
19
+
20
+    public TopTabController(Activity activity, String id, String name, ContainerViewController.ReactViewCreator viewCreator, NavigationOptions initialOptions) {
21
+        super(activity, id);
22
+        this.containerName = name;
23
+        this.viewCreator = viewCreator;
24
+        this.options = initialOptions;
25
+    }
26
+
27
+    @Override
28
+    public void onViewAppeared() {
29
+        super.onViewAppeared();
30
+        isSelectedTab = true;
31
+        applyOptions(options);
32
+        topTab.sendContainerStart();
33
+    }
34
+
35
+    @Override
36
+    public void applyOptions(NavigationOptions options) {
37
+        getParentController().applyOptions(options);
38
+    }
39
+
40
+    @Override
41
+    public void onViewDisappear() {
42
+        super.onViewDisappear();
43
+        isSelectedTab = false;
44
+        topTab.sendContainerStop();
45
+    }
46
+
47
+    @Override
48
+    protected boolean isViewShown() {
49
+        return super.isViewShown() && isSelectedTab;
50
+    }
51
+
52
+    @Override
53
+    public View createView() {
54
+        topTab = new TopTab(
55
+                getActivity(),
56
+                viewCreator.create(getActivity(), getId(), containerName)
57
+        );
58
+        return topTab;
59
+    }
60
+
61
+    @Override
62
+    public void destroy() {
63
+        super.destroy();
64
+        if (topTab != null) topTab.destroy();
65
+        topTab = null;
66
+    }
67
+
68
+    @Override
69
+    public void mergeNavigationOptions(NavigationOptions options) {
70
+        this.options.mergeWith(options);
71
+    }
72
+}

+ 22
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java Vedi File

@@ -1,17 +1,17 @@
1 1
 package com.reactnativenavigation.viewcontrollers.toptabs;
2 2
 
3 3
 import android.support.v4.view.PagerAdapter;
4
+import android.support.v4.view.ViewPager;
4 5
 import android.view.View;
5 6
 import android.view.ViewGroup;
6 7
 
7
-import com.reactnativenavigation.viewcontrollers.ViewController;
8
-
9 8
 import java.util.List;
10 9
 
11
-public class TopTabsAdapter extends PagerAdapter {
12
-    private List<ViewController> tabs;
10
+public class TopTabsAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener {
11
+    private List<TopTabController> tabs;
12
+    private int currentPage = 0;
13 13
 
14
-    TopTabsAdapter(List<ViewController> tabs) {
14
+    TopTabsAdapter(List<TopTabController> tabs) {
15 15
         this.tabs = tabs;
16 16
     }
17 17
 
@@ -29,4 +29,21 @@ public class TopTabsAdapter extends PagerAdapter {
29 29
     public Object instantiateItem(ViewGroup container, int position) {
30 30
         return tabs.get(position).getView();
31 31
     }
32
+
33
+    @Override
34
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
35
+
36
+    }
37
+
38
+    @Override
39
+    public void onPageSelected(int position) {
40
+        tabs.get(currentPage).onViewDisappear();
41
+        tabs.get(position).onViewAppeared();
42
+        currentPage = position;
43
+    }
44
+
45
+    @Override
46
+    public void onPageScrollStateChanged(int state) {
47
+
48
+    }
32 49
 }

+ 33
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java Vedi File

@@ -4,6 +4,8 @@ import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5 5
 import android.view.ViewGroup;
6 6
 
7
+import com.reactnativenavigation.parse.NavigationOptions;
8
+import com.reactnativenavigation.presentation.NavigationOptionsListener;
7 9
 import com.reactnativenavigation.viewcontrollers.ParentController;
8 10
 import com.reactnativenavigation.viewcontrollers.ViewController;
9 11
 import com.reactnativenavigation.views.TopTabsLayout;
@@ -11,29 +13,53 @@ import com.reactnativenavigation.views.TopTabsLayout;
11 13
 import java.util.Collection;
12 14
 import java.util.List;
13 15
 
14
-public class TopTabsController extends ParentController {
16
+public class TopTabsController extends ParentController implements NavigationOptionsListener {
15 17
 
16
-    private List<ViewController> tabs;
18
+    private List<TopTabController> tabs;
19
+    private TopTabsLayout topTabsLayout;
17 20
 
18
-    public TopTabsController(Activity activity, String id, List<ViewController> tabs) {
21
+    public TopTabsController(Activity activity, String id, List<TopTabController> tabs) {
19 22
         super(activity, id);
20 23
         this.tabs = tabs;
24
+        for (ViewController tab : tabs) {
25
+            tab.setParentController(this);
26
+        }
21 27
     }
22 28
 
23 29
     @NonNull
24 30
     @Override
25 31
     protected ViewGroup createView() {
26
-        return new TopTabsLayout(getActivity(), tabs);
32
+        topTabsLayout = new TopTabsLayout(getActivity(), tabs);
33
+        return topTabsLayout;
27 34
     }
28 35
 
29 36
     @NonNull
30 37
     @Override
31
-    public Collection<ViewController> getChildControllers() {
38
+    public Collection<? extends ViewController> getChildControllers() {
32 39
         return tabs;
33 40
     }
34 41
 
35 42
     @Override
36
-    protected boolean isViewShown() {
37
-        return super.isViewShown();
43
+    public void onViewAppeared() {
44
+        topTabsLayout.performOnCurrentTab(TopTabController::onViewAppeared);
45
+    }
46
+
47
+    @Override
48
+    public void onViewDisappear() {
49
+        topTabsLayout.performOnCurrentTab(TopTabController::onViewDisappear);
50
+    }
51
+
52
+    @Override
53
+    public void applyOptions(NavigationOptions options) {
54
+        topTabsLayout.applyOptions(options);
55
+    }
56
+
57
+    @Override
58
+    public void mergeNavigationOptions(NavigationOptions options) {
59
+
60
+    }
61
+
62
+    public void switchToTab(int index) {
63
+        topTabsLayout.switchToTab(index);
38 64
     }
39 65
 }

+ 12
- 31
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsViewPager.java Vedi File

@@ -3,10 +3,8 @@ package com.reactnativenavigation.viewcontrollers.toptabs;
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.support.v4.view.ViewPager;
6
-import android.view.View;
7 6
 import android.view.ViewGroup;
8 7
 
9
-import com.reactnativenavigation.viewcontrollers.ContainerViewController;
10 8
 import com.reactnativenavigation.viewcontrollers.ViewController;
11 9
 
12 10
 import java.util.List;
@@ -14,46 +12,29 @@ import java.util.List;
14 12
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
15 13
 
16 14
 @SuppressLint("ViewConstructor")
17
-public class TopTabsViewPager extends ViewPager implements ContainerViewController.IReactView {
15
+public class TopTabsViewPager extends ViewPager {
18 16
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
19
-    private final List<ViewController> tabs;
17
+    private List<TopTabController> tabs;
20 18
 
21
-    public TopTabsViewPager(Context context, List<ViewController> tabs) {
19
+    public TopTabsViewPager(Context context, List<TopTabController> tabs) {
22 20
         super(context);
23 21
         this.tabs = tabs;
24
-        init(tabs);
22
+        init();
25 23
     }
26 24
 
27
-    private void init(List<ViewController> tabs) {
25
+    private void init() {
28 26
         setOffscreenPageLimit(OFFSCREEN_PAGE_LIMIT);
29 27
         for (ViewController tab : tabs) {
30 28
             addView(tab.getView(), new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
31 29
         }
32
-        setAdapter(new TopTabsAdapter(tabs));
30
+        TopTabsAdapter adapter = new TopTabsAdapter(tabs);
31
+        setAdapter(adapter);
32
+        addOnPageChangeListener(adapter);
33 33
     }
34 34
 
35
-    @Override
36
-    public boolean isReady() {
37
-        return false;
38
-    }
39
-
40
-    @Override
41
-    public View asView() {
42
-        return null;
43
-    }
44
-
45
-    @Override
46 35
     public void destroy() {
47
-
48
-    }
49
-
50
-    @Override
51
-    public void sendContainerStart() {
52
-
53
-    }
54
-
55
-    @Override
56
-    public void sendContainerStop() {
57
-
36
+        for (ViewController tab : tabs) {
37
+            tab.destroy();
38
+        }
58 39
     }
59
-}
40
+}

+ 7
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/Container.java Vedi File

@@ -1,7 +1,12 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3
-import com.reactnativenavigation.viewcontrollers.ContainerViewController;
3
+import android.support.annotation.RestrictTo;
4 4
 
5
-public interface Container extends ContainerViewController.IReactView {
5
+import com.reactnativenavigation.parse.NavigationOptions;
6
+
7
+public interface Container {
8
+    void applyOptions(NavigationOptions options);
9
+
10
+    @RestrictTo(RestrictTo.Scope.TESTS)
6 11
     TopBar getTopBar();
7 12
 }

+ 17
- 12
lib/android/app/src/main/java/com/reactnativenavigation/views/ContainerLayout.java Vedi File

@@ -1,21 +1,28 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3
+import android.annotation.SuppressLint;
3 4
 import android.content.Context;
5
+import android.support.annotation.RestrictTo;
4 6
 import android.view.View;
5 7
 import android.widget.LinearLayout;
6 8
 
9
+import com.reactnativenavigation.parse.NavigationOptions;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
7 11
 import com.reactnativenavigation.viewcontrollers.ContainerViewController.IReactView;
8 12
 
9
-public class ContainerLayout extends LinearLayout implements Container {
13
+@SuppressLint("ViewConstructor")
14
+public class ContainerLayout extends LinearLayout implements ReactContainer {
10 15
 
11 16
 	private TopBar topBar;
12 17
 	private IReactView reactView;
18
+	private final OptionsPresenter optionsPresenter;
13 19
 
14 20
 	public ContainerLayout(Context context, IReactView reactView) {
15 21
 		super(context);
16 22
 		this.topBar = new TopBar(context);
17 23
 		this.reactView = reactView;
18
-		initViews();
24
+        optionsPresenter = new OptionsPresenter(topBar, this);
25
+        initViews();
19 26
 	}
20 27
 
21 28
 	private void initViews() {
@@ -24,10 +31,6 @@ public class ContainerLayout extends LinearLayout implements Container {
24 31
 		addView(reactView.asView());
25 32
 	}
26 33
 
27
-	public ContainerLayout(Context context) {
28
-		super(context);
29
-	}
30
-
31 34
 	@Override
32 35
 	public boolean isReady() {
33 36
 		return reactView.isReady();
@@ -54,11 +57,13 @@ public class ContainerLayout extends LinearLayout implements Container {
54 57
 	}
55 58
 
56 59
     @Override
57
-	public TopBar getTopBar() {
58
-		return topBar;
59
-	}
60
+    public void applyOptions(NavigationOptions options) {
61
+        optionsPresenter.applyOptions(options);
62
+    }
60 63
 
61
-	public IReactView getReactView() {
62
-		return reactView;
63
-	}
64
+    @Override
65
+    @RestrictTo(RestrictTo.Scope.TESTS)
66
+    public TopBar getTopBar() {
67
+        return topBar;
68
+    }
64 69
 }

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/ReactContainer.java Vedi File

@@ -0,0 +1,6 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import com.reactnativenavigation.viewcontrollers.ContainerViewController;
4
+
5
+public interface ReactContainer extends Container, ContainerViewController.IReactView {
6
+}

+ 4
- 10
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTab.java Vedi File

@@ -1,27 +1,21 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3
+import android.annotation.SuppressLint;
3 4
 import android.content.Context;
4 5
 import android.view.View;
5 6
 import android.widget.FrameLayout;
6 7
 
7 8
 import com.reactnativenavigation.viewcontrollers.ContainerViewController.IReactView;
8 9
 
10
+@SuppressLint("ViewConstructor")
9 11
 public class TopTab extends FrameLayout implements IReactView {
10 12
 
11
-	private IReactView reactView;
13
+	private final IReactView reactView;
12 14
 
13 15
 	public TopTab(Context context, IReactView reactView) {
14 16
 		super(context);
15 17
 		this.reactView = reactView;
16
-		initViews();
17
-	}
18
-
19
-	private void initViews() {
20
-		addView(reactView.asView());
21
-	}
22
-
23
-	public TopTab(Context context) {
24
-		super(context);
18
+        addView(reactView.asView());
25 19
 	}
26 20
 
27 21
 	@Override

+ 37
- 3
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java Vedi File

@@ -2,23 +2,32 @@ package com.reactnativenavigation.views;
2 2
 
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5
+import android.support.annotation.RestrictTo;
6
+import android.support.v4.view.ViewPager;
5 7
 import android.widget.LinearLayout;
6 8
 
7
-import com.reactnativenavigation.viewcontrollers.ViewController;
9
+import com.reactnativenavigation.parse.NavigationOptions;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
11
+import com.reactnativenavigation.utils.Task;
12
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
8 13
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
9 14
 
10 15
 import java.util.List;
11 16
 
12 17
 @SuppressLint("ViewConstructor")
13
-public class TopTabsLayout extends LinearLayout {
18
+public class TopTabsLayout extends LinearLayout implements Container {
14 19
 
15 20
     private TopBar topBar;
21
+    private List<TopTabController> tabs;
16 22
     private TopTabsViewPager viewPager;
23
+    private final OptionsPresenter optionsPresenter;
17 24
 
18
-    public TopTabsLayout(Context context, List<ViewController> tabs) {
25
+    public TopTabsLayout(Context context, List<TopTabController> tabs) {
19 26
         super(context);
20 27
         topBar = new TopBar(context);
28
+        this.tabs = tabs;
21 29
         viewPager = new TopTabsViewPager(context, tabs);
30
+        optionsPresenter = new OptionsPresenter(topBar, this);
22 31
         initViews();
23 32
     }
24 33
 
@@ -27,4 +36,29 @@ public class TopTabsLayout extends LinearLayout {
27 36
         addView(topBar);
28 37
         addView(viewPager);
29 38
     }
39
+
40
+    @Override
41
+    public void applyOptions(NavigationOptions options) {
42
+        optionsPresenter.applyOptions(options);
43
+    }
44
+
45
+    @Override
46
+    @RestrictTo(RestrictTo.Scope.TESTS)
47
+    public TopBar getTopBar() {
48
+        return topBar;
49
+    }
50
+
51
+    @RestrictTo(RestrictTo.Scope.TESTS)
52
+    public ViewPager getViewPager() {
53
+        return viewPager;
54
+    }
55
+
56
+
57
+    public void performOnCurrentTab(Task<TopTabController> task) {
58
+        task.run(tabs.get(viewPager.getCurrentItem()));
59
+    }
60
+
61
+    public void switchToTab(int index) {
62
+        viewPager.setCurrentItem(index);
63
+    }
30 64
 }

+ 16
- 9
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestContainerLayout.java Vedi File

@@ -3,18 +3,25 @@ package com.reactnativenavigation.mocks;
3 3
 import android.content.Context;
4 4
 import android.view.View;
5 5
 
6
-import com.reactnativenavigation.viewcontrollers.ContainerViewController;
7
-import com.reactnativenavigation.views.Container;
6
+import com.reactnativenavigation.parse.NavigationOptions;
7
+import com.reactnativenavigation.presentation.OptionsPresenter;
8
+import com.reactnativenavigation.views.ReactContainer;
8 9
 import com.reactnativenavigation.views.TopBar;
9 10
 
10
-public class TestContainerLayout extends View implements ContainerViewController.IReactView, Container {
11
+public class TestContainerLayout extends View implements ReactContainer {
11 12
 
12
-	private TopBar topBar;
13
+    private final TopBar topBar;
14
+    private final OptionsPresenter optionsPresenter;
13 15
 
14
-	public TestContainerLayout(final Context context) {
16
+    public TestContainerLayout(final Context context) {
15 17
 		super(context);
16
-		topBar = new TopBar(context);
17
-	}
18
+        topBar = new TopBar(context);
19
+        optionsPresenter = new OptionsPresenter(topBar, this);
20
+    }
21
+
22
+    public TopBar getTopBar() {
23
+        return topBar;
24
+    }
18 25
 
19 26
     @Override
20 27
 	public boolean isReady() {
@@ -39,7 +46,7 @@ public class TestContainerLayout extends View implements ContainerViewController
39 46
 	}
40 47
 
41 48
     @Override
42
-    public TopBar getTopBar() {
43
-        return topBar;
49
+    public void applyOptions(NavigationOptions options) {
50
+        optionsPresenter.applyOptions(options);
44 51
     }
45 52
 }

+ 38
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopTabLayoutMock.java Vedi File

@@ -0,0 +1,38 @@
1
+package com.reactnativenavigation.mocks;
2
+
3
+import android.content.Context;
4
+import android.view.View;
5
+
6
+import com.reactnativenavigation.viewcontrollers.ContainerViewController;
7
+
8
+public class TopTabLayoutMock extends View implements ContainerViewController.IReactView {
9
+
10
+    public TopTabLayoutMock(Context context) {
11
+        super(context);
12
+    }
13
+
14
+    @Override
15
+    public boolean isReady() {
16
+        return false;
17
+    }
18
+
19
+    @Override
20
+    public View asView() {
21
+        return this;
22
+    }
23
+
24
+    @Override
25
+    public void destroy() {
26
+
27
+    }
28
+
29
+    @Override
30
+    public void sendContainerStart() {
31
+
32
+    }
33
+
34
+    @Override
35
+    public void sendContainerStop() {
36
+
37
+    }
38
+}

+ 7
- 7
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java Vedi File

@@ -26,13 +26,13 @@ public class OptionsApplyingTest extends BaseTest {
26 26
 		activity = newActivity();
27 27
 		initialNavigationOptions = new NavigationOptions();
28 28
 		view = spy(new TestContainerLayout(activity));
29
-		uut = new ContainerViewController(activity, "containerId1", "containerName",
30
-                new ContainerViewController.ReactViewCreator() {
31
-                    @Override
32
-                    public ContainerViewController.IReactView create(final Activity activity1, final String containerId, final String containerName) {
33
-                        return view;
34
-                    }
35
-                }, initialNavigationOptions);
29
+		uut = new ContainerViewController(activity,
30
+                "containerId1",
31
+                "containerName",
32
+                (activity1, containerId, containerName) -> view,
33
+                initialNavigationOptions
34
+        );
35
+		uut.ensureViewIsCreated();
36 36
 	}
37 37
 
38 38
 	@Test

+ 122
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java Vedi File

@@ -0,0 +1,122 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+import android.view.ViewGroup;
5
+
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.TopTabLayoutMock;
8
+import com.reactnativenavigation.parse.NavigationOptions;
9
+import com.reactnativenavigation.viewcontrollers.ContainerViewController.IReactView;
10
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
11
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
12
+import com.reactnativenavigation.views.TopTabsLayout;
13
+
14
+import org.junit.Test;
15
+
16
+import java.util.ArrayList;
17
+import java.util.List;
18
+
19
+import static org.mockito.Mockito.spy;
20
+import static org.mockito.Mockito.times;
21
+import static org.mockito.Mockito.verify;
22
+
23
+public class TopTabsViewControllerTest extends BaseTest {
24
+
25
+    private static final int SIZE = 2;
26
+
27
+    private TopTabsController uut;
28
+    private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
29
+    private List<TopTabController> tabControllers = new ArrayList<>(SIZE);
30
+    private List<NavigationOptions> tabOptions = new ArrayList<>(SIZE);
31
+
32
+    @Override
33
+    public void beforeEach() {
34
+        super.beforeEach();
35
+        tabControllers.clear();
36
+        tabs.clear();
37
+        Activity activity = newActivity();
38
+        createTabs(activity);
39
+        uut = new TopTabsController(activity, "containerId", tabControllers);
40
+    }
41
+
42
+    private void createTabs(Activity activity) {
43
+        for (int i = 0; i < SIZE; i++) {
44
+            TopTabLayoutMock reactView = spy(new TopTabLayoutMock(activity));
45
+            tabs.add(reactView);
46
+            tabOptions.add(new NavigationOptions());
47
+            tabControllers.add(spy(new TopTabController(activity,
48
+                    "idTab" + i,
49
+                    "tab" + i,
50
+                    (activity1, containerId, containerName) -> reactView,
51
+                    tabOptions.get(i))
52
+            ));
53
+        }
54
+    }
55
+
56
+    @Test
57
+    public void createsViewFromContainerViewCreator() throws Exception {
58
+        uut.ensureViewIsCreated();
59
+        for (int i = 0; i < SIZE; i++) {
60
+            verify(tabControllers.get(i), times(1)).createView();
61
+        }
62
+    }
63
+
64
+    @Test
65
+    public void containerViewDestroyedOnDestroy() throws Exception {
66
+        uut.ensureViewIsCreated();
67
+        TopTabsLayout topTabs = (TopTabsLayout) uut.getView();
68
+        for (int i = 0; i < SIZE; i++) {
69
+            verify(tab(topTabs, i), times(0)).destroy();
70
+        }
71
+        uut.destroy();
72
+        for (ViewController tabController : tabControllers) {
73
+            verify(tabController, times(1)).destroy();
74
+        }
75
+    }
76
+
77
+    @Test
78
+    public void lifecycleMethodsSentWhenSelectedTabChanges() throws Exception {
79
+        uut.ensureViewIsCreated();
80
+        TopTabLayoutMock initialTab = tabs.get(0);
81
+        TopTabLayoutMock selectedTab = tabs.get(1);
82
+        uut.onViewAppeared();
83
+        uut.switchToTab(1);
84
+        verify(initialTab, times(1)).sendContainerStop();
85
+        verify(selectedTab, times(1)).sendContainerStart();
86
+        verify(selectedTab, times(0)).sendContainerStop();
87
+    }
88
+
89
+    @Test
90
+    public void lifecycleMethodsSentWhenSelectedPreviouslySelectedTab() throws Exception {
91
+        uut.ensureViewIsCreated();
92
+        uut.onViewAppeared();
93
+        uut.switchToTab(1);
94
+        uut.switchToTab(0);
95
+        verify(tabs.get(0), times(1)).sendContainerStop();
96
+        verify(tabs.get(0), times(2)).sendContainerStart();
97
+        verify(tabs.get(1), times(1)).sendContainerStart();
98
+        verify(tabs.get(1), times(1)).sendContainerStop();
99
+    }
100
+
101
+    @Test
102
+    public void setOptionsOfInitialTab() throws Exception {
103
+        uut.ensureViewIsCreated();
104
+        uut.onViewAppeared();
105
+        verify(tabControllers.get(0), times(1)).applyOptions(tabOptions.get(0));
106
+    }
107
+
108
+    @Test
109
+    public void setOptionsWhenTabChanges() throws Exception {
110
+        uut.ensureViewIsCreated();
111
+        uut.onViewAppeared();
112
+        verify(tabControllers.get(0), times(1)).applyOptions(tabOptions.get(0));
113
+        uut.switchToTab(1);
114
+        verify(tabControllers.get(1), times(1)).applyOptions(tabOptions.get(1));
115
+        uut.switchToTab(0);
116
+        verify(tabControllers.get(0), times(2)).applyOptions(tabOptions.get(0));
117
+    }
118
+
119
+    private IReactView tab(TopTabsLayout topTabs, final int index) {
120
+        return (IReactView) ((ViewGroup) topTabs.getViewPager().getChildAt(index)).getChildAt(0);
121
+    }
122
+}

+ 3
- 3
lib/src/commands/LayoutTreeParser.js Vedi File

@@ -13,7 +13,7 @@ class LayoutTreeParser {
13 13
       return this._createTabs(simpleJsonApi.bottomTabs);
14 14
     }
15 15
     if (simpleJsonApi.topTabs) {
16
-      return this._createTopTabsContainer(simpleJsonApi.topTabs);
16
+      return this._createTopTabs(simpleJsonApi.topTabs);
17 17
     }
18 18
     if (simpleJsonApi.name) {
19 19
       return this._createContainer(simpleJsonApi);
@@ -36,9 +36,9 @@ class LayoutTreeParser {
36 36
     };
37 37
   }
38 38
 
39
-  _createTopTabsContainer(topTabs) {
39
+  _createTopTabs(topTabs) {
40 40
     return {
41
-      type: LayoutTypes.TopTabsContainer,
41
+      type: LayoutTypes.TopTabs,
42 42
       children: _.map(topTabs, (t) => this._createTopTab(t.container))
43 43
     };
44 44
   }

+ 1
- 1
lib/src/commands/LayoutTreeParser.test.js Vedi File

@@ -289,7 +289,7 @@ describe('LayoutTreeParser', () => {
289 289
     it('parses bottomTabs with side menus', () => {
290 290
       expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithTopTabs))
291 291
         .toEqual({
292
-          type: 'TopTabsContainer',
292
+          type: 'TopTabs',
293 293
           children: [
294 294
             {
295 295
               type: 'TopTab',

+ 1
- 1
lib/src/commands/LayoutTypes.js Vedi File

@@ -7,6 +7,6 @@ module.exports = {
7 7
   SideMenuLeft: 'SideMenuLeft',
8 8
   SideMenuRight: 'SideMenuRight',
9 9
   CustomDialog: 'CustomDialog',
10
-  TopTabsContainer: 'TopTabsContainer',
10
+  TopTabs: 'TopTabs',
11 11
   TopTab: 'TopTab'
12 12
 };

+ 5
- 0
playground/android/app/build.gradle Vedi File

@@ -11,6 +11,11 @@ android {
11 11
     compileSdkVersion 25
12 12
     buildToolsVersion "27.0.1"
13 13
 
14
+    compileOptions {
15
+        sourceCompatibility JavaVersion.VERSION_1_8
16
+        targetCompatibility JavaVersion.VERSION_1_8
17
+    }
18
+
14 19
     defaultConfig {
15 20
         applicationId "com.reactnativenavigation.playground"
16 21
         minSdkVersion 19

+ 52
- 0
playground/src/containers/TopTabOptionsScreen.js Vedi File

@@ -0,0 +1,52 @@
1
+const React = require('react');
2
+const { PureComponent } = require('react');
3
+
4
+const { View, Text } = require('react-native');
5
+
6
+class TopTabOptionsScreen extends PureComponent {
7
+  static get navigationOptions() {
8
+    return {
9
+      topBar: {
10
+        title: 'Tab 1',
11
+        textColor: 'black',
12
+        textFontSize: 16,
13
+        textFontFamily: 'HelveticaNeue-Italic'
14
+      }
15
+    };
16
+  }
17
+
18
+  render() {
19
+    return (
20
+      <View style={styles.root}>
21
+        <Text style={styles.h1}>{this.props.text || 'Top Tab Screen'}</Text>
22
+        <Text style={styles.footer}>{`this.props.containerId = ${this.props.containerId}`}</Text>
23
+      </View>
24
+    );
25
+  }
26
+}
27
+
28
+const styles = {
29
+  root: {
30
+    flexGrow: 1,
31
+    justifyContent: 'center',
32
+    alignItems: 'center',
33
+    backgroundColor: '#f5fcff'
34
+  },
35
+  h1: {
36
+    fontSize: 24,
37
+    textAlign: 'center',
38
+    margin: 10
39
+  },
40
+  h2: {
41
+    fontSize: 12,
42
+    textAlign: 'center',
43
+    margin: 10
44
+  },
45
+  footer: {
46
+    fontSize: 10,
47
+    color: '#888',
48
+    marginTop: 10
49
+  }
50
+};
51
+
52
+module.exports = TopTabOptionsScreen;

+ 20
- 0
playground/src/containers/TopTabScreen.js Vedi File

@@ -1,8 +1,28 @@
1 1
 const React = require('react');
2 2
 const { PureComponent } = require('react');
3 3
 const { View, Text } = require('react-native');
4
+const Navigation = require('react-native-navigation');
4 5
 
5 6
 class TopTabScreen extends PureComponent {
7
+  static get navigationOptions() {
8
+    return {
9
+      topBar: {
10
+        textColor: 'black',
11
+        textFontSize: 16,
12
+        textFontFamily: 'HelveticaNeue-Italic'
13
+      }
14
+    };
15
+  }
16
+
17
+  constructor(props) {
18
+    super(props);
19
+    Navigation.setOptions(this.props.containerId, {
20
+      topBar: {
21
+        title: this.props.title
22
+      }
23
+    });
24
+  }
25
+
6 26
   render() {
7 27
     return (
8 28
       <View style={styles.root}>

+ 4
- 1
playground/src/containers/WelcomeScreen.js Vedi File

@@ -152,8 +152,9 @@ class WelcomeScreen extends Component {
152 152
       topTabs: [
153 153
         {
154 154
           container: {
155
-            name: 'navigation.playground.TopTabScreen',
155
+            name: 'navigation.playground.TopTabOptionsScreen',
156 156
             passProps: {
157
+              title: 'Tab 1',
157 158
               text: 'This is top tab 1'
158 159
             }
159 160
           }
@@ -162,6 +163,7 @@ class WelcomeScreen extends Component {
162 163
           container: {
163 164
             name: 'navigation.playground.TopTabScreen',
164 165
             passProps: {
166
+              title: 'Tab 2',
165 167
               text: 'This is top tab 2'
166 168
             }
167 169
           }
@@ -170,6 +172,7 @@ class WelcomeScreen extends Component {
170 172
           container: {
171 173
             name: 'navigation.playground.TopTabScreen',
172 174
             passProps: {
175
+              title: 'Tab 3',
173 176
               text: 'This is top tab 3'
174 177
             }
175 178
           }

+ 2
- 0
playground/src/containers/index.js Vedi File

@@ -14,6 +14,7 @@ const CustomDialog = require('./CustomDialog');
14 14
 const BandHandlerScreen = require('./BackHandlerScreen');
15 15
 const SideMenuScreen = require('./SideMenuScreen');
16 16
 const TopTabScreen = require('./TopTabScreen');
17
+const TopTabOptionsScreen = require('./TopTabOptionsScreen');
17 18
 
18 19
 function registerContainers() {
19 20
   Navigation.registerContainer(`navigation.playground.CustomTransitionDestination`, () => CustomTransitionDestination);
@@ -31,6 +32,7 @@ function registerContainers() {
31 32
   Navigation.registerContainer('navigation.playground.BackHandlerScreen', () => BandHandlerScreen);
32 33
   Navigation.registerContainer('navigation.playground.SideMenuScreen', () => SideMenuScreen);
33 34
   Navigation.registerContainer('navigation.playground.TopTabScreen', () => TopTabScreen);
35
+  Navigation.registerContainer('navigation.playground.TopTabOptionsScreen', () => TopTabOptionsScreen);
34 36
 }
35 37
 
36 38
 module.exports = {