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
         this.animator = new TopBarAnimator(topBar);
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
     public void disableCollapse() {
24
     public void disableCollapse() {

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

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

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

3
 import android.app.Activity;
3
 import android.app.Activity;
4
 
4
 
5
 import com.facebook.react.ReactInstanceManager;
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
 public class ReactComponentViewCreator implements ReactViewCreator {
9
 public class ReactComponentViewCreator implements ReactViewCreator {
10
 	private ReactInstanceManager reactInstanceManager;
10
 	private ReactInstanceManager reactInstanceManager;
14
 	}
14
 	}
15
 
15
 
16
 	@Override
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
 		return new ReactView(activity, reactInstanceManager, componentId, componentName);
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
 import com.facebook.react.uimanager.UIManagerModule;
13
 import com.facebook.react.uimanager.UIManagerModule;
14
 import com.facebook.react.uimanager.events.EventDispatcher;
14
 import com.facebook.react.uimanager.events.EventDispatcher;
15
 import com.reactnativenavigation.interfaces.ScrollEventListener;
15
 import com.reactnativenavigation.interfaces.ScrollEventListener;
16
-import com.reactnativenavigation.viewcontrollers.ComponentViewController;
16
+import com.reactnativenavigation.viewcontrollers.IReactView;
17
 
17
 
18
 @SuppressLint("ViewConstructor")
18
 @SuppressLint("ViewConstructor")
19
-public class ReactView extends ReactRootView implements ComponentViewController.IReactView {
19
+public class ReactView extends ReactRootView implements IReactView {
20
 
20
 
21
 	private final ReactInstanceManager reactInstanceManager;
21
 	private final ReactInstanceManager reactInstanceManager;
22
 	private final String componentId;
22
 	private final String componentId;

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

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
 
13
 
14
 public class UiUtils {
14
 public class UiUtils {
15
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
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
 	public static void tintDrawable(Drawable drawable, int tint) {
26
 	public static void tintDrawable(Drawable drawable, int tint) {
27
 		drawable.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_IN));
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
 package com.reactnativenavigation.viewcontrollers;
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
     private final String componentName;
13
     private final String componentName;
39
 
14
 
40
     private final ReactViewCreator viewCreator;
15
     private final ReactViewCreator viewCreator;
41
-    private ReactComponent component;
42
 
16
 
43
     public ComponentViewController(final Activity activity,
17
     public ComponentViewController(final Activity activity,
44
                                    final String id,
18
                                    final String id,
51
         options = initialNavigationOptions;
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
     @Override
28
     @Override
62
     public void onViewAppeared() {
29
     public void onViewAppeared() {
63
         super.onViewAppeared();
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
     @Override
35
     @Override
74
     public void onViewDisappear() {
36
     public void onViewDisappear() {
37
+        view.sendComponentStop();
75
         super.onViewDisappear();
38
         super.onViewDisappear();
76
-        component.sendComponentStop();
77
     }
39
     }
78
 
40
 
79
     @Override
41
     @Override
80
     protected boolean isViewShown() {
42
     protected boolean isViewShown() {
81
-        return super.isViewShown() && component.isReady();
43
+        return super.isViewShown() && view.isReady();
82
     }
44
     }
83
 
45
 
84
     @NonNull
46
     @NonNull
85
     @Override
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
     @Override
53
     @Override
92
     public void mergeOptions(Options options) {
54
     public void mergeOptions(Options options) {
93
         this.options.mergeWith(options);
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
     Options getOptions() {
60
     Options getOptions() {
100
     }
62
     }
101
 
63
 
102
     ReactComponent getComponent() {
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

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

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
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
82
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
83
 		ViewController from = findControllerById(fromId);
83
 		ViewController from = findControllerById(fromId);
84
 		if (from != null) {
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
 	void pop(final String fromId, Promise promise) {
89
 	void pop(final String fromId, Promise promise) {
90
 		ViewController from = findControllerById(fromId);
90
 		ViewController from = findControllerById(fromId);
91
 		if (from != null) {
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
 	public void popSpecific(final String id, Promise promise) {
96
 	public void popSpecific(final String id, Promise promise) {
97
 		ViewController from = findControllerById(id);
97
 		ViewController from = findControllerById(id);
98
 		if (from != null) {
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
 		} else {
100
 		} else {
101
 			rejectPromise(promise);
101
 			rejectPromise(promise);
102
 		}
102
 		}
105
 	public void popToRoot(final String id, Promise promise) {
105
 	public void popToRoot(final String id, Promise promise) {
106
 		ViewController from = findControllerById(id);
106
 		ViewController from = findControllerById(id);
107
 		if (from != null) {
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
 	public void popTo(final String componentId, Promise promise) {
112
 	public void popTo(final String componentId, Promise promise) {
113
 		ViewController target = findControllerById(componentId);
113
 		ViewController target = findControllerById(componentId);
114
 		if (target != null) {
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
 		} else {
116
 		} else {
117
 			rejectPromise(promise);
117
 			rejectPromise(promise);
118
 		}
118
 		}
131
 	}
131
 	}
132
 
132
 
133
 	public void showOverlay(ViewController overlay) {
133
 	public void showOverlay(ViewController overlay) {
134
-        overlayManager.show((ViewGroup) root.getView(), overlay);
134
+        overlayManager.show(root.getView(), overlay);
135
 	}
135
 	}
136
 
136
 
137
 	public void dismissOverlay(final String componentId) {
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
 	static void rejectPromise(Promise promise) {
141
 	static void rejectPromise(Promise promise) {

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

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
 import com.reactnativenavigation.utils.CompatUtils;
13
 import com.reactnativenavigation.utils.CompatUtils;
14
 import com.reactnativenavigation.utils.StringUtils;
14
 import com.reactnativenavigation.utils.StringUtils;
15
 import com.reactnativenavigation.utils.Task;
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
     public Options options;
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
     private boolean isDestroyed;
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
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
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
     public void applyOptions(Options options) {
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
     boolean performOnParentStack(Task<StackController> task) {
84
     boolean performOnParentStack(Task<StackController> task) {
69
-	    if (parentController instanceof StackController) {
85
+        if (parentController instanceof StackController) {
70
             task.run((StackController) parentController);
86
             task.run((StackController) parentController);
71
             return true;
87
             return true;
72
         }
88
         }
73
         if (this instanceof StackController) {
89
         if (this instanceof StackController) {
74
-	        task.run((StackController) this);
90
+            task.run((StackController) this);
75
             return true;
91
             return true;
76
         }
92
         }
77
         return false;
93
         return false;
78
     }
94
     }
79
 
95
 
80
-    void performOnParentStack(Task<StackController> accept, Runnable  reject) {
96
+    void performOnParentStack(Task accept, Runnable reject) {
81
         if (!performOnParentStack(accept)) {
97
         if (!performOnParentStack(accept)) {
82
             reject.run();
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
             view = createView();
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
         isShown = true;
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
         isShown = false;
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
             isDestroyed = true;
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
         return !isDestroyed && getView().isShown();
174
         return !isDestroyed && getView().isShown();
146
-	}
175
+    }
147
 }
176
 }

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

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
-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
-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
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.support.annotation.NonNull;
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
 import com.reactnativenavigation.parse.Options;
7
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
8
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
10
 import com.reactnativenavigation.utils.Task;
9
 import com.reactnativenavigation.utils.Task;
11
 import com.reactnativenavigation.viewcontrollers.ParentController;
10
 import com.reactnativenavigation.viewcontrollers.ParentController;
12
 import com.reactnativenavigation.viewcontrollers.ViewController;
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
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
14
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
15
+import com.reactnativenavigation.views.TopTabsViewPager;
15
 
16
 
16
 import java.util.Collection;
17
 import java.util.Collection;
17
 import java.util.List;
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
     private List<ViewController> tabs;
22
     private List<ViewController> tabs;
22
-    private TopTabsLayout topTabsLayout;
23
     private TopTabsLayoutCreator viewCreator;
23
     private TopTabsLayoutCreator viewCreator;
24
     private Options options;
24
     private Options options;
25
 
25
 
30
         this.tabs = tabs;
30
         this.tabs = tabs;
31
         for (ViewController tab : tabs) {
31
         for (ViewController tab : tabs) {
32
             tab.setParentController(this);
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
     @NonNull
42
     @NonNull
37
     @Override
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
     @NonNull
49
     @NonNull
49
     @Override
55
     @Override
50
     public void onViewAppeared() {
56
     public void onViewAppeared() {
51
         applyOptions(options);
57
         applyOptions(options);
52
-        applyOnParentController(parentController -> parentController.setupTopTabsWithViewPager((ViewPager) getView()));
58
+        applyOnParentController(parentController -> ((ParentController) parentController).setupTopTabsWithViewPager(getView()));
53
         performOnCurrentTab(ViewController::onViewAppeared);
59
         performOnCurrentTab(ViewController::onViewAppeared);
54
     }
60
     }
55
 
61
 
60
 
66
 
61
     @Override
67
     @Override
62
     public void applyOptions(Options options) {
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
     @Override
77
     @Override
69
     }
80
     }
70
 
81
 
71
     public void switchToTab(int index) {
82
     public void switchToTab(int index) {
72
-        topTabsLayout.switchToTab(index);
83
+        getView().switchToTab(index);
73
     }
84
     }
74
 
85
 
75
     private void performOnCurrentTab(Task<ViewController> task) {
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
 
9
 
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
12
+import com.reactnativenavigation.viewcontrollers.IReactView;
13
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
13
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
14
 
14
 
15
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
15
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
75
 
75
 
76
     @Override
76
     @Override
77
     public void drawBehindTopBar() {
77
     public void drawBehindTopBar() {
78
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
78
+        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
79
         layoutParams.removeRule(BELOW);
79
         layoutParams.removeRule(BELOW);
80
         reactView.asView().setLayoutParams(layoutParams);
80
         reactView.asView().setLayoutParams(layoutParams);
81
     }
81
     }
82
 
82
 
83
     @Override
83
     @Override
84
     public void drawBelowTopBar(TopBar topBar) {
84
     public void drawBelowTopBar(TopBar topBar) {
85
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
85
+        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
86
         layoutParams.addRule(BELOW, topBar.getId());
86
         layoutParams.addRule(BELOW, topBar.getId());
87
         reactView.asView().setLayoutParams(layoutParams);
87
         reactView.asView().setLayoutParams(layoutParams);
88
     }
88
     }

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

4
 
4
 
5
 import com.facebook.react.ReactInstanceManager;
5
 import com.facebook.react.ReactInstanceManager;
6
 import com.reactnativenavigation.react.ReactComponentViewCreator;
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
     private ReactInstanceManager instanceManager;
12
     private ReactInstanceManager instanceManager;
12
 
13
 
15
 	}
16
 	}
16
 
17
 
17
 	@Override
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
         return new ComponentLayout(activity, reactView);
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
 package com.reactnativenavigation.views;
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
         this.onClickListener = onClickListener;
37
         this.onClickListener = onClickListener;
38
         collapsingBehavior = new TopBarCollapseBehavior(this);
38
         collapsingBehavior = new TopBarCollapseBehavior(this);
39
         titleBar = new Toolbar(context);
39
         titleBar = new Toolbar(context);
40
+        titleBar.getMenu();
40
         topTabs = new TopTabs(getContext());
41
         topTabs = new TopTabs(getContext());
41
         this.animator = new TopBarAnimator(this);
42
         this.animator = new TopBarAnimator(this);
42
         addView(titleBar);
43
         addView(titleBar);
156
         addView(topTabs);
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
     public void disableCollapse() {
164
     public void disableCollapse() {
190
         titleBar.setTitle(null);
191
         titleBar.setTitle(null);
191
         titleBar.setNavigationIcon(null);
192
         titleBar.setNavigationIcon(null);
192
         titleBar.getMenu().clear();
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
-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
-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
         this.tabs = tabs;
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
 import android.annotation.SuppressLint;
3
 import android.annotation.SuppressLint;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.support.v4.view.ViewPager;
5
 import android.support.v4.view.ViewPager;
6
+import android.view.View;
6
 import android.view.ViewGroup;
7
 import android.view.ViewGroup;
7
 import android.widget.RelativeLayout;
8
 import android.widget.RelativeLayout;
8
 
9
 
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
10
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
11
+import com.reactnativenavigation.viewcontrollers.IReactView;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
13
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
13
 
14
 
17
 import static android.widget.RelativeLayout.BELOW;
18
 import static android.widget.RelativeLayout.BELOW;
18
 
19
 
19
 @SuppressLint("ViewConstructor")
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
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
23
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
23
     private List<ViewController> tabs;
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
         super(context);
27
         super(context);
27
         this.tabs = tabs;
28
         this.tabs = tabs;
28
         initTabs(adapter);
29
         initTabs(adapter);
70
             tab.destroy();
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
 import android.view.MotionEvent;
5
 import android.view.MotionEvent;
6
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
7
 
7
 
8
-import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
8
+import com.reactnativenavigation.viewcontrollers.IReactView;
9
 
9
 
10
 public class OverlayTouchDelegate {
10
 public class OverlayTouchDelegate {
11
     private final Rect hitRect = new Rect();
11
     private final Rect hitRect = new Rect();

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

6
 import com.reactnativenavigation.viewcontrollers.*;
6
 import com.reactnativenavigation.viewcontrollers.*;
7
 
7
 
8
 public class SimpleComponentViewController extends ComponentViewController {
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
 import android.widget.RelativeLayout;
7
 import android.widget.RelativeLayout;
8
 
8
 
9
 import com.reactnativenavigation.interfaces.ScrollEventListener;
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
     public SimpleOverlay(Context context) {
13
     public SimpleOverlay(Context context) {
14
         super(context);
14
         super(context);
15
     }
15
     }

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

1
 package com.reactnativenavigation.mocks;
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
     public SimpleViewController(final Activity activity, String id) {
18
     public SimpleViewController(final Activity activity, String id) {
11
         super(activity, id);
19
         super(activity, id);
20
+        options = new Options();
12
     }
21
     }
13
 
22
 
14
     @Override
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
     @Override
28
     @Override
20
     public String toString() {
29
     public String toString() {
21
         return "SimpleViewController " + getId();
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
 import android.content.Context;
3
 import android.content.Context;
4
 import android.view.MotionEvent;
4
 import android.view.MotionEvent;
5
 import android.view.View;
5
 import android.view.View;
6
-import android.view.ViewGroup;
7
-import android.widget.RelativeLayout;
8
 
6
 
9
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.parse.Options;
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
 import com.reactnativenavigation.views.TitleBarButton;
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
     @Override
23
     @Override
40
     public boolean isReady() {
24
     public boolean isReady() {
52
 
36
 
53
     @Override
37
     @Override
54
     public void sendComponentStart() {
38
     public void sendComponentStart() {
39
+        reactView.sendComponentStart();
55
     }
40
     }
56
 
41
 
57
     @Override
42
     @Override
58
     public void sendComponentStop() {
43
     public void sendComponentStop() {
44
+        reactView.sendComponentStop();
59
     }
45
     }
60
 
46
 
61
     @Override
47
     @Override

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

1
 package com.reactnativenavigation.mocks;
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
 public class TestComponentViewCreator implements ReactViewCreator {
12
 public class TestComponentViewCreator implements ReactViewCreator {
9
     @Override
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

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
 import android.view.*;
4
 import android.view.*;
5
 
5
 
6
 import com.reactnativenavigation.interfaces.ScrollEventListener;
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
     public TopTabLayoutMock(Context context) {
13
     public TopTabLayoutMock(Context context) {
12
         super(context);
14
         super(context);
51
     public void dispatchTouchEventToJs(MotionEvent event) {
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
 
4
 
5
 import com.reactnativenavigation.BaseTest;
5
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.mocks.TestComponentLayout;
6
 import com.reactnativenavigation.mocks.TestComponentLayout;
7
+import com.reactnativenavigation.mocks.TestReactView;
7
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.views.StackLayout;
9
 import com.reactnativenavigation.views.StackLayout;
9
 
10
 
17
 
18
 
18
 public class ComponentViewControllerTest extends BaseTest {
19
 public class ComponentViewControllerTest extends BaseTest {
19
     private ComponentViewController uut;
20
     private ComponentViewController uut;
20
-    private ParentController<StackLayout> parentController;
21
-    private ComponentViewController.IReactView view;
21
+    private IReactView view;
22
 
22
 
23
     @Override
23
     @Override
24
     public void beforeEach() {
24
     public void beforeEach() {
25
         super.beforeEach();
25
         super.beforeEach();
26
         Activity activity = newActivity();
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
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
29
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30
         uut.setParentController(parentController);
30
         uut.setParentController(parentController);
31
         parentController.ensureViewIsCreated();
31
         parentController.ensureViewIsCreated();
40
     public void componentViewDestroyedOnDestroy() throws Exception {
40
     public void componentViewDestroyedOnDestroy() throws Exception {
41
         uut.ensureViewIsCreated();
41
         uut.ensureViewIsCreated();
42
         verify(view, times(0)).destroy();
42
         verify(view, times(0)).destroy();
43
+        uut.onViewAppeared();
43
         uut.destroy();
44
         uut.destroy();
44
         verify(view, times(1)).destroy();
45
         verify(view, times(1)).destroy();
45
     }
46
     }

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

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

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

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

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

84
         uut.destroy();
84
         uut.destroy();
85
         verify(child1, times(1)).destroy();
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
 
80
 
81
     @Test
81
     @Test
82
     public void pushAssignsRefToSelfOnPushedController() throws Exception {
82
     public void pushAssignsRefToSelfOnPushedController() throws Exception {
83
-        assertThat(child1.getParentStackController()).isNull();
83
+        assertThat(child1.getParentController()).isNull();
84
         uut.animatePush(child1, new MockPromise());
84
         uut.animatePush(child1, new MockPromise());
85
-        assertThat(child1.getParentStackController()).isEqualTo(uut);
85
+        assertThat(child1.getParentController()).isEqualTo(uut);
86
 
86
 
87
         StackController anotherNavController = new StackController(activity, "another");
87
         StackController anotherNavController = new StackController(activity, "another");
88
         anotherNavController.animatePush(child2, new MockPromise());
88
         anotherNavController.animatePush(child2, new MockPromise());
89
-        assertThat(child2.getParentStackController()).isEqualTo(anotherNavController);
89
+        assertThat(child2.getParentController()).isEqualTo(anotherNavController);
90
     }
90
     }
91
 
91
 
92
     @Test
92
     @Test

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

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
 package com.reactnativenavigation.viewcontrollers;
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
 public class TopTabsViewControllerTest extends BaseTest {
28
 public class TopTabsViewControllerTest extends BaseTest {
21
 
29
 
22
     private static final int SIZE = 2;
30
     private static final int SIZE = 2;
23
 
31
 
32
+    private StackController parentController;
24
     private TopTabsController uut;
33
     private TopTabsController uut;
25
-    private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
26
     private List<ViewController> tabControllers = new ArrayList<>(SIZE);
34
     private List<ViewController> tabControllers = new ArrayList<>(SIZE);
27
     private List<Options> tabOptions = new ArrayList<>(SIZE);
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
     @Override
39
     @Override
32
     public void beforeEach() {
40
     public void beforeEach() {
33
         super.beforeEach();
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
         TopTabsLayoutCreator layoutCreator = Mockito.mock(TopTabsLayoutCreator.class);
48
         TopTabsLayoutCreator layoutCreator = Mockito.mock(TopTabsLayoutCreator.class);
42
         Mockito.when(layoutCreator.create()).thenReturn(topTabsLayout);
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
         for (int i = 0; i < SIZE; i++) {
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
             options.topTabOptions.title = new Text("Tab " + i);
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
         return result;
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
     @Test
88
     @Test
66
     public void createsViewFromComponentViewCreator() throws Exception {
89
     public void createsViewFromComponentViewCreator() throws Exception {
67
         uut.ensureViewIsCreated();
90
         uut.ensureViewIsCreated();
73
     @Test
96
     @Test
74
     public void componentViewDestroyedOnDestroy() throws Exception {
97
     public void componentViewDestroyedOnDestroy() throws Exception {
75
         uut.ensureViewIsCreated();
98
         uut.ensureViewIsCreated();
76
-        TopTabsLayout topTabs = (TopTabsLayout) uut.getView();
99
+        TopTabsViewPager topTabs = uut.getView();
77
         for (int i = 0; i < SIZE; i++) {
100
         for (int i = 0; i < SIZE; i++) {
78
             verify(tab(topTabs, i), times(0)).destroy();
101
             verify(tab(topTabs, i), times(0)).destroy();
79
         }
102
         }
85
 
108
 
86
     @Test
109
     @Test
87
     public void lifecycleMethodsSentWhenSelectedTabChanges() throws Exception {
110
     public void lifecycleMethodsSentWhenSelectedTabChanges() throws Exception {
111
+        parentController.ensureViewIsCreated();
88
         uut.ensureViewIsCreated();
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
         uut.onViewAppeared();
118
         uut.onViewAppeared();
119
+
120
+        TestReactView initialTab = getActualTabView(0);
121
+        TestReactView selectedTab = getActualTabView(1);
122
+
92
         uut.switchToTab(1);
123
         uut.switchToTab(1);
93
         verify(initialTab, times(1)).sendComponentStop();
124
         verify(initialTab, times(1)).sendComponentStop();
94
         verify(selectedTab, times(1)).sendComponentStart();
125
         verify(selectedTab, times(1)).sendComponentStart();
97
 
128
 
98
     @Test
129
     @Test
99
     public void lifecycleMethodsSentWhenSelectedPreviouslySelectedTab() throws Exception {
130
     public void lifecycleMethodsSentWhenSelectedPreviouslySelectedTab() throws Exception {
131
+        parentController.ensureViewIsCreated();
100
         uut.ensureViewIsCreated();
132
         uut.ensureViewIsCreated();
101
         uut.onViewAppeared();
133
         uut.onViewAppeared();
102
         uut.switchToTab(1);
134
         uut.switchToTab(1);
103
         uut.switchToTab(0);
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
     @Test
143
     @Test
111
     public void setOptionsOfInitialTab() throws Exception {
144
     public void setOptionsOfInitialTab() throws Exception {
145
+        parentController.ensureViewIsCreated();
112
         uut.ensureViewIsCreated();
146
         uut.ensureViewIsCreated();
113
         uut.onViewAppeared();
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
     @Test
154
     @Test
118
     public void setOptionsWhenTabChanges() throws Exception {
155
     public void setOptionsWhenTabChanges() throws Exception {
156
+        parentController.ensureViewIsCreated();
119
         uut.ensureViewIsCreated();
157
         uut.ensureViewIsCreated();
158
+        tabControllers.get(0).ensureViewIsCreated();
159
+        tabControllers.get(1).ensureViewIsCreated();
160
+
120
         uut.onViewAppeared();
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
         uut.switchToTab(1);
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
         uut.switchToTab(0);
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
     @Test
173
     @Test
129
     public void appliesOptionsOnLayoutWhenVisible() throws Exception {
174
     public void appliesOptionsOnLayoutWhenVisible() throws Exception {
175
+        tabControllers.get(0).ensureViewIsCreated();
176
+        parentController.ensureViewIsCreated();
130
         uut.ensureViewIsCreated();
177
         uut.ensureViewIsCreated();
178
+
131
         uut.onViewAppeared();
179
         uut.onViewAppeared();
180
+
132
         verify(topTabsLayout, times(1)).applyOptions(options);
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
         return (IReactView) ((ViewGroup) topTabs.getChildAt(index)).getChildAt(0);
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
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.view.View;
4
 import android.view.View;
5
 import android.view.ViewParent;
5
 import android.view.ViewParent;
6
+import android.widget.FrameLayout;
6
 import android.widget.LinearLayout;
7
 import android.widget.LinearLayout;
7
 
8
 
8
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.BaseTest;
45
 
46
 
46
     @Test
47
     @Test
47
     public void canOverrideViewCreation() throws Exception {
48
     public void canOverrideViewCreation() throws Exception {
48
-        final View otherView = new View(activity);
49
+        final FrameLayout otherView = new FrameLayout(activity);
49
         ViewController myController = new ViewController(activity, "vc") {
50
         ViewController myController = new ViewController(activity, "vc") {
50
             @Override
51
             @Override
51
-            protected View createView() {
52
+            protected FrameLayout createView() {
52
                 return otherView;
53
                 return otherView;
53
             }
54
             }
54
         };
55
         };
57
 
58
 
58
     @Test
59
     @Test
59
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
60
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
60
-        assertThat(uut.getParentStackController()).isNull();
61
+        assertThat(uut.getParentController()).isNull();
61
         StackController nav = new StackController(activity, "stack");
62
         StackController nav = new StackController(activity, "stack");
62
         nav.animatePush(uut, new MockPromise());
63
         nav.animatePush(uut, new MockPromise());
63
-        assertThat(uut.getParentStackController()).isEqualTo(nav);
64
+        assertThat(uut.getParentController()).isEqualTo(nav);
64
     }
65
     }
65
 
66
 
66
     @Test
67
     @Test

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

8
   static get options() {
8
   static get options() {
9
     return {
9
     return {
10
       topBar: {
10
       topBar: {
11
-        title: 'Tab 1',
12
         textColor: 'black',
11
         textColor: 'black',
13
         textFontSize: 16,
12
         textFontSize: 16,
14
         textFontFamily: 'HelveticaNeue-Italic'
13
         textFontFamily: 'HelveticaNeue-Italic'

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

1
 const React = require('react');
1
 const React = require('react');
2
 const { PureComponent } = require('react');
2
 const { PureComponent } = require('react');
3
 const { View, Text } = require('react-native');
3
 const { View, Text } = require('react-native');
4
-const Navigation = require('react-native-navigation');
5
 
4
 
6
 class TopTabScreen extends PureComponent {
5
 class TopTabScreen extends PureComponent {
7
   static get options() {
6
   static get options() {
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
   render() {
16
   render() {
27
     return (
17
     return (
28
       <View style={styles.root}>
18
       <View style={styles.root}>

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

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