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
 package com.reactnativenavigation.utils;
1
 package com.reactnativenavigation.utils;
2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
+import android.content.res.Resources;
4
 import android.graphics.PorterDuff;
5
 import android.graphics.PorterDuff;
5
 import android.graphics.PorterDuffColorFilter;
6
 import android.graphics.PorterDuffColorFilter;
6
 import android.graphics.drawable.Drawable;
7
 import android.graphics.drawable.Drawable;
8
+import android.os.Build;
7
 import android.os.Handler;
9
 import android.os.Handler;
8
 import android.os.Looper;
10
 import android.os.Looper;
9
 import android.util.DisplayMetrics;
11
 import android.util.DisplayMetrics;
11
 import android.view.ViewTreeObserver;
13
 import android.view.ViewTreeObserver;
12
 import android.view.WindowManager;
14
 import android.view.WindowManager;
13
 
15
 
16
+import com.reactnativenavigation.NavigationApplication;
17
+
14
 public class UiUtils {
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
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
23
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
16
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
24
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
17
             @Override
25
             @Override
39
 		}
47
 		}
40
 		return metrics.heightPixels;
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
 
94
 
95
     @Override
95
     @Override
96
     public boolean onInterceptTouchEvent(MotionEvent ev) {
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
 import android.view.MotionEvent;
5
 import android.view.MotionEvent;
6
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
7
 
7
 
8
+import com.reactnativenavigation.utils.UiUtils;
8
 import com.reactnativenavigation.viewcontrollers.IReactView;
9
 import com.reactnativenavigation.viewcontrollers.IReactView;
9
 
10
 
10
 public class OverlayTouchDelegate {
11
 public class OverlayTouchDelegate {
12
+    private enum TouchLocation {Outside, Inside}
11
     private final Rect hitRect = new Rect();
13
     private final Rect hitRect = new Rect();
12
     private IReactView reactView;
14
     private IReactView reactView;
13
     private boolean interceptTouchOutside;
15
     private boolean interceptTouchOutside;
17
     }
19
     }
18
 
20
 
19
     public boolean onInterceptTouchEvent(MotionEvent event) {
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
     @VisibleForTesting
32
     @VisibleForTesting
28
     public boolean handleDown(MotionEvent event) {
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
     public void setInterceptTouchOutside(boolean interceptTouchOutside) {
51
     public void setInterceptTouchOutside(boolean interceptTouchOutside) {

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

8
 
8
 
9
 import org.junit.Test;
9
 import org.junit.Test;
10
 
10
 
11
+import static org.assertj.core.api.Assertions.assertThat;
11
 import static org.mockito.Mockito.spy;
12
 import static org.mockito.Mockito.spy;
12
 import static org.mockito.Mockito.times;
13
 import static org.mockito.Mockito.times;
13
 import static org.mockito.Mockito.verify;
14
 import static org.mockito.Mockito.verify;
18
     private final int y = 10;
19
     private final int y = 10;
19
     private final MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, x, y, 0);
20
     private final MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, x, y, 0);
20
     private final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, x, y, 0);
21
     private final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, x, y, 0);
22
+    private SimpleOverlay reactView;
21
 
23
 
22
     @Override
24
     @Override
23
     public void beforeEach() {
25
     public void beforeEach() {
24
         super.beforeEach();
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
     @Test
31
     @Test
40
     }
43
     }
41
 
44
 
42
     @Test
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
 }