Skip to content

fix(android): cancel the JS touch when the pager starts dragging - #1183

Merged
krozniata merged 1 commit into
callstack:mainfrom
Luccacvb:fix/android-cancel-touch-on-drag
Oct 15, 2026
Merged

krozniata merged 1 commit into
callstack:mainfrom
Luccacvb:fix/android-cancel-touch-on-drag

Conversation

@Luccacvb

@Luccacvb Luccacvb commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes the Android case of #424, which reports this on Android and on iOS. Related: #960, #954.

In 9.x, on Android, a swipe that starts on a Pressable inside a page never sends touchcancel to JS. Every such swipe ends with touchend, so the Pressable can fire onPress when the dragging event reaches JS late, for example while the JS thread is busy. This is #960 coming back in 9.x.

reproduction

pager-view-before-after.mp4

What happened

  • 8.x called NativeGestureUtil.notifyNativeGestureStarted from NestedScrollableHost.onInterceptTouchEvent once the touch passed the slop (fix(android): prevent taps while scrolling #961), so React Native cancelled the JS touch, as it does for ScrollView.
  • 9.0.0 (feat(android): jetpack compose support #1092) removed NestedScrollableHost.kt, and the call was not ported to ComposePagerView. Only the JS guard is left, and it depends on when the dragging event arrives.

Fix

  • ComposePagerView notifies React Native from onInterceptTouchEvent when the touch moves touchSlop along the pager axis, where the Compose pager starts dragging, and ends the native gesture on ACTION_UP, like ReactScrollView.
  • Mouse drags are skipped, since the Compose pager does not drag with a mouse. Nothing changes when scrollEnabled is false.
  • Once the pager starts dragging from a touch on page content, JS gets touchcancel instead of touchend, as in 8.x.
  • This touches the same dispatchTouchEvent block as fix(android): let nested horizontal ScrollView scroll inside pager #1182, so whichever lands second needs a small rebase. I can do it here.

Test Plan

What's required for testing (prerequisites)?

Android emulator or device.

What are the steps to reproduce (after prerequisites)?

Added Issue #424 Swipe Cancels Press Repro to the example app and e2e/tests/issues/issue-424-swipe-cancels-press.e2e.ts:

  1. Tap the button on page 0: it is pressed (control).
  2. Swipe to page 1 starting on the button. Before: Touch: end. After: Touch: cancel.
  3. Tap Block JS, then do the same swipe. Before: Presses: 1, the swipe pressed the button. After: Presses: 0, Touch: cancel.

Verified

  • format:check, lint (no new warnings), typecheck, prepare, test, example typescript and e2e typecheck pass.
  • On main, 2 of the 3 new tests fail (the tap control passes). With the fix all 3 pass. Same result on every run. Android 16 emulator (arm64), release build.
  • Full Android e2e suite with the fix: 17/17 tests pass.
  • By hand on a Redmi Note 5 (Android 9) with the fix: a tap presses the button, a swipe from it gives Touch: cancel, and a swipe from it while JS is blocked leaves Presses: 0.
  • Scratch screen with a PanResponder in a page: a horizontal drag on it gives the same callbacks on main and with the fix (grant, then terminate, and the page changes).

iOS is untouched.

Compatibility

OS Implemented
iOS n/a
Android ✅

Checklist

  • I have tested this on a device and a simulator
  • I added the documentation in README.md: not applicable
  • I updated the typed files (TS and Flow): not applicable

@Luccacvb
Luccacvb force-pushed the fix/android-cancel-touch-on-drag branch from 3092d90 to 7c94a5b Compare October 14, 2026 21:31
krozniata
krozniata previously approved these changes Oct 15, 2026
troZee
troZee previously approved these changes Oct 15, 2026
@Luccacvb
Luccacvb dismissed stale reviews from troZee and krozniata via 41eddcc October 15, 2026 11:33
@Luccacvb
Luccacvb force-pushed the fix/android-cancel-touch-on-drag branch from 7c94a5b to 41eddcc Compare October 15, 2026 11:33
@Luccacvb

Copy link
Copy Markdown
Contributor Author

Resolved the conflicts with #1182. The only change is that the slop check now uses rawX/rawY, following #1182

@Luccacvb
Luccacvb requested review from krozniata and troZee October 15, 2026 11:36
@krozniata
krozniata merged commit ea1df3b into callstack:main Oct 15, 2026
8 of 10 checks passed
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.

3 participants