mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Refactor EventEmitters to take optional surfaceId, migrate TouchEvent
Summary: Refactor EventEmitters to take an optional SurfaceId that Fabric will use, and non-Fabric will not. Migrating touches is a good proof-of-concept for how this could be used generally, and as it turns out, TouchEvent's API is more flexible than most other event APIs (because it uses a dictionary to pass data around, so we can just stuff SurfaceId into it - not efficient, but flexible). All new APIs are backwards-compatible and designed to work with old-style events, with Fabric and non-Fabric. Native Views that migrate to the new API will be backwards-compatible and get an efficiency boost in Fabric. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D26025135 fbshipit-source-id: 5b418951e9d0a3882f2d67398f2aaadac8a3a556
This commit is contained in:
committed by
Facebook GitHub Bot
parent
2fbbdbb2ce
commit
708038d80e
@@ -9,6 +9,7 @@ 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;
|
||||
@@ -76,14 +77,15 @@ public class FabricEventEmitter implements RCTModernEventEmitter {
|
||||
touch.putArray(TOUCHES_KEY, copyWritableArray(touches));
|
||||
WritableMap nativeEvent = touch;
|
||||
int rootNodeID = 0;
|
||||
int target = nativeEvent.getInt(TARGET_KEY);
|
||||
if (target < 1) {
|
||||
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 = target;
|
||||
rootNodeID = targetReactTag;
|
||||
}
|
||||
|
||||
receiveEvent(rootNodeID, eventTopLevelType, touch);
|
||||
receiveEvent(targetSurfaceId, rootNodeID, eventTopLevelType, touch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,17 @@ public class JSTouchDispatcher {
|
||||
mTargetTag = -1;
|
||||
}
|
||||
|
||||
private int getSurfaceId() {
|
||||
if (mRootViewGroup instanceof ReactRoot) {
|
||||
return ((ReactRoot) mRootViewGroup).getRootViewTag();
|
||||
}
|
||||
if (mRootViewGroup != null && mRootViewGroup.getContext() instanceof ThemedReactContext) {
|
||||
ThemedReactContext context = (ThemedReactContext) mRootViewGroup.getContext();
|
||||
return context.getSurfaceId();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main catalyst view is responsible for collecting and sending touch events to JS. This method
|
||||
* reacts for an incoming android native touch events ({@link MotionEvent}) and calls into {@link
|
||||
@@ -70,9 +81,11 @@ public class JSTouchDispatcher {
|
||||
// this gesture
|
||||
mChildIsHandlingNativeGesture = false;
|
||||
mGestureStartTime = ev.getEventTime();
|
||||
|
||||
mTargetTag = findTargetTagAndSetCoordinates(ev);
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.START,
|
||||
ev,
|
||||
@@ -97,6 +110,7 @@ public class JSTouchDispatcher {
|
||||
findTargetTagAndSetCoordinates(ev);
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.END,
|
||||
ev,
|
||||
@@ -111,6 +125,7 @@ public class JSTouchDispatcher {
|
||||
findTargetTagAndSetCoordinates(ev);
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.MOVE,
|
||||
ev,
|
||||
@@ -122,6 +137,7 @@ public class JSTouchDispatcher {
|
||||
// New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.START,
|
||||
ev,
|
||||
@@ -133,6 +149,7 @@ public class JSTouchDispatcher {
|
||||
// Exactly onw of the pointers goes up
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.END,
|
||||
ev,
|
||||
@@ -181,6 +198,7 @@ public class JSTouchDispatcher {
|
||||
Assertions.assertNotNull(eventDispatcher)
|
||||
.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
mTargetTag,
|
||||
TouchEventType.CANCEL,
|
||||
androidEvent,
|
||||
|
||||
+3
@@ -49,6 +49,9 @@ public class BlackHoleEventDispatcher implements EventDispatcher {
|
||||
@Override
|
||||
public void registerEventEmitter(int uiManagerType, RCTEventEmitter eventEmitter) {}
|
||||
|
||||
@Override
|
||||
public void registerEventEmitter(int uiManagerType, RCTModernEventEmitter eventEmitter) {}
|
||||
|
||||
@Override
|
||||
public void unregisterEventEmitter(int uiManagerType) {}
|
||||
|
||||
|
||||
@@ -26,8 +26,11 @@ public interface EventDispatcher {
|
||||
|
||||
void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener);
|
||||
|
||||
@Deprecated
|
||||
void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter);
|
||||
|
||||
void registerEventEmitter(@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter);
|
||||
|
||||
void unregisterEventEmitter(@UIManagerType int uiManagerType);
|
||||
|
||||
void onCatalystInstanceDestroyed();
|
||||
|
||||
+6
-1
@@ -263,6 +263,11 @@ public class EventDispatcherImpl implements EventDispatcher, LifecycleEventListe
|
||||
mReactEventEmitter.register(uiManagerType, eventEmitter);
|
||||
}
|
||||
|
||||
public void registerEventEmitter(
|
||||
@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) {
|
||||
mReactEventEmitter.register(uiManagerType, eventEmitter);
|
||||
}
|
||||
|
||||
public void unregisterEventEmitter(@UIManagerType int uiManagerType) {
|
||||
mReactEventEmitter.unregister(uiManagerType);
|
||||
}
|
||||
@@ -361,7 +366,7 @@ public class EventDispatcherImpl implements EventDispatcher, LifecycleEventListe
|
||||
}
|
||||
Systrace.endAsyncFlow(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID());
|
||||
event.dispatch(mReactEventEmitter);
|
||||
event.dispatchV2(mReactEventEmitter);
|
||||
event.dispose();
|
||||
}
|
||||
clearEventsToDispatch();
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
/** Deprecated in favor of RCTModernEventEmitter, which extends this interface. */
|
||||
@DoNotStrip
|
||||
@Deprecated
|
||||
public interface RCTEventEmitter extends JavaScriptModule {
|
||||
|
||||
+64
-22
@@ -9,9 +9,7 @@ package com.facebook.react.uimanager.events;
|
||||
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactNoCrashSoftException;
|
||||
@@ -21,11 +19,16 @@ import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
import com.facebook.react.uimanager.common.ViewUtil;
|
||||
|
||||
public class ReactEventEmitter implements RCTEventEmitter {
|
||||
public class ReactEventEmitter implements RCTModernEventEmitter {
|
||||
|
||||
private static final String TAG = "ReactEventEmitter";
|
||||
|
||||
private final SparseArray<RCTEventEmitter> mEventEmitters = new SparseArray<>();
|
||||
@Nullable
|
||||
private RCTModernEventEmitter mFabricEventEmitter =
|
||||
null; /* Corresponds to a Fabric EventEmitter */
|
||||
|
||||
@Nullable
|
||||
private RCTEventEmitter mRCTEventEmitter = null; /* Corresponds to a Non-Fabric EventEmitter */
|
||||
|
||||
private final ReactApplicationContext mReactContext;
|
||||
|
||||
@@ -33,46 +36,61 @@ public class ReactEventEmitter implements RCTEventEmitter {
|
||||
mReactContext = reactContext;
|
||||
}
|
||||
|
||||
public void register(@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) {
|
||||
assert uiManagerType == UIManagerType.FABRIC;
|
||||
mFabricEventEmitter = eventEmitter;
|
||||
}
|
||||
|
||||
public void register(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) {
|
||||
mEventEmitters.put(uiManagerType, eventEmitter);
|
||||
assert uiManagerType == UIManagerType.DEFAULT;
|
||||
mRCTEventEmitter = eventEmitter;
|
||||
}
|
||||
|
||||
public void unregister(@UIManagerType int uiManagerType) {
|
||||
mEventEmitters.remove(uiManagerType);
|
||||
if (uiManagerType == UIManagerType.DEFAULT) {
|
||||
mRCTEventEmitter = null;
|
||||
} else {
|
||||
mFabricEventEmitter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveEvent(int targetReactTag, String eventName, @Nullable WritableMap event) {
|
||||
RCTEventEmitter eventEmitter = getEventEmitter(targetReactTag);
|
||||
if (eventEmitter != null) {
|
||||
eventEmitter.receiveEvent(targetReactTag, eventName, event);
|
||||
}
|
||||
receiveEvent(-1, targetReactTag, eventName, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTouches(
|
||||
String eventName, WritableArray touches, WritableArray changedIndices) {
|
||||
|
||||
Assertions.assertCondition(touches.size() > 0);
|
||||
|
||||
int reactTag = touches.getMap(0).getInt(TARGET_KEY);
|
||||
RCTEventEmitter eventEmitter = getEventEmitter(reactTag);
|
||||
if (eventEmitter != null) {
|
||||
eventEmitter.receiveTouches(eventName, touches, changedIndices);
|
||||
@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) {
|
||||
mRCTEventEmitter.receiveTouches(eventName, touches, changedIndices);
|
||||
} else {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new ReactNoCrashSoftException(
|
||||
"Cannot find EventEmitter for receivedTouches: ReactTag["
|
||||
+ reactTag
|
||||
+ "] UIManagerType["
|
||||
+ uiManagerType
|
||||
+ "] EventName["
|
||||
+ eventName
|
||||
+ "]"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RCTEventEmitter getEventEmitter(int reactTag) {
|
||||
int type = ViewUtil.getUIManagerType(reactTag);
|
||||
RCTEventEmitter eventEmitter = mEventEmitters.get(type);
|
||||
if (eventEmitter == null) {
|
||||
// TODO T54145494: Refactor RN Event Emitter system to make sure reactTags are always managed
|
||||
// by RN
|
||||
FLog.e(
|
||||
TAG, "Unable to find event emitter for reactTag: %d - uiManagerType: %d", reactTag, type);
|
||||
assert type == UIManagerType.DEFAULT;
|
||||
if (mRCTEventEmitter == null) {
|
||||
if (mReactContext.hasActiveCatalystInstance()) {
|
||||
eventEmitter = mReactContext.getJSModule(RCTEventEmitter.class);
|
||||
mRCTEventEmitter = mReactContext.getJSModule(RCTEventEmitter.class);
|
||||
} else {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
@@ -84,6 +102,30 @@ public class ReactEventEmitter implements RCTEventEmitter {
|
||||
+ " - No active Catalyst instance!"));
|
||||
}
|
||||
}
|
||||
return eventEmitter;
|
||||
return mRCTEventEmitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveEvent(
|
||||
int surfaceId, int targetReactTag, String eventName, @Nullable WritableMap event) {
|
||||
@UIManagerType int uiManagerType = ViewUtil.getUIManagerType(targetReactTag);
|
||||
if (uiManagerType == UIManagerType.FABRIC && mFabricEventEmitter != null) {
|
||||
mFabricEventEmitter.receiveEvent(surfaceId, targetReactTag, eventName, event);
|
||||
} else if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(targetReactTag) != null) {
|
||||
mRCTEventEmitter.receiveEvent(targetReactTag, eventName, event);
|
||||
} else {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new ReactNoCrashSoftException(
|
||||
"Cannot find EventEmitter for receiveEvent: SurfaceId["
|
||||
+ surfaceId
|
||||
+ "] ReactTag["
|
||||
+ targetReactTag
|
||||
+ "] UIManagerType["
|
||||
+ uiManagerType
|
||||
+ "] EventName["
|
||||
+ eventName
|
||||
+ "]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public class TouchEvent extends Event<TouchEvent> {
|
||||
public static final long UNSET = Long.MIN_VALUE;
|
||||
|
||||
public static TouchEvent obtain(
|
||||
int surfaceId,
|
||||
int viewTag,
|
||||
TouchEventType touchEventType,
|
||||
MotionEvent motionEventToCopy,
|
||||
@@ -43,6 +44,7 @@ public class TouchEvent extends Event<TouchEvent> {
|
||||
event = new TouchEvent();
|
||||
}
|
||||
event.init(
|
||||
surfaceId,
|
||||
viewTag,
|
||||
touchEventType,
|
||||
motionEventToCopy,
|
||||
@@ -64,6 +66,7 @@ public class TouchEvent extends Event<TouchEvent> {
|
||||
private TouchEvent() {}
|
||||
|
||||
private void init(
|
||||
int surfaceId,
|
||||
int viewTag,
|
||||
TouchEventType touchEventType,
|
||||
MotionEvent motionEventToCopy,
|
||||
@@ -71,7 +74,7 @@ public class TouchEvent extends Event<TouchEvent> {
|
||||
float viewX,
|
||||
float viewY,
|
||||
TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) {
|
||||
super.init(viewTag);
|
||||
super.init(surfaceId, viewTag);
|
||||
|
||||
SoftAssertions.assertCondition(
|
||||
gestureStartTime != UNSET, "Gesture start time must be initialized");
|
||||
@@ -141,7 +144,11 @@ public class TouchEvent extends Event<TouchEvent> {
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
TouchesHelper.sendTouchEvent(
|
||||
rctEventEmitter, Assertions.assertNotNull(mTouchEventType), getViewTag(), this);
|
||||
rctEventEmitter,
|
||||
Assertions.assertNotNull(mTouchEventType),
|
||||
getSurfaceId(),
|
||||
getViewTag(),
|
||||
this);
|
||||
}
|
||||
|
||||
public MotionEvent getMotionEvent() {
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.facebook.react.uimanager.PixelUtil;
|
||||
/** Class responsible for generating catalyst touch events based on android {@link MotionEvent}. */
|
||||
public class TouchesHelper {
|
||||
|
||||
public static final String TARGET_SURFACE_KEY = "targetSurface";
|
||||
public static final String TARGET_KEY = "target";
|
||||
public static final String CHANGED_TOUCHES_KEY = "changedTouches";
|
||||
public static final String TOUCHES_KEY = "touches";
|
||||
@@ -34,7 +35,8 @@ public class TouchesHelper {
|
||||
* given {@param event} instance. This method use {@param reactTarget} parameter to set as a
|
||||
* target view id associated with current gesture.
|
||||
*/
|
||||
private static WritableArray createsPointersArray(int reactTarget, TouchEvent event) {
|
||||
private static WritableArray createsPointersArray(
|
||||
int surfaceId, int reactTarget, TouchEvent event) {
|
||||
WritableArray touches = Arguments.createArray();
|
||||
MotionEvent motionEvent = event.getMotionEvent();
|
||||
|
||||
@@ -60,6 +62,7 @@ public class TouchesHelper {
|
||||
float locationY = motionEvent.getY(index) - targetViewCoordinateY;
|
||||
touch.putDouble(LOCATION_X_KEY, PixelUtil.toDIPFromPixel(locationX));
|
||||
touch.putDouble(LOCATION_Y_KEY, PixelUtil.toDIPFromPixel(locationY));
|
||||
touch.putInt(TARGET_SURFACE_KEY, surfaceId);
|
||||
touch.putInt(TARGET_KEY, reactTarget);
|
||||
touch.putDouble(TIMESTAMP_KEY, event.getTimestampMs());
|
||||
touch.putDouble(POINTER_IDENTIFIER_KEY, motionEvent.getPointerId(index));
|
||||
@@ -81,10 +84,10 @@ public class TouchesHelper {
|
||||
public static void sendTouchEvent(
|
||||
RCTEventEmitter rctEventEmitter,
|
||||
TouchEventType type,
|
||||
int surfaceId,
|
||||
int reactTarget,
|
||||
TouchEvent touchEvent) {
|
||||
|
||||
WritableArray pointers = createsPointersArray(reactTarget, touchEvent);
|
||||
WritableArray pointers = createsPointersArray(surfaceId, reactTarget, touchEvent);
|
||||
MotionEvent motionEvent = touchEvent.getMotionEvent();
|
||||
|
||||
// For START and END events send only index of the pointer that is associated with that event
|
||||
|
||||
Reference in New Issue
Block a user