diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java index 01cfdfcf7e5f9b..f67e79a3b02432 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java @@ -22,7 +22,9 @@ import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * JSPointerDispatcher handles dispatching pointer events to JS from RootViews. If you implement @@ -37,9 +39,8 @@ public class JSPointerDispatcher { private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper = new TouchEventCoalescingKeyHelper(); - private List mLastHitPath = Collections.emptyList(); - private final float[] mLastEventCoordinates = new float[2]; - private final float[] mTargetCoordinates = new float[2]; + private final Map> mLastHitPathByPointerId = new HashMap<>(); + private final Map mLastEventCoodinatesByPointerId = new HashMap<>(); private int mChildHandlingNativeGesture = -1; private int mPrimaryPointerId = UNSET_POINTER_ID; @@ -62,10 +63,11 @@ public void onChildStartedNativeGesture( return; } + float[] targetCoordinates = new float[2]; List hitPath = TouchTargetHelper.findTargetPathAndCoordinatesForTouch( - motionEvent.getX(), motionEvent.getY(), mRootViewGroup, mTargetCoordinates); - dispatchCancelEvent(hitPath, motionEvent, eventDispatcher); + motionEvent.getX(), motionEvent.getY(), mRootViewGroup, targetCoordinates); + dispatchCancelEvent(hitPath, motionEvent, eventDispatcher, targetCoordinates); mChildHandlingNativeGesture = childView.getId(); } @@ -79,7 +81,8 @@ private void onUp( List hitPath, int surfaceId, MotionEvent motionEvent, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + float[] targetCoordinates) { if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { // End of a "down" coalescing key mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime); @@ -97,7 +100,7 @@ private void onUp( surfaceId, activeTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } @@ -111,7 +114,7 @@ private void onUp( surfaceId, activeTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } @@ -124,13 +127,17 @@ private void onUp( leaveViewTargets, eventDispatcher, surfaceId, - motionEvent); + motionEvent, + targetCoordinates); + + int activePointerId = motionEvent.getPointerId(motionEvent.getActionIndex()); + mLastHitPathByPointerId.remove(activePointerId); + mLastEventCoodinatesByPointerId.remove(activePointerId); } if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { mPrimaryPointerId = UNSET_POINTER_ID; } - return; } private void onDown( @@ -138,7 +145,8 @@ private void onDown( List hitPath, int surfaceId, MotionEvent motionEvent, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + float[] targetCoordinates) { if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { mPrimaryPointerId = motionEvent.getPointerId(0); @@ -160,7 +168,7 @@ private void onDown( surfaceId, activeTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } @@ -174,7 +182,8 @@ private void onDown( enterViewTargets, eventDispatcher, surfaceId, - motionEvent); + motionEvent, + targetCoordinates); } boolean listeningForDown = @@ -186,7 +195,7 @@ private void onDown( surfaceId, activeTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } } @@ -199,18 +208,18 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp return; } - boolean supportsHover = PointerEventHelper.supportsHover(motionEvent); int surfaceId = UIManagerHelper.getSurfaceId(mRootViewGroup); // Only relevant for POINTER_UP/POINTER_DOWN actions, otherwise 0 int actionIndex = motionEvent.getActionIndex(); + float[] targetCoordinates = new float[2]; List hitPath = TouchTargetHelper.findTargetPathAndCoordinatesForTouch( motionEvent.getX(actionIndex), motionEvent.getY(actionIndex), mRootViewGroup, - mTargetCoordinates); + targetCoordinates); if (hitPath.isEmpty()) { return; @@ -220,7 +229,7 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp int activeTargetTag = activeViewTarget.getViewId(); if (action == MotionEvent.ACTION_HOVER_MOVE) { - onMove(motionEvent, eventDispatcher, surfaceId, hitPath); + onMove(motionEvent, eventDispatcher, surfaceId, hitPath, targetCoordinates); return; } @@ -234,7 +243,8 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: - onDown(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher); + onDown( + activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher, targetCoordinates); break; case MotionEvent.ACTION_MOVE: // TODO(luwe) - converge this with ACTION_HOVER_MOVE @@ -249,27 +259,22 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp surfaceId, activeTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, coalescingKey, mPrimaryPointerId)); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: - onUp(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher); + onUp(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher, targetCoordinates); break; case MotionEvent.ACTION_CANCEL: - dispatchCancelEvent(hitPath, motionEvent, eventDispatcher); + dispatchCancelEvent(hitPath, motionEvent, eventDispatcher, targetCoordinates); break; default: FLog.w( ReactConstants.TAG, - "Warning : Motion Event was ignored. Action=" - + action - + " Target=" - + activeTargetTag - + " Supports Hover=" - + supportsHover); + "Warning : Motion Event was ignored. Action=" + action + " Target=" + activeTargetTag); return; } } @@ -325,13 +330,14 @@ private void dispatchEventForViewTargets( List viewTargets, EventDispatcher dispatcher, int surfaceId, - MotionEvent motionEvent) { + MotionEvent motionEvent, + float[] targetCoordinates) { for (ViewTarget viewTarget : viewTargets) { int viewId = viewTarget.getViewId(); dispatcher.dispatchEvent( PointerEvent.obtain( - eventName, surfaceId, viewId, motionEvent, mTargetCoordinates, mPrimaryPointerId)); + eventName, surfaceId, viewId, motionEvent, targetCoordinates, mPrimaryPointerId)); } } @@ -340,19 +346,31 @@ private void onMove( MotionEvent motionEvent, EventDispatcher eventDispatcher, int surfaceId, - List hitPath) { + List hitPath, + float[] targetCoordinates) { int action = motionEvent.getActionMasked(); if (action != MotionEvent.ACTION_HOVER_MOVE) { return; } + int actionIndex = motionEvent.getActionIndex(); + int activePointerId = motionEvent.getPointerId(actionIndex); float x = motionEvent.getX(); float y = motionEvent.getY(); + List lastHitPath = + mLastHitPathByPointerId.containsKey(activePointerId) + ? mLastHitPathByPointerId.get(activePointerId) + : Collections.emptyList(); + + float[] lastEventCoordinates = + mLastEventCoodinatesByPointerId.containsKey(activePointerId) + ? mLastEventCoodinatesByPointerId.get(activePointerId) + : new float[] {0, 0}; boolean qualifiedMove = - (Math.abs(mLastEventCoordinates[0] - x) > ONMOVE_EPSILON - || Math.abs(mLastEventCoordinates[1] - y) > ONMOVE_EPSILON); + (Math.abs(lastEventCoordinates[0] - x) > ONMOVE_EPSILON + || Math.abs(lastEventCoordinates[1] - y) > ONMOVE_EPSILON); // Early exit if (!qualifiedMove) { @@ -384,15 +402,15 @@ private void onMove( } // hitState is list ordered from inner child -> parent tag - // Traverse hitState back-to-front to find the first divergence with mLastHitState + // Traverse hitState back-to-front to find the first divergence with lastHitPath // FIXME: this may generate incorrect events when view collapsing changes the hierarchy boolean nonDivergentListeningToEnter = false; boolean nonDivergentListeningToLeave = false; int firstDivergentIndexFromBack = 0; - while (firstDivergentIndexFromBack < Math.min(hitPath.size(), mLastHitPath.size()) + while (firstDivergentIndexFromBack < Math.min(hitPath.size(), lastHitPath.size()) && hitPath .get(hitPath.size() - 1 - firstDivergentIndexFromBack) - .equals(mLastHitPath.get(mLastHitPath.size() - 1 - firstDivergentIndexFromBack))) { + .equals(lastHitPath.get(lastHitPath.size() - 1 - firstDivergentIndexFromBack))) { // Track if any non-diverging views are listening to enter/leave View nonDivergentViewTargetView = @@ -410,17 +428,17 @@ private void onMove( } boolean hasDiverged = - firstDivergentIndexFromBack < Math.max(hitPath.size(), mLastHitPath.size()); + firstDivergentIndexFromBack < Math.max(hitPath.size(), lastHitPath.size()); if (hasDiverged) { // If something has changed in either enter/exit, let's start a new coalescing key mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey); // Out, Leave events - if (mLastHitPath.size() > 0) { - int lastTargetTag = mLastHitPath.get(0).getViewId(); + if (lastHitPath.size() > 0) { + int lastTargetTag = lastHitPath.get(0).getViewId(); boolean listeningForOut = - isAnyoneListeningForBubblingEvent(mLastHitPath, EVENT.OUT, EVENT.OUT_CAPTURE); + isAnyoneListeningForBubblingEvent(lastHitPath, EVENT.OUT, EVENT.OUT_CAPTURE); if (listeningForOut) { eventDispatcher.dispatchEvent( PointerEvent.obtain( @@ -428,14 +446,14 @@ private void onMove( surfaceId, lastTargetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } // target -> root List leaveViewTargets = filterByShouldDispatch( - mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack), + lastHitPath.subList(0, lastHitPath.size() - firstDivergentIndexFromBack), EVENT.LEAVE, EVENT.LEAVE_CAPTURE, nonDivergentListeningToLeave); @@ -446,7 +464,8 @@ private void onMove( leaveViewTargets, eventDispatcher, surfaceId, - motionEvent); + motionEvent, + targetCoordinates); } } @@ -459,7 +478,7 @@ private void onMove( surfaceId, targetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } @@ -479,7 +498,8 @@ private void onMove( enterViewTargets, eventDispatcher, surfaceId, - motionEvent); + motionEvent, + targetCoordinates); } } @@ -493,18 +513,20 @@ private void onMove( surfaceId, targetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, coalescingKey, mPrimaryPointerId)); } - mLastHitPath = hitPath; - mLastEventCoordinates[0] = x; - mLastEventCoordinates[1] = y; + mLastHitPathByPointerId.put(activePointerId, hitPath); + mLastEventCoodinatesByPointerId.put(activePointerId, new float[] {x, y}); } private void dispatchCancelEvent( - List hitPath, MotionEvent motionEvent, EventDispatcher eventDispatcher) { + List hitPath, + MotionEvent motionEvent, + EventDispatcher eventDispatcher, + float[] targetCoordinates) { // This means the gesture has already ended, via some other CANCEL or UP event. This is not // expected to happen very often as it would mean some child View has decided to intercept the // touch stream and start a native gesture only upon receiving the UP/CANCEL event. @@ -526,7 +548,7 @@ private void dispatchCancelEvent( surfaceId, targetTag, motionEvent, - mTargetCoordinates, + targetCoordinates, mPrimaryPointerId)); } @@ -539,7 +561,8 @@ private void dispatchCancelEvent( leaveViewTargets, eventDispatcher, surfaceId, - motionEvent); + motionEvent, + targetCoordinates); mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime); mDownStartTime = TouchEvent.UNSET;