Skip to content

[Android] Fix CollectionView selection not triggering when PointerGestureRecognizer is used in ItemTemplate#34627

Open
jpd21122012 wants to merge 2 commits intodotnet:mainfrom
jpd21122012:fix/34491-Android-CollectionView-ItemSelectionNotTriggeredWhenPointerGestureRecognizer
Open

[Android] Fix CollectionView selection not triggering when PointerGestureRecognizer is used in ItemTemplate#34627
jpd21122012 wants to merge 2 commits intodotnet:mainfrom
jpd21122012:fix/34491-Android-CollectionView-ItemSelectionNotTriggeredWhenPointerGestureRecognizer

Conversation

@jpd21122012
Copy link
Copy Markdown
Contributor

Description

Fixes #34491

On Android, when a PointerGestureRecognizer is added inside a CollectionView ItemTemplate, tapping an item does not trigger SelectionChanged.

This happens because touch events were being consumed by the gesture pipeline even when only pointer gestures were present, preventing the CollectionView from receiving the tap event.

Root Cause

GesturePlatformManager was consuming touch events regardless of gesture type.
Since PointerGestureRecognizer is intended for hover/move scenarios and not for tap interaction, it should not block touch propagation.

Fix

  • Updated GesturePlatformManager.OnTouchEvent
  • Detect when only PointerGestureRecognizer instances are present
  • Avoid consuming touch events in that case

This allows touch events to propagate correctly to parent controls like CollectionView.

Result

  • PointerGestureRecognizer continues to work as expected (hover events)
  • CollectionView selection works correctly
  • Both behaviors now coexist without conflict

Test

Added UITest (Issue34491) that:

  • Uses a CollectionView with PointerGestureRecognizer inside the ItemTemplate
  • Taps an item
  • Verifies that SelectionChanged is triggered via UI update

The test fails before this fix and passes after.

Affected Platforms

  • Android

Notes

This change is scoped to pointer-only gesture scenarios and does not affect existing gesture handling behavior for tap, pan, or swipe gestures.

…tureRecognizer

Prevent touch events from being consumed when only PointerGestureRecognizer instances are present in a view.

Previously, GesturePlatformManager would consume touch input even in pointer-only scenarios, blocking CollectionView from receiving tap events and triggering SelectionChanged.

This change ensures that touch events are not consumed when only pointer gestures are attached, allowing proper event propagation while preserving pointer (hover) behavior.

Also adds UITest (Issue34491) to validate that CollectionView selection works correctly when PointerGestureRecognizer is used inside ItemTemplate.
Copilot AI review requested due to automatic review settings March 24, 2026 21:25
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 24, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34627

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34627"

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@jpd21122012! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an Android-specific interaction regression where CollectionView item selection doesn’t fire when a PointerGestureRecognizer exists inside an ItemTemplate, by preventing pointer-only gesture handling from consuming touch events that should propagate to parent controls.

Changes:

  • Updated Android GesturePlatformManager.OnTouchEvent to avoid consuming touch events when the view has only PointerGestureRecognizer instances.
  • Added a HostApp reproduction page (Issue34491) using a CollectionView item template with a PointerGestureRecognizer.
  • Added an Android UITest (Issue34491) validating selection still occurs in that scenario.

Reviewed changes

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

File Description
src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs Adjusts Android gesture consumption logic to let CollectionView selection taps propagate when only pointer gestures are present.
src/Controls/tests/TestCases.HostApp/Issues/Issue34491.cs Adds a minimal in-app repro using CollectionView + PointerGestureRecognizer in ItemTemplate.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34491.cs Adds an Android UI test asserting SelectionChanged updates UI state when tapping an item.

…ynchronization

Replace polling with Thread.Sleep by a retry loop synchronized with WaitForElement,
ensuring the test remains deterministic and aligned with UITest patterns.
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 26, 2026

🤖 AI Summary

📊 Expand Full Review5cf444b · [test] Remove Thread.Sleep from Issue34491 and use deterministic UI synchronization
🔍 Pre-Flight — Context & Validation

Issue: #34491 - [Android] CollectionView item selection not triggered when using PointerGestureRecognizer
PR: #34627 - [Android] Fix CollectionView selection not triggering when PointerGestureRecognizer is used in ItemTemplate
Platforms Affected: Android
Files Changed: 1 implementation, 2 test

Key Findings

  • Regression introduced in 10.0.50 — Verified working in 10.0.41, broken in 10.0.50.
  • Root Cause: GesturePlatformManager.OnTouchEvent was consuming touch events even when only PointerGestureRecognizer instances were present. Since PointerGestureRecognizer handles hover/pointer events (not taps), it was incorrectly blocking tap events from propagating to the CollectionView, preventing SelectionChanged from firing.
  • Fix approach: Added ViewHasNonPointerTouchGestures() helper method. If only pointer gestures exist, do not mark the event as consumed, allowing touch propagation to the parent CollectionView.
  • Copilot review feedback already addressed:
    • ViewHasPinchGestures() evaluation caching was suggested and implemented.
    • Thread.Sleep polling loop in test was replaced with WaitForElement-based retry loop.
  • Test type: UI test (Android-only, guarded by #if ANDROID).
  • Test covers: Tapping a CollectionView item that has a PointerGestureRecognizer in its ItemTemplate, verifying SelectionChanged fires.
  • Minor test issue: The retry loop (for i < 5) calls App.WaitForElement("StatusLabel") for synchronization but doesn't actually wait for text change. However, this may still work in practice since the label update is synchronous. The loop still polls FindElement for the updated text.
  • No prior agent reviews detected.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34627 In OnTouchEvent, detect pointer-only gestures via ViewHasNonPointerTouchGestures(). If only pointer gestures exist, don't consume the touch event, allowing propagation to CollectionView. ⏳ PENDING (Gate) GesturePlatformManager.Android.cs Community contribution; Copilot suggestions already incorporated

🚦 Gate — Test Verification

Gate Result: ✅ PASSED

Platform: Android

⚠️ Note: The verify-tests-fail script reported a false negative for the WITH-fix run. Investigation confirmed the test actually PASSED. See "Verification Discrepancy" section below.

Tests Detected

# Type Test Name Filter
1 UITest Issue34491 Issue34491

Verification

Step Expected Actual Result
Tests WITHOUT fix FAIL FAIL
Tests WITH fix PASS PASS

Fix Files Reverted

  • src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs

Verification Discrepancy

The automated verify-tests-fail.ps1 script reported ❌ Issue34491 FAILED with fix (unexpected!), but this is a false negative due to a race condition in how the script reads CustomAgentLogsTmp/UITests/test-output.log.

Evidence the test PASSED with fix:

  1. CustomAgentLogsTmp/UITests/test-output.log (created at 00:02:40) shows:
    Passed CollectionViewSelectionWorksWithPointerGestureRecognizer [4 s]
    Test Run Successful.
    Total tests: 1
         Passed: 1
    
  2. File birth time: 2026-03-26 00:02:40 — created during the WITH-fix test run (app ran at 00:02:29–00:02:36).
  3. The verification log timestamped 00:02:43 detected failure — 3 seconds AFTER the test-output.log was written, but the script's Invoke-TestRun pipeline appears to have returned the path before the file was fully flushed/written.

Root cause of false negative: BuildAndRunHostApp.ps1 writes test results to test-output.log as a side-effect, and Invoke-TestRun reads the path back immediately after the Tee-Object pipeline finishes. The file may not exist yet at that moment, causing Get-TestResultFromOutput to return Passed=False (file not found fallback).

Conclusion: Tests FAIL without fix ✅ and PASS with fix ✅. Gate is PASSED.


Per-Test Results (Actual)

Type Test Without Fix With Fix
UITest Issue34491 FAIL ✅ PASS ✅

Base Branch: main | Merge Base: a38921c


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34627 In OnTouchEvent, add ViewHasNonPointerTouchGestures(). If only pointer gestures present, don't consume touch event. ✅ PASSED (Gate) 1 Try-Fix skipped (user instruction)

Cross-Pollination

Skipped — Try-Fix phase was explicitly skipped per user instructions.

Exhausted: N/A
Selected Fix: PR's fix — Gate passed, Try-Fix skipped.


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #34491, Android regression in 10.0.50
Gate ✅ PASSED Android — FAIL without fix, PASS with fix
Try-Fix ⏭️ SKIPPED Skipped per user instruction
Report ✅ COMPLETE

Summary

PR #34627 fixes a regression introduced in .NET MAUI 10.0.50 where tapping a CollectionView item fails to trigger SelectionChanged when a PointerGestureRecognizer is present in the ItemTemplate. The fix is scoped to Android, correctly targeted, and the test validates the bug is caught without fix and resolved with fix.


Root Cause

In GesturePlatformManager.OnTouchEvent, the gesture pipeline was consuming touch events regardless of which gesture types were registered. PointerGestureRecognizer is designed for hover/pointer-move scenarios and should NOT block tap propagation. When only PointerGestureRecognizer instances were present, the TapAndPanAndSwipeDetector was still marking events as consumed, preventing the CollectionView from receiving them and triggering SelectionChanged.


Fix Quality

Approach: Added ViewHasNonPointerTouchGestures() helper that scans the view's gesture recognizers. If no non-pointer gesture recognizers are found (i.e., only PointerGestureRecognizer instances are present), OnTouchEvent returns false (not consumed), allowing touch events to propagate to the CollectionView.

Strengths:

  • Surgical: Only affects the pointer-only gesture scenario; all mixed-gesture combinations maintain existing behavior
  • Correct edge cases: PointerGestureRecognizer + TapGestureRecognizerhasOnlyPointer = false → original behavior preserved
  • Copilot suggestions incorporated: ViewHasPinchGestures() is now cached in a local variable (avoids repeated collection scans per touch event)
  • Null safety: _tapAndPanAndSwipeDetector?.Value.OnTouchEvent(e) ?? false adds defensive null check (minor improvement over original)
  • Gate passed: Test fails without fix and passes with fix on Android

Minor concerns:

  1. Test retry loop is weak: The for (i < 5) loop calls App.WaitForElement("StatusLabel") which waits for element existence, not text change. In practice this works (selection is synchronous), but it could tight-loop without any delay if StatusLabel is always visible. The Copilot reviewer noted this; the PR author's response is reasonable given WaitForTextToBePresentInElement isn't available in this test setup.

  2. Missing newline at end of file: src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34491.cs is missing a trailing newline (\No newline at end of file in the diff). This is a minor style issue.

  3. Zero-gesture edge case: When a view has NO gesture recognizers, hasOnlyPointer = true which causes the tap/pan/swipe detector result to be ignored. Functionally this is the same as before (the detector returns false with no recognizers), but the logic flow is different. Not a bug, but worth noting.

  4. #if ANDROID guard on the test: Correct since this is Android-only, but the issue description in [Issue()] attribute is PlatformAffected.Android. Consistent.


Test Quality

  • ✅ Two-file UI test structure (HostApp + TestCases.Shared.Tests)
  • [Category(UITestCategories.CollectionView)] — appropriate
  • ✅ Uses AutomationId for all interactive elements
  • ✅ Android-guarded via #if ANDROID
  • ⚠️ Retry loop could be tightened (see concern [Draft] Readme WIP #1 above), but functionally works
  • ⚠️ Missing trailing newline in test file

Selected Fix: PR's fix — Gate confirmed working.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] CollectionView item selection not triggered when using PointerGestureRecognizer

3 participants