diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 76e084d9a75..c8f24f4e0c2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -54,6 +54,7 @@ import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; +import com.facebook.react.fabric.events.EventCategoryDef; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.events.FabricEventEmitter; import com.facebook.react.fabric.mounting.MountItemDispatcher; @@ -829,6 +830,23 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { receiveEvent(surfaceId, reactTag, eventName, false, 0, params); } + public void receiveEvent( + int surfaceId, + int reactTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap params) { + receiveEvent( + surfaceId, + reactTag, + eventName, + canCoalesceEvent, + customCoalesceKey, + params, + EventCategoryDef.UNSPECIFIED); + } + /** * receiveEvent API that emits an event to C++. If `canCoalesceEvent` is true, that signals that * C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before @@ -842,6 +860,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { * @param canCoalesceEvent * @param customCoalesceKey * @param params + * @param eventCategory */ public void receiveEvent( int surfaceId, @@ -849,7 +868,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { String eventName, boolean canCoalesceEvent, int customCoalesceKey, - @Nullable WritableMap params) { + @Nullable WritableMap params, + @EventCategoryDef int eventCategory) { if (ReactBuildConfig.DEBUG && surfaceId == View.NO_ID) { FLog.d(TAG, "Emitted event without surfaceId: [%d] %s", reactTag, eventName); } @@ -870,7 +890,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { if (canCoalesceEvent) { eventEmitter.invokeUnique(eventName, params, customCoalesceKey); } else { - eventEmitter.invoke(eventName, params); + eventEmitter.invoke(eventName, params, eventCategory); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java index 234744b2cd9..dc875cea1dc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java @@ -48,12 +48,15 @@ public class EventEmitterWrapper { * @param eventName {@link String} name of the event to execute. * @param params {@link WritableMap} payload of the event */ - public synchronized void invoke(@NonNull String eventName, @Nullable WritableMap params) { + public synchronized void invoke( + @NonNull String eventName, + @Nullable WritableMap params, + @EventCategoryDef int eventCategory) { if (!isValid()) { return; } NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params; - invokeEvent(eventName, payload, EventCategoryDef.UNSPECIFIED); + invokeEvent(eventName, payload, eventCategory); } /** 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 82d209ab494..71cafaabd3c 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 @@ -10,8 +10,6 @@ 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.TOP_TOUCH_CANCEL_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY; import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY; import android.util.Pair; @@ -25,6 +23,7 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.fabric.FabricUIManager; import com.facebook.react.uimanager.events.RCTModernEventEmitter; +import com.facebook.react.uimanager.events.TouchEventType; import com.facebook.systrace.Systrace; import java.util.HashSet; import java.util.Set; @@ -71,15 +70,23 @@ public class FabricEventEmitter implements RCTModernEventEmitter { @NonNull String eventTopLevelType, @NonNull WritableArray touches, @NonNull WritableArray changedIndices) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "FabricEventEmitter.receiveTouches('" + eventTopLevelType + "')"); + + boolean isPointerEndEvent = + TouchEventType.END.getJsName().equalsIgnoreCase(eventTopLevelType) + || TouchEventType.CANCEL.getJsName().equalsIgnoreCase(eventTopLevelType); + Pair result = - TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType) - || TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType) + isPointerEndEvent ? removeTouchesAtIndices(touches, changedIndices) : touchSubsequence(touches, changedIndices); WritableArray changedTouches = result.first; touches = result.second; + int eventCategory = getTouchCategory(eventTopLevelType); 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 @@ -97,8 +104,11 @@ public class FabricEventEmitter implements RCTModernEventEmitter { rootNodeID = targetReactTag; } - receiveEvent(targetSurfaceId, rootNodeID, eventTopLevelType, false, 0, touch); + mUIManager.receiveEvent( + targetSurfaceId, rootNodeID, eventTopLevelType, false, 0, touch, eventCategory); } + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } /** TODO T31905686 optimize this to avoid copying arrays */ @@ -174,4 +184,19 @@ public class FabricEventEmitter implements RCTModernEventEmitter { 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; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java index 7661d5c57fe..06e7aa93664 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java @@ -27,5 +27,12 @@ public interface RCTEventEmitter extends JavaScriptModule { @Deprecated void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event); + /** + * Receive and process touches + * + * @param eventName JS event name + * @param touches active pointers data + * @param changedIndices indices of changed pointers + */ void receiveTouches(String eventName, WritableArray touches, WritableArray changedIndices); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java index f7dd5b1c792..9a60a8c7197 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java @@ -9,23 +9,22 @@ package com.facebook.react.uimanager.events; /** Touch event types that JS module RCTEventEmitter can understand */ public enum TouchEventType { - START, - END, - MOVE, - CANCEL; + START("topTouchStart"), + END("topTouchEnd"), + MOVE("topTouchMove"), + CANCEL("topTouchCancel"); + + private final String mJsName; + + TouchEventType(String jsName) { + mJsName = jsName; + } + + public String getJsName() { + return mJsName; + } public static String getJSEventName(TouchEventType type) { - switch (type) { - case START: - return "topTouchStart"; - case END: - return "topTouchEnd"; - case MOVE: - return "topTouchMove"; - case CANCEL: - return "topTouchCancel"; - default: - throw new IllegalArgumentException("Unexpected type " + type); - } + return type.getJsName(); } } 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 187e6121160..676f6729549 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 @@ -20,8 +20,6 @@ public class TouchesHelper { public static final String TARGET_KEY = "target"; public static final String CHANGED_TOUCHES_KEY = "changedTouches"; public static final String TOUCHES_KEY = "touches"; - public static final String TOP_TOUCH_END_KEY = "topTouchEnd"; - public static final String TOP_TOUCH_CANCEL_KEY = "topTouchCancel"; private static final String PAGE_X_KEY = "pageX"; private static final String PAGE_Y_KEY = "pageY"; private static final String TIMESTAMP_KEY = "timestamp";