From d2939eafbf56f99ea4da7374b9812ae6f54c1d3d Mon Sep 17 00:00:00 2001 From: "wenzhe.lv" Date: Mon, 27 Mar 2017 07:00:58 -0700 Subject: [PATCH] Fix crash when move on the edge of some phones like HUAWEI P9 Summary: The app crashes because of `TouchEventCoalescingKeyHelper.getCoalescingKey` throwing an exception when move on the edge of some phones like HUAWEI P9. It's caused by that the down time differs from `ACTION_DOWN` to `ACTION_MOVE` which belongs to the same gesture when I touched on the edge of my HUAWEI P9. It seems a native bug of manufacturer. Related issue #11302. ``` java.lang.RuntimeException: Tried to get non-existent cookie at com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper.getCoalescingKey(TouchEventCoalescingKeyHelper.java:75) at com.facebook.react.uimanager.events.TouchEvent.init(TouchEvent.java:93) at com.facebook.react.uimanager.events.TouchEvent.obtain(TouchEvent.java:46) at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java:118) at com.facebook.react.ReactRootView.dispatchJSTouchEvent(ReactRootView.java:158) at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java:130) at android.view.Vie Closes https://github.com/facebook/react-native/pull/12063 Differential Revision: D4779060 Pulled By: astreet fbshipit-source-id: 5cf20084ff9081f2535ff9cb3b99d1d52c443f03 --- .../react/uimanager/JSTouchDispatcher.java | 10 ++++++++++ .../react/uimanager/events/TouchEvent.java | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index 97d366a53..ba8d56be8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -30,6 +30,7 @@ public class JSTouchDispatcher { private int mTargetTag = -1; private final float[] mTargetCoordinates = new float[2]; private boolean mChildIsHandlingNativeGesture = false; + private long mGestureStartTime = TouchEvent.UNSET; private final ViewGroup mRootViewGroup; private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper = new TouchEventCoalescingKeyHelper(); @@ -72,6 +73,7 @@ public class JSTouchDispatcher { // {@link #findTargetTagForTouch} to find react view ID that will be responsible for handling // this gesture mChildIsHandlingNativeGesture = false; + mGestureStartTime = ev.getEventTime(); mTargetTag = TouchTargetHelper.findTargetTagAndCoordinatesForTouch( ev.getX(), ev.getY(), @@ -83,6 +85,7 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.START, ev, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); @@ -105,10 +108,12 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.END, ev, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); mTargetTag = -1; + mGestureStartTime = TouchEvent.UNSET; } else if (action == MotionEvent.ACTION_MOVE) { // Update pointer position for current gesture eventDispatcher.dispatchEvent( @@ -116,6 +121,7 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.MOVE, ev, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); @@ -126,6 +132,7 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.START, ev, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); @@ -136,6 +143,7 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.END, ev, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); @@ -149,6 +157,7 @@ public class JSTouchDispatcher { ); } mTargetTag = -1; + mGestureStartTime = TouchEvent.UNSET; } else { FLog.w( ReactConstants.TAG, @@ -176,6 +185,7 @@ public class JSTouchDispatcher { mTargetTag, TouchEventType.CANCEL, androidEvent, + mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java index 0262f0bd0..03d922852 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java @@ -15,6 +15,7 @@ import android.support.v4.util.Pools; import android.view.MotionEvent; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.SoftAssertions; /** * An event representing the start, end or movement of a touch. Corresponds to a single @@ -31,10 +32,13 @@ public class TouchEvent extends Event { private static final Pools.SynchronizedPool EVENTS_POOL = new Pools.SynchronizedPool<>(TOUCH_EVENTS_POOL_SIZE); + public static final long UNSET = Long.MIN_VALUE; + public static TouchEvent obtain( int viewTag, TouchEventType touchEventType, MotionEvent motionEventToCopy, + long gestureStartTime, float viewX, float viewY, TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) { @@ -46,6 +50,7 @@ public class TouchEvent extends Event { viewTag, touchEventType, motionEventToCopy, + gestureStartTime, viewX, viewY, touchEventCoalescingKeyHelper); @@ -67,30 +72,33 @@ public class TouchEvent extends Event { int viewTag, TouchEventType touchEventType, MotionEvent motionEventToCopy, + long gestureStartTime, float viewX, float viewY, TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) { super.init(viewTag); + SoftAssertions.assertCondition(gestureStartTime != UNSET, + "Gesture start time must be initialized"); short coalescingKey = 0; int action = (motionEventToCopy.getAction() & MotionEvent.ACTION_MASK); switch (action) { case MotionEvent.ACTION_DOWN: - touchEventCoalescingKeyHelper.addCoalescingKey(motionEventToCopy.getDownTime()); + touchEventCoalescingKeyHelper.addCoalescingKey(gestureStartTime); break; case MotionEvent.ACTION_UP: - touchEventCoalescingKeyHelper.removeCoalescingKey(motionEventToCopy.getDownTime()); + touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime); break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: - touchEventCoalescingKeyHelper.incrementCoalescingKey(motionEventToCopy.getDownTime()); + touchEventCoalescingKeyHelper.incrementCoalescingKey(gestureStartTime); break; case MotionEvent.ACTION_MOVE: coalescingKey = - touchEventCoalescingKeyHelper.getCoalescingKey(motionEventToCopy.getDownTime()); + touchEventCoalescingKeyHelper.getCoalescingKey(gestureStartTime); break; case MotionEvent.ACTION_CANCEL: - touchEventCoalescingKeyHelper.removeCoalescingKey(motionEventToCopy.getDownTime()); + touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime); break; default: throw new RuntimeException("Unhandled MotionEvent action: " + action);