Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -365,20 +365,13 @@ class GestureHandlerOrchestrator(
event.recycle()
}

/**
* Cancels all handlers created using API v1 and v2
*/
fun cancelAllLegacyHandlers() {
private inline fun cancelTrackedHandlers(predicate: (GestureHandler) -> Boolean) {
val handlersToProcess = obtainHandlerList()
handlersToProcess.addAll(gestureHandlers)

try {
handlersToProcess.forEach {
if (it.actionType == GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API ||
it.actionType == GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API ||
it.actionType == GestureHandler.ACTION_TYPE_REANIMATED_WORKLET ||
it.actionType == GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT
) {
if (predicate(it)) {
it.cancel()
}
}
Expand All @@ -389,6 +382,20 @@ class GestureHandlerOrchestrator(
}
}

fun cancelAllLegacyHandlers() = cancelTrackedHandlers {
it.actionType == GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API ||
it.actionType == GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API ||
it.actionType == GestureHandler.ACTION_TYPE_REANIMATED_WORKLET ||
it.actionType == GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT
}

/**
* Cancels handlers whose view opted out of surviving a native view taking over the touch stream.
*/
fun cancelHandlersOnNativeTouchGrab(grabbedMidGesture: Boolean) = cancelTrackedHandlers {
it is NativeViewGestureHandler && it.shouldCancelOnNativeTouchGrab(grabbedMidGesture)
}

/**
* isViewAttachedUnderWrapper checks whether all of parents for view related to handler
* view are attached. Since there might be an issue rarely observed when view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ class NativeViewGestureHandler : GestureHandler() {

override fun wantsToAttachDirectlyToView() = true

fun shouldCancelOnNativeTouchGrab(grabbedMidGesture: Boolean): Boolean =
hook.shouldCancelOnNativeTouchGrab(grabbedMidGesture)

data class HitSlop(
val left: Float = HIT_SLOP_NONE,
val top: Float = HIT_SLOP_NONE,
Expand Down Expand Up @@ -361,6 +364,11 @@ class NativeViewGestureHandler : GestureHandler() {
*/
fun shouldRecognizeSimultaneously(handler: GestureHandler): Boolean? = null

/**
* Called after a native view grabbed the touch lock; return true to cancel the handler.
*/
fun shouldCancelOnNativeTouchGrab(grabbedMidGesture: Boolean) = false

/**
* shouldActivateOnStart and tryIntercept have priority over this method
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@ class RNGestureHandlerButtonViewManager :
// event).
private var lastEventWasInside = false

// Whether the native dispatch delivered DOWN for the current gesture. False when a native
// ancestor intercepted it — the orchestrator still delivers events then.
private var receivedNativeDown = false

override fun onHandlerUpdate(handler: NativeViewGestureHandler) {
if (managedHandlerTag == null || handler.isWithinBounds == lastEventWasInside) {
return
Expand Down Expand Up @@ -744,6 +748,8 @@ class RNGestureHandlerButtonViewManager :
val localLastEventWasInside = lastEventWasInside

if (newState == GestureHandler.STATE_BEGAN) {
// Reset for the new gesture - BEGAN precedes the native dispatch of DOWN that sets the flag.
receivedNativeDown = false
dispatchJSEvent(EventType.PressIn, handler)
longPressDetected = false

Expand Down Expand Up @@ -815,6 +821,16 @@ class RNGestureHandlerButtonViewManager :
}
}

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
receivedNativeDown = true
}

return super.dispatchTouchEvent(event)
}

override fun shouldCancelOnNativeTouchGrab(grabbedMidGesture: Boolean) = grabbedMidGesture || !receivedNativeDown

override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
if (super.onInterceptTouchEvent(event)) {
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
private var shouldIntercept = false
private var wasIntercepting = false
private var passingTouch = false
private var nativeTouchGrabRequested = false

init {
val registry =
Expand Down Expand Up @@ -120,10 +121,27 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
if (orchestrator != null && !passingTouch) {
// if we are in the process of delivering touch events via GH orchestrator, we don't want to
// treat it as a native gesture capturing the lock
nativeTouchGrabRequested = true
orchestrator.cancelAllLegacyHandlers()
}
}

/**
* A disallow-intercept request may mean a real interception, but it may also be just a
* defensive call from a view that lets the event through (e.g. a nested pager). The two can only
* be told apart after the native dispatch completes, so cancellation runs here, not at request time.
*/
fun onNativeDispatchEnd(event: MotionEvent) {
if (nativeTouchGrabRequested) {
nativeTouchGrabRequested = false

val grabbedMidGesture = event.actionMasked != MotionEvent.ACTION_DOWN &&
event.actionMasked != MotionEvent.ACTION_POINTER_DOWN

orchestrator?.cancelHandlersOnNativeTouchGrab(grabbedMidGesture)
}
}

fun dispatchTouchEvent(event: MotionEvent): Boolean {
// We mark `mPassingTouch` before we get into `mOrchestrator.onTouchEvent` so that we can tell
// if `requestDisallow` has been called as a result of a normal gesture handling process or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ class RNGestureHandlerRootView(context: Context?) : ReactViewGroup(context) {
return if (rootViewEnabled && rootHelper!!.dispatchTouchEvent(event)) {
true
} else {
super.dispatchTouchEvent(event)
val handled = super.dispatchTouchEvent(event)
rootHelper?.onNativeDispatchEnd(event)
handled
}
}

Expand Down
Loading