Skip to content

fix(Android): cancel button handlers when a native view takes the touch lock - #4433

Open
huextrat wants to merge 1 commit into
software-mansion:mainfrom
huextrat:fix/cancel-native-button-handlers-on-native-gesture
Open

fix(Android): cancel button handlers when a native view takes the touch lock#4433
huextrat wants to merge 1 commit into
software-mansion:mainfrom
huextrat:fix/cancel-native-button-handlers-on-native-gesture

Conversation

@huextrat

Copy link
Copy Markdown
Contributor

Description

Fixes #4432

On Android, a Pressable fires onPress when the touch was only meant to stop a fling: put a finger down on a list row while the list decelerates, lift it without moving, and the row under the finger gets pressed.

Pressable without relation props renders PressableWithTouchable, which presses natively through ButtonViewGroup. The NativeViewGestureHandler the button manages for itself is attached with ACTION_TYPE_NONE, so it isn't cancelled when a native view takes the touch lock: RNGestureHandlerRootHelper.requestDisallowInterceptTouchEvent() calls cancelAllLegacyHandlers(), which only cancels the action-driven handlers (JS_FUNCTION_OLD_API, JS_FUNCTION_NEW_API, REANIMATED_WORKLET, NATIVE_ANIMATED_EVENT).

Android's ScrollView intercepts the ACTION_DOWN while its scroller is still running and calls requestDisallowInterceptTouchEvent(true) up the tree, so that path does run — the button handler just isn't part of what it cancels. It then reaches STATE_END on the finger lift and dispatches the press. The framework's own ACTION_CANCEL doesn't reach the button either, since RNGH delivers touches itself and ignores onInterceptTouchEvent.

This cancels NativeViewGestureHandlers attached with ACTION_TYPE_NONE as well. The root view's handler shares that action type but is not a NativeViewGestureHandler, so it keeps running and interception is unaffected.

The v2 Pressable went through GestureDetector, so its handlers carried a JS action type and were cancelled by this very path — LegacyPressable and StatefulPressable (any relation prop) are unaffected today, which is a decent A/B when checking the fix.

I also renamed cancelAllLegacyHandlers to cancelHandlersLosingToNativeGesture, since it no longer cancels only the legacy handlers — happy to drop the rename if you'd rather keep the diff minimal.

Test plan

Repro app: a FlatList (from react-native) whose rows are wrapped in Pressable from react-native-gesture-handler, each row logging its onPress.

  1. Fling the list hard.
  2. While it decelerates, put a finger down on a row to stop the scroll.
  3. Lift the finger without moving it.

Before: onPress fires for the row under the finger.
After: nothing fires, and a deliberate tap on a settled list still presses normally.

…ch lock

`Pressable` (and every component built on the native button) keeps its
`NativeViewGestureHandler` running when a native view grabs the touch lock,
because that handler is attached with `ACTION_TYPE_NONE` and
`cancelAllLegacyHandlers` only cancelled the action-driven ones.

The visible symptom is a phantom press: a finger put down on a list row to
stop a fling makes the enclosing `ScrollView` claim the touch (Android's
`ScrollView` intercepts the `ACTION_DOWN` while the scroller is running and
calls `requestDisallowInterceptTouchEvent`), yet the button handler survives,
ends on the finger lift and dispatches `onPress` for a row the user never
meant to tap.

Cancel `NativeViewGestureHandler`s attached with `ACTION_TYPE_NONE` too. The
root view's own handler shares that action type but is not a
`NativeViewGestureHandler`, so it keeps running and interception is
unaffected. Renamed the method since it no longer cancels only the legacy
handlers.
Copilot AI lite review requested due to automatic review settings August 17, 2026 06:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR refines Android gesture-handler cancellation when a native view takes the touch lock by replacing “cancel all legacy handlers” behavior with a more targeted cancellation API and logic.

Changes:

  • Renamed the orchestrator cancellation method and updated the root helper to call the new method.
  • Expanded cancellation logic to also cancel NativeViewGestureHandler instances with ACTION_TYPE_NONE (e.g., button-managed handlers) when they would otherwise continue running.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt Updates root helper to call the renamed/more specific orchestrator cancellation method.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt Renames and documents cancellation method; adjusts predicate to also cancel certain NativeViewGestureHandlers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

* the button handler and fires a press. The root view's own handler shares that action type and
* must keep running, hence the type check rather than a plain [GestureHandler.ACTION_TYPE_NONE]
* one.
*/
Comment on lines +375 to +377
* the button handler and fires a press. The root view's own handler shares that action type and
* must keep running, hence the type check rather than a plain [GestureHandler.ACTION_TYPE_NONE]
* one.
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 154bcbb5-6da4-433e-b2de-893d1fd47dfc

📥 Commits

Reviewing files that changed from the base of the PR and between 3e4497e and cf73d92.

📒 Files selected for processing (2)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt

Included review availability: Your plan includes up to 8 reviews per rolling hour; 7 remain after this review.


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved gesture handling when native gestures take priority.
    • Prevented unrelated gesture handlers from being cancelled unnecessarily.
    • Preserved the root view’s gesture handler while cancelling competing handlers.
    • Ensured cancelled handlers are still cleaned up as expected.

Walkthrough

Changes

Native gesture cancellation

Layer / File(s) Summary
Orchestrator cancellation scope
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt
Renames the cancellation method and adds NativeViewGestureHandler instances with ACTION_TYPE_NONE to the cancellation scope.
Root helper integration
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt
Uses the renamed method when a native gesture requests disallowed interception.

Possibly related PRs

Suggested reviewers: j-piasecki, m-bert

Merge Risk: ⚪ Minimal · up to cf73d

This localized Android change prevents unintended presses when a native view takes the touch lock; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the Android fix: cancel button handlers when a native view takes the touch lock.
Linked Issues check ✅ Passed The changes cancel ACTION_TYPE_NONE NativeViewGestureHandler instances while preserving the root handler, addressing issue #4432 without changing normal tap behavior.
Out of Scope Changes check ✅ Passed All changes are limited to the gesture cancellation logic required to resolve issue #4432.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Pressable fires onPress when the touch only stops a fling

2 participants