mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Convert TouchesHelper.java (#48447)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48447 # Changelog: [Internal] - As in the title. Reviewed By: tdn120 Differential Revision: D67760245 fbshipit-source-id: 5054408438de7cbdfaa7c98d9f5935f03ee93760
This commit is contained in:
committed by
Facebook GitHub Bot
parent
9e0a7e3263
commit
febf6f4a9c
@@ -5737,6 +5737,7 @@ public final class com/facebook/react/uimanager/events/TouchEvent : com/facebook
|
||||
public fun dispatch (Lcom/facebook/react/uimanager/events/RCTEventEmitter;)V
|
||||
public fun dispatchModern (Lcom/facebook/react/uimanager/events/RCTModernEventEmitter;)V
|
||||
public fun getCoalescingKey ()S
|
||||
public fun getEventCategory ()I
|
||||
public fun getEventName ()Ljava/lang/String;
|
||||
public final fun getMotionEvent ()Landroid/view/MotionEvent;
|
||||
public final fun getTouchEventType ()Lcom/facebook/react/uimanager/events/TouchEventType;
|
||||
@@ -5778,9 +5779,11 @@ public final class com/facebook/react/uimanager/events/TouchEventType$Companion
|
||||
public final fun getJSEventName (Lcom/facebook/react/uimanager/events/TouchEventType;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public class com/facebook/react/uimanager/events/TouchesHelper {
|
||||
public final class com/facebook/react/uimanager/events/TouchesHelper {
|
||||
public static final field INSTANCE Lcom/facebook/react/uimanager/events/TouchesHelper;
|
||||
public static final field TARGET_KEY Ljava/lang/String;
|
||||
public fun <init> ()V
|
||||
public static final fun sendTouchEvent (Lcom/facebook/react/uimanager/events/RCTModernEventEmitter;Lcom/facebook/react/uimanager/events/TouchEvent;)V
|
||||
public static final fun sendTouchesLegacy (Lcom/facebook/react/uimanager/events/RCTEventEmitter;Lcom/facebook/react/uimanager/events/TouchEvent;)V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/uimanager/layoutanimation/InterpolatorType : java/lang/Enum {
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ public class TouchEvent private constructor() : Event<TouchEvent>() {
|
||||
}
|
||||
}
|
||||
|
||||
protected override fun getEventCategory(): Int {
|
||||
public override fun getEventCategory(): Int {
|
||||
val type = touchEventType ?: return EventCategoryDef.UNSPECIFIED
|
||||
return when (type) {
|
||||
TouchEventType.START -> EventCategoryDef.CONTINUOUS_START
|
||||
|
||||
-204
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uimanager.events;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
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}. */
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class TouchesHelper {
|
||||
@Deprecated public static final String TARGET_KEY = "target";
|
||||
|
||||
private static final String TARGET_SURFACE_KEY = "targetSurface";
|
||||
private static final String CHANGED_TOUCHES_KEY = "changedTouches";
|
||||
private static final String TOUCHES_KEY = "touches";
|
||||
private static final String PAGE_X_KEY = "pageX";
|
||||
private static final String PAGE_Y_KEY = "pageY";
|
||||
private static final String TIMESTAMP_KEY = "timestamp";
|
||||
private static final String POINTER_IDENTIFIER_KEY = "identifier";
|
||||
|
||||
private static final String LOCATION_X_KEY = "locationX";
|
||||
private static final String LOCATION_Y_KEY = "locationY";
|
||||
|
||||
private static final String TAG = "TouchesHelper";
|
||||
|
||||
/**
|
||||
* Creates catalyst pointers array in format that is expected by RCTEventEmitter JS module from
|
||||
* given {@param event} instance. This method use {@param reactTarget} parameter to set as a
|
||||
* target view id associated with current gesture.
|
||||
*/
|
||||
private static WritableMap[] createPointersArray(TouchEvent event) {
|
||||
MotionEvent motionEvent = event.getMotionEvent();
|
||||
WritableMap[] touches = new WritableMap[motionEvent.getPointerCount()];
|
||||
|
||||
// Calculate the coordinates for the target view.
|
||||
// The MotionEvent contains the X,Y of the touch in the coordinate space of the root view
|
||||
// The TouchEvent contains the X,Y of the touch in the coordinate space of the target view
|
||||
// Subtracting them allows us to get the coordinates of the target view's top left corner
|
||||
// We then use this when computing the view specific touches below
|
||||
// Since only one view is actually handling even multiple touches, the values are all relative
|
||||
// to this one target view.
|
||||
float targetViewCoordinateX = motionEvent.getX() - event.getViewX();
|
||||
float targetViewCoordinateY = motionEvent.getY() - event.getViewY();
|
||||
|
||||
for (int index = 0; index < motionEvent.getPointerCount(); index++) {
|
||||
WritableMap touch = Arguments.createMap();
|
||||
// pageX,Y values are relative to the RootReactView
|
||||
// the motionEvent already contains coordinates in that view
|
||||
touch.putDouble(PAGE_X_KEY, PixelUtil.toDIPFromPixel(motionEvent.getX(index)));
|
||||
touch.putDouble(PAGE_Y_KEY, PixelUtil.toDIPFromPixel(motionEvent.getY(index)));
|
||||
// locationX,Y values are relative to the target view
|
||||
// To compute the values for the view, we subtract that views location from the event X,Y
|
||||
float locationX = motionEvent.getX(index) - targetViewCoordinateX;
|
||||
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, event.getSurfaceId());
|
||||
touch.putInt(TARGET_KEY, event.getViewTag());
|
||||
touch.putDouble(TIMESTAMP_KEY, event.getTimestampMs());
|
||||
touch.putDouble(POINTER_IDENTIFIER_KEY, motionEvent.getPointerId(index));
|
||||
|
||||
touches[index] = touch;
|
||||
}
|
||||
|
||||
return touches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and send touch event to RCTEventEmitter JS module associated with the given {@param
|
||||
* 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
|
||||
*/
|
||||
/* package */ static void sendTouchesLegacy(
|
||||
RCTEventEmitter rctEventEmitter, TouchEvent touchEvent) {
|
||||
TouchEventType type = touchEvent.getTouchEventType();
|
||||
|
||||
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
|
||||
// For MOVE and CANCEL events 'changedIndices' array should contain all the pointers indices
|
||||
WritableArray changedIndices = Arguments.createArray();
|
||||
if (type == TouchEventType.MOVE || type == TouchEventType.CANCEL) {
|
||||
for (int i = 0; i < motionEvent.getPointerCount(); i++) {
|
||||
changedIndices.pushInt(i);
|
||||
}
|
||||
} else if (type == TouchEventType.START || type == TouchEventType.END) {
|
||||
changedIndices.pushInt(motionEvent.getActionIndex());
|
||||
} else {
|
||||
throw new RuntimeException("Unknown touch type: " + type);
|
||||
}
|
||||
|
||||
rctEventEmitter.receiveTouches(TouchEventType.getJSEventName(type), pointers, changedIndices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate touch event data to match JS expectations. Combines logic in {@link #sendTouchEvent}
|
||||
* and FabricEventEmitter to create the same data structure in a more efficient manner.
|
||||
*
|
||||
* <p>Touches have to be dispatched as separate events for each changed pointer to make JS process
|
||||
* them correctly. To avoid allocations, we preprocess touch events in Java world and then convert
|
||||
* them to native before dispatch.
|
||||
*
|
||||
* @param eventEmitter emitter to dispatch event to
|
||||
* @param event the touch event to extract data from
|
||||
*/
|
||||
/* package */ static void sendTouchEvent(RCTModernEventEmitter eventEmitter, TouchEvent event) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"TouchesHelper.sentTouchEventModern(" + event.getEventName() + ")");
|
||||
try {
|
||||
TouchEventType type = event.getTouchEventType();
|
||||
MotionEvent motionEvent = event.getMotionEvent();
|
||||
|
||||
if (motionEvent == null) {
|
||||
ReactSoftExceptionLogger.logSoftException(
|
||||
TAG,
|
||||
new IllegalStateException(
|
||||
"Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been"
|
||||
+ " recycled"));
|
||||
return;
|
||||
}
|
||||
|
||||
WritableMap[] touches = createPointersArray(event);
|
||||
WritableMap[] changedTouches = null;
|
||||
|
||||
switch (type) {
|
||||
case START:
|
||||
int newPointerIndex = motionEvent.getActionIndex();
|
||||
|
||||
changedTouches = new WritableMap[] {touches[newPointerIndex].copy()};
|
||||
break;
|
||||
case END:
|
||||
int finishedPointerIndex = motionEvent.getActionIndex();
|
||||
/*
|
||||
* Clear finished pointer index for compatibility with W3C touch "end" events, where the
|
||||
* active touches don't include the set that has just been "ended".
|
||||
*/
|
||||
WritableMap finishedPointer = touches[finishedPointerIndex];
|
||||
touches[finishedPointerIndex] = null;
|
||||
|
||||
changedTouches = new WritableMap[] {finishedPointer};
|
||||
break;
|
||||
case MOVE:
|
||||
changedTouches = new WritableMap[touches.length];
|
||||
for (int i = 0; i < touches.length; i++) {
|
||||
changedTouches[i] = touches[i].copy();
|
||||
}
|
||||
break;
|
||||
case CANCEL:
|
||||
changedTouches = touches;
|
||||
touches = new WritableMap[0];
|
||||
break;
|
||||
}
|
||||
|
||||
if (changedTouches != null) {
|
||||
for (WritableMap touchData : changedTouches) {
|
||||
WritableMap eventData = touchData.copy();
|
||||
WritableArray changedTouchesArray =
|
||||
getWritableArray(/* copyObjects */ true, changedTouches);
|
||||
WritableArray touchesArray = getWritableArray(/* copyObjects */ true, touches);
|
||||
|
||||
eventData.putArray(CHANGED_TOUCHES_KEY, changedTouchesArray);
|
||||
eventData.putArray(TOUCHES_KEY, touchesArray);
|
||||
|
||||
eventEmitter.receiveEvent(
|
||||
event.getSurfaceId(),
|
||||
event.getViewTag(),
|
||||
event.getEventName(),
|
||||
event.canCoalesce(),
|
||||
0,
|
||||
eventData,
|
||||
event.getEventCategory());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static WritableArray getWritableArray(boolean copyObjects, WritableMap... objects) {
|
||||
WritableArray result = Arguments.createArray();
|
||||
for (WritableMap object : objects) {
|
||||
if (object != null) {
|
||||
result.pushMap(copyObjects ? object.copy() : object);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uimanager.events
|
||||
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.WritableArray
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import com.facebook.react.uimanager.PixelUtil.pxToDp
|
||||
import com.facebook.react.uimanager.events.TouchEventType.Companion.getJSEventName
|
||||
import com.facebook.systrace.Systrace
|
||||
|
||||
/** Class responsible for generating catalyst touch events based on android [MotionEvent]. */
|
||||
public object TouchesHelper {
|
||||
@JvmField @Deprecated("Not used in New Architecture") public val TARGET_KEY: String = "target"
|
||||
|
||||
private const val TARGET_SURFACE_KEY = "targetSurface"
|
||||
private const val CHANGED_TOUCHES_KEY = "changedTouches"
|
||||
private const val TOUCHES_KEY = "touches"
|
||||
private const val PAGE_X_KEY = "pageX"
|
||||
private const val PAGE_Y_KEY = "pageY"
|
||||
private const val TIMESTAMP_KEY = "timestamp"
|
||||
private const val POINTER_IDENTIFIER_KEY = "identifier"
|
||||
|
||||
private const val LOCATION_X_KEY = "locationX"
|
||||
private const val LOCATION_Y_KEY = "locationY"
|
||||
|
||||
/**
|
||||
* Creates catalyst pointers array in format that is expected by RCTEventEmitter JS module from
|
||||
* given {@param event} instance. This method use {@param reactTarget} parameter to set as a
|
||||
* target view id associated with current gesture.
|
||||
*/
|
||||
private fun createPointersArray(event: TouchEvent): Array<WritableMap?> {
|
||||
val motionEvent = event.getMotionEvent()
|
||||
val touches = arrayOfNulls<WritableMap>(motionEvent.pointerCount)
|
||||
|
||||
// Calculate the coordinates for the target view.
|
||||
// The MotionEvent contains the X,Y of the touch in the coordinate space of the root view
|
||||
// The TouchEvent contains the X,Y of the touch in the coordinate space of the target view
|
||||
// Subtracting them allows us to get the coordinates of the target view's top left corner
|
||||
// We then use this when computing the view specific touches below
|
||||
// Since only one view is actually handling even multiple touches, the values are all relative
|
||||
// to this one target view.
|
||||
val targetViewCoordinateX = motionEvent.x - event.viewX
|
||||
val targetViewCoordinateY = motionEvent.y - event.viewY
|
||||
|
||||
for (index in 0 until motionEvent.pointerCount) {
|
||||
val touch = Arguments.createMap()
|
||||
|
||||
// pageX,Y values are relative to the RootReactView
|
||||
// the motionEvent already contains coordinates in that view
|
||||
touch.putDouble(PAGE_X_KEY, motionEvent.getX(index).pxToDp().toDouble())
|
||||
touch.putDouble(PAGE_Y_KEY, motionEvent.getY(index).pxToDp().toDouble())
|
||||
|
||||
// locationX,Y values are relative to the target view
|
||||
// To compute the values for the view, we subtract that views location from the event X,Y
|
||||
val locationX = motionEvent.getX(index) - targetViewCoordinateX
|
||||
val locationY = motionEvent.getY(index) - targetViewCoordinateY
|
||||
touch.putDouble(LOCATION_X_KEY, locationX.pxToDp().toDouble())
|
||||
touch.putDouble(LOCATION_Y_KEY, locationY.pxToDp().toDouble())
|
||||
|
||||
touch.putInt(TARGET_SURFACE_KEY, event.surfaceId)
|
||||
@Suppress("DEPRECATION") touch.putInt(TARGET_KEY, event.viewTag)
|
||||
touch.putDouble(TIMESTAMP_KEY, event.timestampMs.toDouble())
|
||||
touch.putDouble(POINTER_IDENTIFIER_KEY, motionEvent.getPointerId(index).toDouble())
|
||||
|
||||
touches[index] = touch
|
||||
}
|
||||
|
||||
return touches
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and send touch event to RCTEventEmitter JS module associated with the given {@param *
|
||||
* 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
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@JvmStatic
|
||||
public fun sendTouchesLegacy(rctEventEmitter: RCTEventEmitter, touchEvent: TouchEvent) {
|
||||
val type = touchEvent.getTouchEventType()
|
||||
|
||||
val pointers = getWritableArray(/* copyObjects */ false, createPointersArray(touchEvent))
|
||||
val motionEvent = touchEvent.getMotionEvent()
|
||||
|
||||
// For START and END events send only index of the pointer that is associated with that event
|
||||
// For MOVE and CANCEL events 'changedIndices' array should contain all the pointers indices
|
||||
val changedIndices = Arguments.createArray()
|
||||
if (type == TouchEventType.MOVE || type == TouchEventType.CANCEL) {
|
||||
for (i in 0 until motionEvent.pointerCount) {
|
||||
changedIndices.pushInt(i)
|
||||
}
|
||||
} else if (type == TouchEventType.START || type == TouchEventType.END) {
|
||||
changedIndices.pushInt(motionEvent.actionIndex)
|
||||
} else {
|
||||
throw RuntimeException("Unknown touch type: $type")
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
rctEventEmitter.receiveTouches(getJSEventName(type), pointers, changedIndices)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate touch event data to match JS expectations. Combines logic in [sendTouchEvent] and
|
||||
* [FabricEventEmitter] to create the same data structure in a more efficient manner.
|
||||
*
|
||||
* Touches have to be dispatched as separate events for each changed pointer to make JS process
|
||||
* them correctly. To avoid allocations, we preprocess touch events in Java world and then convert
|
||||
* them to native before dispatch.
|
||||
*
|
||||
* @param eventEmitter emitter to dispatch event to
|
||||
* @param event the touch event to extract data from
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun sendTouchEvent(eventEmitter: RCTModernEventEmitter, event: TouchEvent) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"TouchesHelper.sentTouchEventModern(" + event.eventName + ")")
|
||||
try {
|
||||
val type = event.getTouchEventType()
|
||||
val motionEvent = event.getMotionEvent()
|
||||
var touches = createPointersArray(event)
|
||||
var changedTouches: Array<WritableMap?>? = null
|
||||
|
||||
when (type) {
|
||||
TouchEventType.START -> {
|
||||
val newPointerIndex = motionEvent.actionIndex
|
||||
|
||||
changedTouches = arrayOf(touches[newPointerIndex]?.copy())
|
||||
}
|
||||
TouchEventType.END -> {
|
||||
val finishedPointerIndex = motionEvent.actionIndex
|
||||
/*
|
||||
* Clear finished pointer index for compatibility with W3C touch "end" events, where the
|
||||
* active touches don't include the set that has just been "ended".
|
||||
*/
|
||||
val finishedPointer = touches[finishedPointerIndex]
|
||||
touches[finishedPointerIndex] = null
|
||||
|
||||
changedTouches = arrayOf(finishedPointer)
|
||||
}
|
||||
TouchEventType.MOVE -> {
|
||||
changedTouches = arrayOfNulls(touches.size)
|
||||
var i = 0
|
||||
while (i < touches.size) {
|
||||
changedTouches[i] = touches[i]?.copy()
|
||||
i++
|
||||
}
|
||||
}
|
||||
TouchEventType.CANCEL -> {
|
||||
changedTouches = touches
|
||||
touches = arrayOfNulls(0)
|
||||
}
|
||||
}
|
||||
|
||||
for (touchData in changedTouches) {
|
||||
val eventData =
|
||||
touchData?.let { td ->
|
||||
val ed = td.copy()
|
||||
val changedTouchesArray = getWritableArray(/* copyObjects */ true, changedTouches)
|
||||
val touchesArray = getWritableArray(/* copyObjects */ true, touches)
|
||||
ed.putArray(CHANGED_TOUCHES_KEY, changedTouchesArray)
|
||||
ed.putArray(TOUCHES_KEY, touchesArray)
|
||||
ed
|
||||
}
|
||||
|
||||
eventEmitter.receiveEvent(
|
||||
event.surfaceId,
|
||||
event.viewTag,
|
||||
event.eventName,
|
||||
event.canCoalesce(),
|
||||
0,
|
||||
eventData,
|
||||
event.eventCategory)
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWritableArray(copyObjects: Boolean, objects: Array<WritableMap?>): WritableArray {
|
||||
val result = Arguments.createArray()
|
||||
for (obj in objects) {
|
||||
if (obj != null) {
|
||||
result.pushMap(if (copyObjects) obj.copy() else obj)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user