Browse Source

Support react-native-youtube (#5903)

react-native-youtubte is a popular library which wraps the native youtube library. The native lib attempts to detect if the player is hidden behind other views in order to prevent developers from playing videos in the background.
Since the overlay container was always attached to hierarchy, the library stopped playback as it mistakingly detected the player was used in the background.

This commit simply attaches the overlay container only when needed so as long as no overlays are displayed, the lib can be used.
Guy Carmeli 5 years ago
parent
commit
ffbd2882b2
No account linked to committer's email address

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

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

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

@@ -64,9 +64,9 @@ public class Navigator extends ParentController {
64 64
 
65 65
     public void setContentLayout(ViewGroup contentLayout) {
66 66
         this.contentLayout = contentLayout;
67
+        overlayManager.setContentLayout(contentLayout);
67 68
         contentLayout.addView(rootLayout);
68 69
         contentLayout.addView(modalsLayout);
69
-        contentLayout.addView(overlaysLayout);
70 70
     }
71 71
 
72 72
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager, RootPresenter rootPresenter) {

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

@@ -2,6 +2,7 @@ package com.reactnativenavigation.viewcontrollers.navigator;
2 2
 
3 3
 import android.os.Bundle;
4 4
 import android.view.View;
5
+import android.view.ViewGroup;
5 6
 import android.widget.FrameLayout;
6 7
 
7 8
 import com.facebook.react.ReactInstanceManager;
@@ -118,6 +119,14 @@ public class NavigatorTest extends BaseTest {
118 119
         activityController.postCreate(Bundle.EMPTY);
119 120
     }
120 121
 
122
+    @Test
123
+    public void setContentLayout() {
124
+        ViewGroup contentLayout = Mockito.mock(ViewGroup.class);
125
+        uut.setContentLayout(contentLayout);
126
+
127
+        verify(overlayManager).setContentLayout(contentLayout);
128
+    }
129
+
121 130
     @Test
122 131
     public void bindViews() {
123 132
         verify(rootPresenter).setRootContainer(uut.getRootLayout());
@@ -149,11 +158,11 @@ public class NavigatorTest extends BaseTest {
149 158
     @Test
150 159
     public void setRoot_clearsSplashLayout() {
151 160
         FrameLayout content = activity.findViewById(android.R.id.content);
152
-        assertThat(content.getChildCount()).isEqualTo(4); // 3 frame layouts and the default splash layout
161
+        assertThat(content.getChildCount()).isEqualTo(3); // 2 frame layouts (root and modal containers) and the default splash layout
153 162
 
154 163
         uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
155 164
 
156
-        assertThat(content.getChildCount()).isEqualTo(3);
165
+        assertThat(content.getChildCount()).isEqualTo(2);
157 166
     }
158 167
 
159 168
     @Test

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

@@ -26,33 +26,43 @@ public class OverlayManagerTest extends BaseTest {
26 26
     private OverlayManager uut;
27 27
     private SimpleViewController overlay1;
28 28
     private SimpleViewController overlay2;
29
-    private FrameLayout root;
29
+    private FrameLayout contentLayout;
30
+    private FrameLayout overlayContainer;
30 31
 
31 32
     @Override
32 33
     public void beforeEach() {
33 34
         Activity activity = newActivity();
34
-        root = new FrameLayout(activity);
35
-        root.layout(0, 0, 1000, 1000);
36
-        activity.setContentView(root);
35
+        contentLayout = new FrameLayout(activity);
36
+        contentLayout.layout(0, 0, 1000, 1000);
37
+        activity.setContentView(contentLayout);
38
+        overlayContainer = new FrameLayout(activity);
37 39
 
38 40
         ChildControllersRegistry childRegistry = new ChildControllersRegistry();
39 41
         overlay1 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_1, new Options()));
40 42
         overlay2 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_2, new Options()));
41 43
         uut = new OverlayManager();
44
+        uut.setContentLayout(contentLayout);
45
+    }
46
+
47
+    @Test
48
+    public void show_attachesOverlayContainerToContentLayout() {
49
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
50
+        assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
51
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
42 52
     }
43 53
 
44 54
     @Test
45 55
     public void show() {
46 56
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
47
-        uut.show(root, overlay1, listener);
57
+        uut.show(overlayContainer, overlay1, listener);
48 58
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
49
-        assertThat(overlay1.getView().getParent()).isEqualTo(root);
59
+        assertThat(overlay1.getView().getParent()).isEqualTo(overlayContainer);
50 60
         assertMatchParent(overlay1.getView());
51 61
     }
52 62
 
53 63
     @Test
54 64
     public void dismiss() {
55
-        uut.show(root, overlay1, new CommandListenerAdapter());
65
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
56 66
         assertThat(uut.size()).isOne();
57 67
         CommandListener listener = spy(new CommandListenerAdapter());
58 68
         uut.dismiss(overlay1.getId(), listener);
@@ -70,11 +80,22 @@ public class OverlayManagerTest extends BaseTest {
70 80
 
71 81
     @Test
72 82
     public void dismiss_onViewReturnedToFront() {
73
-        uut.show(root, overlay1, new CommandListenerAdapter());
74
-        uut.show(root, overlay2, new CommandListenerAdapter());
83
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
84
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
75 85
         verify(overlay1, times(0)).onViewBroughtToFront();
76 86
 
77 87
         uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
78 88
         verify(overlay1, times(1)).onViewBroughtToFront();
79 89
     }
90
+
91
+    @Test
92
+    public void dismiss_overlayContainerIsRemovedIfAllOverlaysAreDismissed() {
93
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
94
+        uut.show(overlayContainer, overlay2, new CommandListenerAdapter());
95
+
96
+        uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
97
+        assertThat(overlayContainer.getParent()).isEqualTo(contentLayout);
98
+        uut.dismiss(OVERLAY_ID_1, new CommandListenerAdapter());
99
+        assertThat(overlayContainer.getParent()).isNull();
100
+    }
80 101
 }