From 64ae12e55388d56268017b7a67cad689d5d74599 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Thu, 1 Sep 2022 13:11:44 -0700 Subject: [PATCH] PointerEvents: Refactor JSPointerDispatcher to share logic for UP/DOWN for secondary pointers Summary: Changelog: [Internal] - Refactor JSPointerDispatcher to group secondary actions (pointer_up, pointer_down) to use same logic and emit non-direct events This diff re-arranges some code and re-uses some logic for different MotionEvent types. Specifically, before our code for handling a motionEvent looked like ``` func onMotionEvent(event) { if (event is HOVER_MOVE) { handleHoverEvent(event) } if (event is ACTION_DOWN) { // logic for dispatching down, enter, over } if (event is ACTION_POINTER_DOWN) { // this represents any secondary pointer going down // logic for dispatching down } if (event is ACTION_MOVE) { // logic for dispatching touch move } if (event is ACTION_UP) { // logic for dispatching up, out, leave } if (event is ACTION_POINTER_UP) { // this represents any secondary pointer going up // logic for dispatching up } .... } ``` now, we've refactored it to be like: ``` func onMotionEvent(event) { if (event is HOVER_MOVE) { // Still keep this as separate because it does some special things. Will refactor in follow-up diff handleHoverEvent(event) } switch() { case ACTION_DOWN or ACTION_POINTER_DOWN: // do same logic break; case ACTION_UP or ACTION_POINTER_UP: // do same logic break; case ACTION_MOVE: // do same logic, we will want to converge this logic with HOVER_MOVE, todo in a follow-up break; ... } .... } ``` This refactor adds the functionality of `enter, over, out, leave` to secondary pointer events in which they weren't fired before. Reviewed By: vincentriemer Differential Revision: D39142820 fbshipit-source-id: 8b89db6dc22f24583f8c14dbb8392d4dc8ff6e4d --- .../react/uimanager/JSPointerDispatcher.java | 322 ++++++++---------- 1 file changed, 151 insertions(+), 171 deletions(-) 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 d547137ccee..01cfdfcf7e5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java @@ -74,6 +74,123 @@ public class JSPointerDispatcher { mChildHandlingNativeGesture = -1; } + private void onUp( + int activeTargetTag, + List hitPath, + int surfaceId, + MotionEvent motionEvent, + EventDispatcher eventDispatcher) { + if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { + // End of a "down" coalescing key + mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime); + mDownStartTime = TouchEvent.UNSET; + } else { + mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime); + } + + boolean supportsHover = PointerEventHelper.supportsHover(motionEvent); + boolean listeningForUp = isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE); + if (listeningForUp) { + eventDispatcher.dispatchEvent( + PointerEvent.obtain( + PointerEventHelper.POINTER_UP, + surfaceId, + activeTargetTag, + motionEvent, + mTargetCoordinates, + mPrimaryPointerId)); + } + + if (!supportsHover) { + boolean listeningForOut = + isAnyoneListeningForBubblingEvent(hitPath, EVENT.OUT, EVENT.OUT_CAPTURE); + if (listeningForOut) { + eventDispatcher.dispatchEvent( + PointerEvent.obtain( + PointerEventHelper.POINTER_OUT, + surfaceId, + activeTargetTag, + motionEvent, + mTargetCoordinates, + mPrimaryPointerId)); + } + + List leaveViewTargets = + filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false); + + // target -> root + dispatchEventForViewTargets( + PointerEventHelper.POINTER_LEAVE, + leaveViewTargets, + eventDispatcher, + surfaceId, + motionEvent); + } + + if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { + mPrimaryPointerId = UNSET_POINTER_ID; + } + return; + } + + private void onDown( + int activeTargetTag, + List hitPath, + int surfaceId, + MotionEvent motionEvent, + EventDispatcher eventDispatcher) { + + if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + mPrimaryPointerId = motionEvent.getPointerId(0); + mDownStartTime = motionEvent.getEventTime(); + mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime); + } else { + mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime); + } + + boolean supportsHover = PointerEventHelper.supportsHover(motionEvent); + if (!supportsHover) { + // Indirect OVER event dispatches before ENTER + boolean listeningForOver = + isAnyoneListeningForBubblingEvent(hitPath, EVENT.OVER, EVENT.OVER_CAPTURE); + if (listeningForOver) { + eventDispatcher.dispatchEvent( + PointerEvent.obtain( + PointerEventHelper.POINTER_OVER, + surfaceId, + activeTargetTag, + motionEvent, + mTargetCoordinates, + mPrimaryPointerId)); + } + + List enterViewTargets = + filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false); + + // Dispatch root -> target, we need to reverse order of enterViewTargets + Collections.reverse(enterViewTargets); + dispatchEventForViewTargets( + PointerEventHelper.POINTER_ENTER, + enterViewTargets, + eventDispatcher, + surfaceId, + motionEvent); + } + + boolean listeningForDown = + isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE); + if (listeningForDown) { + eventDispatcher.dispatchEvent( + PointerEvent.obtain( + PointerEventHelper.POINTER_DOWN, + surfaceId, + activeTargetTag, + motionEvent, + mTargetCoordinates, + mPrimaryPointerId)); + } + } + public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDispatcher) { int action = motionEvent.getActionMasked(); @@ -103,195 +220,58 @@ public class JSPointerDispatcher { int activeTargetTag = activeViewTarget.getViewId(); if (action == MotionEvent.ACTION_HOVER_MOVE) { - handleHoverEvent(motionEvent, eventDispatcher, surfaceId, hitPath); - return; - } - - // First down pointer - if (action == MotionEvent.ACTION_DOWN) { - mPrimaryPointerId = motionEvent.getPointerId(actionIndex); - - // Start a "down" coalescing key - mDownStartTime = motionEvent.getEventTime(); - mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime); - - if (!supportsHover) { - // Indirect OVER event dispatches before ENTER - boolean listeningForOver = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.OVER, EVENT.OVER_CAPTURE); - if (listeningForOver) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_OVER, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - mPrimaryPointerId)); - } - - List enterViewTargets = - filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false); - - // Dispatch root -> target, we need to reverse order of enterViewTargets - Collections.reverse(enterViewTargets); - dispatchEventForViewTargets( - PointerEventHelper.POINTER_ENTER, - enterViewTargets, - eventDispatcher, - surfaceId, - motionEvent); - } - - boolean listeningForDown = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE); - if (listeningForDown) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_DOWN, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - mPrimaryPointerId)); - } - + onMove(motionEvent, eventDispatcher, surfaceId, hitPath); return; } + // TODO(luwe) - Update this to properly handle native gesture handling for non-hover move events // If the touch was intercepted by a child, we've already sent a cancel event to JS for this // gesture, so we shouldn't send any more pointer events related to it. if (mChildHandlingNativeGesture != -1) { return; } - // New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer - if (action == MotionEvent.ACTION_POINTER_DOWN) { - mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + onDown(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher); + break; + case MotionEvent.ACTION_MOVE: + // TODO(luwe) - converge this with ACTION_HOVER_MOVE + int coalescingKey = mTouchEventCoalescingKeyHelper.getCoalescingKey(mDownStartTime); - // TODO(luwe) We need to fire indirect over,enter events for secondary pointers - - boolean listeningForDown = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE); - if (listeningForDown) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_DOWN, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - mPrimaryPointerId)); - } - - return; - } - - if (action == MotionEvent.ACTION_MOVE) { - int coalescingKey = mTouchEventCoalescingKeyHelper.getCoalescingKey(mDownStartTime); - - boolean listeningForMove = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.MOVE, EVENT.MOVE_CAPTURE); - if (listeningForMove) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_MOVE, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - coalescingKey, - mPrimaryPointerId)); - } - - return; - } - - // Exactly one of the pointers goes up, not the last one - if (action == MotionEvent.ACTION_POINTER_UP) { - mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime); - - // TODO(luwe) We need to fire indirect out,leave events for secondary pointers - - boolean listeningForUp = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE); - if (listeningForUp) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_UP, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - mPrimaryPointerId)); - } - - return; - } - - // Last pointer comes up - if (action == MotionEvent.ACTION_UP) { - - // End of a "down" coalescing key - mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime); - mDownStartTime = TouchEvent.UNSET; - - boolean listeningForUp = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE); - if (listeningForUp) { - eventDispatcher.dispatchEvent( - PointerEvent.obtain( - PointerEventHelper.POINTER_UP, - surfaceId, - activeTargetTag, - motionEvent, - mTargetCoordinates, - mPrimaryPointerId)); - } - - if (!supportsHover) { - boolean listeningForOut = - isAnyoneListeningForBubblingEvent(hitPath, EVENT.OUT, EVENT.OUT_CAPTURE); - if (listeningForOut) { + boolean listeningForMove = + isAnyoneListeningForBubblingEvent(hitPath, EVENT.MOVE, EVENT.MOVE_CAPTURE); + if (listeningForMove) { eventDispatcher.dispatchEvent( PointerEvent.obtain( - PointerEventHelper.POINTER_OUT, + PointerEventHelper.POINTER_MOVE, surfaceId, activeTargetTag, motionEvent, mTargetCoordinates, + coalescingKey, mPrimaryPointerId)); } - - List leaveViewTargets = - filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false); - - // target -> root - dispatchEventForViewTargets( - PointerEventHelper.POINTER_LEAVE, - leaveViewTargets, - eventDispatcher, - surfaceId, - motionEvent); - } - - mPrimaryPointerId = UNSET_POINTER_ID; - return; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + onUp(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher); + break; + case MotionEvent.ACTION_CANCEL: + dispatchCancelEvent(hitPath, motionEvent, eventDispatcher); + break; + default: + FLog.w( + ReactConstants.TAG, + "Warning : Motion Event was ignored. Action=" + + action + + " Target=" + + activeTargetTag + + " Supports Hover=" + + supportsHover); + return; } - - if (action == MotionEvent.ACTION_CANCEL) { - dispatchCancelEvent(hitPath, motionEvent, eventDispatcher); - return; - } - - FLog.w( - ReactConstants.TAG, - "Warning : Motion Event was ignored. Action=" - + action - + " Target=" - + activeTargetTag - + " Supports Hover=" - + supportsHover); } private static boolean isAnyoneListeningForBubblingEvent( @@ -356,7 +336,7 @@ public class JSPointerDispatcher { } // called on hover_move motion events only - private void handleHoverEvent( + private void onMove( MotionEvent motionEvent, EventDispatcher eventDispatcher, int surfaceId,