1717import com .facebook .react .uimanager .events .EventDispatcher ;
1818import com .facebook .react .uimanager .events .PointerEvent ;
1919import com .facebook .react .uimanager .events .PointerEventHelper ;
20+ import com .facebook .react .uimanager .events .PointerEventHelper .EVENT ;
2021import com .facebook .react .uimanager .events .TouchEvent ;
2122import com .facebook .react .uimanager .events .TouchEventCoalescingKeyHelper ;
2223import java .util .Collections ;
@@ -37,7 +38,7 @@ public class JSPointerDispatcher {
3738 private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper =
3839 new TouchEventCoalescingKeyHelper ();
3940
40- private static final float ONMOVE_EPSILON = 1f ;
41+ private static final float ONMOVE_EPSILON = 0. 1f ;
4142
4243 // Set globally for hover interactions, referenced for coalescing hover events
4344 private long mHoverInteractionKey = TouchEvent .UNSET ;
@@ -82,7 +83,9 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
8283 if (hitPath .isEmpty ()) {
8384 return ;
8485 }
85- int targetTag = hitPath .get (0 ).getViewId ();
86+
87+ TouchTargetHelper .ViewTarget activeViewTarget = hitPath .get (0 );
88+ int activeTargetTag = activeViewTarget .getViewId ();
8689
8790 if (supportsHover ) {
8891 if (action == MotionEvent .ACTION_HOVER_MOVE ) {
@@ -107,15 +110,17 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
107110 mTouchEventCoalescingKeyHelper .addCoalescingKey (mDownStartTime );
108111
109112 if (!supportsHover ) {
110- // Enter root -> child
111- for (int i = hitPath .size (); i -- > 0 ; ) {
112- int tag = hitPath .get (i ).getViewId ();
113- eventDispatcher .dispatchEvent (
114- PointerEvent .obtain (PointerEventHelper .POINTER_ENTER , surfaceId , tag , motionEvent ));
115- }
113+ dispatchNonBubblingEventForPathWhenListened (
114+ EVENT .ENTER , EVENT .ENTER_CAPTURE , hitPath , eventDispatcher , surfaceId , motionEvent );
115+ }
116+
117+ boolean listeningForDown =
118+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .DOWN , EVENT .DOWN_CAPTURE );
119+ if (listeningForDown ) {
120+ eventDispatcher .dispatchEvent (
121+ PointerEvent .obtain (
122+ PointerEventHelper .POINTER_DOWN , surfaceId , activeTargetTag , motionEvent ));
116123 }
117- eventDispatcher .dispatchEvent (
118- PointerEvent .obtain (PointerEventHelper .POINTER_DOWN , surfaceId , targetTag , motionEvent ));
119124
120125 return ;
121126 }
@@ -129,25 +134,47 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
129134 // New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer
130135 if (action == MotionEvent .ACTION_POINTER_DOWN ) {
131136 mTouchEventCoalescingKeyHelper .incrementCoalescingKey (mDownStartTime );
132- eventDispatcher .dispatchEvent (
133- PointerEvent .obtain (PointerEventHelper .POINTER_DOWN , surfaceId , targetTag , motionEvent ));
137+
138+ boolean listeningForDown =
139+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .DOWN , EVENT .DOWN_CAPTURE );
140+ if (listeningForDown ) {
141+ eventDispatcher .dispatchEvent (
142+ PointerEvent .obtain (
143+ PointerEventHelper .POINTER_DOWN , surfaceId , activeTargetTag , motionEvent ));
144+ }
134145
135146 return ;
136147 }
137148
138149 if (action == MotionEvent .ACTION_MOVE ) {
139150 int coalescingKey = mTouchEventCoalescingKeyHelper .getCoalescingKey (mDownStartTime );
140- eventDispatcher .dispatchEvent (
141- PointerEvent .obtain (
142- PointerEventHelper .POINTER_MOVE , surfaceId , targetTag , motionEvent , coalescingKey ));
151+
152+ boolean listeningForMove =
153+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .MOVE , EVENT .MOVE_CAPTURE );
154+ if (listeningForMove ) {
155+ eventDispatcher .dispatchEvent (
156+ PointerEvent .obtain (
157+ PointerEventHelper .POINTER_MOVE ,
158+ surfaceId ,
159+ activeTargetTag ,
160+ motionEvent ,
161+ coalescingKey ));
162+ }
163+
143164 return ;
144165 }
145166
146167 // Exactly one of the pointers goes up, not the last one
147168 if (action == MotionEvent .ACTION_POINTER_UP ) {
148169 mTouchEventCoalescingKeyHelper .incrementCoalescingKey (mDownStartTime );
149- eventDispatcher .dispatchEvent (
150- PointerEvent .obtain (PointerEventHelper .POINTER_UP , surfaceId , targetTag , motionEvent ));
170+
171+ boolean listeningForUp =
172+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .UP , EVENT .UP_CAPTURE );
173+ if (listeningForUp ) {
174+ eventDispatcher .dispatchEvent (
175+ PointerEvent .obtain (
176+ PointerEventHelper .POINTER_UP , surfaceId , activeTargetTag , motionEvent ));
177+ }
151178
152179 return ;
153180 }
@@ -159,16 +186,17 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
159186 mTouchEventCoalescingKeyHelper .removeCoalescingKey (mDownStartTime );
160187 mDownStartTime = TouchEvent .UNSET ;
161188
162- eventDispatcher .dispatchEvent (
163- PointerEvent .obtain (PointerEventHelper .POINTER_UP , surfaceId , targetTag , motionEvent ));
189+ boolean listeningForUp =
190+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .UP , EVENT .UP_CAPTURE );
191+ if (listeningForUp ) {
192+ eventDispatcher .dispatchEvent (
193+ PointerEvent .obtain (
194+ PointerEventHelper .POINTER_UP , surfaceId , activeTargetTag , motionEvent ));
195+ }
164196
165197 if (!supportsHover ) {
166- // Leave child -> root
167- for (int i = 0 ; i < hitPath .size (); i ++) {
168- int tag = hitPath .get (i ).getViewId ();
169- eventDispatcher .dispatchEvent (
170- PointerEvent .obtain (PointerEventHelper .POINTER_LEAVE , surfaceId , tag , motionEvent ));
171- }
198+ dispatchNonBubblingEventForPathWhenListened (
199+ EVENT .LEAVE , EVENT .LEAVE_CAPTURE , hitPath , eventDispatcher , surfaceId , motionEvent );
172200 }
173201 return ;
174202 }
@@ -183,15 +211,54 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
183211 "Warning : Motion Event was ignored. Action="
184212 + action
185213 + " Target="
186- + targetTag
214+ + activeTargetTag
187215 + " Supports Hover="
188216 + supportsHover );
189217 }
190218
191- private int findTargetTagAndSetCoordinates (MotionEvent ev ) {
192- // This method updates `mTargetCoordinates` with coordinates for the motion event.
193- return TouchTargetHelper .findTargetTagAndCoordinatesForTouch (
194- ev .getX (), ev .getY (), mRootViewGroup , mTargetCoordinates , null );
219+ private static boolean isAnyoneListeningForBubblingEvent (
220+ List <ViewTarget > hitPath , EVENT event , EVENT captureEvent ) {
221+ for (ViewTarget viewTarget : hitPath ) {
222+ if (PointerEventHelper .isListening (viewTarget .getView (), event )
223+ || PointerEventHelper .isListening (viewTarget .getView (), captureEvent )) {
224+ return true ;
225+ }
226+ }
227+ return false ;
228+ }
229+
230+ /*
231+ Dispatch event only if ancestor is listening to relevant event.
232+ This should only be relevant for ENTER/LEAVE events.
233+ @param hitPath - ordered from inner target to root
234+ */
235+
236+ /** Dispatch non-bubbling event along the hit path only when relevant listeners */
237+ private static void dispatchNonBubblingEventForPathWhenListened (
238+ EVENT event ,
239+ EVENT captureEvent ,
240+ List <ViewTarget > hitPath ,
241+ EventDispatcher dispatcher ,
242+ int surfaceId ,
243+ MotionEvent motionEvent ) {
244+
245+ boolean ancestorListening = false ;
246+ String eventName = PointerEventHelper .getDispatchableEventName (event );
247+ if (eventName == null ) {
248+ return ;
249+ }
250+
251+ // iterate through hitPath from ancestor -> target
252+ for (int i = hitPath .size () - 1 ; i >= 0 ; i --) {
253+ View view = hitPath .get (i ).getView ();
254+ int viewId = hitPath .get (i ).getViewId ();
255+ if (ancestorListening
256+ || (i == 0 && PointerEventHelper .isListening (view , event ))
257+ || PointerEventHelper .isListening (view , captureEvent )) {
258+ dispatcher .dispatchEvent (PointerEvent .obtain (eventName , surfaceId , viewId , motionEvent ));
259+ ancestorListening = true ;
260+ }
261+ }
195262 }
196263
197264 // called on hover_move motion events only
@@ -311,21 +378,21 @@ private void dispatchCancelEvent(
311378 int surfaceId = UIManagerHelper .getSurfaceId (mRootViewGroup );
312379
313380 if (!hitPath .isEmpty ()) {
314- int targetTag = hitPath .get (0 ).getViewId ();
315- // Question: Does cancel fire on all in hit path?
316- Assertions .assertNotNull (eventDispatcher )
317- .dispatchEvent (
318- PointerEvent .obtain (
319- PointerEventHelper .POINTER_CANCEL , surfaceId , targetTag , motionEvent ));
320-
321- for (ViewTarget viewTarget : hitPath ) {
322- eventDispatcher .dispatchEvent (
323- PointerEvent .obtain (
324- PointerEventHelper .POINTER_LEAVE , surfaceId , viewTarget .getViewId (), motionEvent ));
381+ boolean listeningForCancel =
382+ isAnyoneListeningForBubblingEvent (hitPath , EVENT .CANCEL , EVENT .CANCEL_CAPTURE );
383+ if (listeningForCancel ) {
384+ int targetTag = hitPath .get (0 ).getViewId ();
385+ Assertions .assertNotNull (eventDispatcher )
386+ .dispatchEvent (
387+ PointerEvent .obtain (
388+ PointerEventHelper .POINTER_CANCEL , surfaceId , targetTag , motionEvent ));
325389 }
326- }
327390
328- mTouchEventCoalescingKeyHelper .removeCoalescingKey (mDownStartTime );
329- mDownStartTime = TouchEvent .UNSET ;
391+ dispatchNonBubblingEventForPathWhenListened (
392+ EVENT .LEAVE , EVENT .LEAVE_CAPTURE , hitPath , eventDispatcher , surfaceId , motionEvent );
393+
394+ mTouchEventCoalescingKeyHelper .removeCoalescingKey (mDownStartTime );
395+ mDownStartTime = TouchEvent .UNSET ;
396+ }
330397 }
331398}
0 commit comments