Browse Source

Fix interceptTouchOutside

It was handled inappropriately and some touch events were
intercepted unintentionally.
Guy Carmeli 6 years ago
parent
commit
5c48211e76

+ 26
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java View File

@@ -1,9 +1,11 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.content.Context;
4
+import android.content.res.Resources;
4 5
 import android.graphics.PorterDuff;
5 6
 import android.graphics.PorterDuffColorFilter;
6 7
 import android.graphics.drawable.Drawable;
8
+import android.os.Build;
7 9
 import android.os.Handler;
8 10
 import android.os.Looper;
9 11
 import android.util.DisplayMetrics;
@@ -11,7 +13,13 @@ import android.view.View;
11 13
 import android.view.ViewTreeObserver;
12 14
 import android.view.WindowManager;
13 15
 
16
+import com.reactnativenavigation.NavigationApplication;
17
+
14 18
 public class UiUtils {
19
+    public static final int STATUS_BAR_HEIGHT_M = 24;
20
+    public static final int STATUS_BAR_HEIGHT_L = 25;
21
+    private static int statusBarHeight = -1;
22
+
15 23
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
16 24
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
17 25
             @Override
@@ -39,4 +47,22 @@ public class UiUtils {
39 47
 		}
40 48
 		return metrics.heightPixels;
41 49
 	}
50
+
51
+    public static int getStatusBarHeight(Context context) {
52
+        if (statusBarHeight > 0) {
53
+            return statusBarHeight;
54
+        }
55
+        final Resources resources = context.getResources();
56
+        final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
57
+        statusBarHeight = resourceId > 0 ?
58
+                resources.getDimensionPixelSize(resourceId) :
59
+                (int) dpToPx(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? STATUS_BAR_HEIGHT_M : STATUS_BAR_HEIGHT_L);
60
+        return statusBarHeight;
61
+    }
62
+
63
+    public static float dpToPx(float dp) {
64
+        float scale = NavigationApplication.instance.getResources().getDisplayMetrics().density;
65
+        return dp * scale + 0.5f;
66
+    }
67
+
42 68
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java View File

@@ -94,6 +94,6 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, Titl
94 94
 
95 95
     @Override
96 96
     public boolean onInterceptTouchEvent(MotionEvent ev) {
97
-        return touchDelegate.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
97
+        return touchDelegate.onInterceptTouchEvent(ev);
98 98
     }
99 99
 }

+ 23
- 10
lib/android/app/src/main/java/com/reactnativenavigation/views/touch/OverlayTouchDelegate.java View File

@@ -5,9 +5,11 @@ import android.support.annotation.VisibleForTesting;
5 5
 import android.view.MotionEvent;
6 6
 import android.view.ViewGroup;
7 7
 
8
+import com.reactnativenavigation.utils.UiUtils;
8 9
 import com.reactnativenavigation.viewcontrollers.IReactView;
9 10
 
10 11
 public class OverlayTouchDelegate {
12
+    private enum TouchLocation {Outside, Inside}
11 13
     private final Rect hitRect = new Rect();
12 14
     private IReactView reactView;
13 15
     private boolean interceptTouchOutside;
@@ -17,22 +19,33 @@ public class OverlayTouchDelegate {
17 19
     }
18 20
 
19 21
     public boolean onInterceptTouchEvent(MotionEvent event) {
20
-        return interceptTouchOutside && isDown(event) && handleDown(event);
21
-    }
22
-
23
-    private boolean isDown(MotionEvent event) {
24
-        return event.getActionMasked() == MotionEvent.ACTION_DOWN;
22
+        switch (event.getActionMasked()) {
23
+            case MotionEvent.ACTION_DOWN:
24
+                return handleDown(event);
25
+            default:
26
+                reactView.dispatchTouchEventToJs(event);
27
+                return false;
28
+
29
+        }
25 30
     }
26 31
 
27 32
     @VisibleForTesting
28 33
     public boolean handleDown(MotionEvent event) {
29
-        ((ViewGroup) reactView.asView()).getChildAt(0).getHitRect(hitRect);
30
-        reactView.dispatchTouchEventToJs(event);
31
-        return isTouchOutside(event);
34
+        TouchLocation location = getTouchLocation(event);
35
+        if (location == TouchLocation.Inside) {
36
+            reactView.dispatchTouchEventToJs(event);
37
+        }
38
+        if (interceptTouchOutside) {
39
+            return location == TouchLocation.Inside;
40
+        }
41
+        return location == TouchLocation.Outside;
32 42
     }
33 43
 
34
-    private boolean isTouchOutside(MotionEvent ev) {
35
-        return !hitRect.contains((int) ev.getRawX(), (int) ev.getRawY());
44
+    private TouchLocation getTouchLocation(MotionEvent ev) {
45
+        ((ViewGroup) reactView.asView()).getChildAt(0).getHitRect(hitRect);
46
+        return hitRect.contains((int) ev.getRawX(), (int) ev.getRawY() - UiUtils.getStatusBarHeight(reactView.asView().getContext())) ?
47
+                TouchLocation.Inside :
48
+                TouchLocation.Outside;
36 49
     }
37 50
 
38 51
     public void setInterceptTouchOutside(boolean interceptTouchOutside) {

+ 14
- 4
lib/android/app/src/test/java/com/reactnativenavigation/views/TouchDelegateTest.java View File

@@ -8,6 +8,7 @@ import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
8 8
 
9 9
 import org.junit.Test;
10 10
 
11
+import static org.assertj.core.api.Assertions.assertThat;
11 12
 import static org.mockito.Mockito.spy;
12 13
 import static org.mockito.Mockito.times;
13 14
 import static org.mockito.Mockito.verify;
@@ -18,11 +19,13 @@ public class TouchDelegateTest extends BaseTest {
18 19
     private final int y = 10;
19 20
     private final MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, x, y, 0);
20 21
     private final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, x, y, 0);
22
+    private SimpleOverlay reactView;
21 23
 
22 24
     @Override
23 25
     public void beforeEach() {
24 26
         super.beforeEach();
25
-        uut = spy(new OverlayTouchDelegate(new SimpleOverlay(newActivity())));
27
+        reactView = spy(new SimpleOverlay(newActivity()));
28
+        uut = spy(new OverlayTouchDelegate(reactView));
26 29
     }
27 30
 
28 31
     @Test
@@ -40,8 +43,15 @@ public class TouchDelegateTest extends BaseTest {
40 43
     }
41 44
 
42 45
     @Test
43
-    public void downIsHandledOnlyIfInterceptTouchOutsideIsTrue() throws Exception {
44
-        uut.onInterceptTouchEvent(downEvent);
45
-        verify(uut, times(0)).handleDown(downEvent);
46
+    public void nonDownEventsDontIntercept() throws Exception {
47
+        uut.setInterceptTouchOutside(true);
48
+        assertThat(uut.onInterceptTouchEvent(upEvent)).isFalse();
49
+    }
50
+
51
+    @Test
52
+    public void nonDownEventsDispatchTouchEventsToJs() throws Exception {
53
+        uut.setInterceptTouchOutside(true);
54
+        uut.onInterceptTouchEvent(upEvent);
55
+        verify(reactView, times(1)).dispatchTouchEventToJs(upEvent);
46 56
     }
47 57
 }