Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce the enableEventEmitterRetentionDuringGesturesOnAndroid to gate the Pressable fix #45930

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -4302,6 +4308,7 @@ public class com/facebook/react/uimanager/JSPointerDispatcher {
public class com/facebook/react/uimanager/JSTouchDispatcher {
public fun <init> (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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ public class SurfaceMountingManager {
@ThreadConfined(UI)
private final Set<Integer> 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<Integer> mViewsWithActiveTouches = new HashSet<>();

// This set contains the views that are scheduled to be removed after their touch finishes.
@ThreadConfined(UI)
private final Set<Integer> mViewsToDeleteAfterTouchFinishes = new HashSet<>();

// This is null *until* StopSurface is called.
private SparseArrayCompat<Object> mTagSetForStoppedSurface;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>>
*/

/**
Expand Down Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<<c646b32a6639fdb0e794f3a2b6e01171>>
* @generated SignedSource<<e58ecac6e67cd870b8ac077cbdfd7469>>
*/

/**
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<<fec9ad21cd603e23ef38d926ba693340>>
* @generated SignedSource<<1d79b037e6e8cf2e7306b7f5b3798b9a>>
*/

/**
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<<c9d11f2cf414d546f5c9be0a05b22e85>>
*/

/**
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<<a0b0cc1b62abded9dd8a7b70f8d897da>>
* @generated SignedSource<<408d66cdad8b708c06ca844cd6524ba4>>
*/

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>>
*/

/**
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading
Loading