[Android] Fixed PointerGestureRecognizer not triggering PointerMoved event#33889
Conversation
|
Hey there @@KarthikRajaKalaimani! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where PointerGestureRecognizer.PointerMoved events were not firing on Android. The root cause was that the TapAndPanGestureDetector was filtering out MotionEventActions.Move events and not routing them to the PointerGestureHandler.
Changes:
- Added
MotionEventActions.Moveto the pattern match inTapAndPanGestureDetector.OnTouchEvent()to route move events to the pointer gesture handler - Created comprehensive UI test (Issue33690) with both HostApp page and NUnit test to verify all three pointer events fire correctly
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Controls/src/Core/Platform/Android/TapAndPanGestureDetector.cs |
Added MotionEventActions.Move to the event filter so move events are routed to PointerGestureHandler.OnTouch() |
src/Controls/tests/TestCases.HostApp/Issues/Issue33690.cs |
HostApp test page with Image and PointerGestureRecognizer that counts Pressed/Moved/Released events |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33690.cs |
NUnit UI test that verifies drag interaction triggers all three pointer events |
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Included test cases ·
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #33889 | Add MotionEventActions.Move to pattern match in TapAndPanGestureDetector.OnTouchEvent() to route move events to pointer gesture handler |
✅ PASS (Gate) | TapAndPanGestureDetector.cs (+1 -1) |
Original PR - 1 line change, verified by Gate |
| 6 | try-fix #6 | Process pointer events BEFORE base.OnTouchEvent() to prevent base detector from consuming Move events. Architectural change to event flow. | ✅ PASS | TapAndPanGestureDetector.cs (+4 -3) |
Alternative fix: Reorders execution to handle pointer gestures first, then base detector. More robust as pointer events get priority. |
| 7 | try-fix #7 | Handle Move events at GesturePlatformManager level - route Move events directly to PointerGestureHandler.OnTouch() in OnTouchEvent() BEFORE passing to TapAndPanGestureDetector. | ✅ PASS | GesturePlatformManager.Android.cs (+10 -1) |
Different layer fix: Handles at manager level instead of detector level. Better separation of concerns - gesture detection vs pointer tracking. |
| 8 | try-fix #8 | Synthesize PointerMoved from PointerDown/PointerUp/ButtonPress/ButtonRelease during active press in PointerGestureHandler.OnTouch() |
❌ FAIL | PointerGestureHandler.cs (+14) |
Does not help for single-finger drag; those actions aren’t emitted during drag so moved still 0. |
| 9 | try-fix #9 | Centralize pointer dispatch in GesturePlatformManager (route Down/Move/Up/Cancel at manager, remove from detector) | ✅ PASS | TapAndPanGestureDetector.cs, GesturePlatformManager.Android.cs |
Pointer events routed at manager entry; Move delivered reliably |
| 10 | try-fix #10 | Unfiltered Pointer Dispatch | ✅ PASS | TapAndPanGestureDetector.cs |
Removed explicit action whitelist in detector; delegates filtering to PointerGestureHandler. Safe and future-proof. |
| 11 | try-fix #11 | Direct Touch Event Subscription - Subscribe PointerGestureHandler to View.Touch event, bypassing GestureDetector pipeline entirely | ✅ PASS | PointerGestureHandler.cs (+18 -3), TapAndPanGestureDetector.cs (+1 -4) |
NEW IDEA from cross-pollination: Uses Touch event subscription instead of GestureDetector routing. Most architecturally sound - fixes root cause (GesturePlatformManager doesn't subscribe when only PointerGestures present). Clean, minimal, follows .NET event patterns. |
| 12 | try-fix #12 | Active touch tracking at detector level: always forward Down and (while active) Move, plus Up/Cancel to PointerGestureHandler even if base detector consumes event | ✅ PASS | TapAndPanGestureDetector.cs |
Forwards Move reliably during active press even when GestureDetector consumes events. Simple bool tracking; no pointerId. |
Exhausted: Yes (3 rounds of cross-pollination completed, 3/5 models say NO NEW IDEAS)
Cross-Pollination Results:
| Model | Round 2 Response | Round 3 Response |
|---|---|---|
| claude-sonnet-4.5 | NEW IDEA: Separate touch event path (implemented as #11) | NO NEW IDEAS |
| claude-opus-4.5 | NO NEW IDEAS | NO NEW IDEAS |
| gpt-5.2 | NEW IDEA: Pointer capture/tracking (implemented as #12) | NEW IDEA: Per-pointer state machine (variation of #12) |
| gpt-5.2-codex | NO NEW IDEAS | NO NEW IDEAS |
| gemini-3-pro-preview | NEW IDEA: Debouncing (doesn't solve bug) | NEW IDEA: InputTransparent (doesn't solve bug) |
Selected Fix: PR's fix (Add MotionEventActions.Move to pattern match)
Reasoning:
While 6 different passing fixes were found through try-fix exploration, the PR's original fix is the best choice:
- Simplest solution - Literally 1 character change: adding
Moveto an existing pattern match - Minimal diff - Only touches 1 file, changes 1 line (+1/-1)
- Lowest complexity - No architectural changes, no new state tracking, no event subscription management
- Matches codebase patterns - Consistent with existing Down/Up/Cancel pattern in same method
- Safest for backport - Trivial change minimizes risk for stable branches
- No performance impact - All alternatives add extra event handling, dispatching, or state tracking
Alternative fixes analysis:
- [Spec] Transitions #6 (reorder execution): More complex (+4/-3), changes execution flow semantics
- [Spec] TabView #7, [Spec] Shadows #9 (manager level): Higher layer change, affects more gesture types
- [Spec] Rounded Corners #10 (unfiltered): Passes ALL events to handler (more work), less explicit about intent
- [Spec] Brushes #11 (direct subscription): Most architectural change, bypasses established patterns (though it revealed the GesturePlatformManager optimization issue)
- [Spec] Border #12 (active tracking): Adds state tracking overhead, more complex logic
Root Cause:
The TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match filtering out MotionEventActions.Move events, preventing them from being routed to PointerGestureHandler.OnTouch(). The pattern only included Up, Down, and Cancel.
Key Learning from try-fix #11:
Attempt #11 discovered that GesturePlatformManager (lines 200-216) has optimization logic that intentionally does NOT subscribe to touch events when ONLY PointerGestureRecognizers are present. This is a deeper architectural issue that explains WHY the missing Move in TapAndPanGestureDetector caused such complete failure - but fixing this optimization would be a much larger change suitable for a separate PR.
Selected Fix: PR's fix
📋 PR Finalization ReviewTitle: ✅ GoodCurrent: Description: ✅ ExcellentDescription needs updates. See details below. **
📋 Recommended Changes✨ Suggested PR Descriptionnote in for people that find this PR -->
|
|
/azp run maui-pr-uitests,maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…event (#33889) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details: The pointer moved event is not triggered of the PointerGestureRecognizer on Android platform. ### Root Cause: The PointerGestureRecognizer.PointerMoved event was not firing on Android because the TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match that was filtering out Move events. The gesture detector was only forwarding touch events to the pointer gesture handler for these actions: - MotionEventActions.Up (finger released) - MotionEventActions.Down (finger pressed) - MotionEventActions.Cancel (gesture cancelled) Missing Elements: MotionEventActions.Move (finger moved while pressed) ### Description of Change: Added MotionEventActions.Move to the pattern, the gesture detector now: - Captures move events from Android's touch system - Routes them to _pointerGestureHandler.OnTouch() - The handler processes them and fires the PointerMoved event. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference: N/A ### Issues Fixed: Fixes #33690 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/cb3a1f4e-b3fc-4815-bc99-fecd4ddcb158" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/fcea1f0c-ca82-4863-a48b-e89cd185e0bc" Width="300" Height="600"> |
…event (#33889) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details: The pointer moved event is not triggered of the PointerGestureRecognizer on Android platform. ### Root Cause: The PointerGestureRecognizer.PointerMoved event was not firing on Android because the TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match that was filtering out Move events. The gesture detector was only forwarding touch events to the pointer gesture handler for these actions: - MotionEventActions.Up (finger released) - MotionEventActions.Down (finger pressed) - MotionEventActions.Cancel (gesture cancelled) Missing Elements: MotionEventActions.Move (finger moved while pressed) ### Description of Change: Added MotionEventActions.Move to the pattern, the gesture detector now: - Captures move events from Android's touch system - Routes them to _pointerGestureHandler.OnTouch() - The handler processes them and fires the PointerMoved event. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference: N/A ### Issues Fixed: Fixes #33690 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/cb3a1f4e-b3fc-4815-bc99-fecd4ddcb158" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/fcea1f0c-ca82-4863-a48b-e89cd185e0bc" Width="300" Height="600"> |
…event (#33889) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details: The pointer moved event is not triggered of the PointerGestureRecognizer on Android platform. ### Root Cause: The PointerGestureRecognizer.PointerMoved event was not firing on Android because the TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match that was filtering out Move events. The gesture detector was only forwarding touch events to the pointer gesture handler for these actions: - MotionEventActions.Up (finger released) - MotionEventActions.Down (finger pressed) - MotionEventActions.Cancel (gesture cancelled) Missing Elements: MotionEventActions.Move (finger moved while pressed) ### Description of Change: Added MotionEventActions.Move to the pattern, the gesture detector now: - Captures move events from Android's touch system - Routes them to _pointerGestureHandler.OnTouch() - The handler processes them and fires the PointerMoved event. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference: N/A ### Issues Fixed: Fixes #33690 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/cb3a1f4e-b3fc-4815-bc99-fecd4ddcb158" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/fcea1f0c-ca82-4863-a48b-e89cd185e0bc" Width="300" Height="600"> |
…event (#33889) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details: The pointer moved event is not triggered of the PointerGestureRecognizer on Android platform. ### Root Cause: The PointerGestureRecognizer.PointerMoved event was not firing on Android because the TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match that was filtering out Move events. The gesture detector was only forwarding touch events to the pointer gesture handler for these actions: - MotionEventActions.Up (finger released) - MotionEventActions.Down (finger pressed) - MotionEventActions.Cancel (gesture cancelled) Missing Elements: MotionEventActions.Move (finger moved while pressed) ### Description of Change: Added MotionEventActions.Move to the pattern, the gesture detector now: - Captures move events from Android's touch system - Routes them to _pointerGestureHandler.OnTouch() - The handler processes them and fires the PointerMoved event. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference: N/A ### Issues Fixed: Fixes #33690 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/cb3a1f4e-b3fc-4815-bc99-fecd4ddcb158" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/fcea1f0c-ca82-4863-a48b-e89cd185e0bc" Width="300" Height="600"> |
…erMoved event (dotnet#33889)" This reverts commit 4aa6b81.
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Issue Details:
The pointer moved event is not triggered of the PointerGestureRecognizer on Android platform.
Root Cause:
The PointerGestureRecognizer.PointerMoved event was not firing on Android because the TapAndPanGestureDetector.OnTouchEvent() method had an incomplete pattern match that was filtering out Move events.
The gesture detector was only forwarding touch events to the pointer gesture handler for these actions:
- MotionEventActions.Up (finger released)
- MotionEventActions.Down (finger pressed)
- MotionEventActions.Cancel (gesture cancelled)
Missing Elements: MotionEventActions.Move (finger moved while pressed)
Description of Change:
Added MotionEventActions.Move to the pattern, the gesture detector now:
- Captures move events from Android's touch system
- Routes them to _pointerGestureHandler.OnTouch()
- The handler processes them and fires the PointerMoved event.
Tested the behavior in the following platforms:
Reference:
N/A
Issues Fixed:
Fixes #33690
Screenshots
Screen.Recording.2026-02-04.at.2.23.57.PM.mov
Screen.Recording.2026-02-04.at.2.20.07.PM.mov