Browse Source

Fix showing Modal from TopBar components in RN 62 (#6199)

When showing React Native's Modal from a TopBar component - RNN mistakingly considered Modals as yellow boxes and removed them. Apparently this error surfaced in RN62
Guy Carmeli 4 years ago
parent
commit
94862ed668
No account linked to committer's email address
17 changed files with 92 additions and 85 deletions
  1. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  2. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  3. 0
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/NoOpYellowBoxDelegate.java
  4. 8
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/NoOpYellowBoxDelegate.kt
  5. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  6. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  7. 0
    63
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegate.java
  8. 45
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegate.kt
  9. 7
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxHelper.java
  10. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java
  11. 0
    0
      lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/DevBundleDownloadListenerAdapter.java
  12. 0
    0
      lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/JsDevReloadHandlerFacade.java
  13. 0
    0
      lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/NavigationReactNativeHost.java
  14. 0
    0
      lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/ReloadHandlerFacade.java
  15. 25
    1
      lib/android/app/src/test/java/com/reactnativenavigation/TestApplication.java
  16. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  17. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegateTest.java

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java View File

@@ -43,7 +43,7 @@ public class ViewUtils {
43 43
         for (int i = 0; i < root.getChildCount(); i++) {
44 44
             View view = root.getChildAt(i);
45 45
             if (view instanceof ViewGroup) {
46
-                ret.addAll(findChildrenByClassRecursive((ViewGroup) view, clazz));
46
+                ret.addAll(findChildrenByClassRecursive((ViewGroup) view, clazz, matcher));
47 47
             }
48 48
             if (clazz.isAssignableFrom(view.getClass()) && matcher.match((T) view)) {
49 49
                 ret.add((T) view);

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

@@ -24,7 +24,7 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
24 24
     }
25 25
 
26 26
     public ChildController(Activity activity, ChildControllersRegistry childRegistry, String id, Presenter presenter, Options initialOptions) {
27
-        super(activity, id, new NoOpYellowBoxDelegate(), initialOptions, new ViewControllerOverlay(activity));
27
+        super(activity, id, new NoOpYellowBoxDelegate(activity), initialOptions, new ViewControllerOverlay(activity));
28 28
         this.presenter = presenter;
29 29
         this.childRegistry = childRegistry;
30 30
     }

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

@@ -1,10 +0,0 @@
1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import android.view.View;
4
-
5
-public class NoOpYellowBoxDelegate extends YellowBoxDelegate {
6
-    @Override
7
-    public void onChildViewAdded(View parent, View child) {
8
-
9
-    }
10
-}

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

@@ -0,0 +1,8 @@
1
+package com.reactnativenavigation.viewcontrollers
2
+
3
+import android.content.Context
4
+import android.view.View
5
+
6
+class NoOpYellowBoxDelegate(context: Context) : YellowBoxDelegate(context) {
7
+    override fun onChildViewAdded(parent: View, child: View?) {}
8
+}

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

@@ -67,7 +67,7 @@ public class TitleBarButtonController extends ViewController<TitleBarReactButton
67 67
                                     Button button,
68 68
                                     TitleBarButtonCreator viewCreator,
69 69
                                     OnClickListener onClickListener) {
70
-        super(activity, button.id, new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
70
+        super(activity, button.id, new YellowBoxDelegate(activity), new Options(), new ViewControllerOverlay(activity));
71 71
         this.navigationIconResolver = navigationIconResolver;
72 72
         this.presenter = presenter;
73 73
         this.button = button;

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

@@ -20,7 +20,7 @@ public class TitleBarReactViewController extends ViewController<TitleBarReactVie
20 20
     }
21 21
 
22 22
     public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator, Component component) {
23
-        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
23
+        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(activity), new Options(), new ViewControllerOverlay(activity));
24 24
         this.reactViewCreator = reactViewCreator;
25 25
         this.component = component;
26 26
     }

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

@@ -1,63 +0,0 @@
1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import androidx.annotation.RestrictTo;
4
-import android.view.View;
5
-import android.view.ViewGroup;
6
-
7
-import com.reactnativenavigation.utils.UiUtils;
8
-
9
-import java.util.ArrayList;
10
-import java.util.List;
11
-
12
-public class YellowBoxDelegate {
13
-    private ViewGroup parent;
14
-    private YellowBoxHelper yellowBoxHelper;
15
-    private boolean isDestroyed;
16
-    private ArrayList<View> yellowBoxViews = new ArrayList<>();
17
-
18
-    public YellowBoxDelegate() {
19
-        this.yellowBoxHelper = new YellowBoxHelper();
20
-    }
21
-
22
-    YellowBoxDelegate(YellowBoxHelper yellowBoxHelper) {
23
-        this.yellowBoxHelper = yellowBoxHelper;
24
-    }
25
-
26
-    public void onChildViewAdded(View parent, View child) {
27
-        UiUtils.runOnPreDrawOnce(child, () -> {
28
-            if (yellowBoxHelper.isYellowBox(parent, child)) {
29
-                onYellowBoxAdded(parent);
30
-            }
31
-        });
32
-    }
33
-
34
-    void onYellowBoxAdded(View parent) {
35
-        if (isDestroyed) return;
36
-        this.parent = (ViewGroup) parent;
37
-
38
-        for (int i = 1; i < this.parent.getChildCount(); i++) {
39
-            yellowBoxViews.add(this.parent.getChildAt(i));
40
-            this.parent.removeView(this.parent.getChildAt(i));
41
-            this.parent.addView(new View(parent.getContext()), i);
42
-        }
43
-    }
44
-
45
-    public void destroy() {
46
-        isDestroyed = true;
47
-        if (!yellowBoxViews.isEmpty()) {
48
-            for (View view : yellowBoxViews) {
49
-                parent.addView(view);
50
-            }
51
-        }
52
-    }
53
-
54
-    @RestrictTo(RestrictTo.Scope.TESTS)
55
-    public ViewGroup getParent() {
56
-        return parent;
57
-    }
58
-
59
-    @RestrictTo(RestrictTo.Scope.TESTS)
60
-    public List<View> getYellowBoxes() {
61
-        return yellowBoxViews;
62
-    }
63
-}

+ 45
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxDelegate.kt View File

@@ -0,0 +1,45 @@
1
+package com.reactnativenavigation.viewcontrollers
2
+
3
+import android.content.Context
4
+import android.view.View
5
+import android.view.ViewGroup
6
+import androidx.annotation.RestrictTo
7
+import androidx.core.view.doOnPreDraw
8
+import androidx.core.view.forEachIndexed
9
+import androidx.core.view.get
10
+import com.reactnativenavigation.utils.isDebug
11
+import java.util.*
12
+
13
+open class YellowBoxDelegate(private val context: Context, private val yellowBoxHelper: YellowBoxHelper = YellowBoxHelper()) {
14
+    constructor(context: Context) : this(context, YellowBoxHelper())
15
+
16
+    @get:RestrictTo(RestrictTo.Scope.TESTS)
17
+    var parent: ViewGroup? = null
18
+        private set
19
+    @get:RestrictTo(RestrictTo.Scope.TESTS)
20
+    val yellowBoxes: List<View>
21
+        get() = yellowBoxViews
22
+
23
+    private var isDestroyed = false
24
+    private val yellowBoxViews = ArrayList<View>()
25
+
26
+    open fun onChildViewAdded(parent: View, child: View?) {
27
+        if (!context.isDebug()) return
28
+        child?.doOnPreDraw { if (yellowBoxHelper.isYellowBox(parent, child)) onYellowBoxAdded(parent) }
29
+    }
30
+
31
+    fun onYellowBoxAdded(parent: View) {
32
+        if (isDestroyed) return
33
+        this.parent = parent as ViewGroup
34
+        for (i in 1 until parent.childCount) {
35
+            yellowBoxViews.add(parent[i])
36
+            parent.removeView(parent[i])
37
+            parent.addView(View(context), i)
38
+        }
39
+    }
40
+
41
+    fun destroy() {
42
+        isDestroyed = true
43
+        if (yellowBoxViews.isNotEmpty()) yellowBoxViews.forEach { parent?.addView(it) }
44
+    }
45
+}

+ 7
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/YellowBoxHelper.java View File

@@ -1,24 +1,27 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import androidx.annotation.NonNull;
4 3
 import android.view.View;
5 4
 import android.view.ViewGroup;
6 5
 
7 6
 import com.facebook.react.views.view.ReactViewBackgroundDrawable;
8 7
 import com.reactnativenavigation.utils.ViewUtils;
9 8
 
10
-class YellowBoxHelper {
9
+import androidx.annotation.NonNull;
10
+
11
+import static com.reactnativenavigation.utils.ViewUtils.findChildrenByClassRecursive;
12
+
13
+public class YellowBoxHelper {
11 14
     private final static int YELLOW_BOX_COLOR = -218449360;
12 15
 
13 16
     boolean isYellowBox(View parent, View child) {
14 17
         return parent instanceof ViewGroup &&
15 18
                child instanceof ViewGroup &&
16 19
                ((ViewGroup) parent).getChildCount() > 1 &&
17
-               !ViewUtils.findChildrenByClassRecursive((ViewGroup) child, View.class, YellowBackgroundMather((ViewGroup) child)).isEmpty();
20
+               !findChildrenByClassRecursive((ViewGroup) child, View.class, YellowBackgroundMather()).isEmpty();
18 21
     }
19 22
 
20 23
     @NonNull
21
-    private static ViewUtils.Matcher<View> YellowBackgroundMather(ViewGroup vg) {
24
+    private static ViewUtils.Matcher<View> YellowBackgroundMather() {
22 25
         return child1 -> child1.getBackground() instanceof ReactViewBackgroundDrawable && ((ReactViewBackgroundDrawable) child1.getBackground()).getColor() == YELLOW_BOX_COLOR;
23 26
     }
24 27
 }

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

@@ -18,7 +18,7 @@ public class TopBarBackgroundViewController extends ViewController<TopBarBackgro
18 18
     private Component component;
19 19
 
20 20
     public TopBarBackgroundViewController(Activity activity, TopBarBackgroundViewCreator viewCreator) {
21
-        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
21
+        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(activity), new Options(), new ViewControllerOverlay(activity));
22 22
         this.viewCreator = viewCreator;
23 23
     }
24 24
 

lib/android/app/src/reactNative62/java/reactnativenavigation/react/DevBundleDownloadListenerAdapter.java → lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/DevBundleDownloadListenerAdapter.java View File


lib/android/app/src/reactNative62/java/reactnativenavigation/react/JsDevReloadHandlerFacade.java → lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/JsDevReloadHandlerFacade.java View File


lib/android/app/src/reactNative62/java/reactnativenavigation/react/NavigationReactNativeHost.java → lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/NavigationReactNativeHost.java View File


lib/android/app/src/reactNative62/java/reactnativenavigation/react/ReloadHandlerFacade.java → lib/android/app/src/reactNative62/java/com/reactnativenavigation/react/ReloadHandlerFacade.java View File


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

@@ -2,10 +2,34 @@ package com.reactnativenavigation;
2 2
 
3 3
 import android.app.*;
4 4
 
5
-public class TestApplication extends Application {
5
+import com.facebook.react.ReactApplication;
6
+import com.facebook.react.ReactNativeHost;
7
+import com.facebook.react.ReactPackage;
8
+
9
+import java.util.Collections;
10
+import java.util.List;
11
+
12
+public class TestApplication extends Application implements ReactApplication {
13
+    private final ReactNativeHost host = new ReactNativeHost(this) {
14
+        @Override
15
+        public boolean getUseDeveloperSupport() {
16
+            return true;
17
+        }
18
+
19
+        @Override
20
+        protected List<ReactPackage> getPackages() {
21
+            return Collections.emptyList();
22
+        }
23
+    };
24
+
6 25
     @Override
7 26
     public void onCreate() {
8 27
         super.onCreate();
9 28
         setTheme(R.style.Theme_AppCompat);
10 29
     }
30
+
31
+    @Override
32
+    public ReactNativeHost getReactNativeHost() {
33
+        return host;
34
+    }
11 35
 }

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

@@ -65,7 +65,7 @@ public class ViewControllerTest extends BaseTest {
65 65
     @Test
66 66
     public void canOverrideViewCreation() {
67 67
         final FrameLayout otherView = new FrameLayout(activity);
68
-        yellowBoxDelegate = spy(new YellowBoxDelegate());
68
+        yellowBoxDelegate = spy(new YellowBoxDelegate(activity));
69 69
         ViewController myController = new ViewController(activity, "vc", yellowBoxDelegate, new Options(), new ViewControllerOverlay(activity)) {
70 70
             @Override
71 71
             protected FrameLayout createView() {

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

@@ -25,7 +25,7 @@ public class YellowBoxDelegateTest extends BaseTest {
25 25
         yellowBox = new View(context);
26 26
         parent = new FrameLayout(context);
27 27
         yellowBoxHelper = Mockito.mock(YellowBoxHelper.class);
28
-        uut = new YellowBoxDelegate(yellowBoxHelper);
28
+        uut = new YellowBoxDelegate(context, yellowBoxHelper);
29 29
         parent.addView(new View(context)); // We assume view at index 0 is not a yellow box
30 30
         parent.addView(yellowBox);
31 31
     }