diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index c7410ce8199..2136c6cce57 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -105,10 +105,6 @@ public class ReactFeatureFlags { public static boolean enableScrollViewSnapToAlignmentProp = true; - public static boolean useDispatchUniqueForCoalescableEvents = false; - - public static boolean useUpdatedTouchPreprocessing = false; - /** TODO: T103427072 Delete ReactFeatureFlags.enableNestedTextOnPressEventFix */ public static boolean enableNestedTextOnPressEventFix = true; diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java index 983466ab382..425e7d8f2bc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java @@ -7,27 +7,16 @@ package com.facebook.react.fabric.events; -import static com.facebook.react.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_SURFACE_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY; - -import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeArray; -import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.fabric.FabricUIManager; import com.facebook.react.uimanager.events.EventCategoryDef; import com.facebook.react.uimanager.events.RCTModernEventEmitter; -import com.facebook.react.uimanager.events.TouchEventType; +import com.facebook.react.uimanager.events.TouchEvent; +import com.facebook.react.uimanager.events.TouchesHelper; import com.facebook.systrace.Systrace; -import java.util.HashSet; -import java.util.Set; public class FabricEventEmitter implements RCTModernEventEmitter { @@ -67,145 +56,17 @@ public class FabricEventEmitter implements RCTModernEventEmitter { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } - /** - * Processes touches in a JS compatible way and send it to Fabric core - * - * @param eventName the event name (see {@link TouchEventType}) - * @param touches all the touch data extracted from MotionEvent - * @param changedIndices the indices of the pointers that changed (MOVE/CANCEL includes all - * touches, START/END only the one that was added/removed) - */ + /** Touches are dispatched by {@link #receiveTouches(TouchEvent)} */ @Override public void receiveTouches( @NonNull String eventName, @NonNull WritableArray touches, @NonNull WritableArray changedIndices) { - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "FabricEventEmitter.receiveTouches('" + eventName + "')"); - - boolean isFinalEvent = - TouchEventType.END.getJsName().equalsIgnoreCase(eventName) - || TouchEventType.CANCEL.getJsName().equalsIgnoreCase(eventName); - - Pair result = - isFinalEvent - ? removeTouchesAtIndices(touches, changedIndices) - : touchSubsequence(touches, changedIndices); - - WritableArray changedTouches = result.first; - touches = result.second; - - int eventCategory = getTouchCategory(eventName); - for (int jj = 0; jj < changedTouches.size(); jj++) { - WritableMap touch = getWritableMap(changedTouches.getMap(jj)); - // Touch objects can fulfill the role of `DOM` `Event` objects if we set - // the `changedTouches`/`touches`. This saves allocations. - - touch.putArray(CHANGED_TOUCHES_KEY, copyWritableArray(changedTouches)); - touch.putArray(TOUCHES_KEY, copyWritableArray(touches)); - WritableMap nativeEvent = touch; - int rootNodeID = 0; - int targetSurfaceId = nativeEvent.getInt(TARGET_SURFACE_KEY); - int targetReactTag = nativeEvent.getInt(TARGET_KEY); - if (targetReactTag < 1) { - FLog.e(TAG, "A view is reporting that a touch occurred on tag zero."); - } else { - rootNodeID = targetReactTag; - } - - receiveEvent(targetSurfaceId, rootNodeID, eventName, false, 0, touch, eventCategory); - } - - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + throw new IllegalStateException("EventEmitter#receiveTouches is not supported by Fabric"); } - /** TODO T31905686 optimize this to avoid copying arrays */ - private WritableArray copyWritableArray(@NonNull WritableArray array) { - WritableNativeArray ret = new WritableNativeArray(); - for (int i = 0; i < array.size(); i++) { - ret.pushMap(getWritableMap(array.getMap(i))); - } - return ret; - } - - /** - * Destroys `touches` by removing touch objects at indices `indices`. This is to maintain - * compatibility with W3C touch "end" events, where the active touches don't include the set that - * has just been "ended". - * - *

This method was originally in ReactNativeRenderer.js - * - *

TODO: this method is a copy from ReactNativeRenderer.removeTouchesAtIndices and it needs to - * be rewritten in a more efficient way, - * - * @param touches {@link WritableArray} Deserialized touch objects. - * @param indices {WritableArray} Indices to remove from `touches`. - * @return {Array} Subsequence of removed touch objects. - */ - private @NonNull Pair removeTouchesAtIndices( - @NonNull WritableArray touches, @NonNull WritableArray indices) { - WritableArray rippedOut = new WritableNativeArray(); - // use an unsafe downcast to alias to nullable elements, - // so we can delete and then compact. - WritableArray tempTouches = new WritableNativeArray(); - Set rippedOutIndices = new HashSet<>(); - for (int i = 0; i < indices.size(); i++) { - int index = indices.getInt(i); - rippedOut.pushMap(getWritableMap(touches.getMap(index))); - rippedOutIndices.add(index); - } - for (int j = 0; j < touches.size(); j++) { - if (!rippedOutIndices.contains(j)) { - tempTouches.pushMap(getWritableMap(touches.getMap(j))); - } - } - - return new Pair<>(rippedOut, tempTouches); - } - - /** - * Selects a subsequence of `Touch`es, without destroying `touches`. - * - *

This method was originally in ReactNativeRenderer.js - * - * @param touches {@link WritableArray} Deserialized touch objects. - * @param changedIndices {@link WritableArray} Indices by which to pull subsequence. - * @return {Array} Subsequence of touch objects. - */ - private @NonNull Pair touchSubsequence( - @NonNull WritableArray touches, @NonNull WritableArray changedIndices) { - WritableArray result = new WritableNativeArray(); - for (int i = 0; i < changedIndices.size(); i++) { - result.pushMap(getWritableMap(touches.getMap(changedIndices.getInt(i)))); - } - return new Pair<>(result, touches); - } - - /** - * TODO: this is required because the WritableNativeArray.getMap() returns a ReadableMap instead - * of the original writableMap. this will change in the near future. - * - * @param readableMap {@link ReadableMap} source map - */ - private @NonNull WritableMap getWritableMap(@NonNull ReadableMap readableMap) { - WritableNativeMap map = new WritableNativeMap(); - map.merge(readableMap); - return map; - } - - @EventCategoryDef - private static int getTouchCategory(String touchEventType) { - int category = EventCategoryDef.UNSPECIFIED; - if (TouchEventType.MOVE.getJsName().equals(touchEventType)) { - category = EventCategoryDef.CONTINUOUS; - } else if (TouchEventType.START.getJsName().equals(touchEventType)) { - category = EventCategoryDef.CONTINUOUS_START; - } else if (TouchEventType.END.getJsName().equals(touchEventType) - || TouchEventType.CANCEL.getJsName().equals(touchEventType)) { - category = EventCategoryDef.CONTINUOUS_END; - } - - return category; + @Override + public void receiveTouches(TouchEvent event) { + TouchesHelper.sendTouchEvent(this, event); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java index b205bdeb3c7..7a67e7a2a82 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java @@ -159,7 +159,7 @@ public abstract class Event { /** * Dispatch this event to JS using the given event emitter. Compatible with old and new renderer. * Instead of using this or dispatchModern, it is recommended that you simply override - * `getEventData`. In the future + * `getEventData`. */ @Deprecated public void dispatch(RCTEventEmitter rctEventEmitter) { @@ -192,25 +192,14 @@ public abstract class Event { * non-null, this will use the RCTModernEventEmitter API. Otherwise, it falls back to the * old-style dispatch function. For Event classes that need to do something different, this method * can always be overridden entirely, but it is not recommended. - */ - public void dispatchModern(RCTModernEventEmitter rctEventEmitter) { - if (getSurfaceId() != -1) { - WritableMap eventData = getEventData(); - if (eventData != null) { - rctEventEmitter.receiveEvent(getSurfaceId(), getViewTag(), getEventName(), eventData); - return; - } - } - dispatch(rctEventEmitter); - } - - /** - * Dispatch this event to JS using a V2 version of dispatchModern. See all comments from - * `dispatchModern` - all still apply. This method additionally allows C++ to coalesce events - * (Fabric only). This will ONLY be called in an experimental path, and in Fabric only. + * + *

This method additionally allows C++ to coalesce events and detect continuous ones for + * concurrent mode (Fabric only). + * + * @see #dispatch */ @Deprecated - public void dispatchModernV2(RCTModernEventEmitter rctEventEmitter) { + public void dispatchModern(RCTModernEventEmitter rctEventEmitter) { if (getSurfaceId() != -1) { WritableMap eventData = getEventData(); if (eventData != null) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java index c972fe03ad1..29039f8790d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java @@ -13,7 +13,6 @@ import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.MapBuilder; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.modules.core.ChoreographerCompat; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.uimanager.common.UIManagerType; @@ -368,11 +367,7 @@ public class EventDispatcherImpl implements EventDispatcher, LifecycleEventListe Systrace.endAsyncFlow( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); - if (ReactFeatureFlags.useDispatchUniqueForCoalescableEvents) { - event.dispatchModernV2(mReactEventEmitter); - } else { - event.dispatchModern(mReactEventEmitter); - } + event.dispatchModern(mReactEventEmitter); event.dispose(); } clearEventsToDispatch(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java index a16f43494a2..fff8349b3e7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java @@ -84,7 +84,7 @@ public class LockFreeEventDispatcherImpl implements EventDispatcher, LifecycleEv listener.onEventDispatch(event); } - event.dispatchModernV2(mReactEventEmitter); + event.dispatchModern(mReactEventEmitter); event.dispose(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java index 4466e07b11c..66cccdcaaad 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java @@ -30,4 +30,6 @@ public interface RCTModernEventEmitter extends RCTEventEmitter { int customCoalesceKey, @Nullable WritableMap event, @EventCategoryDef int category); + + void receiveTouches(TouchEvent event); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java index 1ab5400106e..200f98e2b82 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java @@ -70,14 +70,27 @@ public class ReactEventEmitter implements RCTModernEventEmitter { @Override public void receiveTouches( String eventName, WritableArray touches, WritableArray changedIndices) { + /* + * This method should be unused by default processing pipeline, but leaving it here to make sure + * that any custom code using it in legacy renderer is compatible + */ Assertions.assertCondition(touches.size() > 0); int reactTag = touches.getMap(0).getInt(TARGET_KEY); @UIManagerType int uiManagerType = ViewUtil.getUIManagerType(reactTag); - if (uiManagerType == UIManagerType.FABRIC && mFabricEventEmitter != null) { - mFabricEventEmitter.receiveTouches(eventName, touches, changedIndices); - } else if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(reactTag) != null) { + if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(reactTag) != null) { mRCTEventEmitter.receiveTouches(eventName, touches, changedIndices); + } + } + + @Override + public void receiveTouches(TouchEvent event) { + int reactTag = event.getViewTag(); + @UIManagerType int uiManagerType = ViewUtil.getUIManagerType(reactTag); + if (uiManagerType == UIManagerType.FABRIC && mFabricEventEmitter != null) { + mFabricEventEmitter.receiveTouches(event); + } else if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(reactTag) != null) { + TouchesHelper.sendTouchesLegacy(mRCTEventEmitter, event); } else { ReactSoftExceptionLogger.logSoftException( TAG, @@ -87,7 +100,7 @@ public class ReactEventEmitter implements RCTModernEventEmitter { + "] UIManagerType[" + uiManagerType + "] EventName[" - + eventName + + event.getEventName() + "]")); } } 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 f9d6661684d..b00ac95dac1 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 @@ -13,7 +13,6 @@ import androidx.core.util.Pools; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.SoftAssertions; -import com.facebook.react.config.ReactFeatureFlags; /** * An event representing the start, end or movement of a touch. Corresponds to a single {@link @@ -183,29 +182,14 @@ public class TouchEvent extends Event { @Override public void dispatch(RCTEventEmitter rctEventEmitter) { if (verifyMotionEvent()) { - TouchesHelper.sendTouchEvent(rctEventEmitter, this); + TouchesHelper.sendTouchesLegacy(rctEventEmitter, this); } } @Override public void dispatchModern(RCTModernEventEmitter rctEventEmitter) { - if (ReactFeatureFlags.useUpdatedTouchPreprocessing) { - if (verifyMotionEvent()) { - TouchesHelper.sendTouchEventModern(rctEventEmitter, this, /* useDispatchV2 */ false); - } - } else { - dispatch(rctEventEmitter); - } - } - - @Override - public void dispatchModernV2(RCTModernEventEmitter rctEventEmitter) { - if (ReactFeatureFlags.useUpdatedTouchPreprocessing) { - if (verifyMotionEvent()) { - TouchesHelper.sendTouchEventModern(rctEventEmitter, this, /* useDispatchV2 */ true); - } - } else { - dispatch(rctEventEmitter); + if (verifyMotionEvent()) { + rctEventEmitter.receiveTouches(this); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java index 42610200d5a..c7761261737 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java @@ -13,6 +13,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.PixelUtil; +import com.facebook.systrace.Systrace; /** Class responsible for generating catalyst touch events based on android {@link MotionEvent}. */ public class TouchesHelper { @@ -74,15 +75,16 @@ public class TouchesHelper { /** * Generate and send touch event to RCTEventEmitter JS module associated with the given {@param - * context}. Touch event can encode multiple concurrent touches (pointers). + * context} for legacy renderer. Touch event can encode multiple concurrent touches (pointers). * * @param rctEventEmitter Event emitter used to execute JS module call * @param touchEvent native touch event to read pointers count and coordinates from */ - public static void sendTouchEvent(RCTEventEmitter rctEventEmitter, TouchEvent touchEvent) { + public static void sendTouchesLegacy(RCTEventEmitter rctEventEmitter, TouchEvent touchEvent) { TouchEventType type = touchEvent.getTouchEventType(); - WritableArray pointers = getWritableArray(createPointersArray(touchEvent)); + WritableArray pointers = + getWritableArray(/* copyObjects */ false, createPointersArray(touchEvent)); MotionEvent motionEvent = touchEvent.getMotionEvent(); // For START and END events send only index of the pointer that is associated with that event @@ -111,10 +113,11 @@ public class TouchesHelper { * * @param eventEmitter emitter to dispatch event to * @param event the touch event to extract data from - * @param useDispatchV2 whether to dispatch additional data used by {@link Event#dispatchModernV2} */ - public static void sendTouchEventModern( - RCTModernEventEmitter eventEmitter, TouchEvent event, boolean useDispatchV2) { + public static void sendTouchEvent(RCTModernEventEmitter eventEmitter, TouchEvent event) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "TouchesHelper.sentTouchEventModern(" + event.getEventName() + ")"); TouchEventType type = event.getTouchEventType(); MotionEvent motionEvent = event.getMotionEvent(); @@ -166,24 +169,17 @@ public class TouchesHelper { eventData.putArray(CHANGED_TOUCHES_KEY, changedTouchesArray); eventData.putArray(TOUCHES_KEY, touchesArray); - if (useDispatchV2) { - eventEmitter.receiveEvent( - event.getSurfaceId(), - event.getViewTag(), - event.getEventName(), - event.canCoalesce(), - 0, - eventData, - event.getEventCategory()); - } else { - eventEmitter.receiveEvent( - event.getSurfaceId(), event.getViewTag(), event.getEventName(), eventData); - } + eventEmitter.receiveEvent( + event.getSurfaceId(), + event.getViewTag(), + event.getEventName(), + event.canCoalesce(), + 0, + eventData, + event.getEventCategory()); } - } - private static WritableArray getWritableArray(WritableMap... objects) { - return getWritableArray(false, objects); + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } private static WritableArray getWritableArray(boolean copyObjects, WritableMap... objects) {