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
 package com.reactnativenavigation.presentation;
1
 package com.reactnativenavigation.presentation;
2
 
2
 
3
+import android.view.View;
3
 import android.view.ViewGroup;
4
 import android.view.ViewGroup;
4
 
5
 
5
 import com.reactnativenavigation.utils.CommandListener;
6
 import com.reactnativenavigation.utils.CommandListener;
7
+import com.reactnativenavigation.utils.ViewUtils;
6
 import com.reactnativenavigation.viewcontrollers.ViewController;
8
 import com.reactnativenavigation.viewcontrollers.ViewController;
7
 import com.reactnativenavigation.views.BehaviourDelegate;
9
 import com.reactnativenavigation.views.BehaviourDelegate;
8
 
10
 
9
 import java.util.HashMap;
11
 import java.util.HashMap;
10
 
12
 
13
+import static com.reactnativenavigation.utils.CollectionUtils.*;
11
 import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
14
 import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
12
 
15
 
13
 public class OverlayManager {
16
 public class OverlayManager {
14
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
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
     public void show(ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) {
24
     public void show(ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) {
25
+        if (overlaysContainer.getParent() == null) contentLayout.addView(overlaysContainer);
17
         overlayRegistry.put(overlay.getId(), overlay);
26
         overlayRegistry.put(overlay.getId(), overlay);
18
         overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId()));
27
         overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId()));
19
         overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay)));
28
         overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay)));
24
         if (overlay == null) {
33
         if (overlay == null) {
25
             listener.onError("Could not dismiss Overlay. Overlay with id " + componentId + " was not found.");
34
             listener.onError("Could not dismiss Overlay. Overlay with id " + componentId + " was not found.");
26
         } else {
35
         } else {
27
-            overlay.destroy();
28
-            overlayRegistry.remove(componentId);
36
+            destroyOverlay(overlay);
29
             listener.onSuccess(componentId);
37
             listener.onSuccess(componentId);
30
         }
38
         }
31
     }
39
     }
32
 
40
 
33
     public void destroy() {
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
     public int size() {
46
     public int size() {
44
     public ViewController findControllerById(String id) {
50
     public ViewController findControllerById(String id) {
45
         return overlayRegistry.get(id);
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
 
64
 
65
     public void setContentLayout(ViewGroup contentLayout) {
65
     public void setContentLayout(ViewGroup contentLayout) {
66
         this.contentLayout = contentLayout;
66
         this.contentLayout = contentLayout;
67
+        overlayManager.setContentLayout(contentLayout);
67
         contentLayout.addView(rootLayout);
68
         contentLayout.addView(rootLayout);
68
         contentLayout.addView(modalsLayout);
69
         contentLayout.addView(modalsLayout);
69
-        contentLayout.addView(overlaysLayout);
70
     }
70
     }
71
 
71
 
72
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, ModalStack modalStack, OverlayManager overlayManager, RootPresenter rootPresenter) {
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
 
2
 
3
 import android.os.Bundle;
3
 import android.os.Bundle;
4
 import android.view.View;
4
 import android.view.View;
5
+import android.view.ViewGroup;
5
 import android.widget.FrameLayout;
6
 import android.widget.FrameLayout;
6
 
7
 
7
 import com.facebook.react.ReactInstanceManager;
8
 import com.facebook.react.ReactInstanceManager;
118
         activityController.postCreate(Bundle.EMPTY);
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
     @Test
130
     @Test
122
     public void bindViews() {
131
     public void bindViews() {
123
         verify(rootPresenter).setRootContainer(uut.getRootLayout());
132
         verify(rootPresenter).setRootContainer(uut.getRootLayout());
149
     @Test
158
     @Test
150
     public void setRoot_clearsSplashLayout() {
159
     public void setRoot_clearsSplashLayout() {
151
         FrameLayout content = activity.findViewById(android.R.id.content);
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
         uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
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
     @Test
168
     @Test

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

26
     private OverlayManager uut;
26
     private OverlayManager uut;
27
     private SimpleViewController overlay1;
27
     private SimpleViewController overlay1;
28
     private SimpleViewController overlay2;
28
     private SimpleViewController overlay2;
29
-    private FrameLayout root;
29
+    private FrameLayout contentLayout;
30
+    private FrameLayout overlayContainer;
30
 
31
 
31
     @Override
32
     @Override
32
     public void beforeEach() {
33
     public void beforeEach() {
33
         Activity activity = newActivity();
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
         ChildControllersRegistry childRegistry = new ChildControllersRegistry();
40
         ChildControllersRegistry childRegistry = new ChildControllersRegistry();
39
         overlay1 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_1, new Options()));
41
         overlay1 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_1, new Options()));
40
         overlay2 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_2, new Options()));
42
         overlay2 = spy(new SimpleViewController(activity, childRegistry, OVERLAY_ID_2, new Options()));
41
         uut = new OverlayManager();
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
     @Test
54
     @Test
45
     public void show() {
55
     public void show() {
46
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
56
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
47
-        uut.show(root, overlay1, listener);
57
+        uut.show(overlayContainer, overlay1, listener);
48
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
58
         verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
49
-        assertThat(overlay1.getView().getParent()).isEqualTo(root);
59
+        assertThat(overlay1.getView().getParent()).isEqualTo(overlayContainer);
50
         assertMatchParent(overlay1.getView());
60
         assertMatchParent(overlay1.getView());
51
     }
61
     }
52
 
62
 
53
     @Test
63
     @Test
54
     public void dismiss() {
64
     public void dismiss() {
55
-        uut.show(root, overlay1, new CommandListenerAdapter());
65
+        uut.show(overlayContainer, overlay1, new CommandListenerAdapter());
56
         assertThat(uut.size()).isOne();
66
         assertThat(uut.size()).isOne();
57
         CommandListener listener = spy(new CommandListenerAdapter());
67
         CommandListener listener = spy(new CommandListenerAdapter());
58
         uut.dismiss(overlay1.getId(), listener);
68
         uut.dismiss(overlay1.getId(), listener);
70
 
80
 
71
     @Test
81
     @Test
72
     public void dismiss_onViewReturnedToFront() {
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
         verify(overlay1, times(0)).onViewBroughtToFront();
85
         verify(overlay1, times(0)).onViewBroughtToFront();
76
 
86
 
77
         uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
87
         uut.dismiss(OVERLAY_ID_2, new CommandListenerAdapter());
78
         verify(overlay1, times(1)).onViewBroughtToFront();
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
 }