Skip to content

[Android] Keep detector gestures active through a transient window detach - #4609

Closed
roidayan1 wants to merge 1 commit into
software-mansion:mainfrom
roidayan1:roid/detector-transient-detach
Closed

roidayan1 wants to merge 1 commit into
software-mansion:mainfrom
roidayan1:roid/detector-transient-detach

Conversation

@roidayan1

Copy link
Copy Markdown

Description

Fixes #4608

On Android, a gesture in a GestureDetector is cancelled when a re-render moves the detector's view while the gesture is active, e.g. a drag-to-reorder list moving the held row. Fabric moves a view by removing it and adding it back within one mount, so the detector is detached from the window only for that moment, but onDetachedFromWindow detaches its handlers right away, and the registry cancels a handler whose view is set.

This PR defers that detach to a main-looper message posted from onDetachedFromWindow. The message detaches the handlers only if the view is still off the window when it runs. onAttachedToWindow removes it, so a view moved within one mount keeps its handlers, and an active gesture carries on. A view that stays detached (hidden by Activity, or clipped) loses its handlers one message later than before. A view that's dropped is still detached at once by onDropViewInstance, and detachAllHandlers() now also removes a pending message, so a recycled view never loses its new handlers to it.

The change is Android only. iOS's detector also detaches in willMoveToWindow: with a nil window; we haven't checked whether a move cancels a gesture there.

It adds a "Moved detector" screen under Tests in the example app: grabbing a row moves it to the top while its pan is active.

Test plan

On the Android emulator (Pixel, Android 16, API 36), Release builds of expo-example, each screen driven by Maestro, before this change and after it:

Check Before After
"Moved detector": drag row C up; it moves to the top as its pan activates onDeactivate, canceled: true 35 ms after onActivate; "cancelled" canceled: false on release, 1.5 s later; "ended"
The issue's repro: a FlatList row dragged sideways, moved to the top "cancelled" "ended"
"Reattaching" not rerun taps alternate between the boxes, as before
#3965: a hook tap inside <Activity>, hidden and shown again, then tapped taps taps

We also ran it in the app where we found it, a drag-to-reorder board, on the same build with and without the patch: its drag and section-drag flows failed without it and pass with it.

onAttachedToWindow calls attachHandlers(handlersToAttach) again for a detector whose handlers were never detached. That's a no-op for them: attachHandlers subscribes only tags it isn't subscribed to yet and detaches only tags no longer listed, so registry.attachHandlerToView, and the cancel in detachHandlerInternal, never run for an attached handler. To check it on the device, we logged each attached handler's state (tag:state, 4 = ACTIVE, 5 = END) around the window hooks during a drag, in a temporary build:

13:24:08.207  detached 624: 3:4
13:24:08.208  attached 624: before 3:4
13:24:08.208  attached 624: after 3:4
13:24:08.608  detached 624: 3:4
13:24:08.609  attached 624: before 3:4
13:24:08.609  attached 624: after 3:4
13:24:09.291  detached 624: 3:4
13:24:09.291  attached 624: before 3:4
13:24:09.291  attached 624: after 3:4
13:24:09.706  detached 624: 3:5

The pan stays ACTIVE through three moves of its detector, and the deferred detach never runs for it; it ends as END on release. A detector that stays off the window is still detached once the mount is done (40 ms later in the same log).

The Kotlin compiles in basic-example, spotlessCheck passes, and ts-check, ESLint and Prettier pass for the new screen.

🤖 Generated with Claude Code

…tach

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 18, 2026 10:44
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5e11bfdd-a944-42e5-94a4-177ed09ff33d
📥 Commits

Reviewing files that changed from the base of the PR and between 0c953f5 and 17de59d.

📒 Files selected for processing: 3
  • apps/common-app/src/new_api/index.tsx
  • apps/common-app/src/new_api/tests/movedDetector/index.tsx
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

📝 Summary

Summary by CodeRabbit

  • New Features
    • Added a “Moved detector” example where dragging a row moves it to the top, with feedback when the gesture ends or is canceled.
  • Bug Fixes
    • Improved gesture handling when a view is quickly removed and reattached, helping active gestures continue working correctly.

The Android detector now defers handler detachment after a view leaves the window. The common app adds a moved-row example that demonstrates gesture activation, row reordering, and release status.

Changes: Moved detector handling

Layer / File(s) Summary
Defer handler detachment
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt
Window detachment schedules handler cleanup on the main thread. Reattachment cancels the pending cleanup, and cleanup also clears any pending callback.
Add moved-row example
apps/common-app/src/new_api/tests/movedDetector/index.tsx, apps/common-app/src/new_api/index.tsx
The example moves the grabbed row to the top, logs gesture activation and deactivation, and displays the release status. The Tests section registers the example as “Moved detector.”

Suggested reviewers: m-bert

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 17de5

This Android change keeps an active gesture running through a transient window detach during a Fabric move. No concrete merge-blocking risk was identified in the supplied changes.

Pre-merge checks: Passed 3
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main Android change: preserving detector gestures during a transient window detach.
Linked Issues check Passed For [#4608], the Android detector now posts a deferred detach and runs it only when the view remains detached. onAttachedToWindow removes the pending callback, and detachAllHandlers() removes it b…
Out of Scope Changes check Passed The changes stay within [#4608]. The Kotlin change implements the Android lifecycle fix. The example-app screen and its registration provide a focused reproduction and verification screen for the repo…
Full details: Linked Issues check

Explanation

For [#4608], the Android detector now posts a deferred detach and runs it only when the view remains detached. onAttachedToWindow removes the pending callback, and detachAllHandlers() removes it before immediate cleanup. This preserves handlers during a transient Fabric remove/add and detaches handlers for a view that remains detached. The added “Moved detector” screen exercises row movement during an active pan and reports ended versus cancelled. The PR description reports the issue reproduction changed to normal release in Android testing.

Full details: Out of Scope Changes check

Explanation

The changes stay within [#4608]. The Kotlin change implements the Android lifecycle fix. The example-app screen and its registration provide a focused reproduction and verification screen for the reported gesture behavior. No unrelated platform or feature changes are present.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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.

🟢 Approval recommended

The focused lifecycle change preserves explicit cleanup, and no blocking issues remain.

No open issues 🎉

What changed in this PR

Fixes #4608 by preserving active Android detector gestures when Fabric briefly detaches and reattaches a view during a move.

Changes:

  • Defers handler cleanup until the next main-looper message.
  • Cancels pending cleanup on reattachment or explicit disposal.
  • Adds a v3 drag-to-reorder reproduction screen.
File Description
packages/​react-native-gesture-handler/​android/​src/​main/​java/​com/​swmansion/​gesturehandler/​react/​RNGestureHandlerDetectorView.kt Defers window-detach cleanup and cancels pending callbacks.
apps/​common-app/​src/​new_api/​tests/​movedDetector/​index.tsx Adds a screen demonstrating gesture continuity during row moves.
apps/​common-app/​src/​new_api/​index.tsx Registers the “Moved detector” test screen.

Review effort: Balanced

@roidayan1

Copy link
Copy Markdown
Author

Closing: as explained in #4608, this is expected in Gesture Handler 3, and VirtualGestureDetector is the way for rows that move.

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] An active gesture is cancelled when Fabric moves its GestureDetector's view

2 participants