diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index ce384231d433c5..07a77e1e9189ea 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -5666,8 +5666,9 @@ public class com/facebook/react/uimanager/events/PointerEvent$PointerEventState public fun supportsHover (I)Z } -public class com/facebook/react/uimanager/events/PointerEventHelper { +public final class com/facebook/react/uimanager/events/PointerEventHelper { public static final field CLICK Ljava/lang/String; + public static final field INSTANCE Lcom/facebook/react/uimanager/events/PointerEventHelper; public static final field POINTER_CANCEL Ljava/lang/String; public static final field POINTER_DOWN Ljava/lang/String; public static final field POINTER_ENTER Ljava/lang/String; @@ -5680,16 +5681,15 @@ public class com/facebook/react/uimanager/events/PointerEventHelper { public static final field POINTER_TYPE_TOUCH Ljava/lang/String; public static final field POINTER_TYPE_UNKNOWN Ljava/lang/String; public static final field POINTER_UP Ljava/lang/String; - public fun ()V - public static fun getButtonChange (Ljava/lang/String;II)I - public static fun getButtons (Ljava/lang/String;Ljava/lang/String;I)I - public static fun getEventCategory (Ljava/lang/String;)I - public static fun getPressure (ILjava/lang/String;)D - public static fun getW3CPointerType (I)Ljava/lang/String; - public static fun isBubblingEvent (Ljava/lang/String;)Z - public static fun isExitEvent (Ljava/lang/String;)Z - public static fun isListening (Landroid/view/View;Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT;)Z - public static fun supportsHover (Landroid/view/MotionEvent;)Z + public static final fun getButtonChange (Ljava/lang/String;II)I + public static final fun getButtons (Ljava/lang/String;Ljava/lang/String;I)I + public static final fun getEventCategory (Ljava/lang/String;)I + public static final fun getPressure (ILjava/lang/String;)D + public static final fun getW3CPointerType (I)Ljava/lang/String; + public static final fun isBubblingEvent (Ljava/lang/String;)Z + public final fun isExitEvent (Ljava/lang/String;)Z + public static final fun isListening (Landroid/view/View;Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT;)Z + public final fun supportsHover (Landroid/view/MotionEvent;)Z } public final class com/facebook/react/uimanager/events/PointerEventHelper$EVENT : java/lang/Enum { @@ -5711,6 +5711,7 @@ public final class com/facebook/react/uimanager/events/PointerEventHelper$EVENT public static final field OVER_CAPTURE Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT; public static final field UP Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT; public static final field UP_CAPTURE Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT; public static fun values ()[Lcom/facebook/react/uimanager/events/PointerEventHelper$EVENT; } @@ -5779,13 +5780,6 @@ public final class com/facebook/react/uimanager/events/TouchEventType$Companion public final fun getJSEventName (Lcom/facebook/react/uimanager/events/TouchEventType;)Ljava/lang/String; } -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 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 { public static final field Companion Lcom/facebook/react/uimanager/layoutanimation/InterpolatorType$Companion; public static final field EASE_IN Lcom/facebook/react/uimanager/layoutanimation/InterpolatorType; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.java deleted file mode 100644 index e80353c5a57dea..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.java +++ /dev/null @@ -1,210 +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.InputDevice; -import android.view.MotionEvent; -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.react.R; - -/** Class responsible for generating catalyst touch events based on android {@link MotionEvent}. */ -@Nullsafe(Nullsafe.Mode.LOCAL) -public class PointerEventHelper { - - public static final String POINTER_TYPE_TOUCH = "touch"; - public static final String POINTER_TYPE_PEN = "pen"; - public static final String POINTER_TYPE_MOUSE = "mouse"; - public static final String POINTER_TYPE_UNKNOWN = ""; - private static final int X_FLAG_SUPPORTS_HOVER = 0x01000000; - - public static enum EVENT { - CANCEL, - CANCEL_CAPTURE, - CLICK, - CLICK_CAPTURE, - DOWN, - DOWN_CAPTURE, - ENTER, - ENTER_CAPTURE, - LEAVE, - LEAVE_CAPTURE, - MOVE, - MOVE_CAPTURE, - UP, - UP_CAPTURE, - OUT, - OUT_CAPTURE, - OVER, - OVER_CAPTURE, - }; - - public static final String POINTER_CANCEL = "topPointerCancel"; - public static final String POINTER_DOWN = "topPointerDown"; - public static final String POINTER_ENTER = "topPointerEnter"; - public static final String POINTER_LEAVE = "topPointerLeave"; - public static final String POINTER_MOVE = "topPointerMove"; - public static final String POINTER_UP = "topPointerUp"; - public static final String POINTER_OVER = "topPointerOver"; - public static final String POINTER_OUT = "topPointerOut"; - public static final String CLICK = "topClick"; - - // https://w3c.github.io/pointerevents/#the-buttons-property - public static int getButtons(String eventName, String pointerType, int buttonState) { - if (isExitEvent(eventName)) { - return 0; - } - if (POINTER_TYPE_TOUCH.equals(pointerType)) { - return 1; - } - return buttonState; - } - - // https://w3c.github.io/pointerevents/#the-button-property - public static int getButtonChange( - String pointerType, int lastButtonState, int currentButtonState) { - // Always return 0 for touch - if (POINTER_TYPE_TOUCH.equals(pointerType)) { - return 0; - } - - int changedMask = currentButtonState ^ lastButtonState; - if (changedMask == 0) { - return -1; - } - - switch (changedMask) { - case MotionEvent.BUTTON_PRIMARY: // left button, touch/pen contact - return 0; - case MotionEvent.BUTTON_TERTIARY: // middle mouse - return 1; - case MotionEvent.BUTTON_SECONDARY: // rightbutton, Pen barrel button - return 2; - case MotionEvent.BUTTON_BACK: - return 3; - case MotionEvent.BUTTON_FORWARD: - return 4; - // TOD0 - Pen eraser button maps to what? - } - return -1; - } - - public static String getW3CPointerType(final int toolType) { - // https://www.w3.org/TR/pointerevents3/#dom-pointerevent-pointertype - switch (toolType) { - case MotionEvent.TOOL_TYPE_FINGER: - return POINTER_TYPE_TOUCH; - - case MotionEvent.TOOL_TYPE_STYLUS: - return POINTER_TYPE_PEN; - - case MotionEvent.TOOL_TYPE_MOUSE: - return POINTER_TYPE_MOUSE; - } - return POINTER_TYPE_UNKNOWN; - } - - public static boolean isListening(@Nullable View view, EVENT event) { - if (view == null) { - return true; - } - - switch (event) { - case DOWN: - case DOWN_CAPTURE: - case UP: - case UP_CAPTURE: - case CANCEL: - case CANCEL_CAPTURE: - case CLICK: - case CLICK_CAPTURE: - return true; - } - - Integer pointerEvents = (Integer) view.getTag(R.id.pointer_events); - if (pointerEvents != null) { - return (pointerEvents.intValue() & (1 << event.ordinal())) != 0; - } - return false; - } - - public static int getEventCategory(String pointerEventType) { - if (pointerEventType == null) { - return EventCategoryDef.UNSPECIFIED; - } - // Following: - // https://github.com/facebook/react/blob/main/packages/react-dom/src/events/ReactDOMEventListener.js#L435-L437 - switch (pointerEventType) { - case POINTER_DOWN: - case POINTER_CANCEL: - case POINTER_UP: - return EventCategoryDef.DISCRETE; - case POINTER_MOVE: - case POINTER_ENTER: - case POINTER_LEAVE: - case POINTER_OVER: - case POINTER_OUT: - return EventCategoryDef.CONTINUOUS; - } - - return EventCategoryDef.UNSPECIFIED; - } - - public static boolean supportsHover(MotionEvent motionEvent) { - // A flag has been set on the MotionEvent to indicate it supports hover - // See D36958947 on justifications for this. - // TODO(luwe): Leverage previous events to determine if MotionEvent - // is from an input device that supports hover - boolean supportsHoverFlag = (motionEvent.getFlags() & X_FLAG_SUPPORTS_HOVER) != 0; - if (supportsHoverFlag) { - return true; - } - - return motionEvent.isFromSource(InputDevice.SOURCE_MOUSE); - } - - public static boolean isExitEvent(String eventName) { - switch (eventName) { - case POINTER_UP: - case POINTER_LEAVE: - case POINTER_OUT: - return true; - default: - return false; - } - } - - // https://w3c.github.io/pointerevents/#dom-pointerevent-pressure - public static double getPressure(int buttonState, String eventName) { - if (isExitEvent(eventName)) { - return 0; - } - - // Assume we don't support pressure on our platform for now - // For hardware and platforms that do not support pressure, - // the value MUST be 0.5 when in the active buttons state - // and 0 otherwise. - boolean inActiveButtonState = buttonState != 0; - return inActiveButtonState ? 0.5 : 0; - } - - public static boolean isBubblingEvent(String eventName) { - switch (eventName) { - case POINTER_UP: - case POINTER_DOWN: - case POINTER_OVER: - case POINTER_OUT: - case POINTER_MOVE: - case POINTER_CANCEL: - return true; - default: - return false; - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.kt new file mode 100644 index 00000000000000..363fd9e11dc15a --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEventHelper.kt @@ -0,0 +1,193 @@ +/* + * 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.InputDevice +import android.view.MotionEvent +import android.view.View +import com.facebook.react.R + +/** Class responsible for generating catalyst touch events based on android [MotionEvent]. */ +public object PointerEventHelper { + public const val POINTER_TYPE_TOUCH: String = "touch" + public const val POINTER_TYPE_PEN: String = "pen" + public const val POINTER_TYPE_MOUSE: String = "mouse" + public const val POINTER_TYPE_UNKNOWN: String = "" + internal const val X_FLAG_SUPPORTS_HOVER = 0x01000000 + + public const val POINTER_CANCEL: String = "topPointerCancel" + public const val POINTER_DOWN: String = "topPointerDown" + public const val POINTER_ENTER: String = "topPointerEnter" + public const val POINTER_LEAVE: String = "topPointerLeave" + public const val POINTER_MOVE: String = "topPointerMove" + public const val POINTER_UP: String = "topPointerUp" + public const val POINTER_OVER: String = "topPointerOver" + public const val POINTER_OUT: String = "topPointerOut" + public const val CLICK: String = "topClick" + + public enum class EVENT { + CANCEL, + CANCEL_CAPTURE, + CLICK, + CLICK_CAPTURE, + DOWN, + DOWN_CAPTURE, + ENTER, + ENTER_CAPTURE, + LEAVE, + LEAVE_CAPTURE, + MOVE, + MOVE_CAPTURE, + UP, + UP_CAPTURE, + OUT, + OUT_CAPTURE, + OVER, + OVER_CAPTURE, + } + + // https://w3c.github.io/pointerevents/#the-buttons-property + @JvmStatic + public fun getButtons(eventName: String?, pointerType: String, buttonState: Int): Int { + if (isExitEvent(eventName)) { + return 0 + } + if (POINTER_TYPE_TOUCH == pointerType) { + return 1 + } + return buttonState + } + + // https://w3c.github.io/pointerevents/#the-button-property + @JvmStatic + public fun getButtonChange( + pointerType: String, + lastButtonState: Int, + currentButtonState: Int + ): Int { + // Always return 0 for touch + if (POINTER_TYPE_TOUCH == pointerType) { + return 0 + } + + val changedMask = currentButtonState xor lastButtonState + if (changedMask == 0) { + return -1 + } + + return when (changedMask) { + MotionEvent.BUTTON_PRIMARY -> 0 + MotionEvent.BUTTON_TERTIARY -> 1 + MotionEvent.BUTTON_SECONDARY -> 2 + MotionEvent.BUTTON_BACK -> 3 + MotionEvent.BUTTON_FORWARD -> 4 + else -> -1 + } + } + + @JvmStatic + public fun getW3CPointerType(toolType: Int): String { + // https://www.w3.org/TR/pointerevents3/#dom-pointerevent-pointertype + return when (toolType) { + MotionEvent.TOOL_TYPE_FINGER -> POINTER_TYPE_TOUCH + MotionEvent.TOOL_TYPE_STYLUS -> POINTER_TYPE_PEN + MotionEvent.TOOL_TYPE_MOUSE -> POINTER_TYPE_MOUSE + else -> POINTER_TYPE_UNKNOWN + } + } + + @JvmStatic + public fun isListening(view: View?, event: EVENT): Boolean { + if (view == null) { + return true + } + + return when (event) { + EVENT.DOWN, + EVENT.DOWN_CAPTURE, + EVENT.UP, + EVENT.UP_CAPTURE, + EVENT.CANCEL, + EVENT.CANCEL_CAPTURE, + EVENT.CLICK, + EVENT.CLICK_CAPTURE -> true + else -> { + val pointerEvents = view.getTag(R.id.pointer_events) as? Int + (pointerEvents != null) && (pointerEvents and (1 shl event.ordinal)) != 0 + } + } + } + + @JvmStatic + public fun getEventCategory(pointerEventType: String?): Int { + if (pointerEventType == null) { + return EventCategoryDef.UNSPECIFIED + } + return when (pointerEventType) { + POINTER_DOWN, + POINTER_CANCEL, + POINTER_UP -> EventCategoryDef.DISCRETE + POINTER_MOVE, + POINTER_ENTER, + POINTER_LEAVE, + POINTER_OVER, + POINTER_OUT -> EventCategoryDef.CONTINUOUS + else -> EventCategoryDef.UNSPECIFIED + } + } + + public fun supportsHover(motionEvent: MotionEvent): Boolean { + // A flag has been set on the MotionEvent to indicate it supports hover + // See D36958947 on justifications for this. + // TODO(luwe): Leverage previous events to determine if MotionEvent + // is from an input device that supports hover + val supportsHoverFlag = (motionEvent.flags and X_FLAG_SUPPORTS_HOVER) != 0 + if (supportsHoverFlag) { + return true + } + + return motionEvent.isFromSource(InputDevice.SOURCE_MOUSE) + } + + public fun isExitEvent(eventName: String?): Boolean { + return when (eventName) { + POINTER_UP, + POINTER_LEAVE, + POINTER_OUT -> true + else -> false + } + } + + // https://w3c.github.io/pointerevents/#dom-pointerevent-pressure + @JvmStatic + public fun getPressure(buttonState: Int, eventName: String?): Double { + if (isExitEvent(eventName)) { + return 0.0 + } + + // Assume we don't support pressure on our platform for now + // For hardware and platforms that do not support pressure, + // the value MUST be 0.5 when in the active buttons state + // and 0 otherwise. + val inActiveButtonState = buttonState != 0 + return if (inActiveButtonState) 0.5 else 0.0 + } + + @JvmStatic + public fun isBubblingEvent(eventName: String?): Boolean { + return when (eventName) { + POINTER_UP, + POINTER_DOWN, + POINTER_OVER, + POINTER_OUT, + POINTER_MOVE, + POINTER_CANCEL -> true + else -> false + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.kt index a51a181e3267d0..77f694d08ff79e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.kt @@ -15,7 +15,7 @@ import com.facebook.react.uimanager.events.TouchEventType.Companion.getJSEventNa import com.facebook.systrace.Systrace /** Class responsible for generating catalyst touch events based on android [MotionEvent]. */ -public object TouchesHelper { +internal object TouchesHelper { @JvmField @Deprecated("Not used in New Architecture") public val TARGET_KEY: String = "target" private const val TARGET_SURFACE_KEY = "targetSurface"