mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Migrate TouchEvent to Kotlin (#43982)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/43982 ## Changelog: [Internal] - As in the title. Reviewed By: javache Differential Revision: D55877979 fbshipit-source-id: 63ca9b403509e08632ad1a3c0aa866f6df3f423b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c24a9a9309
commit
b23960ad99
@@ -5646,23 +5646,28 @@ public abstract interface class com/facebook/react/uimanager/events/RCTModernEve
|
||||
public abstract fun receiveTouches (Lcom/facebook/react/uimanager/events/TouchEvent;)V
|
||||
}
|
||||
|
||||
public class com/facebook/react/uimanager/events/TouchEvent : com/facebook/react/uimanager/events/Event {
|
||||
public final class com/facebook/react/uimanager/events/TouchEvent : com/facebook/react/uimanager/events/Event {
|
||||
public static final field Companion Lcom/facebook/react/uimanager/events/TouchEvent$Companion;
|
||||
public static final field UNSET J
|
||||
public fun canCoalesce ()Z
|
||||
public fun dispatch (Lcom/facebook/react/uimanager/events/RCTEventEmitter;)V
|
||||
public fun dispatchModern (Lcom/facebook/react/uimanager/events/RCTModernEventEmitter;)V
|
||||
public fun getCoalescingKey ()S
|
||||
protected fun getEventCategory ()I
|
||||
public fun getEventName ()Ljava/lang/String;
|
||||
public fun getMotionEvent ()Landroid/view/MotionEvent;
|
||||
public fun getTouchEventType ()Lcom/facebook/react/uimanager/events/TouchEventType;
|
||||
public fun getViewX ()F
|
||||
public fun getViewY ()F
|
||||
public static fun obtain (IILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
public static fun obtain (ILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
public final fun getMotionEvent ()Landroid/view/MotionEvent;
|
||||
public final fun getTouchEventType ()Lcom/facebook/react/uimanager/events/TouchEventType;
|
||||
public final fun getViewX ()F
|
||||
public final fun getViewY ()F
|
||||
public static final fun obtain (IILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
public static final fun obtain (ILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
public fun onDispose ()V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/uimanager/events/TouchEvent$Companion {
|
||||
public final fun obtain (IILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
public final fun obtain (ILcom/facebook/react/uimanager/events/TouchEventType;Landroid/view/MotionEvent;JFFLcom/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper;)Lcom/facebook/react/uimanager/events/TouchEvent;
|
||||
}
|
||||
|
||||
public class com/facebook/react/uimanager/events/TouchEventCoalescingKeyHelper {
|
||||
public fun <init> ()V
|
||||
public fun addCoalescingKey (J)V
|
||||
|
||||
-250
@@ -1,250 +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 androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pools;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.ReactSoftExceptionLogger;
|
||||
import com.facebook.react.bridge.SoftAssertions;
|
||||
|
||||
/**
|
||||
* An event representing the start, end or movement of a touch. Corresponds to a single {@link
|
||||
* android.view.MotionEvent}.
|
||||
*
|
||||
* <p>TouchEvent coalescing can happen for move events if two move events have the same target view
|
||||
* and coalescing key. See {@link TouchEventCoalescingKeyHelper} for more information about how
|
||||
* these coalescing keys are determined.
|
||||
*/
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class TouchEvent extends Event<TouchEvent> {
|
||||
private static final String TAG = TouchEvent.class.getSimpleName();
|
||||
|
||||
private static final int TOUCH_EVENTS_POOL_SIZE = 3;
|
||||
|
||||
private static final Pools.SynchronizedPool<TouchEvent> EVENTS_POOL =
|
||||
new Pools.SynchronizedPool<>(TOUCH_EVENTS_POOL_SIZE);
|
||||
|
||||
public static final long UNSET = Long.MIN_VALUE;
|
||||
|
||||
@Deprecated
|
||||
public static TouchEvent obtain(
|
||||
int viewTag,
|
||||
TouchEventType touchEventType,
|
||||
MotionEvent motionEventToCopy,
|
||||
long gestureStartTime,
|
||||
float viewX,
|
||||
float viewY,
|
||||
TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) {
|
||||
return obtain(
|
||||
-1,
|
||||
viewTag,
|
||||
touchEventType,
|
||||
Assertions.assertNotNull(motionEventToCopy),
|
||||
gestureStartTime,
|
||||
viewX,
|
||||
viewY,
|
||||
touchEventCoalescingKeyHelper);
|
||||
}
|
||||
|
||||
public static TouchEvent obtain(
|
||||
int surfaceId,
|
||||
int viewTag,
|
||||
TouchEventType touchEventType,
|
||||
MotionEvent motionEventToCopy,
|
||||
long gestureStartTime,
|
||||
float viewX,
|
||||
float viewY,
|
||||
TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) {
|
||||
TouchEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new TouchEvent();
|
||||
}
|
||||
event.init(
|
||||
surfaceId,
|
||||
viewTag,
|
||||
touchEventType,
|
||||
Assertions.assertNotNull(motionEventToCopy),
|
||||
gestureStartTime,
|
||||
viewX,
|
||||
viewY,
|
||||
touchEventCoalescingKeyHelper);
|
||||
return event;
|
||||
}
|
||||
|
||||
private @Nullable MotionEvent mMotionEvent;
|
||||
private @Nullable TouchEventType mTouchEventType;
|
||||
private short mCoalescingKey;
|
||||
|
||||
// Coordinates in the ViewTag coordinate space
|
||||
private float mViewX;
|
||||
private float mViewY;
|
||||
|
||||
private TouchEvent() {}
|
||||
|
||||
private void init(
|
||||
int surfaceId,
|
||||
int viewTag,
|
||||
TouchEventType touchEventType,
|
||||
MotionEvent motionEventToCopy,
|
||||
long gestureStartTime,
|
||||
float viewX,
|
||||
float viewY,
|
||||
TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) {
|
||||
super.init(surfaceId, viewTag, motionEventToCopy.getEventTime());
|
||||
|
||||
SoftAssertions.assertCondition(
|
||||
gestureStartTime != UNSET, "Gesture start time must be initialized");
|
||||
short coalescingKey = 0;
|
||||
int action = (motionEventToCopy.getAction() & MotionEvent.ACTION_MASK);
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
touchEventCoalescingKeyHelper.addCoalescingKey(gestureStartTime);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime);
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
touchEventCoalescingKeyHelper.incrementCoalescingKey(gestureStartTime);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
coalescingKey = touchEventCoalescingKeyHelper.getCoalescingKey(gestureStartTime);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unhandled MotionEvent action: " + action);
|
||||
}
|
||||
mTouchEventType = touchEventType;
|
||||
mMotionEvent = MotionEvent.obtain(motionEventToCopy);
|
||||
mCoalescingKey = coalescingKey;
|
||||
mViewX = viewX;
|
||||
mViewY = viewY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDispose() {
|
||||
MotionEvent motionEvent = mMotionEvent;
|
||||
mMotionEvent = null;
|
||||
if (motionEvent != null) {
|
||||
motionEvent.recycle();
|
||||
}
|
||||
|
||||
// Either `this` is in the event pool, or motionEvent
|
||||
// is null. It is in theory not possible for a TouchEvent to
|
||||
// be in the EVENTS_POOL but for motionEvent to be null. However,
|
||||
// out of an abundance of caution and to avoid memory leaks or
|
||||
// other crashes at all costs, we attempt to release here and log
|
||||
// a soft exception here if release throws an IllegalStateException
|
||||
// due to `this` being over-released. This may indicate that there is
|
||||
// a logic error in our events system or pooling mechanism.
|
||||
try {
|
||||
EVENTS_POOL.release(this);
|
||||
} catch (IllegalStateException e) {
|
||||
ReactSoftExceptionLogger.logSoftException(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return TouchEventType.getJSEventName(Assertions.assertNotNull(mTouchEventType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCoalesce() {
|
||||
// We can coalesce move events but not start/end events. Coalescing move events should probably
|
||||
// append historical move data like MotionEvent batching does. This is left as an exercise for
|
||||
// the reader.
|
||||
switch (Assertions.assertNotNull(mTouchEventType)) {
|
||||
case START:
|
||||
case END:
|
||||
case CANCEL:
|
||||
return false;
|
||||
case MOVE:
|
||||
return true;
|
||||
default:
|
||||
throw new RuntimeException("Unknown touch event type: " + mTouchEventType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
return mCoalescingKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
if (verifyMotionEvent()) {
|
||||
TouchesHelper.sendTouchesLegacy(rctEventEmitter, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchModern(RCTModernEventEmitter rctEventEmitter) {
|
||||
if (verifyMotionEvent()) {
|
||||
// TouchesHelper.sendTouchEvent can be inlined here post Fabric rollout
|
||||
// For now, we go via the event emitter, which will decide whether the legacy or modern
|
||||
// event path is required
|
||||
rctEventEmitter.receiveTouches(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getEventCategory() {
|
||||
TouchEventType type = mTouchEventType;
|
||||
if (type == null) {
|
||||
return EventCategoryDef.UNSPECIFIED;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case START:
|
||||
return EventCategoryDef.CONTINUOUS_START;
|
||||
case END:
|
||||
case CANCEL:
|
||||
return EventCategoryDef.CONTINUOUS_END;
|
||||
case MOVE:
|
||||
return EventCategoryDef.CONTINUOUS;
|
||||
}
|
||||
|
||||
// Something something smart compiler...
|
||||
return super.getEventCategory();
|
||||
}
|
||||
|
||||
public MotionEvent getMotionEvent() {
|
||||
Assertions.assertNotNull(mMotionEvent);
|
||||
return mMotionEvent;
|
||||
}
|
||||
|
||||
private boolean verifyMotionEvent() {
|
||||
if (mMotionEvent == null) {
|
||||
ReactSoftExceptionLogger.logSoftException(
|
||||
TAG,
|
||||
new IllegalStateException(
|
||||
"Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been"
|
||||
+ " recycled"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public TouchEventType getTouchEventType() {
|
||||
return Assertions.assertNotNull(mTouchEventType);
|
||||
}
|
||||
|
||||
public float getViewX() {
|
||||
return mViewX;
|
||||
}
|
||||
|
||||
public float getViewY() {
|
||||
return mViewY;
|
||||
}
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.facebook.react.uimanager.events
|
||||
|
||||
import android.view.MotionEvent
|
||||
import androidx.core.util.Pools.SynchronizedPool
|
||||
import com.facebook.infer.annotation.Assertions
|
||||
import com.facebook.react.bridge.ReactSoftExceptionLogger
|
||||
import com.facebook.react.bridge.SoftAssertions
|
||||
import com.facebook.react.uimanager.events.TouchEventType.Companion.getJSEventName
|
||||
|
||||
/**
|
||||
* An event representing the start, end or movement of a touch. Corresponds to a single [ ].
|
||||
*
|
||||
* TouchEvent coalescing can happen for move events if two move events have the same target view and
|
||||
* coalescing key. See [TouchEventCoalescingKeyHelper] for more information about how these
|
||||
* coalescing keys are determined.
|
||||
*/
|
||||
public class TouchEvent private constructor() : Event<TouchEvent>() {
|
||||
private var motionEvent: MotionEvent? = null
|
||||
private var touchEventType: TouchEventType? = null
|
||||
private var coalescingKey: Short = 0
|
||||
|
||||
// Coordinates in the ViewTag coordinate space
|
||||
public var viewX: Float = 0f
|
||||
private set
|
||||
|
||||
public var viewY: Float = 0f
|
||||
private set
|
||||
|
||||
public fun getMotionEvent(): MotionEvent = Assertions.assertNotNull(motionEvent)
|
||||
|
||||
public fun getTouchEventType(): TouchEventType = Assertions.assertNotNull(touchEventType)
|
||||
|
||||
private fun init(
|
||||
surfaceId: Int,
|
||||
viewTag: Int,
|
||||
touchEventType: TouchEventType?,
|
||||
motionEventToCopy: MotionEvent,
|
||||
gestureStartTime: Long,
|
||||
viewX: Float,
|
||||
viewY: Float,
|
||||
touchEventCoalescingKeyHelper: TouchEventCoalescingKeyHelper
|
||||
) {
|
||||
super.init(surfaceId, viewTag, motionEventToCopy.eventTime)
|
||||
SoftAssertions.assertCondition(
|
||||
gestureStartTime != UNSET, "Gesture start time must be initialized")
|
||||
var coalescingKey: Short = 0
|
||||
val action = motionEventToCopy.action and MotionEvent.ACTION_MASK
|
||||
when (action) {
|
||||
MotionEvent.ACTION_DOWN -> touchEventCoalescingKeyHelper.addCoalescingKey(gestureStartTime)
|
||||
MotionEvent.ACTION_UP -> touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime)
|
||||
MotionEvent.ACTION_POINTER_DOWN,
|
||||
MotionEvent.ACTION_POINTER_UP ->
|
||||
touchEventCoalescingKeyHelper.incrementCoalescingKey(gestureStartTime)
|
||||
MotionEvent.ACTION_MOVE ->
|
||||
coalescingKey = touchEventCoalescingKeyHelper.getCoalescingKey(gestureStartTime)
|
||||
MotionEvent.ACTION_CANCEL ->
|
||||
touchEventCoalescingKeyHelper.removeCoalescingKey(gestureStartTime)
|
||||
else -> throw RuntimeException("Unhandled MotionEvent action: $action")
|
||||
}
|
||||
|
||||
motionEvent = MotionEvent.obtain(motionEventToCopy)
|
||||
|
||||
this.touchEventType = touchEventType
|
||||
this.coalescingKey = coalescingKey
|
||||
this.viewX = viewX
|
||||
this.viewY = viewY
|
||||
}
|
||||
|
||||
override fun onDispose() {
|
||||
motionEvent?.recycle()
|
||||
motionEvent = null
|
||||
|
||||
// Either `this` is in the event pool, or motionEvent
|
||||
// is null. It is in theory not possible for a TouchEvent to
|
||||
// be in the EVENTS_POOL but for motionEvent to be null. However,
|
||||
// out of an abundance of caution and to avoid memory leaks or
|
||||
// other crashes at all costs, we attempt to release here and log
|
||||
// a soft exception here if release throws an IllegalStateException
|
||||
// due to `this` being over-released. This may indicate that there is
|
||||
// a logic error in our events system or pooling mechanism.
|
||||
try {
|
||||
EVENTS_POOL.release(this)
|
||||
} catch (e: IllegalStateException) {
|
||||
ReactSoftExceptionLogger.logSoftException(TAG, e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEventName(): String = getJSEventName(Assertions.assertNotNull(touchEventType))
|
||||
|
||||
// We can coalesce move events but not start/end events. Coalescing move events should probably
|
||||
// append historical move data like MotionEvent batching does. This is left as an exercise for
|
||||
// the reader.
|
||||
override fun canCoalesce(): Boolean =
|
||||
when (Assertions.assertNotNull(touchEventType)) {
|
||||
TouchEventType.START,
|
||||
TouchEventType.END,
|
||||
TouchEventType.CANCEL -> false
|
||||
TouchEventType.MOVE -> true
|
||||
else -> throw RuntimeException("Unknown touch event type: $touchEventType")
|
||||
}
|
||||
|
||||
override fun getCoalescingKey(): Short = coalescingKey
|
||||
|
||||
override fun dispatch(rctEventEmitter: RCTEventEmitter) {
|
||||
if (verifyMotionEvent()) {
|
||||
TouchesHelper.sendTouchesLegacy(rctEventEmitter, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchModern(rctEventEmitter: RCTModernEventEmitter) {
|
||||
if (verifyMotionEvent()) {
|
||||
// TouchesHelper.sendTouchEvent can be inlined here post Fabric rollout
|
||||
// For now, we go via the event emitter, which will decide whether the legacy or modern
|
||||
// event path is required
|
||||
rctEventEmitter.receiveTouches(this)
|
||||
}
|
||||
}
|
||||
|
||||
protected override fun getEventCategory(): Int {
|
||||
val type = touchEventType ?: return EventCategoryDef.UNSPECIFIED
|
||||
return when (type) {
|
||||
TouchEventType.START -> EventCategoryDef.CONTINUOUS_START
|
||||
TouchEventType.END,
|
||||
TouchEventType.CANCEL -> EventCategoryDef.CONTINUOUS_END
|
||||
TouchEventType.MOVE -> EventCategoryDef.CONTINUOUS
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyMotionEvent(): Boolean {
|
||||
if (motionEvent == null) {
|
||||
ReactSoftExceptionLogger.logSoftException(
|
||||
TAG,
|
||||
IllegalStateException(
|
||||
"Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been" +
|
||||
" recycled"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val TAG = TouchEvent::class.java.simpleName
|
||||
private const val TOUCH_EVENTS_POOL_SIZE = 3
|
||||
private val EVENTS_POOL = SynchronizedPool<TouchEvent>(TOUCH_EVENTS_POOL_SIZE)
|
||||
public const val UNSET: Long = Long.MIN_VALUE
|
||||
|
||||
@Deprecated(
|
||||
"Please use the other overload of the obtain method, which explicitly provides surfaceId",
|
||||
ReplaceWith("obtain(surfaceId, ...)"))
|
||||
@JvmStatic
|
||||
public fun obtain(
|
||||
viewTag: Int,
|
||||
touchEventType: TouchEventType?,
|
||||
motionEventToCopy: MotionEvent?,
|
||||
gestureStartTime: Long,
|
||||
viewX: Float,
|
||||
viewY: Float,
|
||||
touchEventCoalescingKeyHelper: TouchEventCoalescingKeyHelper
|
||||
): TouchEvent {
|
||||
return obtain(
|
||||
-1,
|
||||
viewTag,
|
||||
touchEventType,
|
||||
Assertions.assertNotNull(motionEventToCopy),
|
||||
gestureStartTime,
|
||||
viewX,
|
||||
viewY,
|
||||
touchEventCoalescingKeyHelper)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun obtain(
|
||||
surfaceId: Int,
|
||||
viewTag: Int,
|
||||
touchEventType: TouchEventType?,
|
||||
motionEventToCopy: MotionEvent?,
|
||||
gestureStartTime: Long,
|
||||
viewX: Float,
|
||||
viewY: Float,
|
||||
touchEventCoalescingKeyHelper: TouchEventCoalescingKeyHelper
|
||||
): TouchEvent {
|
||||
var event = EVENTS_POOL.acquire()
|
||||
if (event == null) {
|
||||
event = TouchEvent()
|
||||
}
|
||||
event.init(
|
||||
surfaceId,
|
||||
viewTag,
|
||||
touchEventType,
|
||||
Assertions.assertNotNull(motionEventToCopy),
|
||||
gestureStartTime,
|
||||
viewX,
|
||||
viewY,
|
||||
touchEventCoalescingKeyHelper)
|
||||
return event
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user