Browse Source

Android fixes (#2583)

Continued work on Android

* Rename TopTabsLayout to TopTabsViewPager
* Pass ScrollEventListener from component
* Change ViewController.view to protected
* Don't keep ref to view in ComponentController
* Pull IReactView and ReactViewCreator to separate classes
* Ensure ToolBar.menu is created before clearing menu items
* Unmount react view before destroy
* Let TopTabsController set mediator in view visibility handling
Guy Carmeli 6 years ago
parent
commit
16da8968cf
No account linked to committer's email address
43 changed files with 597 additions and 556 deletions
  1. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java
  2. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  3. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactComponentViewCreator.java
  4. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  5. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/DeviceScreen.java
  6. 9
    9
      lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java
  7. 17
    55
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  8. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Destroyable.java
  9. 23
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IReactView.java
  10. 7
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  11. 8
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ReactViewCreator.java
  12. 116
    87
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  13. 15
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewVisibilityListenerAdapter.java
  14. 0
    27
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/DialogViewController.java
  15. 0
    80
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabController.java
  16. 23
    12
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  17. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  18. 5
    4
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentViewCreator.java
  19. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/ReactComponent.java
  20. 4
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  21. 0
    62
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTab.java
  22. 0
    23
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabCreator.java
  23. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayoutCreator.java
  24. 13
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsViewPager.java
  25. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/touch/OverlayTouchDelegate.java
  26. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleComponentViewController.java
  27. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleOverlay.java
  28. 77
    6
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java
  29. 9
    23
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java
  30. 10
    5
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentViewCreator.java
  31. 57
    0
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestReactView.java
  32. 19
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopTabLayoutMock.java
  33. 5
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  34. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  35. 13
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  36. 10
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  37. 3
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  38. 0
    51
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabControllerTest.java
  39. 100
    51
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  40. 5
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  41. 0
    1
      playground/src/screens/TopTabOptionsScreen.js
  42. 0
    10
      playground/src/screens/TopTabScreen.js
  43. 9
    0
      playground/src/screens/WelcomeScreen.js

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java View File

@@ -16,8 +16,9 @@ public class TopBarCollapseBehavior implements ScrollEventListener.OnScrollListe
16 16
         this.animator = new TopBarAnimator(topBar);
17 17
     }
18 18
 
19
-    public void enableCollapse() {
20
-        scrollEventListener.register(topBar, this, this);
19
+    public void enableCollapse(ScrollEventListener scrollEventListener) {
20
+        this.scrollEventListener = scrollEventListener;
21
+        this.scrollEventListener.register(topBar, this, this);
21 22
     }
22 23
 
23 24
     public void disableCollapse() {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

@@ -49,7 +49,7 @@ public class OptionsPresenter {
49 49
         }
50 50
 
51 51
         if (options.hideOnScroll == True) {
52
-            topBar.enableCollapse();
52
+            topBar.enableCollapse(component.getScrollEventListener());
53 53
         } else if (options.hideOnScroll == False) {
54 54
             topBar.disableCollapse();
55 55
         }

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactComponentViewCreator.java View File

@@ -3,8 +3,8 @@ package com.reactnativenavigation.react;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
7
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.ReactViewCreator;
6
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
7
+import com.reactnativenavigation.viewcontrollers.IReactView;
8 8
 
9 9
 public class ReactComponentViewCreator implements ReactViewCreator {
10 10
 	private ReactInstanceManager reactInstanceManager;
@@ -14,7 +14,7 @@ public class ReactComponentViewCreator implements ReactViewCreator {
14 14
 	}
15 15
 
16 16
 	@Override
17
-	public ComponentViewController.IReactView create(final Activity activity, final String componentId, final String componentName) {
17
+	public IReactView create(final Activity activity, final String componentId, final String componentName) {
18 18
 		return new ReactView(activity, reactInstanceManager, componentId, componentName);
19 19
 	}
20 20
 }

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java View File

@@ -13,10 +13,10 @@ import com.facebook.react.uimanager.JSTouchDispatcher;
13 13
 import com.facebook.react.uimanager.UIManagerModule;
14 14
 import com.facebook.react.uimanager.events.EventDispatcher;
15 15
 import com.reactnativenavigation.interfaces.ScrollEventListener;
16
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
16
+import com.reactnativenavigation.viewcontrollers.IReactView;
17 17
 
18 18
 @SuppressLint("ViewConstructor")
19
-public class ReactView extends ReactRootView implements ComponentViewController.IReactView {
19
+public class ReactView extends ReactRootView implements IReactView {
20 20
 
21 21
 	private final ReactInstanceManager reactInstanceManager;
22 22
 	private final String componentId;

+ 13
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/DeviceScreen.java View File

@@ -0,0 +1,13 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.content.res.Resources;
4
+
5
+public class DeviceScreen {
6
+    public static int width(Resources resources) {
7
+        return resources.getDisplayMetrics().widthPixels;
8
+    }
9
+
10
+    public static int height(Resources resources) {
11
+        return resources.getDisplayMetrics().heightPixels;
12
+    }
13
+}

+ 9
- 9
lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java View File

@@ -13,15 +13,15 @@ import android.view.WindowManager;
13 13
 
14 14
 public class UiUtils {
15 15
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
16
-		view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
17
-			@Override
18
-			public boolean onPreDraw() {
19
-				view.getViewTreeObserver().removeOnPreDrawListener(this);
20
-				task.run();
21
-				return true;
22
-			}
23
-		});
24
-	}
16
+        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
17
+            @Override
18
+            public boolean onPreDraw() {
19
+                view.getViewTreeObserver().removeOnPreDrawListener(this);
20
+                task.run();
21
+                return true;
22
+            }
23
+        });
24
+    }
25 25
 
26 26
 	public static void tintDrawable(Drawable drawable, int tint) {
27 27
 		drawable.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_IN));

+ 17
- 55
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java View File

@@ -1,44 +1,18 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.app.*;
4
-import android.support.annotation.*;
5
-import android.view.*;
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
6 5
 
7
-import com.reactnativenavigation.interfaces.ScrollEventListener;
8
-import com.reactnativenavigation.parse.*;
9
-import com.reactnativenavigation.presentation.*;
10
-import com.reactnativenavigation.views.*;
6
+import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.presentation.NavigationOptionsListener;
8
+import com.reactnativenavigation.views.ComponentLayout;
9
+import com.reactnativenavigation.views.ReactComponent;
11 10
 
12
-public class ComponentViewController extends ViewController implements NavigationOptionsListener {
13
-
14
-    public interface ReactViewCreator {
15
-
16
-        IReactView create(Activity activity, String componentId, String componentName);
17
-    }
18
-
19
-    public interface IReactView {
20
-
21
-        boolean isReady();
22
-
23
-        View asView();
24
-
25
-        void destroy();
26
-
27
-        void sendComponentStart();
28
-
29
-        void sendComponentStop();
30
-
31
-        void sendOnNavigationButtonPressed(String buttonId);
32
-
33
-        ScrollEventListener getScrollEventListener();
34
-
35
-        void dispatchTouchEventToJs(MotionEvent event);
36
-    }
11
+public class ComponentViewController extends ViewController<ComponentLayout> implements NavigationOptionsListener {
37 12
 
38 13
     private final String componentName;
39 14
 
40 15
     private final ReactViewCreator viewCreator;
41
-    private ReactComponent component;
42 16
 
43 17
     public ComponentViewController(final Activity activity,
44 18
                                    final String id,
@@ -51,48 +25,36 @@ public class ComponentViewController extends ViewController implements Navigatio
51 25
         options = initialNavigationOptions;
52 26
     }
53 27
 
54
-    @Override
55
-    public void destroy() {
56
-        super.destroy();
57
-        if (component != null) component.destroy();
58
-        component = null;
59
-    }
60
-
61 28
     @Override
62 29
     public void onViewAppeared() {
63 30
         super.onViewAppeared();
64
-        ensureViewIsCreated();
65
-        component.applyOptions(options);
66
-        applyOnParentController(parentController -> {
67
-            parentController.clearOptions();
68
-            parentController.applyOptions(options, component);
69
-        });
70
-        component.sendComponentStart();
31
+        view.applyOptions(options);
32
+        view.sendComponentStart();
71 33
     }
72 34
 
73 35
     @Override
74 36
     public void onViewDisappear() {
37
+        view.sendComponentStop();
75 38
         super.onViewDisappear();
76
-        component.sendComponentStop();
77 39
     }
78 40
 
79 41
     @Override
80 42
     protected boolean isViewShown() {
81
-        return super.isViewShown() && component.isReady();
43
+        return super.isViewShown() && view.isReady();
82 44
     }
83 45
 
84 46
     @NonNull
85 47
     @Override
86
-    protected View createView() {
87
-        component = (ReactComponent) viewCreator.create(getActivity(), getId(), componentName);
88
-        return component.asView();
48
+    protected ComponentLayout createView() {
49
+        view = (ComponentLayout) viewCreator.create(getActivity(), getId(), componentName);
50
+        return (ComponentLayout) view.asView();
89 51
     }
90 52
 
91 53
     @Override
92 54
     public void mergeOptions(Options options) {
93 55
         this.options.mergeWith(options);
94
-        component.applyOptions(this.options);
95
-        applyOnParentController(parentController -> parentController.applyOptions(this.options, component));
56
+        view.applyOptions(this.options);
57
+        applyOnParentController(parentController -> parentController.applyOptions(this.options, view));
96 58
     }
97 59
 
98 60
     Options getOptions() {
@@ -100,6 +62,6 @@ public class ComponentViewController extends ViewController implements Navigatio
100 62
     }
101 63
 
102 64
     ReactComponent getComponent() {
103
-        return component;
65
+        return view;
104 66
     }
105 67
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Destroyable.java View File

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+public interface Destroyable {
4
+    void destroy();
5
+}

+ 23
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IReactView.java View File

@@ -0,0 +1,23 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.view.MotionEvent;
4
+import android.view.View;
5
+
6
+import com.reactnativenavigation.interfaces.ScrollEventListener;
7
+
8
+public interface IReactView extends Destroyable {
9
+
10
+    boolean isReady();
11
+
12
+    View asView();
13
+
14
+    void sendComponentStart();
15
+
16
+    void sendComponentStop();
17
+
18
+    void sendOnNavigationButtonPressed(String buttonId);
19
+
20
+    ScrollEventListener getScrollEventListener();
21
+
22
+    void dispatchTouchEventToJs(MotionEvent event);
23
+}

+ 7
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java View File

@@ -82,21 +82,21 @@ public class Navigator extends ParentController {
82 82
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
83 83
 		ViewController from = findControllerById(fromId);
84 84
 		if (from != null) {
85
-		    from.performOnParentStack(stack -> stack.animatePush(viewController, promise));
85
+		    from.performOnParentStack(stack -> ((StackController) stack).animatePush(viewController, promise));
86 86
 		}
87 87
 	}
88 88
 
89 89
 	void pop(final String fromId, Promise promise) {
90 90
 		ViewController from = findControllerById(fromId);
91 91
 		if (from != null) {
92
-		    from.performOnParentStack(stack -> stack.pop(promise));
92
+		    from.performOnParentStack(stack -> ((StackController) stack).pop(promise));
93 93
 		}
94 94
 	}
95 95
 
96 96
 	public void popSpecific(final String id, Promise promise) {
97 97
 		ViewController from = findControllerById(id);
98 98
 		if (from != null) {
99
-		    from.performOnParentStack(stack -> stack.popSpecific(from, promise), () -> rejectPromise(promise));
99
+		    from.performOnParentStack(stack -> ((StackController) stack).popSpecific(from, promise), () -> rejectPromise(promise));
100 100
 		} else {
101 101
 			rejectPromise(promise);
102 102
 		}
@@ -105,14 +105,14 @@ public class Navigator extends ParentController {
105 105
 	public void popToRoot(final String id, Promise promise) {
106 106
 		ViewController from = findControllerById(id);
107 107
 		if (from != null) {
108
-		    from.performOnParentStack(stack -> stack.popToRoot(promise));
108
+		    from.performOnParentStack(stack -> ((StackController) stack).popToRoot(promise));
109 109
 		}
110 110
 	}
111 111
 
112 112
 	public void popTo(final String componentId, Promise promise) {
113 113
 		ViewController target = findControllerById(componentId);
114 114
 		if (target != null) {
115
-		    target.performOnParentStack(stack -> stack.popTo(target, promise), () -> rejectPromise(promise));
115
+		    target.performOnParentStack(stack -> ((StackController) stack).popTo(target, promise), () -> rejectPromise(promise));
116 116
 		} else {
117 117
 			rejectPromise(promise);
118 118
 		}
@@ -131,11 +131,11 @@ public class Navigator extends ParentController {
131 131
 	}
132 132
 
133 133
 	public void showOverlay(ViewController overlay) {
134
-        overlayManager.show((ViewGroup) root.getView(), overlay);
134
+        overlayManager.show(root.getView(), overlay);
135 135
 	}
136 136
 
137 137
 	public void dismissOverlay(final String componentId) {
138
-		overlayManager.dismiss((ViewGroup) root.getView(), componentId);
138
+		overlayManager.dismiss(root.getView(), componentId);
139 139
 	}
140 140
 
141 141
 	static void rejectPromise(Promise promise) {

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

@@ -0,0 +1,8 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+
5
+public interface ReactViewCreator {
6
+
7
+    IReactView create(Activity activity, String componentId, String componentName);
8
+}

+ 116
- 87
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java View File

@@ -13,135 +13,164 @@ import com.reactnativenavigation.parse.Options;
13 13
 import com.reactnativenavigation.utils.CompatUtils;
14 14
 import com.reactnativenavigation.utils.StringUtils;
15 15
 import com.reactnativenavigation.utils.Task;
16
+import com.reactnativenavigation.views.ReactComponent;
16 17
 
17
-public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutListener {
18
+public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener {
19
+
20
+    public interface ViewVisibilityListener {
21
+        /**
22
+         * @return true if the event is consumed, false otherwise
23
+         */
24
+        boolean onViewAppeared(View view);
25
+
26
+        /**
27
+         * @return true if the event is consumed, false otherwise
28
+         */
29
+        boolean onViewDisappear(View view);
30
+    }
18 31
 
19 32
     public Options options;
20 33
 
21
-	private final Activity activity;
22
-	private final String id;
23
-	private View view;
24
-	private ParentController parentController;
25
-	private boolean isShown = false;
34
+    private final Activity activity;
35
+    private final String id;
36
+    protected T view;
37
+    @Nullable private ParentController<T> parentController;
38
+    private boolean isShown;
26 39
     private boolean isDestroyed;
40
+    private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
41
+
42
+    public ViewController(Activity activity, String id) {
43
+        this.activity = activity;
44
+        this.id = id;
45
+    }
27 46
 
28
-	public ViewController(Activity activity, String id) {
29
-		this.activity = activity;
30
-		this.id = id;
31
-	}
47
+    protected abstract T createView();
32 48
 
33
-	protected abstract View createView();
49
+    public void setViewVisibilityListener(ViewVisibilityListener viewVisibilityListener) {
50
+        this.viewVisibilityListener = viewVisibilityListener;
51
+    }
34 52
 
35
-	@SuppressWarnings("WeakerAccess")
53
+    @SuppressWarnings("WeakerAccess")
36 54
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
37
-	public void ensureViewIsCreated() {
38
-		getView();
39
-	}
55
+    public void ensureViewIsCreated() {
56
+        getView();
57
+    }
40 58
 
41
-	public boolean handleBack() {
42
-		return false;
43
-	}
59
+    public boolean handleBack() {
60
+        return false;
61
+    }
44 62
 
45 63
     public void applyOptions(Options options) {
46 64
 
47 65
     }
48 66
 
49
-	public Activity getActivity() {
50
-		return activity;
51
-	}
67
+    public Activity getActivity() {
68
+        return activity;
69
+    }
52 70
 
53
-	protected void applyOnParentController(Task<ParentController> task) {
54
-        if (parentController != null) {
55
-            task.run(parentController);
56
-        }
71
+    protected void applyOnParentController(Task<ParentController> task) {
72
+        if (parentController != null) task.run(parentController);
57 73
     }
58 74
 
59
-	@Nullable
60
-    ParentController getParentStackController() {
61
-		return parentController;
62
-	}
75
+    @Nullable
76
+    ParentController getParentController() {
77
+        return parentController;
78
+    }
63 79
 
64
-	public void setParentController(final ParentController parentController) {
65
-		this.parentController = parentController;
66
-	}
80
+    public void setParentController(@NonNull final ParentController parentController) {
81
+        this.parentController = parentController;
82
+    }
67 83
 
68 84
     boolean performOnParentStack(Task<StackController> task) {
69
-	    if (parentController instanceof StackController) {
85
+        if (parentController instanceof StackController) {
70 86
             task.run((StackController) parentController);
71 87
             return true;
72 88
         }
73 89
         if (this instanceof StackController) {
74
-	        task.run((StackController) this);
90
+            task.run((StackController) this);
75 91
             return true;
76 92
         }
77 93
         return false;
78 94
     }
79 95
 
80
-    void performOnParentStack(Task<StackController> accept, Runnable  reject) {
96
+    void performOnParentStack(Task accept, Runnable reject) {
81 97
         if (!performOnParentStack(accept)) {
82 98
             reject.run();
83 99
         }
84 100
     }
85 101
 
86
-	@NonNull
87
-	public View getView() {
88
-		if (view == null) {
89
-		    if (isDestroyed) throw new RuntimeException("Tried to create view after it has already been destroyed");
102
+    @NonNull
103
+    public T getView() {
104
+        if (view == null) {
105
+            if (isDestroyed) {
106
+                throw new RuntimeException("Tried to create view after it has already been destroyed");
107
+            }
90 108
             view = createView();
91
-			view.setId(CompatUtils.generateViewId());
92
-			view.getViewTreeObserver().addOnGlobalLayoutListener(this);
93
-		}
94
-		return view;
95
-	}
96
-
97
-	public String getId() {
98
-		return id;
99
-	}
100
-
101
-	boolean isSameId(final String id) {
102
-		return StringUtils.isEqual(this.id, id);
103
-	}
104
-
105
-	@Nullable
106
-	public ViewController findControllerById(String id) {
107
-		return isSameId(id) ? this : null;
108
-	}
109
-
110
-	public void onViewAppeared() {
109
+            view.setId(CompatUtils.generateViewId());
110
+            view.getViewTreeObserver().addOnGlobalLayoutListener(this);
111
+        }
112
+        return view;
113
+    }
114
+
115
+    public String getId() {
116
+        return id;
117
+    }
118
+
119
+    boolean isSameId(final String id) {
120
+        return StringUtils.isEqual(this.id, id);
121
+    }
122
+
123
+    @Nullable
124
+    public ViewController findControllerById(String id) {
125
+        return isSameId(id) ? this : null;
126
+    }
127
+
128
+    public void onViewAppeared() {
111 129
         isShown = true;
130
+        applyOnParentController(parentController -> {
131
+            parentController.clearOptions();
132
+            parentController.applyOptions(options, (ReactComponent) getView());
133
+        });
112 134
     }
113 135
 
114
-	public void onViewDisappear() {
136
+    public void onViewDisappear() {
115 137
         isShown = false;
116 138
     }
117 139
 
118
-	public void destroy() {
119
-		if (isShown) {
120
-			isShown = false;
121
-			onViewDisappear();
122
-		}
123
-		if (view != null) {
124
-			view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
125
-			if (view.getParent() instanceof ViewGroup) {
126
-				((ViewManager) view.getParent()).removeView(view);
127
-			}
128
-			view = null;
140
+    public void destroy() {
141
+        if (isShown) {
142
+            isShown = false;
143
+            onViewDisappear();
144
+            if (view instanceof Destroyable) {
145
+                ((Destroyable) view).destroy();
146
+            }
147
+        }
148
+        if (view != null) {
149
+            view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
150
+            if (view.getParent() instanceof ViewGroup) {
151
+                ((ViewManager) view.getParent()).removeView(view);
152
+            }
153
+            view = null;
129 154
             isDestroyed = true;
130
-		}
131
-	}
132
-
133
-	@Override
134
-	public void onGlobalLayout() {
135
-		if (!isShown && isViewShown()) {
136
-			isShown = true;
137
-			onViewAppeared();
138
-		} else if (isShown && !isViewShown()) {
139
-			isShown = false;
140
-			onViewDisappear();
141
-		}
142
-	}
143
-
144
-	protected boolean isViewShown() {
155
+        }
156
+    }
157
+
158
+    @Override
159
+    public void onGlobalLayout() {
160
+        if (!isShown && isViewShown()) {
161
+            if (!viewVisibilityListener.onViewAppeared(view)) {
162
+                isShown = true;
163
+                onViewAppeared();
164
+            }
165
+        } else if (isShown && !isViewShown()) {
166
+            if (!viewVisibilityListener.onViewDisappear(view)) {
167
+                isShown = false;
168
+                onViewDisappear();
169
+            }
170
+        }
171
+    }
172
+
173
+    protected boolean isViewShown() {
145 174
         return !isDestroyed && getView().isShown();
146
-	}
175
+    }
147 176
 }

+ 15
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewVisibilityListenerAdapter.java View File

@@ -0,0 +1,15 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.view.View;
4
+
5
+public class ViewVisibilityListenerAdapter implements ViewController.ViewVisibilityListener {
6
+    @Override
7
+    public boolean onViewAppeared(View view) {
8
+        return false;
9
+    }
10
+
11
+    @Override
12
+    public boolean onViewDisappear(View view) {
13
+        return false;
14
+    }
15
+}

+ 0
- 27
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/DialogViewController.java View File

@@ -1,27 +0,0 @@
1
-package com.reactnativenavigation.viewcontrollers.overlay;
2
-
3
-import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.view.View;
6
-
7
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
8
-import com.reactnativenavigation.viewcontrollers.ViewController;
9
-
10
-
11
-public class DialogViewController extends ViewController {
12
-
13
-	private ComponentViewController.ReactViewCreator viewCreator;
14
-	private String componentName;
15
-
16
-	public DialogViewController(Activity activity, String id, String componentName, ComponentViewController.ReactViewCreator viewCreator) {
17
-		super(activity, id);
18
-		this.viewCreator = viewCreator;
19
-		this.componentName = componentName;
20
-	}
21
-
22
-	@NonNull
23
-	@Override
24
-	protected View createView() {
25
-		return viewCreator.create(getActivity(), getId(), componentName).asView();
26
-	}
27
-}

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

@@ -1,80 +0,0 @@
1
-package com.reactnativenavigation.viewcontrollers.toptabs;
2
-
3
-import android.app.Activity;
4
-import android.view.View;
5
-
6
-import com.reactnativenavigation.parse.Options;
7
-import com.reactnativenavigation.presentation.NavigationOptionsListener;
8
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
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 componentName;
15
-    private ComponentViewController.ReactViewCreator viewCreator;
16
-    private TopTab topTab;
17
-    private boolean isSelectedTab;
18
-
19
-    public TopTabController(Activity activity, String id, String name, ComponentViewController.ReactViewCreator viewCreator, Options initialOptions) {
20
-        super(activity, id);
21
-        this.componentName = name;
22
-        this.viewCreator = viewCreator;
23
-        this.options = initialOptions;
24
-    }
25
-
26
-    @Override
27
-    public void onViewAppeared() {
28
-        super.onViewAppeared();
29
-        isSelectedTab = true;
30
-        applyOptions(options);
31
-        topTab.sendComponentStart();
32
-    }
33
-
34
-    @Override
35
-    public void applyOptions(Options options) {
36
-        applyOnParentController(parentController -> parentController.applyOptions(options));
37
-    }
38
-
39
-    @Override
40
-    public void onViewDisappear() {
41
-        super.onViewDisappear();
42
-        isSelectedTab = false;
43
-        topTab.sendComponentStop();
44
-    }
45
-
46
-    @Override
47
-    protected boolean isViewShown() {
48
-        return super.isViewShown() && isSelectedTab;
49
-    }
50
-
51
-    @Override
52
-    public View createView() {
53
-        topTab = new TopTab(
54
-                getActivity(),
55
-                viewCreator.create(getActivity(), getId(), componentName)
56
-        );
57
-        return topTab;
58
-    }
59
-
60
-    @Override
61
-    public void destroy() {
62
-        super.destroy();
63
-        if (topTab != null) topTab.destroy();
64
-        topTab = null;
65
-    }
66
-
67
-    @Override
68
-    public void mergeOptions(Options options) {
69
-        this.options.mergeWith(options);
70
-        applyOptions(this.options);
71
-    }
72
-
73
-    String getTabTitle() {
74
-        return options.topTabOptions.title.get("");
75
-    }
76
-
77
-    public void setTabIndex(int i) {
78
-        options.topTabOptions.tabIndex = i;
79
-    }
80
-}

+ 23
- 12
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

@@ -2,24 +2,24 @@ package com.reactnativenavigation.viewcontrollers.toptabs;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5
-import android.support.v4.view.ViewPager;
6
-import android.view.ViewGroup;
5
+import android.view.View;
7 6
 
8 7
 import com.reactnativenavigation.parse.Options;
9 8
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
10 9
 import com.reactnativenavigation.utils.Task;
11 10
 import com.reactnativenavigation.viewcontrollers.ParentController;
12 11
 import com.reactnativenavigation.viewcontrollers.ViewController;
13
-import com.reactnativenavigation.views.TopTabsLayout;
12
+import com.reactnativenavigation.viewcontrollers.ViewVisibilityListenerAdapter;
13
+import com.reactnativenavigation.views.ReactComponent;
14 14
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
15
+import com.reactnativenavigation.views.TopTabsViewPager;
15 16
 
16 17
 import java.util.Collection;
17 18
 import java.util.List;
18 19
 
19
-public class TopTabsController extends ParentController implements NavigationOptionsListener {
20
+public class TopTabsController extends ParentController<TopTabsViewPager> implements NavigationOptionsListener {
20 21
 
21 22
     private List<ViewController> tabs;
22
-    private TopTabsLayout topTabsLayout;
23 23
     private TopTabsLayoutCreator viewCreator;
24 24
     private Options options;
25 25
 
@@ -30,14 +30,20 @@ public class TopTabsController extends ParentController implements NavigationOpt
30 30
         this.tabs = tabs;
31 31
         for (ViewController tab : tabs) {
32 32
             tab.setParentController(this);
33
+            tab.setViewVisibilityListener(new ViewVisibilityListenerAdapter() {
34
+                @Override
35
+                public boolean onViewAppeared(View view) {
36
+                    return getView().isCurrentView(view);
37
+                }
38
+            });
33 39
         }
34 40
     }
35 41
 
36 42
     @NonNull
37 43
     @Override
38
-    protected ViewGroup createView() {
39
-        topTabsLayout = viewCreator.create();
40
-        return topTabsLayout;
44
+    protected TopTabsViewPager createView() {
45
+        view = viewCreator.create();
46
+        return (TopTabsViewPager) view;
41 47
     }
42 48
 
43 49
     @NonNull
@@ -49,7 +55,7 @@ public class TopTabsController extends ParentController implements NavigationOpt
49 55
     @Override
50 56
     public void onViewAppeared() {
51 57
         applyOptions(options);
52
-        applyOnParentController(parentController -> parentController.setupTopTabsWithViewPager((ViewPager) getView()));
58
+        applyOnParentController(parentController -> ((ParentController) parentController).setupTopTabsWithViewPager(getView()));
53 59
         performOnCurrentTab(ViewController::onViewAppeared);
54 60
     }
55 61
 
@@ -60,7 +66,12 @@ public class TopTabsController extends ParentController implements NavigationOpt
60 66
 
61 67
     @Override
62 68
     public void applyOptions(Options options) {
63
-        topTabsLayout.applyOptions(options);
69
+        getView().applyOptions(options);
70
+    }
71
+
72
+    @Override
73
+    public void applyOptions(Options options, ReactComponent childComponent) {
74
+        applyOnParentController(parentController -> ((ParentController) parentController).applyOptions(options, childComponent));
64 75
     }
65 76
 
66 77
     @Override
@@ -69,10 +80,10 @@ public class TopTabsController extends ParentController implements NavigationOpt
69 80
     }
70 81
 
71 82
     public void switchToTab(int index) {
72
-        topTabsLayout.switchToTab(index);
83
+        getView().switchToTab(index);
73 84
     }
74 85
 
75 86
     private void performOnCurrentTab(Task<ViewController> task) {
76
-        task.run(tabs.get(topTabsLayout.getCurrentItem()));
87
+        task.run(tabs.get(getView().getCurrentItem()));
77 88
     }
78 89
 }

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java View File

@@ -9,7 +9,7 @@ import android.widget.RelativeLayout;
9 9
 
10 10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11 11
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
12
+import com.reactnativenavigation.viewcontrollers.IReactView;
13 13
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
14 14
 
15 15
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -75,14 +75,14 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, Titl
75 75
 
76 76
     @Override
77 77
     public void drawBehindTopBar() {
78
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
78
+        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
79 79
         layoutParams.removeRule(BELOW);
80 80
         reactView.asView().setLayoutParams(layoutParams);
81 81
     }
82 82
 
83 83
     @Override
84 84
     public void drawBelowTopBar(TopBar topBar) {
85
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
85
+        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
86 86
         layoutParams.addRule(BELOW, topBar.getId());
87 87
         reactView.asView().setLayoutParams(layoutParams);
88 88
     }

+ 5
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentViewCreator.java View File

@@ -4,9 +4,10 @@ import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6 6
 import com.reactnativenavigation.react.ReactComponentViewCreator;
7
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
7
+import com.reactnativenavigation.viewcontrollers.IReactView;
8
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8 9
 
9
-public class ComponentViewCreator implements ComponentViewController.ReactViewCreator {
10
+public class ComponentViewCreator implements ReactViewCreator {
10 11
 
11 12
     private ReactInstanceManager instanceManager;
12 13
 
@@ -15,8 +16,8 @@ public class ComponentViewCreator implements ComponentViewController.ReactViewCr
15 16
 	}
16 17
 
17 18
 	@Override
18
-	public ComponentViewController.IReactView create(Activity activity, String componentId, String componentName) {
19
-        ComponentViewController.IReactView reactView = new ReactComponentViewCreator(instanceManager).create(activity, componentId, componentName);
19
+	public IReactView create(Activity activity, String componentId, String componentName) {
20
+        IReactView reactView = new ReactComponentViewCreator(instanceManager).create(activity, componentId, componentName);
20 21
         return new ComponentLayout(activity, reactView);
21 22
 	}
22 23
 }

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/ReactComponent.java View File

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
3
+import com.reactnativenavigation.viewcontrollers.IReactView;
4 4
 
5
-public interface ReactComponent extends Component, ComponentViewController.IReactView {
5
+public interface ReactComponent extends Component, IReactView {
6 6
 }

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

@@ -37,6 +37,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
37 37
         this.onClickListener = onClickListener;
38 38
         collapsingBehavior = new TopBarCollapseBehavior(this);
39 39
         titleBar = new Toolbar(context);
40
+        titleBar.getMenu();
40 41
         topTabs = new TopTabs(getContext());
41 42
         this.animator = new TopBarAnimator(this);
42 43
         addView(titleBar);
@@ -156,8 +157,8 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
156 157
         addView(topTabs);
157 158
     }
158 159
 
159
-    public void enableCollapse() {
160
-        collapsingBehavior.enableCollapse();
160
+    public void enableCollapse(ScrollEventListener scrollEventListener) {
161
+        collapsingBehavior.enableCollapse(scrollEventListener);
161 162
     }
162 163
 
163 164
     public void disableCollapse() {
@@ -190,5 +191,6 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
190 191
         titleBar.setTitle(null);
191 192
         titleBar.setNavigationIcon(null);
192 193
         titleBar.getMenu().clear();
194
+        removeView(topTabs);
193 195
     }
194 196
 }

+ 0
- 62
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTab.java View File

@@ -1,62 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.annotation.SuppressLint;
4
-import android.content.Context;
5
-import android.view.MotionEvent;
6
-import android.view.View;
7
-import android.widget.FrameLayout;
8
-
9
-import com.reactnativenavigation.interfaces.ScrollEventListener;
10
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
11
-
12
-@SuppressLint("ViewConstructor")
13
-public class TopTab extends FrameLayout implements IReactView {
14
-
15
-	private final IReactView reactView;
16
-
17
-	public TopTab(Context context, IReactView reactView) {
18
-		super(context);
19
-		this.reactView = reactView;
20
-        addView(reactView.asView());
21
-	}
22
-
23
-	@Override
24
-	public boolean isReady() {
25
-		return reactView.isReady();
26
-	}
27
-
28
-	@Override
29
-	public View asView() {
30
-		return this;
31
-	}
32
-
33
-	@Override
34
-	public void destroy() {
35
-		reactView.destroy();
36
-	}
37
-
38
-	@Override
39
-	public void sendComponentStart() {
40
-		reactView.sendComponentStart();
41
-	}
42
-
43
-	@Override
44
-	public void sendComponentStop() {
45
-		reactView.sendComponentStop();
46
-	}
47
-
48
-    @Override
49
-    public void sendOnNavigationButtonPressed(String buttonId) {
50
-        reactView.sendOnNavigationButtonPressed(buttonId);
51
-    }
52
-
53
-    @Override
54
-    public ScrollEventListener getScrollEventListener() {
55
-        return reactView.getScrollEventListener();
56
-    }
57
-
58
-    @Override
59
-    public void dispatchTouchEventToJs(MotionEvent event) {
60
-        reactView.dispatchTouchEventToJs(event);
61
-    }
62
-}

+ 0
- 23
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabCreator.java View File

@@ -1,23 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.app.Activity;
4
-
5
-import com.facebook.react.ReactInstanceManager;
6
-import com.reactnativenavigation.react.ReactComponentViewCreator;
7
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
8
-
9
-public class TopTabCreator implements ComponentViewController.ReactViewCreator {
10
-
11
-
12
-    private ReactInstanceManager instanceManager;
13
-
14
-    public TopTabCreator(ReactInstanceManager instanceManager) {
15
-        this.instanceManager = instanceManager;
16
-    }
17
-
18
-	@Override
19
-	public ComponentViewController.IReactView create(Activity activity, String componentId, String componentName) {
20
-        ComponentViewController.IReactView reactView = new ReactComponentViewCreator(instanceManager).create(activity, componentId, componentName);
21
-        return new TopTab(activity, reactView);
22
-	}
23
-}

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayoutCreator.java View File

@@ -16,7 +16,7 @@ public class TopTabsLayoutCreator {
16 16
         this.tabs = tabs;
17 17
     }
18 18
 
19
-    public TopTabsLayout create() {
20
-        return new TopTabsLayout(context, tabs, new TopTabsAdapter(tabs));
19
+    public TopTabsViewPager create() {
20
+        return new TopTabsViewPager(context, tabs, new TopTabsAdapter(tabs));
21 21
     }
22 22
 }

lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java → lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsViewPager.java View File

@@ -3,11 +3,12 @@ package com.reactnativenavigation.views;
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;
6 7
 import android.view.ViewGroup;
7 8
 import android.widget.RelativeLayout;
8 9
 
9 10
 import com.reactnativenavigation.parse.Options;
10
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
11
+import com.reactnativenavigation.viewcontrollers.IReactView;
11 12
 import com.reactnativenavigation.viewcontrollers.ViewController;
12 13
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
13 14
 
@@ -17,12 +18,12 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
17 18
 import static android.widget.RelativeLayout.BELOW;
18 19
 
19 20
 @SuppressLint("ViewConstructor")
20
-public class TopTabsLayout extends ViewPager implements Component, TitleBarButton.OnClickListener {
21
+public class TopTabsViewPager extends ViewPager implements Component, TitleBarButton.OnClickListener {
21 22
 
22 23
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
23 24
     private List<ViewController> tabs;
24 25
 
25
-    public TopTabsLayout(Context context, List<ViewController> tabs, TopTabsAdapter adapter) {
26
+    public TopTabsViewPager(Context context, List<ViewController> tabs, TopTabsAdapter adapter) {
26 27
         super(context);
27 28
         this.tabs = tabs;
28 29
         initTabs(adapter);
@@ -70,4 +71,13 @@ public class TopTabsLayout extends ViewPager implements Component, TitleBarButto
70 71
             tab.destroy();
71 72
         }
72 73
     }
74
+
75
+    public boolean isCurrentView(View view) {
76
+        for (ViewController tab : tabs) {
77
+            if (tab.getView() == view) {
78
+                return true;
79
+            }
80
+        }
81
+        return false;
82
+    }
73 83
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/touch/OverlayTouchDelegate.java View File

@@ -5,7 +5,7 @@ import android.support.annotation.VisibleForTesting;
5 5
 import android.view.MotionEvent;
6 6
 import android.view.ViewGroup;
7 7
 
8
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
8
+import com.reactnativenavigation.viewcontrollers.IReactView;
9 9
 
10 10
 public class OverlayTouchDelegate {
11 11
     private final Rect hitRect = new Rect();

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleComponentViewController.java View File

@@ -6,7 +6,7 @@ import com.reactnativenavigation.parse.*;
6 6
 import com.reactnativenavigation.viewcontrollers.*;
7 7
 
8 8
 public class SimpleComponentViewController extends ComponentViewController {
9
-    public SimpleComponentViewController(final Activity activity, final String id) {
10
-        super(activity, id, "theComponentName", new TestComponentViewCreator(), new Options());
9
+    public SimpleComponentViewController(final Activity activity, final String id, Options initialOptions) {
10
+        super(activity, id, "theComponentName", new TestComponentViewCreator(), initialOptions);
11 11
     }
12 12
 }

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleOverlay.java View File

@@ -7,9 +7,9 @@ import android.widget.FrameLayout;
7 7
 import android.widget.RelativeLayout;
8 8
 
9 9
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
10
+import com.reactnativenavigation.viewcontrollers.IReactView;
11 11
 
12
-public class SimpleOverlay extends RelativeLayout implements ComponentViewController.IReactView {
12
+public class SimpleOverlay extends RelativeLayout implements IReactView {
13 13
     public SimpleOverlay(Context context) {
14 14
         super(context);
15 15
     }

+ 77
- 6
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java View File

@@ -1,23 +1,94 @@
1 1
 package com.reactnativenavigation.mocks;
2 2
 
3
-import android.app.*;
4
-import android.view.*;
3
+import android.app.Activity;
4
+import android.content.Context;
5
+import android.support.annotation.NonNull;
6
+import android.view.MotionEvent;
7
+import android.view.View;
8
+import android.widget.FrameLayout;
5 9
 
6
-import com.reactnativenavigation.viewcontrollers.*;
10
+import com.reactnativenavigation.interfaces.ScrollEventListener;
11
+import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.viewcontrollers.ViewController;
13
+import com.reactnativenavigation.views.ReactComponent;
14
+import com.reactnativenavigation.views.TopBar;
7 15
 
8
-public class SimpleViewController extends ViewController {
16
+public class SimpleViewController extends ViewController<FrameLayout> {
9 17
 
10 18
     public SimpleViewController(final Activity activity, String id) {
11 19
         super(activity, id);
20
+        options = new Options();
12 21
     }
13 22
 
14 23
     @Override
15
-    protected View createView() {
16
-        return new View(getActivity());
24
+    protected FrameLayout createView() {
25
+        return new SimpleView(getActivity());
17 26
     }
18 27
 
19 28
     @Override
20 29
     public String toString() {
21 30
         return "SimpleViewController " + getId();
22 31
     }
32
+
33
+    public class SimpleView extends FrameLayout implements ReactComponent {
34
+
35
+        public SimpleView(@NonNull Context context) {
36
+            super(context);
37
+        }
38
+
39
+        @Override
40
+        public void applyOptions(Options options) {
41
+
42
+        }
43
+
44
+        @Override
45
+        public void drawBehindTopBar() {
46
+
47
+        }
48
+
49
+        @Override
50
+        public void drawBelowTopBar(TopBar topBar) {
51
+
52
+        }
53
+
54
+        @Override
55
+        public boolean isReady() {
56
+            return false;
57
+        }
58
+
59
+        @Override
60
+        public View asView() {
61
+            return null;
62
+        }
63
+
64
+        @Override
65
+        public void destroy() {
66
+
67
+        }
68
+
69
+        @Override
70
+        public void sendComponentStart() {
71
+
72
+        }
73
+
74
+        @Override
75
+        public void sendComponentStop() {
76
+
77
+        }
78
+
79
+        @Override
80
+        public void sendOnNavigationButtonPressed(String buttonId) {
81
+
82
+        }
83
+
84
+        @Override
85
+        public ScrollEventListener getScrollEventListener() {
86
+            return null;
87
+        }
88
+
89
+        @Override
90
+        public void dispatchTouchEventToJs(MotionEvent event) {
91
+
92
+        }
93
+    }
23 94
 }

+ 9
- 23
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java View File

@@ -3,38 +3,22 @@ package com.reactnativenavigation.mocks;
3 3
 import android.content.Context;
4 4
 import android.view.MotionEvent;
5 5
 import android.view.View;
6
-import android.view.ViewGroup;
7
-import android.widget.RelativeLayout;
8 6
 
9 7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10 8
 import com.reactnativenavigation.parse.Options;
11
-import com.reactnativenavigation.views.ReactComponent;
9
+import com.reactnativenavigation.viewcontrollers.IReactView;
10
+import com.reactnativenavigation.views.ComponentLayout;
12 11
 import com.reactnativenavigation.views.TitleBarButton;
13
-import com.reactnativenavigation.views.TopBar;
14 12
 
15
-public class TestComponentLayout extends RelativeLayout implements ReactComponent, TitleBarButton.OnClickListener {
13
+public class TestComponentLayout extends ComponentLayout implements TitleBarButton.OnClickListener {
16 14
 
17
-    private final View contentView;
15
+    private IReactView reactView;
18 16
 
19
-    public TestComponentLayout(final Context context) {
20
-        super(context);
21
-        contentView = new View(context);
22
-        addView(contentView);
17
+    public TestComponentLayout(final Context context, IReactView reactView) {
18
+        super(context, reactView);
19
+        this.reactView = reactView;
23 20
     }
24 21
 
25
-    @Override
26
-    public void drawBehindTopBar() {
27
-        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
28
-        layoutParams.removeRule(BELOW);
29
-        contentView.setLayoutParams(layoutParams);
30
-    }
31
-
32
-    @Override
33
-    public void drawBelowTopBar(TopBar topBar) {
34
-        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
35
-        layoutParams.addRule(BELOW, topBar.getId());
36
-        contentView.setLayoutParams(layoutParams);
37
-    }
38 22
 
39 23
     @Override
40 24
     public boolean isReady() {
@@ -52,10 +36,12 @@ public class TestComponentLayout extends RelativeLayout implements ReactComponen
52 36
 
53 37
     @Override
54 38
     public void sendComponentStart() {
39
+        reactView.sendComponentStart();
55 40
     }
56 41
 
57 42
     @Override
58 43
     public void sendComponentStop() {
44
+        reactView.sendComponentStop();
59 45
     }
60 46
 
61 47
     @Override

+ 10
- 5
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentViewCreator.java View File

@@ -1,13 +1,18 @@
1 1
 package com.reactnativenavigation.mocks;
2 2
 
3
-import android.app.*;
3
+import android.app.Activity;
4 4
 
5
-import com.reactnativenavigation.viewcontrollers.*;
6
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.*;
5
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
6
+import com.reactnativenavigation.viewcontrollers.IReactView;
7
+import com.reactnativenavigation.views.ComponentLayout;
8
+import com.reactnativenavigation.views.ReactComponent;
9
+
10
+import static org.mockito.Mockito.spy;
7 11
 
8 12
 public class TestComponentViewCreator implements ReactViewCreator {
9 13
     @Override
10
-    public ComponentViewController.IReactView create(final Activity activity, final String componentId, final String componentName) {
11
-        return new TestComponentLayout(activity);
14
+    public ReactComponent create(final Activity activity, final String componentId, final String componentName) {
15
+        IReactView reactView = spy(new TestReactView(activity));
16
+        return new ComponentLayout(activity, reactView);
12 17
     }
13 18
 }

+ 57
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestReactView.java View File

@@ -0,0 +1,57 @@
1
+package com.reactnativenavigation.mocks;
2
+
3
+import android.content.Context;
4
+import android.support.annotation.NonNull;
5
+import android.view.MotionEvent;
6
+import android.view.View;
7
+import android.widget.FrameLayout;
8
+
9
+import com.reactnativenavigation.interfaces.ScrollEventListener;
10
+import com.reactnativenavigation.viewcontrollers.IReactView;
11
+
12
+public class TestReactView extends FrameLayout implements IReactView {
13
+
14
+    public TestReactView(@NonNull Context context) {
15
+        super(context);
16
+    }
17
+
18
+    @Override
19
+    public boolean isReady() {
20
+        return false;
21
+    }
22
+
23
+    @Override
24
+    public View asView() {
25
+        return this;
26
+    }
27
+
28
+    @Override
29
+    public void destroy() {
30
+
31
+    }
32
+
33
+    @Override
34
+    public void sendComponentStart() {
35
+
36
+    }
37
+
38
+    @Override
39
+    public void sendComponentStop() {
40
+
41
+    }
42
+
43
+    @Override
44
+    public void sendOnNavigationButtonPressed(String buttonId) {
45
+
46
+    }
47
+
48
+    @Override
49
+    public ScrollEventListener getScrollEventListener() {
50
+        return null;
51
+    }
52
+
53
+    @Override
54
+    public void dispatchTouchEventToJs(MotionEvent event) {
55
+
56
+    }
57
+}

+ 19
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopTabLayoutMock.java View File

@@ -4,9 +4,11 @@ import android.content.*;
4 4
 import android.view.*;
5 5
 
6 6
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7
-import com.reactnativenavigation.viewcontrollers.*;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.views.ReactComponent;
9
+import com.reactnativenavigation.views.TopBar;
8 10
 
9
-public class TopTabLayoutMock extends View implements ComponentViewController.IReactView {
11
+public class TopTabLayoutMock extends View implements ReactComponent {
10 12
 
11 13
     public TopTabLayoutMock(Context context) {
12 14
         super(context);
@@ -51,4 +53,19 @@ public class TopTabLayoutMock extends View implements ComponentViewController.IR
51 53
     public void dispatchTouchEventToJs(MotionEvent event) {
52 54
 
53 55
     }
56
+
57
+    @Override
58
+    public void applyOptions(Options options) {
59
+
60
+    }
61
+
62
+    @Override
63
+    public void drawBehindTopBar() {
64
+
65
+    }
66
+
67
+    @Override
68
+    public void drawBelowTopBar(TopBar topBar) {
69
+
70
+    }
54 71
 }

+ 5
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java View File

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.BaseTest;
6 6
 import com.reactnativenavigation.mocks.TestComponentLayout;
7
+import com.reactnativenavigation.mocks.TestReactView;
7 8
 import com.reactnativenavigation.parse.Options;
8 9
 import com.reactnativenavigation.views.StackLayout;
9 10
 
@@ -17,15 +18,14 @@ import static org.mockito.Mockito.when;
17 18
 
18 19
 public class ComponentViewControllerTest extends BaseTest {
19 20
     private ComponentViewController uut;
20
-    private ParentController<StackLayout> parentController;
21
-    private ComponentViewController.IReactView view;
21
+    private IReactView view;
22 22
 
23 23
     @Override
24 24
     public void beforeEach() {
25 25
         super.beforeEach();
26 26
         Activity activity = newActivity();
27
-        view = spy(new TestComponentLayout(activity));
28
-        parentController = new StackController(activity, "stack");
27
+        view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
28
+        ParentController<StackLayout> parentController = new StackController(activity, "stack");
29 29
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30 30
         uut.setParentController(parentController);
31 31
         parentController.ensureViewIsCreated();
@@ -40,6 +40,7 @@ public class ComponentViewControllerTest extends BaseTest {
40 40
     public void componentViewDestroyedOnDestroy() throws Exception {
41 41
         uut.ensureViewIsCreated();
42 42
         verify(view, times(0)).destroy();
43
+        uut.onViewAppeared();
43 44
         uut.destroy();
44 45
         verify(view, times(1)).destroy();
45 46
     }

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java View File

@@ -204,7 +204,7 @@ public class NavigatorTest extends BaseTest {
204 204
 
205 205
     @Test
206 206
     public void setOptions_CallsApplyNavigationOptions() {
207
-        ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId");
207
+        ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId", new Options());
208 208
         componentVc.setParentController(parentController);
209 209
         assertThat(componentVc.getOptions().topBarOptions.title.get("")).isEmpty();
210 210
         uut.setRoot(componentVc, new MockPromise());

+ 13
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java View File

@@ -10,6 +10,7 @@ import android.widget.RelativeLayout;
10 10
 import com.reactnativenavigation.BaseTest;
11 11
 import com.reactnativenavigation.mocks.MockPromise;
12 12
 import com.reactnativenavigation.mocks.TestComponentLayout;
13
+import com.reactnativenavigation.mocks.TestReactView;
13 14
 import com.reactnativenavigation.parse.Fraction;
14 15
 import com.reactnativenavigation.parse.Options;
15 16
 import com.reactnativenavigation.parse.Text;
@@ -18,6 +19,7 @@ import org.junit.Test;
18 19
 
19 20
 import javax.annotation.Nullable;
20 21
 
22
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21 23
 import static android.widget.RelativeLayout.BELOW;
22 24
 import static org.assertj.core.api.Java6Assertions.assertThat;
23 25
 import static org.mockito.Mockito.spy;
@@ -26,7 +28,7 @@ public class OptionsApplyingTest extends BaseTest {
26 28
     private Activity activity;
27 29
     private StackController stackController;
28 30
     private ComponentViewController uut;
29
-    private ComponentViewController.IReactView view;
31
+    private IReactView view;
30 32
     private Options initialNavigationOptions;
31 33
 
32 34
     @Override
@@ -34,7 +36,8 @@ public class OptionsApplyingTest extends BaseTest {
34 36
         super.beforeEach();
35 37
         activity = newActivity();
36 38
         initialNavigationOptions = new Options();
37
-        view = spy(new TestComponentLayout(activity));
39
+        view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
40
+        view.asView().setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
38 41
         uut = new ComponentViewController(activity,
39 42
                 "componentId1",
40 43
                 "componentName",
@@ -49,9 +52,10 @@ public class OptionsApplyingTest extends BaseTest {
49 52
     @Test
50 53
     public void applyNavigationOptionsHandlesNoParentStack() throws Exception {
51 54
         uut.setParentController(null);
52
-        assertThat(uut.getParentStackController()).isNull();
55
+        assertThat(uut.getParentController()).isNull();
56
+        uut.ensureViewIsCreated();
53 57
         uut.onViewAppeared();
54
-        assertThat(uut.getParentStackController()).isNull();
58
+        assertThat(uut.getParentController()).isNull();
55 59
     }
56 60
 
57 61
     @Test
@@ -79,6 +83,7 @@ public class OptionsApplyingTest extends BaseTest {
79 83
 
80 84
     @Test
81 85
     public void reappliesOptionsOnMerge() throws Exception {
86
+        uut.ensureViewIsCreated();
82 87
         uut.onViewAppeared();
83 88
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
84 89
 
@@ -91,6 +96,7 @@ public class OptionsApplyingTest extends BaseTest {
91 96
 
92 97
     @Test
93 98
     public void appliesTopBackBackgroundColor() throws Exception {
99
+        uut.ensureViewIsCreated();
94 100
         uut.onViewAppeared();
95 101
 
96 102
         Options opts = new Options();
@@ -122,6 +128,7 @@ public class OptionsApplyingTest extends BaseTest {
122 128
     public void appliesTopBarTextSize() throws Exception {
123 129
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
124 130
         initialNavigationOptions.topBarOptions.title = new Text("the title");
131
+        uut.ensureViewIsCreated();
125 132
         uut.onViewAppeared();
126 133
 
127 134
         Options opts = new Options();
@@ -137,6 +144,7 @@ public class OptionsApplyingTest extends BaseTest {
137 144
     public void appliesTopBarHidden() throws Exception {
138 145
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
139 146
         initialNavigationOptions.topBarOptions.title = new Text("the title");
147
+        uut.ensureViewIsCreated();
140 148
         uut.onViewAppeared();
141 149
         assertThat(stackController.getTopBar().getVisibility()).isNotEqualTo(View.GONE);
142 150
 
@@ -152,6 +160,7 @@ public class OptionsApplyingTest extends BaseTest {
152 160
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
153 161
         initialNavigationOptions.topBarOptions.title = new Text("the title");
154 162
         initialNavigationOptions.topBarOptions.drawBehind = Options.BooleanOptions.False;
163
+        uut.ensureViewIsCreated();
155 164
         uut.onViewAppeared();
156 165
         stackController.animatePush(uut, new MockPromise() {
157 166
             @Override

+ 10
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java View File

@@ -84,4 +84,14 @@ public class ParentControllerTest extends BaseTest {
84 84
         uut.destroy();
85 85
         verify(child1, times(1)).destroy();
86 86
     }
87
+
88
+    @Test
89
+    public void optionsAreClearedWhenChildIsAppeared() throws Exception {
90
+        StackController stackController = spy(new StackController(activity, "stack"));
91
+        SimpleViewController child1 = new SimpleViewController(activity, "child1");
92
+        stackController.animatePush(child1, new MockPromise());
93
+
94
+        child1.onViewAppeared();
95
+        verify(stackController, times(1)).clearOptions();
96
+    }
87 97
 }

+ 3
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java View File

@@ -80,13 +80,13 @@ public class StackControllerTest extends BaseTest {
80 80
 
81 81
     @Test
82 82
     public void pushAssignsRefToSelfOnPushedController() throws Exception {
83
-        assertThat(child1.getParentStackController()).isNull();
83
+        assertThat(child1.getParentController()).isNull();
84 84
         uut.animatePush(child1, new MockPromise());
85
-        assertThat(child1.getParentStackController()).isEqualTo(uut);
85
+        assertThat(child1.getParentController()).isEqualTo(uut);
86 86
 
87 87
         StackController anotherNavController = new StackController(activity, "another");
88 88
         anotherNavController.animatePush(child2, new MockPromise());
89
-        assertThat(child2.getParentStackController()).isEqualTo(anotherNavController);
89
+        assertThat(child2.getParentController()).isEqualTo(anotherNavController);
90 90
     }
91 91
 
92 92
     @Test

+ 0
- 51
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabControllerTest.java View File

@@ -1,51 +0,0 @@
1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import android.app.*;
4
-
5
-import com.reactnativenavigation.*;
6
-import com.reactnativenavigation.mocks.*;
7
-import com.reactnativenavigation.parse.*;
8
-import com.reactnativenavigation.viewcontrollers.toptabs.*;
9
-
10
-import org.junit.*;
11
-
12
-import static org.mockito.Mockito.*;
13
-
14
-public class TopTabControllerTest extends BaseTest {
15
-    private TopTabController uut;
16
-    private TopTabLayoutMock view;
17
-    private ParentController parentController;
18
-    private Options initialOptions;
19
-
20
-    @Override
21
-    public void beforeEach() {
22
-        super.beforeEach();
23
-        Activity activity = newActivity();
24
-        view = spy(new TopTabLayoutMock(activity));
25
-        initialOptions = new Options();
26
-        uut = new TopTabController(activity,
27
-                "componentId",
28
-                "componentName",
29
-                (activity1, componentId, componentName) -> view,
30
-                initialOptions
31
-        );
32
-        parentController = spy(new TopTabsControllerMock(activity, "parentComponentId"));
33
-        uut.setParentController(parentController);
34
-    }
35
-
36
-    @Test
37
-    public void styleIsAppliedOnParentControllerWhenTabIsVisible() throws Exception {
38
-        uut.ensureViewIsCreated();
39
-        uut.onViewAppeared();
40
-        verify(parentController, times(1)).applyOptions(initialOptions);
41
-    }
42
-
43
-    @Test
44
-    public void styleIsAppliedOnParentControllerWhenOptionsAreSetDynamically() throws Exception {
45
-        uut.ensureViewIsCreated();
46
-        uut.onViewAppeared();
47
-        uut.mergeOptions(new Options());
48
-        verify(parentController, times(2)).applyOptions(initialOptions);
49
-    }
50
-
51
-}

+ 100
- 51
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

@@ -1,67 +1,90 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.app.*;
4
-import android.view.*;
5
-
6
-import com.reactnativenavigation.*;
7
-import com.reactnativenavigation.mocks.*;
8
-import com.reactnativenavigation.parse.*;
9
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.*;
10
-import com.reactnativenavigation.viewcontrollers.toptabs.*;
11
-import com.reactnativenavigation.views.*;
12
-
13
-import org.junit.*;
14
-import org.mockito.*;
15
-
16
-import java.util.*;
17
-
18
-import static org.mockito.Mockito.*;
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
5
+import android.view.ViewGroup;
6
+
7
+import com.reactnativenavigation.BaseTest;
8
+import com.reactnativenavigation.mocks.TestComponentViewCreator;
9
+import com.reactnativenavigation.mocks.TestReactView;
10
+import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.parse.Text;
12
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
13
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
14
+import com.reactnativenavigation.views.ReactComponent;
15
+import com.reactnativenavigation.views.TopTabsLayoutCreator;
16
+import com.reactnativenavigation.views.TopTabsViewPager;
17
+
18
+import org.junit.Test;
19
+import org.mockito.Mockito;
20
+
21
+import java.util.ArrayList;
22
+import java.util.List;
23
+
24
+import static org.mockito.Mockito.spy;
25
+import static org.mockito.Mockito.times;
26
+import static org.mockito.Mockito.verify;
19 27
 
20 28
 public class TopTabsViewControllerTest extends BaseTest {
21 29
 
22 30
     private static final int SIZE = 2;
23 31
 
32
+    private StackController parentController;
24 33
     private TopTabsController uut;
25
-    private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
26 34
     private List<ViewController> tabControllers = new ArrayList<>(SIZE);
27 35
     private List<Options> tabOptions = new ArrayList<>(SIZE);
28
-    private Options options;
29
-    private TopTabsLayout topTabsLayout;
36
+    private final Options options = new Options();
37
+    private TopTabsViewPager topTabsLayout;
30 38
 
31 39
     @Override
32 40
     public void beforeEach() {
33 41
         super.beforeEach();
34
-        tabControllers.clear();
35
-        tabs.clear();
36
-        Activity activity = newActivity();
37
-        tabControllers = createTabs(activity);
38
-        options = new Options();
39
-        topTabsLayout = spy(new TopTabsLayout(activity, tabControllers, new TopTabsAdapter(tabControllers)));
40 42
 
43
+        final Activity activity = newActivity();
44
+        tabOptions = createOptions();
45
+        tabControllers = createTabsControllers(activity);
46
+
47
+        topTabsLayout = spy(new TopTabsViewPager(activity, tabControllers, new TopTabsAdapter(tabControllers)));
41 48
         TopTabsLayoutCreator layoutCreator = Mockito.mock(TopTabsLayoutCreator.class);
42 49
         Mockito.when(layoutCreator.create()).thenReturn(topTabsLayout);
43
-        uut = new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options);
50
+        uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
51
+        tabControllers.forEach(viewController -> viewController.setParentController(uut));
52
+
53
+        parentController = spy(new StackController(activity, "stackId"));
54
+        uut.setParentController(parentController);
44 55
     }
45 56
 
46
-    private List<ViewController> createTabs(Activity activity) {
47
-        List<ViewController> result = new ArrayList<>(SIZE);
48
-        tabOptions = new ArrayList<>();
57
+    @NonNull
58
+    private ArrayList<Options> createOptions() {
59
+        ArrayList result = new ArrayList();
49 60
         for (int i = 0; i < SIZE; i++) {
50
-            TopTabLayoutMock reactView = spy(new TopTabLayoutMock(activity));
51
-            tabs.add(reactView);
52
-            Options options = new Options();
61
+            final Options options = new Options();
53 62
             options.topTabOptions.title = new Text("Tab " + i);
54
-            tabOptions.add(options);
55
-            result.add(spy(new TopTabController(activity,
56
-                    "idTab" + i,
57
-                    "tab" + i,
58
-                    (activity1, componentId, componentName) -> reactView,
59
-                    options)
60
-            ));
63
+            options.topBarOptions.title = new Text("Title " + i);
64
+            result.add(options);
61 65
         }
62 66
         return result;
63 67
     }
64 68
 
69
+    private List<ViewController> createTabsControllers(Activity activity) {
70
+        List<ViewController> tabControllers = new ArrayList<>(SIZE);
71
+        for (int i = 0; i < SIZE; i++) {
72
+            ComponentViewController viewController = new ComponentViewController(
73
+                    activity,
74
+                    "idTab" + i,
75
+                    "theComponentName",
76
+                    new TestComponentViewCreator(),
77
+                    tabOptions.get(i)
78
+            );
79
+            tabControllers.add(spy(viewController));
80
+        }
81
+        return tabControllers;
82
+    }
83
+
84
+    private ReactComponent tabView(int index) {
85
+        return (ReactComponent) tabControllers.get(index).getView();
86
+    }
87
+
65 88
     @Test
66 89
     public void createsViewFromComponentViewCreator() throws Exception {
67 90
         uut.ensureViewIsCreated();
@@ -73,7 +96,7 @@ public class TopTabsViewControllerTest extends BaseTest {
73 96
     @Test
74 97
     public void componentViewDestroyedOnDestroy() throws Exception {
75 98
         uut.ensureViewIsCreated();
76
-        TopTabsLayout topTabs = (TopTabsLayout) uut.getView();
99
+        TopTabsViewPager topTabs = uut.getView();
77 100
         for (int i = 0; i < SIZE; i++) {
78 101
             verify(tab(topTabs, i), times(0)).destroy();
79 102
         }
@@ -85,10 +108,18 @@ public class TopTabsViewControllerTest extends BaseTest {
85 108
 
86 109
     @Test
87 110
     public void lifecycleMethodsSentWhenSelectedTabChanges() throws Exception {
111
+        parentController.ensureViewIsCreated();
88 112
         uut.ensureViewIsCreated();
89
-        TopTabLayoutMock initialTab = tabs.get(0);
90
-        TopTabLayoutMock selectedTab = tabs.get(1);
113
+        tabControllers.get(0).ensureViewIsCreated();
114
+        tabControllers.get(1).ensureViewIsCreated();
115
+
116
+        tabControllers.get(0).onViewAppeared();
117
+
91 118
         uut.onViewAppeared();
119
+
120
+        TestReactView initialTab = getActualTabView(0);
121
+        TestReactView selectedTab = getActualTabView(1);
122
+
92 123
         uut.switchToTab(1);
93 124
         verify(initialTab, times(1)).sendComponentStop();
94 125
         verify(selectedTab, times(1)).sendComponentStart();
@@ -97,42 +128,60 @@ public class TopTabsViewControllerTest extends BaseTest {
97 128
 
98 129
     @Test
99 130
     public void lifecycleMethodsSentWhenSelectedPreviouslySelectedTab() throws Exception {
131
+        parentController.ensureViewIsCreated();
100 132
         uut.ensureViewIsCreated();
101 133
         uut.onViewAppeared();
102 134
         uut.switchToTab(1);
103 135
         uut.switchToTab(0);
104
-        verify(tabs.get(0), times(1)).sendComponentStop();
105
-        verify(tabs.get(0), times(2)).sendComponentStart();
106
-        verify(tabs.get(1), times(1)).sendComponentStart();
107
-        verify(tabs.get(1), times(1)).sendComponentStop();
136
+
137
+        verify(getActualTabView(0), times(1)).sendComponentStop();
138
+        verify(getActualTabView(0), times(2)).sendComponentStart();
139
+        verify(getActualTabView(1), times(1)).sendComponentStart();
140
+        verify(getActualTabView(1), times(1)).sendComponentStop();
108 141
     }
109 142
 
110 143
     @Test
111 144
     public void setOptionsOfInitialTab() throws Exception {
145
+        parentController.ensureViewIsCreated();
112 146
         uut.ensureViewIsCreated();
113 147
         uut.onViewAppeared();
114
-        verify(tabControllers.get(0), times(1)).applyOptions(tabOptions.get(0));
148
+        verify(tabControllers.get(0), times(1)).onViewAppeared();
149
+        verify(tabControllers.get(1), times(0)).onViewAppeared();
150
+
151
+        verify(uut, times(1)).applyOptions(tabOptions.get(0), ((ComponentViewController) tabControllers.get(0)).getComponent());
115 152
     }
116 153
 
117 154
     @Test
118 155
     public void setOptionsWhenTabChanges() throws Exception {
156
+        parentController.ensureViewIsCreated();
119 157
         uut.ensureViewIsCreated();
158
+        tabControllers.get(0).ensureViewIsCreated();
159
+        tabControllers.get(1).ensureViewIsCreated();
160
+
120 161
         uut.onViewAppeared();
121
-        verify(tabControllers.get(0), times(1)).applyOptions(tabOptions.get(0));
162
+        verify(uut, times(1)).applyOptions(tabOptions.get(0), tabView(0));
122 163
         uut.switchToTab(1);
123
-        verify(tabControllers.get(1), times(1)).applyOptions(tabOptions.get(1));
164
+        verify(uut, times(1)).applyOptions(tabOptions.get(1), tabView(1));
124 165
         uut.switchToTab(0);
125
-        verify(tabControllers.get(0), times(2)).applyOptions(tabOptions.get(0));
166
+        verify(uut, times(2)).applyOptions(tabOptions.get(0), tabView(0));
167
+    }
168
+
169
+    private TestReactView getActualTabView(int index) {
170
+        return (TestReactView) tabControllers.get(index).getView().getChildAt(0);
126 171
     }
127 172
 
128 173
     @Test
129 174
     public void appliesOptionsOnLayoutWhenVisible() throws Exception {
175
+        tabControllers.get(0).ensureViewIsCreated();
176
+        parentController.ensureViewIsCreated();
130 177
         uut.ensureViewIsCreated();
178
+
131 179
         uut.onViewAppeared();
180
+
132 181
         verify(topTabsLayout, times(1)).applyOptions(options);
133 182
     }
134 183
 
135
-    private IReactView tab(TopTabsLayout topTabs, final int index) {
184
+    private IReactView tab(TopTabsViewPager topTabs, final int index) {
136 185
         return (IReactView) ((ViewGroup) topTabs.getChildAt(index)).getChildAt(0);
137 186
     }
138 187
 }

+ 5
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java View File

@@ -3,6 +3,7 @@ package com.reactnativenavigation.viewcontrollers;
3 3
 import android.app.Activity;
4 4
 import android.view.View;
5 5
 import android.view.ViewParent;
6
+import android.widget.FrameLayout;
6 7
 import android.widget.LinearLayout;
7 8
 
8 9
 import com.reactnativenavigation.BaseTest;
@@ -45,10 +46,10 @@ public class ViewControllerTest extends BaseTest {
45 46
 
46 47
     @Test
47 48
     public void canOverrideViewCreation() throws Exception {
48
-        final View otherView = new View(activity);
49
+        final FrameLayout otherView = new FrameLayout(activity);
49 50
         ViewController myController = new ViewController(activity, "vc") {
50 51
             @Override
51
-            protected View createView() {
52
+            protected FrameLayout createView() {
52 53
                 return otherView;
53 54
             }
54 55
         };
@@ -57,10 +58,10 @@ public class ViewControllerTest extends BaseTest {
57 58
 
58 59
     @Test
59 60
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
60
-        assertThat(uut.getParentStackController()).isNull();
61
+        assertThat(uut.getParentController()).isNull();
61 62
         StackController nav = new StackController(activity, "stack");
62 63
         nav.animatePush(uut, new MockPromise());
63
-        assertThat(uut.getParentStackController()).isEqualTo(nav);
64
+        assertThat(uut.getParentController()).isEqualTo(nav);
64 65
     }
65 66
 
66 67
     @Test

+ 0
- 1
playground/src/screens/TopTabOptionsScreen.js View File

@@ -8,7 +8,6 @@ class TopTabOptionsScreen extends PureComponent {
8 8
   static get options() {
9 9
     return {
10 10
       topBar: {
11
-        title: 'Tab 1',
12 11
         textColor: 'black',
13 12
         textFontSize: 16,
14 13
         textFontFamily: 'HelveticaNeue-Italic'

+ 0
- 10
playground/src/screens/TopTabScreen.js View File

@@ -1,7 +1,6 @@
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');
5 4
 
6 5
 class TopTabScreen extends PureComponent {
7 6
   static get options() {
@@ -14,15 +13,6 @@ class TopTabScreen extends PureComponent {
14 13
     };
15 14
   }
16 15
 
17
-  constructor(props) {
18
-    super(props);
19
-    Navigation.setOptions(this.props.componentId, {
20
-      topBar: {
21
-        title: this.props.title
22
-      }
23
-    });
24
-  }
25
-
26 16
   render() {
27 17
     return (
28 18
       <View style={styles.root}>

+ 9
- 0
playground/src/screens/WelcomeScreen.js View File

@@ -232,6 +232,9 @@ class WelcomeScreen extends Component {
232 232
               options: {
233 233
                 topTab: {
234 234
                   title: 'Tab 1'
235
+                },
236
+                topBar: {
237
+                  title: 'One'
235 238
                 }
236 239
               }
237 240
             }
@@ -247,6 +250,9 @@ class WelcomeScreen extends Component {
247 250
                 topTab: {
248 251
                   title: 'Tab 2',
249 252
                   titleFontFamily: 'HelveticaNeue-Italic'
253
+                },
254
+                topBar: {
255
+                  title: 'Two'
250 256
                 }
251 257
               }
252 258
             }
@@ -261,6 +267,9 @@ class WelcomeScreen extends Component {
261 267
               options: {
262 268
                 topTab: {
263 269
                   title: 'Tab 3'
270
+                },
271
+                topBar: {
272
+                  title: 'Three'
264 273
                 }
265 274
               }
266 275
             }