Browse Source

Refactor root presentation

Pull presentation logic from Navigator to rootPresenter
Guy Carmeli 6 years ago
parent
commit
079b1272a5

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

@@ -19,8 +19,9 @@ import com.reactnativenavigation.react.JsDevReloadHandler;
19 19
 import com.reactnativenavigation.react.ReactGateway;
20 20
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21 21
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
22
-import com.reactnativenavigation.viewcontrollers.Navigator;
23 22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
23
+import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
24
+import com.reactnativenavigation.viewcontrollers.navigator.RootPresenter;
24 25
 
25 26
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity, JsDevReloadHandler.ReloadListener {
26 27
     @Nullable
@@ -32,7 +33,8 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
32 33
     protected void onCreate(@Nullable Bundle savedInstanceState) {
33 34
         super.onCreate(savedInstanceState);
34 35
         addDefaultSplashLayout();
35
-        navigator = new Navigator(this, new ChildControllersRegistry(), new ModalStack(this), new OverlayManager());
36
+        navigator = new Navigator(this, new ChildControllersRegistry(), new ModalStack(this), new OverlayManager(), new RootPresenter(this));
37
+        navigator.bindViews();
36 38
         getReactGateway().onActivityCreated(this);
37 39
     }
38 40
 

+ 5
- 6
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java View File

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.anim;
2 2
 
3 3
 import android.animation.Animator;
4
-import android.animation.Animator.AnimatorListener;
5 4
 import android.animation.AnimatorListenerAdapter;
6 5
 import android.animation.AnimatorSet;
7 6
 import android.content.Context;
@@ -93,18 +92,18 @@ public class NavigationAnimator extends BaseAnimator {
93 92
         set.start();
94 93
     }
95 94
 
96
-    public void animateStartApp(View view, AnimationOptions startApp, AnimatorListener listener) {
97
-        view.setVisibility(View.INVISIBLE);
98
-        AnimatorSet set = startApp.getAnimation(view);
95
+    public void setRoot(View root, AnimationOptions setRoot, Runnable onAnimationEnd) {
96
+        root.setVisibility(View.INVISIBLE);
97
+        AnimatorSet set = setRoot.getAnimation(root);
99 98
         set.addListener(new AnimatorListenerAdapter() {
100 99
             @Override
101 100
             public void onAnimationStart(Animator animation) {
102
-                view.setVisibility(View.VISIBLE);
101
+                root.setVisibility(View.VISIBLE);
103 102
             }
104 103
 
105 104
             @Override
106 105
             public void onAnimationEnd(Animator animation) {
107
-                listener.onAnimationEnd(animation);
106
+                onAnimationEnd.run();
108 107
             }
109 108
         });
110 109
         set.start();

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

@@ -23,7 +23,7 @@ import com.reactnativenavigation.utils.Now;
23 23
 import com.reactnativenavigation.utils.TypefaceLoader;
24 24
 import com.reactnativenavigation.utils.UiThread;
25 25
 import com.reactnativenavigation.utils.UiUtils;
26
-import com.reactnativenavigation.viewcontrollers.Navigator;
26
+import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
27 27
 import com.reactnativenavigation.viewcontrollers.ViewController;
28 28
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
29 29
 

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

@@ -6,6 +6,7 @@ import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.parse.Options;
8 8
 import com.reactnativenavigation.presentation.OptionsPresenter;
9
+import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
9 10
 import com.reactnativenavigation.views.Component;
10 11
 
11 12
 public abstract class ChildController<T extends ViewGroup> extends ViewController<T>  {

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

@@ -137,7 +137,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
137 137
         this.parentController = parentController;
138 138
     }
139 139
 
140
-    void performOnParentStack(Task<StackController> task) {
140
+    public void performOnParentStack(Task<StackController> task) {
141 141
         if (parentController instanceof StackController) {
142 142
             task.run((StackController) parentController);
143 143
         } else if (this instanceof StackController) {

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java → lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java View File

@@ -1,14 +1,12 @@
1
-package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers.navigator;
2 2
 
3
-import android.animation.Animator;
4
-import android.animation.AnimatorListenerAdapter;
5 3
 import android.app.Activity;
6 4
 import android.support.annotation.NonNull;
7 5
 import android.support.annotation.Nullable;
6
+import android.support.annotation.RestrictTo;
8 7
 import android.view.ViewGroup;
9 8
 import android.widget.FrameLayout;
10 9
 
11
-import com.reactnativenavigation.anim.NavigationAnimator;
12 10
 import com.reactnativenavigation.parse.Options;
13 11
 import com.reactnativenavigation.presentation.OptionsPresenter;
14 12
 import com.reactnativenavigation.presentation.OverlayManager;
@@ -16,9 +14,11 @@ import com.reactnativenavigation.react.EventEmitter;
16 14
 import com.reactnativenavigation.utils.CommandListener;
17 15
 import com.reactnativenavigation.utils.CompatUtils;
18 16
 import com.reactnativenavigation.utils.Task;
17
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
18
+import com.reactnativenavigation.viewcontrollers.ParentController;
19
+import com.reactnativenavigation.viewcontrollers.ViewController;
19 20
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
20 21
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
21
-import com.reactnativenavigation.views.element.ElementTransitionManager;
22 22
 
23 23
 import java.util.Collection;
24 24
 import java.util.Collections;
@@ -27,6 +27,7 @@ public class Navigator extends ParentController {
27 27
 
28 28
     private final ModalStack modalStack;
29 29
     private final OverlayManager overlayManager;
30
+    private final RootPresenter rootPresenter;
30 31
     private ViewController root;
31 32
     private final FrameLayout rootLayout;
32 33
     private final FrameLayout modalsLayout;
@@ -60,14 +61,19 @@ public class Navigator extends ParentController {
60 61
         contentLayout.addView(overlaysLayout);
61 62
     }
62 63
 
63
-    public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager) {
64
+    public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager, RootPresenter rootPresenter) {
64 65
         super(activity, childRegistry,"navigator" + CompatUtils.generateViewId(), new OptionsPresenter(activity, new Options()), new Options());
65 66
         this.modalStack = modalStack;
66 67
         this.overlayManager = overlayManager;
68
+        this.rootPresenter = rootPresenter;
67 69
         rootLayout = new FrameLayout(getActivity());
68 70
         modalsLayout = new FrameLayout(getActivity());
69 71
         overlaysLayout = new FrameLayout(getActivity());
72
+    }
73
+
74
+    public void bindViews() {
70 75
         modalStack.setModalsContainer(modalsLayout);
76
+        rootPresenter.setRootContainer(rootLayout);
71 77
     }
72 78
 
73 79
     @NonNull
@@ -123,19 +129,7 @@ public class Navigator extends ParentController {
123 129
             getView();
124 130
         }
125 131
         root = viewController;
126
-        rootLayout.addView(viewController.getView());
127
-        Options options = viewController.resolveCurrentOptions().withDefaultOptions(defaultOptions);
128
-        if (options.animations.setRoot.hasAnimation()) {
129
-            new NavigationAnimator(viewController.getActivity(), new ElementTransitionManager())
130
-                    .animateStartApp(viewController.getView(), options.animations.setRoot, new AnimatorListenerAdapter() {
131
-                        @Override
132
-                        public void onAnimationEnd(Animator animation) {
133
-                            commandListener.onSuccess(viewController.getId());
134
-                        }
135
-                    });
136
-        } else {
137
-            commandListener.onSuccess(viewController.getId());
138
-        }
132
+        rootPresenter.setRoot(root, defaultOptions, commandListener);
139 133
     }
140 134
 
141 135
     private void removePreviousContentView() {
@@ -221,4 +215,9 @@ public class Navigator extends ParentController {
221 215
     private boolean isRootNotCreated() {
222 216
         return view == null;
223 217
     }
218
+
219
+    @RestrictTo(RestrictTo.Scope.TESTS)
220
+    public FrameLayout getModalsLayout() {
221
+        return modalsLayout;
222
+    }
224 223
 }

+ 37
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenter.java View File

@@ -0,0 +1,37 @@
1
+package com.reactnativenavigation.viewcontrollers.navigator;
2
+
3
+import android.content.Context;
4
+import android.widget.FrameLayout;
5
+
6
+import com.reactnativenavigation.anim.NavigationAnimator;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.utils.CommandListener;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+import com.reactnativenavigation.views.element.ElementTransitionManager;
11
+
12
+public class RootPresenter {
13
+    private NavigationAnimator animator;
14
+    private FrameLayout rootLayout;
15
+
16
+    public void setRootContainer(FrameLayout rootLayout) {
17
+        this.rootLayout = rootLayout;
18
+    }
19
+
20
+    public RootPresenter(Context context) {
21
+        animator = new NavigationAnimator(context, new ElementTransitionManager());
22
+    }
23
+
24
+    RootPresenter(NavigationAnimator animator) {
25
+        this.animator = animator;
26
+    }
27
+
28
+    public void setRoot(ViewController root, Options defaultOptions, CommandListener listener) {
29
+        rootLayout.addView(root.getView());
30
+        Options options = root.resolveCurrentOptions(defaultOptions);
31
+        if (options.animations.setRoot.hasAnimation()) {
32
+            animator.setRoot(root.getView(), options.animations.setRoot, () -> listener.onSuccess(root.getId()));
33
+        } else {
34
+            listener.onSuccess(root.getId());
35
+        }
36
+    }
37
+}

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

@@ -1,7 +1,7 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import com.reactnativenavigation.react.ReactGateway;
4
-import com.reactnativenavigation.viewcontrollers.Navigator;
4
+import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
5 5
 
6 6
 import org.mockito.Mockito;
7 7
 

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java → lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers.navigator;
2 2
 
3 3
 import android.os.Bundle;
4 4
 import android.support.annotation.NonNull;
@@ -25,6 +25,9 @@ import com.reactnativenavigation.utils.CompatUtils;
25 25
 import com.reactnativenavigation.utils.ImageLoader;
26 26
 import com.reactnativenavigation.utils.OptionHelper;
27 27
 import com.reactnativenavigation.utils.ViewUtils;
28
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
29
+import com.reactnativenavigation.viewcontrollers.ComponentViewController;
30
+import com.reactnativenavigation.viewcontrollers.ViewController;
28 31
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
29 32
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
30 33
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
@@ -47,6 +50,7 @@ public class NavigatorTest extends BaseTest {
47 50
     private TestActivity activity;
48 51
     private ChildControllersRegistry childRegistry;
49 52
     private Navigator uut;
53
+    private RootPresenter rootPresenter;
50 54
     private StackController parentController;
51 55
     private SimpleViewController child1;
52 56
     private ViewController child2;
@@ -70,8 +74,9 @@ public class NavigatorTest extends BaseTest {
70 74
         activityController = newActivityController(TestActivity.class);
71 75
         activity = activityController.create().get();
72 76
         modalStack = spy(new ModalStack(activity));
77
+        rootPresenter = spy(new RootPresenter(activity));
73 78
         modalStack.setEventEmitter(Mockito.mock(EventEmitter.class));
74
-        uut = new Navigator(activity, childRegistry, modalStack, overlayManager);
79
+        uut = new Navigator(activity, childRegistry, modalStack, overlayManager, rootPresenter);
75 80
         activity.setNavigator(uut);
76 81
 
77 82
         parentController = newStack();
@@ -93,10 +98,18 @@ public class NavigatorTest extends BaseTest {
93 98
         child4 = new SimpleViewController(activity, childRegistry, "child4", tabOptions);
94 99
         child5 = new SimpleViewController(activity, childRegistry, "child5", tabOptions);
95 100
 
101
+        uut.bindViews();
102
+
96 103
         activityController.visible();
97 104
         activityController.postCreate(Bundle.EMPTY);
98 105
     }
99 106
 
107
+    @Test
108
+    public void bindViews() {
109
+        verify(rootPresenter).setRootContainer(uut.getRootLayout());
110
+        verify(modalStack).setModalsContainer(uut.getModalsLayout());
111
+    }
112
+
100 113
     @Test
101 114
     public void setDefaultOptions() {
102 115
         uut.setDefaultOptions(new Options());
@@ -110,6 +123,13 @@ public class NavigatorTest extends BaseTest {
110 123
         verify(modalStack).setDefaultOptions(defaultOptions);
111 124
     }
112 125
 
126
+    @Test
127
+    public void setRoot_delegatesToRootPresenter() {
128
+        CommandListenerAdapter listener = new CommandListenerAdapter();
129
+        uut.setRoot(child1, listener);
130
+        verify(rootPresenter).setRoot(child1, uut.getDefaultOptions(), listener);
131
+    }
132
+
113 133
     @Test
114 134
     public void setRoot_clearsSplashLayout() {
115 135
         disableModalAnimations(child1);
@@ -138,7 +158,7 @@ public class NavigatorTest extends BaseTest {
138 158
     @Test
139 159
     public void hasUniqueId() {
140 160
         assertThat(uut.getId()).startsWith("navigator");
141
-        assertThat(new Navigator(activity, childRegistry, modalStack, overlayManager).getId()).isNotEqualTo(uut.getId());
161
+        assertThat(new Navigator(activity, childRegistry, modalStack, overlayManager, rootPresenter).getId()).isNotEqualTo(uut.getId());
142 162
     }
143 163
 
144 164
     @Test

+ 97
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java View File

@@ -0,0 +1,97 @@
1
+package com.reactnativenavigation.viewcontrollers.navigator;
2
+
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
5
+import android.view.View;
6
+import android.widget.FrameLayout;
7
+
8
+import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.anim.NavigationAnimator;
10
+import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.parse.AnimationOptions;
12
+import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.utils.CommandListenerAdapter;
14
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
15
+import com.reactnativenavigation.viewcontrollers.ViewController;
16
+import com.reactnativenavigation.views.element.ElementTransitionManager;
17
+
18
+import org.junit.Test;
19
+
20
+import static org.assertj.core.api.Java6Assertions.assertThat;
21
+import static org.mockito.ArgumentMatchers.any;
22
+import static org.mockito.ArgumentMatchers.eq;
23
+import static org.mockito.Mockito.mock;
24
+import static org.mockito.Mockito.spy;
25
+import static org.mockito.Mockito.verify;
26
+import static org.mockito.Mockito.verifyZeroInteractions;
27
+import static org.mockito.Mockito.when;
28
+
29
+public class RootPresenterTest extends BaseTest {
30
+    private RootPresenter uut;
31
+    private FrameLayout rootContainer;
32
+    private ViewController root;
33
+    private NavigationAnimator animator;
34
+    private Options defaultOptions;
35
+
36
+
37
+    @Override
38
+    public void beforeEach() {
39
+        Activity activity = newActivity();
40
+        rootContainer = new FrameLayout(activity);
41
+        root = new SimpleViewController(activity, new ChildControllersRegistry(), "child1", new Options());
42
+        animator = spy(createAnimator(activity));
43
+        uut = new RootPresenter(animator);
44
+        uut.setRootContainer(rootContainer);
45
+        defaultOptions = new Options();
46
+    }
47
+
48
+    @Test
49
+    public void setRoot_viewIsAddedToContainer() {
50
+        uut.setRoot(root, defaultOptions, new CommandListenerAdapter());
51
+        assertThat(root.getView().getParent()).isEqualTo(rootContainer);
52
+    }
53
+
54
+    @Test
55
+    public void setRoot_reportsOnSuccess() {
56
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
57
+        uut.setRoot(root, defaultOptions, listener);
58
+        verify(listener).onSuccess(root.getId());
59
+    }
60
+
61
+    @Test
62
+    public void setRoot_doesNotAnimateByDefault() {
63
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
64
+        uut.setRoot(root, defaultOptions, listener);
65
+        verifyZeroInteractions(animator);
66
+        verify(listener).onSuccess(root.getId());
67
+    }
68
+
69
+    @Test
70
+    public void setRoot_animates() {
71
+        Options animatedSetRoot = new Options();
72
+        animatedSetRoot.animations.setRoot = new AnimationOptions() {
73
+            @Override
74
+            public boolean hasAnimation() {
75
+                return true;
76
+            }
77
+        };
78
+
79
+        ViewController spy = spy(root);
80
+        when(spy.resolveCurrentOptions(defaultOptions)).thenReturn(animatedSetRoot);
81
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
82
+
83
+        uut.setRoot(spy, defaultOptions, listener);
84
+        verify(animator).setRoot(eq(spy.getView()), eq(animatedSetRoot.animations.setRoot), any());
85
+        verify(listener).onSuccess(spy.getId());
86
+    }
87
+
88
+    @NonNull
89
+    private NavigationAnimator createAnimator(Activity activity) {
90
+        return new NavigationAnimator(activity, mock(ElementTransitionManager.class)) {
91
+            @Override
92
+            public void setRoot(View root, AnimationOptions setRoot, Runnable onAnimationEnd) {
93
+                onAnimationEnd.run();
94
+            }
95
+        };
96
+    }
97
+}