diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 0a3a9bbcc6cb..65b7d4c16e2c 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1569,6 +1569,7 @@ public abstract interface class com/facebook/react/bridge/UIManager : com/facebo public abstract fun getEventDispatcher ()Ljava/lang/Object; public abstract fun initialize ()V public abstract fun invalidate ()V + public abstract fun markActiveTouchForTag (II)V public abstract fun receiveEvent (IILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V public abstract fun receiveEvent (ILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V public abstract fun removeUIManagerEventListener (Lcom/facebook/react/bridge/UIManagerListener;)V @@ -1577,6 +1578,7 @@ public abstract interface class com/facebook/react/bridge/UIManager : com/facebo public abstract fun sendAccessibilityEvent (II)V public abstract fun startSurface (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/WritableMap;II)I public abstract fun stopSurface (I)V + public abstract fun sweepActiveTouchForTag (II)V public abstract fun synchronouslyUpdateViewOnUIThread (ILcom/facebook/react/bridge/ReadableMap;)V public abstract fun updateRootLayoutSpecs (IIIII)V } @@ -2710,6 +2712,7 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid public fun getThemeData (I[F)Z public fun initialize ()V public fun invalidate ()V + public fun markActiveTouchForTag (II)V public fun onAllAnimationsComplete ()V public fun onAnimationStarted ()V public fun onHostDestroy ()V @@ -2733,6 +2736,7 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid public fun startSurface (Lcom/facebook/react/interfaces/fabric/SurfaceHandler;Landroid/content/Context;Landroid/view/View;)V public fun stopSurface (I)V public fun stopSurface (Lcom/facebook/react/interfaces/fabric/SurfaceHandler;)V + public fun sweepActiveTouchForTag (II)V public fun synchronouslyUpdateViewOnUIThread (ILcom/facebook/react/bridge/ReadableMap;)V public fun updateRootLayoutSpecs (IIIII)V } @@ -2888,6 +2892,7 @@ public class com/facebook/react/fabric/mounting/SurfaceMountingManager { public fun getViewExists (I)Z public fun isRootViewAttached ()Z public fun isStopped ()Z + public fun markActiveTouchForTag (I)V public fun preallocateView (Ljava/lang/String;ILcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/uimanager/StateWrapper;Z)V public fun printSurfaceState ()V public fun receiveCommand (IILcom/facebook/react/bridge/ReadableArray;)V @@ -2897,6 +2902,7 @@ public class com/facebook/react/fabric/mounting/SurfaceMountingManager { public fun sendAccessibilityEvent (II)V public fun setJSResponder (IIZ)V public fun stopSurface ()V + public fun sweepActiveTouchForTag (I)V public fun updateEventEmitter (ILcom/facebook/react/fabric/events/EventEmitterWrapper;)V public fun updateLayout (IIIIIIII)V public fun updateOverflowInset (IIIII)V @@ -4302,6 +4308,7 @@ public class com/facebook/react/uimanager/JSPointerDispatcher { public class com/facebook/react/uimanager/JSTouchDispatcher { public fun (Landroid/view/ViewGroup;)V public fun handleTouchEvent (Landroid/view/MotionEvent;Lcom/facebook/react/uimanager/events/EventDispatcher;)V + public fun handleTouchEvent (Landroid/view/MotionEvent;Lcom/facebook/react/uimanager/events/EventDispatcher;Lcom/facebook/react/bridge/ReactContext;)V public fun onChildEndedNativeGesture (Landroid/view/MotionEvent;Lcom/facebook/react/uimanager/events/EventDispatcher;)V public fun onChildStartedNativeGesture (Landroid/view/MotionEvent;Lcom/facebook/react/uimanager/events/EventDispatcher;)V } @@ -5284,6 +5291,7 @@ public class com/facebook/react/uimanager/UIManagerModule : com/facebook/react/b public fun invalidate ()V public fun invalidateNodeLayout (I)V public fun manageChildren (ILcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V + public fun markActiveTouchForTag (II)V public fun measure (ILcom/facebook/react/bridge/Callback;)V public fun measureInWindow (ILcom/facebook/react/bridge/Callback;)V public fun measureLayout (IILcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V @@ -5309,6 +5317,7 @@ public class com/facebook/react/uimanager/UIManagerModule : com/facebook/react/b public fun setViewLocalData (ILjava/lang/Object;)V public fun startSurface (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/WritableMap;II)I public fun stopSurface (I)V + public fun sweepActiveTouchForTag (II)V public fun synchronouslyUpdateViewOnUIThread (ILcom/facebook/react/bridge/ReadableMap;)V public fun updateNodeSize (III)V public fun updateRootLayoutSpecs (IIIII)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index f08550eebed9..4aa8d031e4e6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -360,7 +360,7 @@ protected void dispatchJSTouchEvent(MotionEvent event) { EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType()); if (eventDispatcher != null) { - mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher); + mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher, getCurrentReactContext()); } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.kt index b12de9737904..6aab9810556c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.kt @@ -157,4 +157,22 @@ public interface UIManager : PerformanceCounter { /** Called before React Native instance is destroyed. */ public fun invalidate() + + /** + * Mark a view as currently active for a touch event. This information could be used by the + * [UIManager] to decide if a view could be safely destroyed or not. + * + * @param surfaceId The surface ID where the view is rendered. + * @param reactTag The react tag for the specific view + */ + public fun markActiveTouchForTag(surfaceId: Int, reactTag: Int) + + /** + * Sweep a view as currently not active for a touch event. This tells the [UIManager] that the + * view is not being interacted by the user and can safely be destroyed. + * + * @param surfaceId The surface ID where the view is rendered. + * @param reactTag The react tag for the specific view + */ + public fun sweepActiveTouchForTag(surfaceId: Int, reactTag: Int) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt index d7bf878a38b0..38b3be0c8839 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultNewArchitectureEntryPoint.kt @@ -46,6 +46,11 @@ public object DefaultNewArchitectureEntryPoint { ReactNativeFeatureFlags.override( object : ReactNativeNewArchitectureFeatureFlagsDefaults(newArchitectureEnabled = true) { override fun useFabricInterop(): Boolean = fabricEnabled + + // We turn this feature flag to true for OSS to fix #44610 and #45126 and other + // similar bugs related to pressable. + override fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean = + fabricEnabled }) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index db5fa13e85fb..fa79f0df2683 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -452,6 +452,16 @@ public void invalidate() { } } + @Override + public void markActiveTouchForTag(int surfaceId, int reactTag) { + mMountingManager.getSurfaceManager(surfaceId).markActiveTouchForTag(reactTag); + } + + @Override + public void sweepActiveTouchForTag(int surfaceId, int reactTag) { + mMountingManager.getSurfaceManager(surfaceId).sweepActiveTouchForTag(reactTag); + } + /** * Method added to Fabric for backward compatibility reasons, as users on Paper could call * [addUiBlock] and [prependUiBlock] on UIManagerModule. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java index 0c58c4cf2177..feef4f1eb035 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java @@ -76,6 +76,17 @@ public class SurfaceMountingManager { @ThreadConfined(UI) private final Set mErroneouslyReaddedReactTags = new HashSet<>(); + // This set is used to keep track of views that are currently being interacted with (i.e. + // views that saw a ACTION_DOWN but not a ACTION_UP event yet). This is used to prevent + // views from being removed while they are being interacted with as their event emitter will + // also be removed, and `Pressables` will look "stuck". + @ThreadConfined(UI) + private final Set mViewsWithActiveTouches = new HashSet<>(); + + // This set contains the views that are scheduled to be removed after their touch finishes. + @ThreadConfined(UI) + private final Set mViewsToDeleteAfterTouchFinishes = new HashSet<>(); + // This is null *until* StopSurface is called. private SparseArrayCompat mTagSetForStoppedSurface; @@ -1031,12 +1042,22 @@ public void deleteView(int reactTag) { return; } - // To delete we simply remove the tag from the registry. - // We want to rely on the correct set of MountInstructions being sent to the platform, - // or StopSurface being called, so we do not handle deleting descendents of the View. - mTagToViewState.remove(reactTag); + if (ReactNativeFeatureFlags.enableEventEmitterRetentionDuringGesturesOnAndroid() + && mViewsWithActiveTouches.contains(reactTag)) { + // If the view that went offscreen is still being touched, we can't delete it yet. + // We have to delay the deletion till the touch is completed. + // This is causing bugs like those otherwise: + // - https://github.com/facebook/react-native/issues/44610 + // - https://github.com/facebook/react-native/issues/45126 + mViewsToDeleteAfterTouchFinishes.add(reactTag); + } else { + // To delete we simply remove the tag from the registry. + // We want to rely on the correct set of MountInstructions being sent to the platform, + // or StopSurface being called, so we do not handle deleting descendants of the View. + mTagToViewState.remove(reactTag); - onViewStateDeleted(viewState); + onViewStateDeleted(viewState); + } } @UiThread @@ -1160,6 +1181,24 @@ public void run() { }); } + public void markActiveTouchForTag(int reactTag) { + if (!ReactNativeFeatureFlags.enableEventEmitterRetentionDuringGesturesOnAndroid()) { + return; + } + mViewsWithActiveTouches.add(reactTag); + } + + public void sweepActiveTouchForTag(int reactTag) { + if (!ReactNativeFeatureFlags.enableEventEmitterRetentionDuringGesturesOnAndroid()) { + return; + } + mViewsWithActiveTouches.remove(reactTag); + if (mViewsToDeleteAfterTouchFinishes.contains(reactTag)) { + mViewsToDeleteAfterTouchFinishes.remove(reactTag); + deleteView(reactTag); + } + } + /** * This class holds view state for react tags. Objects of this class are stored into the {@link * #mTagToViewState}, and they should be updated in the same thread. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index cf54437762cf..275f6fbe871e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<11bd299e21bf2eb1c320e657885826e4>> + * @generated SignedSource<<275859c437f60e7733da2b238c911152>> */ /** @@ -88,6 +88,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableEagerRootViewAttachment(): Boolean = accessor.enableEagerRootViewAttachment() + /** + * Enables the retention of EventEmitterWrapper on Android till the touch gesture is over to fix a bug on pressable (#44610) + */ + @JvmStatic + public fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean = accessor.enableEventEmitterRetentionDuringGesturesOnAndroid() + /** * This feature flag enables logs for Fabric. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index e0cfeb5f0adf..6c32b6fddcad 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -30,6 +30,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var enableBackgroundStyleApplicatorCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null + private var enableEventEmitterRetentionDuringGesturesOnAndroidCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null @@ -154,6 +155,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean { + var cached = enableEventEmitterRetentionDuringGesturesOnAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableEventEmitterRetentionDuringGesturesOnAndroid() + enableEventEmitterRetentionDuringGesturesOnAndroidCache = cached + } + return cached + } + override fun enableFabricLogs(): Boolean { var cached = enableFabricLogsCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 3b15ce274633..51ff0117ac27 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1d79b037e6e8cf2e7306b7f5b3798b9a>> */ /** @@ -48,6 +48,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableEagerRootViewAttachment(): Boolean + @DoNotStrip @JvmStatic public external fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableFabricLogs(): Boolean @DoNotStrip @JvmStatic public external fun enableFabricRendererExclusively(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 16514aa88abc..96948995e2d0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<06c99ad6853216864f14c96fdc9704dd>> + * @generated SignedSource<> */ /** @@ -43,6 +43,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableEagerRootViewAttachment(): Boolean = false + override fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean = false + override fun enableFabricLogs(): Boolean = false override fun enableFabricRendererExclusively(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 02a2f9ed372b..903f978fd9f2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<408d66cdad8b708c06ca844cd6524ba4>> */ /** @@ -34,6 +34,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var enableBackgroundStyleApplicatorCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null + private var enableEventEmitterRetentionDuringGesturesOnAndroidCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null @@ -168,6 +169,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean { + var cached = enableEventEmitterRetentionDuringGesturesOnAndroidCache + if (cached == null) { + cached = currentProvider.enableEventEmitterRetentionDuringGesturesOnAndroid() + accessedFeatureFlags.add("enableEventEmitterRetentionDuringGesturesOnAndroid") + enableEventEmitterRetentionDuringGesturesOnAndroidCache = cached + } + return cached + } + override fun enableFabricLogs(): Boolean { var cached = enableFabricLogsCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 8d6c9dc44683..e1db6abce717 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6c32d13e793895356e70e01c2fa784b6>> + * @generated SignedSource<<040b9cb9cec727e2eb31769712980528>> */ /** @@ -43,6 +43,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableEagerRootViewAttachment(): Boolean + @DoNotStrip public fun enableEventEmitterRetentionDuringGesturesOnAndroid(): Boolean + @DoNotStrip public fun enableFabricLogs(): Boolean @DoNotStrip public fun enableFabricRendererExclusively(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.kt index bc979de9c329..d926007ff259 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.kt @@ -139,7 +139,8 @@ public class ReactSurfaceView(context: Context?, private val surface: ReactSurfa override fun dispatchJSTouchEvent(event: MotionEvent) { val eventDispatcher = surface.eventDispatcher if (eventDispatcher != null) { - jsTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jsTouchDispatcher.handleTouchEvent( + event, eventDispatcher, surface.reactHost.currentReactContext) } else { FLog.w( TAG, "Unable to dispatch touch events to JS as the React instance has not been attached") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index 77143c90e30f..d763bcb4f48b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -9,10 +9,15 @@ import android.view.MotionEvent; import android.view.ViewGroup; +import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Nullsafe; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.UIManager; import com.facebook.react.common.ReactConstants; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; +import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; @@ -30,12 +35,14 @@ public class JSTouchDispatcher { private final float[] mTargetCoordinates = new float[2]; private boolean mChildIsHandlingNativeGesture = false; private long mGestureStartTime = TouchEvent.UNSET; - private final ViewGroup mRootViewGroup; + + private final ViewGroup mViewGroup; + private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper = new TouchEventCoalescingKeyHelper(); public JSTouchDispatcher(ViewGroup viewGroup) { - mRootViewGroup = viewGroup; + mViewGroup = viewGroup; } public void onChildStartedNativeGesture( @@ -57,6 +64,10 @@ public void onChildEndedNativeGesture(MotionEvent androidEvent, EventDispatcher mChildIsHandlingNativeGesture = false; } + public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { + handleTouchEvent(ev, eventDispatcher, null); + } + /** * 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 @@ -65,7 +76,8 @@ public void onChildEndedNativeGesture(MotionEvent androidEvent, EventDispatcher * method for figuring out a react view ID in the case of ACTION_DOWN event (when the gesture * starts). */ - public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { + public void handleTouchEvent( + MotionEvent ev, EventDispatcher eventDispatcher, @Nullable ReactContext reactContext) { int action = ev.getAction() & MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_DOWN) { if (mTargetTag != -1) { @@ -78,11 +90,13 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // this gesture mChildIsHandlingNativeGesture = false; mGestureStartTime = ev.getEventTime(); - mTargetTag = findTargetTagAndSetCoordinates(ev); + int surfaceId = UIManagerHelper.getSurfaceId(mViewGroup); + markActiveTouchForTag(surfaceId, mTargetTag, reactContext); + eventDispatcher.dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + UIManagerHelper.getSurfaceId(mViewGroup), mTargetTag, TouchEventType.START, ev, @@ -105,9 +119,10 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // End of the gesture. We reset target tag to -1 and expect no further event associated with // this gesture. findTargetTagAndSetCoordinates(ev); + int surfaceId = UIManagerHelper.getSurfaceId(mViewGroup); eventDispatcher.dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + surfaceId, mTargetTag, TouchEventType.END, ev, @@ -115,6 +130,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); + sweepActiveTouchForTag(surfaceId, mTargetTag, reactContext); mTargetTag = -1; mGestureStartTime = TouchEvent.UNSET; } else if (action == MotionEvent.ACTION_MOVE) { @@ -122,7 +138,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { findTargetTagAndSetCoordinates(ev); eventDispatcher.dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + UIManagerHelper.getSurfaceId(mViewGroup), mTargetTag, TouchEventType.MOVE, ev, @@ -134,7 +150,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer eventDispatcher.dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + UIManagerHelper.getSurfaceId(mViewGroup), mTargetTag, TouchEventType.START, ev, @@ -146,7 +162,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // Exactly one of the pointers goes up eventDispatcher.dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + UIManagerHelper.getSurfaceId(mViewGroup), mTargetTag, TouchEventType.END, ev, @@ -162,6 +178,9 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { ReactConstants.TAG, "Received an ACTION_CANCEL touch event for which we have no corresponding ACTION_DOWN"); } + int surfaceId = UIManagerHelper.getSurfaceId(mViewGroup); + sweepActiveTouchForTag(surfaceId, mTargetTag, reactContext); + mTargetTag = -1; mGestureStartTime = TouchEvent.UNSET; } else { @@ -171,10 +190,38 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { } } + private void markActiveTouchForTag( + int surfaceId, int reactTag, @Nullable ReactContext reactContext) { + if (!ReactNativeFeatureFlags.enableEventEmitterRetentionDuringGesturesOnAndroid()) { + return; + } + if (reactContext == null) { + return; + } + UIManager uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC); + if (uiManager != null) { + uiManager.markActiveTouchForTag(surfaceId, reactTag); + } + } + + private void sweepActiveTouchForTag( + int surfaceId, int reactTag, @Nullable ReactContext reactContext) { + if (!ReactNativeFeatureFlags.enableEventEmitterRetentionDuringGesturesOnAndroid()) { + return; + } + if (reactContext == null) { + return; + } + UIManager uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC); + if (uiManager != null) { + uiManager.sweepActiveTouchForTag(surfaceId, reactTag); + } + } + private int findTargetTagAndSetCoordinates(MotionEvent ev) { // This method updates `mTargetCoordinates` with coordinates for the motion event. return TouchTargetHelper.findTargetTagAndCoordinatesForTouch( - ev.getX(), ev.getY(), mRootViewGroup, mTargetCoordinates, null); + ev.getX(), ev.getY(), mViewGroup, mTargetCoordinates, null); } private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher eventDispatcher) { @@ -195,7 +242,7 @@ private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher event Assertions.assertNotNull(eventDispatcher) .dispatchEvent( TouchEvent.obtain( - UIManagerHelper.getSurfaceId(mRootViewGroup), + UIManagerHelper.getSurfaceId(mViewGroup), mTargetTag, TouchEventType.CANCEL, androidEvent, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 09862f765d9d..ea72dc0ca175 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -207,6 +207,16 @@ public void invalidate() { ViewManagerPropertyUpdater.clear(); } + @Override + public void markActiveTouchForTag(int surfaceId, int reactTag) { + // Not implemented for Paper. + } + + @Override + public void sweepActiveTouchForTag(int surfaceId, int reactTag) { + // Not implemented for Paper. + } + /** * This method is intended to reuse the {@link ViewManagerRegistry} with FabricUIManager. Do not * use this method as this will be removed in the near future. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt index f6e0d82fc01a..3a5841b1c7bc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt @@ -483,7 +483,7 @@ public class ReactModalHostView(context: ThemedReactContext) : override fun onInterceptTouchEvent(event: MotionEvent): Boolean { eventDispatcher?.let { eventDispatcher -> - jSTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jSTouchDispatcher.handleTouchEvent(event, eventDispatcher, reactContext) jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true) } return super.onInterceptTouchEvent(event) @@ -492,7 +492,7 @@ public class ReactModalHostView(context: ThemedReactContext) : @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean { eventDispatcher?.let { eventDispatcher -> - jSTouchDispatcher.handleTouchEvent(event, eventDispatcher) + jSTouchDispatcher.handleTouchEvent(event, eventDispatcher, reactContext) jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false) } super.onTouchEvent(event) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 49120d82a171..71924a1f99f7 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<12c6250b601db5f8db15dc1bed57405c>> + * @generated SignedSource<> */ /** @@ -99,6 +99,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool enableEventEmitterRetentionDuringGesturesOnAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableEventEmitterRetentionDuringGesturesOnAndroid"); + return method(javaProvider_); + } + bool enableFabricLogs() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableFabricLogs"); @@ -351,6 +357,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableEagerRootViewAttachment( return ReactNativeFeatureFlags::enableEagerRootViewAttachment(); } +bool JReactNativeFeatureFlagsCxxInterop::enableEventEmitterRetentionDuringGesturesOnAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableEventEmitterRetentionDuringGesturesOnAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableFabricLogs( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableFabricLogs(); @@ -563,6 +574,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableEagerRootViewAttachment", JReactNativeFeatureFlagsCxxInterop::enableEagerRootViewAttachment), + makeNativeMethod( + "enableEventEmitterRetentionDuringGesturesOnAndroid", + JReactNativeFeatureFlagsCxxInterop::enableEventEmitterRetentionDuringGesturesOnAndroid), makeNativeMethod( "enableFabricLogs", JReactNativeFeatureFlagsCxxInterop::enableFabricLogs), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index bb7d763462db..b7274bcde5c3 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5399269d162bd4823a7ec36198c0604f>> + * @generated SignedSource<> */ /** @@ -60,6 +60,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableEagerRootViewAttachment( facebook::jni::alias_ref); + static bool enableEventEmitterRetentionDuringGesturesOnAndroid( + facebook::jni::alias_ref); + static bool enableFabricLogs( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeUIManager.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeUIManager.kt index c51c01483290..55f287820a58 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeUIManager.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeUIManager.kt @@ -25,12 +25,12 @@ class FakeUIManager : UIManager, UIBlockViewResolver { var resolvedViewCount = 0 override fun profileNextBatch() { - TODO("Not yet implemented") + error("Not yet implemented") } @Deprecated("") override fun addRootView(rootView: T, initialProps: WritableMap?): Int { - TODO("Not yet implemented") + error("Not yet implemented") } override fun startSurface( @@ -40,11 +40,11 @@ class FakeUIManager : UIManager, UIBlockViewResolver { widthMeasureSpec: Int, heightMeasureSpec: Int ): Int { - TODO("Not yet implemented") + error("Not yet implemented") } override fun stopSurface(surfaceId: Int) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun updateRootLayoutSpecs( @@ -54,35 +54,35 @@ class FakeUIManager : UIManager, UIBlockViewResolver { offsetX: Int, offsetY: Int ) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun dispatchCommand(reactTag: Int, commandId: Int, commandArgs: ReadableArray?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun dispatchCommand(reactTag: Int, commandId: String, commandArgs: ReadableArray?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun getEventDispatcher(): T { - TODO("Not yet implemented") + error("Not yet implemented") } override fun synchronouslyUpdateViewOnUIThread(reactTag: Int, props: ReadableMap?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun sendAccessibilityEvent(reactTag: Int, eventType: Int) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun addUIManagerEventListener(listener: UIManagerListener?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun removeUIManagerEventListener(listener: UIManagerListener?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun resolveView(reactTag: Int): View? { @@ -92,24 +92,32 @@ class FakeUIManager : UIManager, UIBlockViewResolver { @Deprecated("") override fun receiveEvent(reactTag: Int, eventName: String, event: WritableMap?) { - TODO("Not yet implemented") + error("Not yet implemented") } override fun receiveEvent(surfaceId: Int, reactTag: Int, eventName: String, event: WritableMap?) { - TODO("Not yet implemented") + error("Not yet implemented") } @Deprecated("") override fun resolveCustomDirectEventName(eventName: String): String? { - TODO("Not yet implemented") + error("Not yet implemented") } override fun initialize() { - TODO("Not yet implemented") + error("Not yet implemented") } override fun invalidate() { - TODO("Not yet implemented") + error("Not yet implemented") + } + + override fun markActiveTouchForTag(surfaceId: Int, reactTag: Int) { + error("Not yet implemented") + } + + override fun sweepActiveTouchForTag(surfaceId: Int, reactTag: Int) { + error("Not yet implemented") } override val performanceCounters: Map? diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index f49e406f182a..549658d6f844 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -61,6 +61,10 @@ bool ReactNativeFeatureFlags::enableEagerRootViewAttachment() { return getAccessor().enableEagerRootViewAttachment(); } +bool ReactNativeFeatureFlags::enableEventEmitterRetentionDuringGesturesOnAndroid() { + return getAccessor().enableEventEmitterRetentionDuringGesturesOnAndroid(); +} + bool ReactNativeFeatureFlags::enableFabricLogs() { return getAccessor().enableFabricLogs(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index d8473b36178e..656fe5736b5d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<93b1717fb2e45f00fb068a4ffa248afe>> + * @generated SignedSource<<45ae072fb6a0b1ec404b3dc8adc13186>> */ /** @@ -87,6 +87,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableEagerRootViewAttachment(); + /** + * Enables the retention of EventEmitterWrapper on Android till the touch gesture is over to fix a bug on pressable (#44610) + */ + RN_EXPORT static bool enableEventEmitterRetentionDuringGesturesOnAndroid(); + /** * This feature flag enables logs for Fabric. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 0c13880ebf5e..b4f8cf56b2b9 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9e7498efd93e3c23b74219b7b30ec851>> */ /** @@ -209,6 +209,24 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableEventEmitterRetentionDuringGesturesOnAndroid() { + auto flagValue = enableEventEmitterRetentionDuringGesturesOnAndroid_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(10, "enableEventEmitterRetentionDuringGesturesOnAndroid"); + + flagValue = currentProvider_->enableEventEmitterRetentionDuringGesturesOnAndroid(); + enableEventEmitterRetentionDuringGesturesOnAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { auto flagValue = enableFabricLogs_.load(); @@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(10, "enableFabricLogs"); + markFlagAsAccessed(11, "enableFabricLogs"); flagValue = currentProvider_->enableFabricLogs(); enableFabricLogs_ = flagValue; @@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRendererExclusively() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(11, "enableFabricRendererExclusively"); + markFlagAsAccessed(12, "enableFabricRendererExclusively"); flagValue = currentProvider_->enableFabricRendererExclusively(); enableFabricRendererExclusively_ = flagValue; @@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliatio // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(12, "enableGranularShadowTreeStateReconciliation"); + markFlagAsAccessed(13, "enableGranularShadowTreeStateReconciliation"); flagValue = currentProvider_->enableGranularShadowTreeStateReconciliation(); enableGranularShadowTreeStateReconciliation_ = flagValue; @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "enableLongTaskAPI"); + markFlagAsAccessed(14, "enableLongTaskAPI"); flagValue = currentProvider_->enableLongTaskAPI(); enableLongTaskAPI_ = flagValue; @@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(14, "enableMicrotasks"); + markFlagAsAccessed(15, "enableMicrotasks"); flagValue = currentProvider_->enableMicrotasks(); enableMicrotasks_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(16, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableReportEventPaintTime() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(16, "enableReportEventPaintTime"); + markFlagAsAccessed(17, "enableReportEventPaintTime"); flagValue = currentProvider_->enableReportEventPaintTime(); enableReportEventPaintTime_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "enableSynchronousStateUpdates"); + markFlagAsAccessed(18, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(18, "enableUIConsistency"); + markFlagAsAccessed(19, "enableUIConsistency"); flagValue = currentProvider_->enableUIConsistency(); enableUIConsistency_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "enableViewRecycling"); + markFlagAsAccessed(20, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::excludeYogaFromRawProps() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "excludeYogaFromRawProps"); + markFlagAsAccessed(21, "excludeYogaFromRawProps"); flagValue = currentProvider_->excludeYogaFromRawProps(); excludeYogaFromRawProps_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::fetchImagesInViewPreallocation() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "fetchImagesInViewPreallocation"); + markFlagAsAccessed(22, "fetchImagesInViewPreallocation"); flagValue = currentProvider_->fetchImagesInViewPreallocation(); fetchImagesInViewPreallocation_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::fixIncorrectScrollViewStateUpdateOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "fixIncorrectScrollViewStateUpdateOnAndroid"); + markFlagAsAccessed(23, "fixIncorrectScrollViewStateUpdateOnAndroid"); flagValue = currentProvider_->fixIncorrectScrollViewStateUpdateOnAndroid(); fixIncorrectScrollViewStateUpdateOnAndroid_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(24, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMissedFabricStateUpdatesOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "fixMissedFabricStateUpdatesOnAndroid"); + markFlagAsAccessed(25, "fixMissedFabricStateUpdatesOnAndroid"); flagValue = currentProvider_->fixMissedFabricStateUpdatesOnAndroid(); fixMissedFabricStateUpdatesOnAndroid_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::forceBatchingMountItemsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "forceBatchingMountItemsOnAndroid"); + markFlagAsAccessed(26, "forceBatchingMountItemsOnAndroid"); flagValue = currentProvider_->forceBatchingMountItemsOnAndroid(); forceBatchingMountItemsOnAndroid_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "fuseboxEnabledDebug"); + markFlagAsAccessed(27, "fuseboxEnabledDebug"); flagValue = currentProvider_->fuseboxEnabledDebug(); fuseboxEnabledDebug_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "fuseboxEnabledRelease"); + markFlagAsAccessed(28, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "initEagerTurboModulesOnNativeModulesQueueAndroid"); + markFlagAsAccessed(29, "initEagerTurboModulesOnNativeModulesQueueAndroid"); flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid(); initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "lazyAnimationCallbacks"); + markFlagAsAccessed(30, "lazyAnimationCallbacks"); flagValue = currentProvider_->lazyAnimationCallbacks(); lazyAnimationCallbacks_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "loadVectorDrawablesOnImages"); + markFlagAsAccessed(31, "loadVectorDrawablesOnImages"); flagValue = currentProvider_->loadVectorDrawablesOnImages(); loadVectorDrawablesOnImages_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "setAndroidLayoutDirection"); + markFlagAsAccessed(32, "setAndroidLayoutDirection"); flagValue = currentProvider_->setAndroidLayoutDirection(); setAndroidLayoutDirection_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(33, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "useFabricInterop"); + markFlagAsAccessed(34, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(35, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "useModernRuntimeScheduler"); + markFlagAsAccessed(36, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(37, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::useNewReactImageViewBackgroundDrawing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "useNewReactImageViewBackgroundDrawing"); + markFlagAsAccessed(38, "useNewReactImageViewBackgroundDrawing"); flagValue = currentProvider_->useNewReactImageViewBackgroundDrawing(); useNewReactImageViewBackgroundDrawing_ = flagValue; @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "useOptimisedViewPreallocationOnAndroid"); + markFlagAsAccessed(39, "useOptimisedViewPreallocationOnAndroid"); flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid(); useOptimisedViewPreallocationOnAndroid_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(40, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "useRuntimeShadowNodeReferenceUpdateOnLayout"); + markFlagAsAccessed(41, "useRuntimeShadowNodeReferenceUpdateOnLayout"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout(); useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "useStateAlignmentMechanism"); + markFlagAsAccessed(42, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useTurboModuleInterop"); + markFlagAsAccessed(43, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 99c85da10448..7471b7d91fe0 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<42259cb82f9ac5b3c0918339b45e6a2a>> + * @generated SignedSource<> */ /** @@ -41,6 +41,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableBackgroundStyleApplicator(); bool enableCleanTextInputYogaNode(); bool enableEagerRootViewAttachment(); + bool enableEventEmitterRetentionDuringGesturesOnAndroid(); bool enableFabricLogs(); bool enableFabricRendererExclusively(); bool enableGranularShadowTreeStateReconciliation(); @@ -84,7 +85,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 43> accessedFeatureFlags_; + std::array, 44> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowRecursiveCommitsWithSynchronousMountOnAndroid_; @@ -96,6 +97,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableBackgroundStyleApplicator_; std::atomic> enableCleanTextInputYogaNode_; std::atomic> enableEagerRootViewAttachment_; + std::atomic> enableEventEmitterRetentionDuringGesturesOnAndroid_; std::atomic> enableFabricLogs_; std::atomic> enableFabricRendererExclusively_; std::atomic> enableGranularShadowTreeStateReconciliation_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index e557ea0baff7..34dc9784a07f 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6624485d7a7712c8243e4a00621fa10d>> + * @generated SignedSource<<5984a783c431c11b00c2dd8321432235>> */ /** @@ -67,6 +67,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableEventEmitterRetentionDuringGesturesOnAndroid() override { + return false; + } + bool enableFabricLogs() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index d5d57251de0a..1e7392fdba31 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<28c8387a6ed837bcfbe75da8dae74e63>> + * @generated SignedSource<<362ab4dfd19d6f3189b3c3e5aafd009a>> */ /** @@ -35,6 +35,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableBackgroundStyleApplicator() = 0; virtual bool enableCleanTextInputYogaNode() = 0; virtual bool enableEagerRootViewAttachment() = 0; + virtual bool enableEventEmitterRetentionDuringGesturesOnAndroid() = 0; virtual bool enableFabricLogs() = 0; virtual bool enableFabricRendererExclusively() = 0; virtual bool enableGranularShadowTreeStateReconciliation() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 54e14c2cef0a..56b406deca47 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -87,6 +87,11 @@ bool NativeReactNativeFeatureFlags::enableEagerRootViewAttachment( return ReactNativeFeatureFlags::enableEagerRootViewAttachment(); } +bool NativeReactNativeFeatureFlags::enableEventEmitterRetentionDuringGesturesOnAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableEventEmitterRetentionDuringGesturesOnAndroid(); +} + bool NativeReactNativeFeatureFlags::enableFabricLogs( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableFabricLogs(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index fc97c4b479a4..4bc9ec789057 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6b0ad22b5e8302c1d4b0dae4d1f2eb19>> */ /** @@ -55,6 +55,8 @@ class NativeReactNativeFeatureFlags bool enableEagerRootViewAttachment(jsi::Runtime& runtime); + bool enableEventEmitterRetentionDuringGesturesOnAndroid(jsi::Runtime& runtime); + bool enableFabricLogs(jsi::Runtime& runtime); bool enableFabricRendererExclusively(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 7dfe3df73739..a85eb9e1dacd 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -83,6 +83,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Feature flag to configure eager attachment of the root view/initialisation of the JS code.', }, + enableEventEmitterRetentionDuringGesturesOnAndroid: { + defaultValue: false, + description: + 'Enables the retention of EventEmitterWrapper on Android till the touch gesture is over to fix a bug on pressable (#44610)', + }, enableFabricLogs: { defaultValue: false, description: 'This feature flag enables logs for Fabric.', diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index e0947d444e39..37ac42ca7202 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6896ec018143f40b7d08487fb10239d7>> + * @generated SignedSource<<453a0231b2d396d59727425f008c65b8>> * @flow strict-local */ @@ -54,6 +54,7 @@ export type ReactNativeFeatureFlags = { enableBackgroundStyleApplicator: Getter, enableCleanTextInputYogaNode: Getter, enableEagerRootViewAttachment: Getter, + enableEventEmitterRetentionDuringGesturesOnAndroid: Getter, enableFabricLogs: Getter, enableFabricRendererExclusively: Getter, enableGranularShadowTreeStateReconciliation: Getter, @@ -189,6 +190,10 @@ export const enableCleanTextInputYogaNode: Getter = createNativeFlagGet * Feature flag to configure eager attachment of the root view/initialisation of the JS code. */ export const enableEagerRootViewAttachment: Getter = createNativeFlagGetter('enableEagerRootViewAttachment', false); +/** + * Enables the retention of EventEmitterWrapper on Android till the touch gesture is over to fix a bug on pressable (#44610) + */ +export const enableEventEmitterRetentionDuringGesturesOnAndroid: Getter = createNativeFlagGetter('enableEventEmitterRetentionDuringGesturesOnAndroid', false); /** * This feature flag enables logs for Fabric. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index dfc17488309e..97b556181a94 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8e7f7c0b5eccf4db77ab4b65a4aae313>> + * @generated SignedSource<> * @flow strict-local */ @@ -33,6 +33,7 @@ export interface Spec extends TurboModule { +enableBackgroundStyleApplicator?: () => boolean; +enableCleanTextInputYogaNode?: () => boolean; +enableEagerRootViewAttachment?: () => boolean; + +enableEventEmitterRetentionDuringGesturesOnAndroid?: () => boolean; +enableFabricLogs?: () => boolean; +enableFabricRendererExclusively?: () => boolean; +enableGranularShadowTreeStateReconciliation?: () => boolean;