Browse Source

Root layouts refactor (#5999)

* Hide modal and overlay containers

This commit improves support for react-native-youtube library. This library uses the native Youtube player which requires that no visible views will be laid out on top of the player. This commit set visibility of Modal and Overlay containers to GONE when they are empty.

* fix tests
Guy Carmeli 4 years ago
parent
commit
2793a02272
No account linked to committer's email address

+ 8
- 13
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java View File

4
 import android.view.ViewGroup;
4
 import android.view.ViewGroup;
5
 
5
 
6
 import com.reactnativenavigation.utils.CommandListener;
6
 import com.reactnativenavigation.utils.CommandListener;
7
-import com.reactnativenavigation.utils.ViewUtils;
8
 import com.reactnativenavigation.viewcontrollers.ViewController;
7
 import com.reactnativenavigation.viewcontrollers.ViewController;
9
 import com.reactnativenavigation.views.BehaviourDelegate;
8
 import com.reactnativenavigation.views.BehaviourDelegate;
10
 
9
 
11
 import java.util.HashMap;
10
 import java.util.HashMap;
12
 
11
 
13
-import androidx.annotation.Nullable;
14
-
15
 import static com.reactnativenavigation.utils.CollectionUtils.*;
12
 import static com.reactnativenavigation.utils.CollectionUtils.*;
16
 import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
13
 import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
17
 
14
 
18
 public class OverlayManager {
15
 public class OverlayManager {
19
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
16
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
20
 
17
 
21
-    public void show(@Nullable ViewGroup contentLayout, ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) {
22
-        if (contentLayout == null) return;
23
-        if (overlaysContainer.getParent() == null) contentLayout.addView(overlaysContainer);
18
+    public void show(ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) {
19
+        overlaysContainer.setVisibility(View.VISIBLE);
24
         overlayRegistry.put(overlay.getId(), overlay);
20
         overlayRegistry.put(overlay.getId(), overlay);
25
         overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId()));
21
         overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId()));
26
         overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay)));
22
         overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay)));
27
     }
23
     }
28
 
24
 
29
-    public void dismiss(String componentId, CommandListener listener) {
25
+    public void dismiss(ViewGroup overlaysContainer, String componentId, CommandListener listener) {
30
         ViewController overlay = overlayRegistry.get(componentId);
26
         ViewController overlay = overlayRegistry.get(componentId);
31
         if (overlay == null) {
27
         if (overlay == null) {
32
             listener.onError("Could not dismiss Overlay. Overlay with id " + componentId + " was not found.");
28
             listener.onError("Could not dismiss Overlay. Overlay with id " + componentId + " was not found.");
33
         } else {
29
         } else {
34
-            destroyOverlay(overlay);
30
+            destroyOverlay(overlaysContainer, overlay);
35
             listener.onSuccess(componentId);
31
             listener.onSuccess(componentId);
36
         }
32
         }
37
     }
33
     }
38
 
34
 
39
-    public void destroy() {
40
-        forEach(overlayRegistry.values(), this::destroyOverlay);
35
+    public void destroy(ViewGroup overlaysContainer) {
36
+        forEach(overlayRegistry.values(), overlay -> destroyOverlay(overlaysContainer, overlay));
41
     }
37
     }
42
 
38
 
43
     public int size() {
39
     public int size() {
48
         return overlayRegistry.get(id);
44
         return overlayRegistry.get(id);
49
     }
45
     }
50
 
46
 
51
-    private void destroyOverlay(ViewController overlay) {
52
-        View parent = (View) overlay.getView().getParent();
47
+    private void destroyOverlay(ViewGroup overlaysContainer, ViewController overlay) {
53
         overlay.destroy();
48
         overlay.destroy();
54
         overlayRegistry.remove(overlay.getId());
49
         overlayRegistry.remove(overlay.getId());
55
-        if (isEmpty()) ViewUtils.removeFromParent(parent);
50
+        if (isEmpty()) overlaysContainer.setVisibility(View.GONE);
56
     }
51
     }
57
 
52
 
58
     private boolean isEmpty() {
53
     private boolean isEmpty() {

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

2
 
2
 
3
 import android.animation.Animator;
3
 import android.animation.Animator;
4
 import android.animation.AnimatorListenerAdapter;
4
 import android.animation.AnimatorListenerAdapter;
5
+import android.view.View;
5
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
6
 
7
 
7
 import com.reactnativenavigation.anim.ModalAnimator;
8
 import com.reactnativenavigation.anim.ModalAnimator;
46
 
47
 
47
         Options options = toAdd.resolveCurrentOptions(defaultOptions);
48
         Options options = toAdd.resolveCurrentOptions(defaultOptions);
48
         toAdd.setWaitForRender(options.animations.showModal.waitForRender);
49
         toAdd.setWaitForRender(options.animations.showModal.waitForRender);
50
+        modalsLayout.setVisibility(View.VISIBLE);
49
         modalsLayout.addView(toAdd.getView(), matchParentLP());
51
         modalsLayout.addView(toAdd.getView(), matchParentLP());
50
 
52
 
51
         if (options.animations.showModal.enabled.isTrueOrUndefined()) {
53
         if (options.animations.showModal.enabled.isTrueOrUndefined()) {
107
     private void onDismissEnd(ViewController toDismiss, CommandListener listener) {
109
     private void onDismissEnd(ViewController toDismiss, CommandListener listener) {
108
         listener.onSuccess(toDismiss.getId());
110
         listener.onSuccess(toDismiss.getId());
109
         toDismiss.destroy();
111
         toDismiss.destroy();
112
+        if (isEmpty()) modalsLayout.setVisibility(View.GONE);
113
+    }
114
+
115
+    private boolean isEmpty() {
116
+        return modalsLayout.getChildCount() == 0;
110
     }
117
     }
111
 }
118
 }

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

64
         this.contentLayout = contentLayout;
64
         this.contentLayout = contentLayout;
65
         contentLayout.addView(rootLayout);
65
         contentLayout.addView(rootLayout);
66
         contentLayout.addView(modalsLayout);
66
         contentLayout.addView(modalsLayout);
67
+        contentLayout.addView(overlaysLayout);
67
     }
68
     }
68
 
69
 
69
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager, RootPresenter rootPresenter) {
70
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager, RootPresenter rootPresenter) {
114
 
115
 
115
     public void destroyViews() {
116
     public void destroyViews() {
116
         modalStack.destroy();
117
         modalStack.destroy();
117
-        overlayManager.destroy();
118
+        overlayManager.destroy(overlaysLayout);
118
         destroyRoot();
119
         destroyRoot();
119
     }
120
     }
120
 
121
 
198
     }
199
     }
199
 
200
 
200
     public void showOverlay(ViewController overlay, CommandListener listener) {
201
     public void showOverlay(ViewController overlay, CommandListener listener) {
201
-        overlayManager.show(contentLayout, overlaysLayout, overlay, listener);
202
+        overlayManager.show(overlaysLayout, overlay, listener);
202
     }
203
     }
203
 
204
 
204
     public void dismissOverlay(final String componentId, CommandListener listener) {
205
     public void dismissOverlay(final String componentId, CommandListener listener) {
205
-        overlayManager.dismiss(componentId, listener);
206
+        overlayManager.dismiss(overlaysLayout, componentId, listener);
206
     }
207
     }
207
 
208
 
208
     @Nullable
209
     @Nullable
239
     CoordinatorLayout getModalsLayout() {
240
     CoordinatorLayout getModalsLayout() {
240
         return modalsLayout;
241
         return modalsLayout;
241
     }
242
     }
243
+
244
+    @RestrictTo(RestrictTo.Scope.TESTS)
245
+    CoordinatorLayout getOverlaysLayout() {
246
+        return overlaysLayout;
247
+    }
242
 }
248
 }

+ 8
- 0
lib/android/app/src/test/java/com/reactnativenavigation/BaseTest.java View File

121
         when(mock.getContext()).thenReturn(activity);
121
         when(mock.getContext()).thenReturn(activity);
122
         return mock;
122
         return mock;
123
     }
123
     }
124
+
125
+    protected void assertVisible(View view) {
126
+        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
127
+    }
128
+
129
+    protected void assertGone(View view) {
130
+        assertThat(view.getVisibility()).isEqualTo(View.GONE);
131
+    }
124
 }
132
 }

+ 16
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java View File

257
         inOrder.verify(listener).onSuccess(modal.getId());
257
         inOrder.verify(listener).onSuccess(modal.getId());
258
         inOrder.verify(modal).destroy();
258
         inOrder.verify(modal).destroy();
259
     }
259
     }
260
+
261
+    @Test
262
+    public void dismissModal_modalsLayoutIfHiddenIsAllModalsAreDismissed() {
263
+        disableShowModalAnimation(modal1, modal2);
264
+        disableDismissModalAnimation(modal1, modal2);
265
+
266
+        uut.showModal(modal1, root, new CommandListenerAdapter());
267
+        assertVisible(modalsLayout);
268
+        uut.showModal(modal2, modal1, new CommandListenerAdapter());
269
+        assertVisible(modalsLayout);
270
+
271
+        uut.dismissModal(modal2, modal1, root, new CommandListenerAdapter());
272
+        assertVisible(modalsLayout);
273
+        uut.dismissModal(modal1, root, root, new CommandListenerAdapter());
274
+        assertGone(modalsLayout);
275
+    }
260
 }
276
 }

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

155
     @Test
155
     @Test
156
     public void setRoot_clearsSplashLayout() {
156
     public void setRoot_clearsSplashLayout() {
157
         FrameLayout content = activity.findViewById(android.R.id.content);
157
         FrameLayout content = activity.findViewById(android.R.id.content);
158
-        assertThat(content.getChildCount()).isEqualTo(3); // 2 frame layouts (root and modal containers) and the default splash layout
158
+        assertThat(content.getChildCount()).isEqualTo(4); // 3 frame layouts (root, modal and overlay containers) and the default splash layout
159
 
159
 
160
         uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
160
         uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
161
 
161
 
162
-        assertThat(content.getChildCount()).isEqualTo(2);
162
+        assertThat(content.getChildCount()).isEqualTo(3);
163
     }
163
     }
164
 
164
 
165
     @Test
165
     @Test
658
     public void destroy_destroyOverlayManager() {
658
     public void destroy_destroyOverlayManager() {
659
         uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
659
         uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
660
         activityController.destroy();
660
         activityController.destroy();
661
-        verify(overlayManager).destroy();
661
+        verify(overlayManager).destroy(uut.getOverlaysLayout());
662
     }
662
     }
663
 
663
 
664
     @Test
664
     @Test

+ 18
- 15
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManagerTest.java View File

1
 package com.reactnativenavigation.viewcontrollers.overlay;
1
 package com.reactnativenavigation.viewcontrollers.overlay;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
+import android.view.View;
4
 import android.widget.FrameLayout;
5
 import android.widget.FrameLayout;
5
 
6
 
6
 import com.reactnativenavigation.BaseTest;
7
 import com.reactnativenavigation.BaseTest;
36
         contentLayout.layout(0, 0, 1000, 1000);
37
         contentLayout.layout(0, 0, 1000, 1000);
37
         activity.setContentView(contentLayout);
38
         activity.setContentView(contentLayout);
38
         overlayContainer = new FrameLayout(activity);
39
         overlayContainer = new FrameLayout(activity);
40
+        contentLayout.addView(overlayContainer);
39
 
41
 
40
         ChildControllersRegistry childRegistry = new ChildControllersRegistry();
42
         ChildControllersRegistry childRegistry = new ChildControllersRegistry();
41
         overlay1 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_1, new Options()));
43
         overlay1 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_1, new Options()));
45
 
47
 
46
     @Test
48
     @Test
47
     public void show_attachesOverlayContainerToContentLayout() {
49
     public void show_attachesOverlayContainerToContentLayout() {
48
-        uut.show(contentLayout, overlayContainer, overlay1, new CommandListenerAdapter());
50
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
49
         assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
51
         assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
50
-        uut.show(contentLayout, overlayContainer, overlay2, new CommandListenerAdapter());
52
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
51
     }
53
     }
52
 
54
 
53
     @Test
55
     @Test
54
     public void show() {
56
     public void show() {
55
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
57
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
56
-        uut.show(contentLayout, overlayContainer, overlay1, listener);
58
+        uut.show(overlayContainer, overlay1, listener);
57
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
59
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
58
         assertThat(overlay1.getView().getParent()).isEqualTo(overlayContainer);
60
         assertThat(overlay1.getView().getParent()).isEqualTo(overlayContainer);
59
         assertMatchParent(overlay1.getView());
61
         assertMatchParent(overlay1.getView());
61
 
63
 
62
     @Test
64
     @Test
63
     public void dismiss() {
65
     public void dismiss() {
64
-        uut.show(contentLayout, overlayContainer, overlay1, new CommandListenerAdapter());
66
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
65
         assertThat(uut.size()).isOne();
67
         assertThat(uut.size()).isOne();
66
         CommandListener listener = spy(new CommandListenerAdapter());
68
         CommandListener listener = spy(new CommandListenerAdapter());
67
-        uut.dismiss(overlay1.getId(), listener);
69
+        uut.dismiss(overlayContainer, overlay1.getId(), listener);
68
         assertThat(uut.size()).isZero();
70
         assertThat(uut.size()).isZero();
69
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
71
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
70
         verify(overlay1, times(1)).destroy();
72
         verify(overlay1, times(1)).destroy();
73
     @Test
75
     @Test
74
     public void dismiss_rejectIfOverlayNotFound() {
76
     public void dismiss_rejectIfOverlayNotFound() {
75
         CommandListener listener = spy(new CommandListenerAdapter());
77
         CommandListener listener = spy(new CommandListenerAdapter());
76
-        uut.dismiss(overlay1.getId(), listener);
78
+        uut.dismiss(overlayContainer, overlay1.getId(), listener);
77
         verify(listener, times(1)).onError(any());
79
         verify(listener, times(1)).onError(any());
78
     }
80
     }
79
 
81
 
80
     @Test
82
     @Test
81
     public void dismiss_onViewReturnedToFront() {
83
     public void dismiss_onViewReturnedToFront() {
82
-        uut.show(contentLayout, overlayContainer, overlay1, new CommandListenerAdapter());
83
-        uut.show(contentLayout, overlayContainer, overlay2, new CommandListenerAdapter());
84
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
85
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
84
         verify(overlay1, times(0)).onViewBroughtToFront();
86
         verify(overlay1, times(0)).onViewBroughtToFront();
85
 
87
 
86
-        uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
88
+        uut.dismiss(overlayContainer, OVERLAY_ID_2, new CommandListenerAdapter());
87
         verify(overlay1, times(1)).onViewBroughtToFront();
89
         verify(overlay1, times(1)).onViewBroughtToFront();
88
     }
90
     }
89
 
91
 
90
     @Test
92
     @Test
91
-    public void dismiss_overlayContainerIsRemovedIfAllOverlaysAreDismissed() {
92
-        uut.show(contentLayout, overlayContainer, overlay1, new CommandListenerAdapter());
93
-        uut.show(contentLayout, overlayContainer, overlay2, new CommandListenerAdapter());
93
+    public void dismiss_overlayContainerIsHiddenIfAllOverlaysAreDismissed() {
94
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
95
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
94
 
96
 
95
-        uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
97
+        uut.dismiss(overlayContainer, OVERLAY_ID_2, new CommandListenerAdapter());
98
+        assertThat(overlayContainer.getVisibility()).isEqualTo(View.VISIBLE);
96
         assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
99
         assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
97
-        uut.dismiss(OVERLAY_ID_1, new CommandListenerAdapter());
98
-        assertThat(overlayContainer.getParent()).isNull();
100
+        uut.dismiss(overlayContainer, OVERLAY_ID_1, new CommandListenerAdapter());
101
+        assertThat(overlayContainer.getVisibility()).isEqualTo(View.GONE);
99
     }
102
     }
100
 }
103
 }