Skip to content

[Android] Fix DragGestureRecognizer DragStarting Command/Event fired prematurely - #35778

Merged
kubaflo merged 7 commits into
dotnet:inflight/currentfrom
HarishwaranVijayakumar:fix-35752
Jun 8, 2026
Merged

[Android] Fix DragGestureRecognizer DragStarting Command/Event fired prematurely#35778
kubaflo merged 7 commits into
dotnet:inflight/currentfrom
HarishwaranVijayakumar:fix-35752

Conversation

@HarishwaranVijayakumar

Copy link
Copy Markdown
Contributor

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

  • On Android, a view that has only a DragGestureRecognizer incorrectly raises DragStarting on a quick tap. The drag event fires after the user lifts their finger, even though no long-press or drag motion occurred.

Root Cause of the issue

  • Android's native GestureDetector starts an internal long-press timer (~400ms) on ACTION_DOWN. It relies on receiving ACTION_UP to cancel that timer when the user lifts their finger quickly (tap).

  • Key fact: If a view does NOT consume ACTION_DOWN (returns e.Handled = false), Android stops delivering subsequent touch events (ACTION_MOVE, ACTION_UP) to that view.

Before Regression

e.Handled was always true in OnPlatformViewTouched, so drag-only views always consumed ACTION_DOWN.

  • Tap: ACTION_DOWN consumed → timer starts → finger lifts → ACTION_UP delivered → timer cancelled → no DragStarting
  • Long press: ACTION_DOWN consumed → timer starts → finger stays → timer expires → OnLongPress() → DragStarting fires

After Regression

PR #21547 changed to e.Handled = OnTouchEvent(e.Event), which returns the result of InnerGestureListener.OnDown(). The OnDown() method checks HasAnyGestures() which only considers Pan/Tap/Swipe — not Drag. So for drag-only views, OnDown() returns falsee.Handled = false.

  • Tap: ACTION_DOWN NOT consumed → timer starts → finger lifts → ACTION_UP never delivered → timer never cancelled → timer expires → DragStarting fires
  • Long press: Same as tap — DragStarting fires regardless of touch duration

Description of Change

Gesture Handling Improvements:

  • Updated the HasAnyGestures() method in InnerGestureListener to include a check for drag gestures using the _dragAndDropGestureHandler?.HasAnyDragGestures() condition, ensuring drag gestures are properly distinguished from taps.

Testing and Verification:

  • Added a new test page (Issue35752) in TestCases.HostApp that visually and programmatically demonstrates the issue and its resolution, allowing manual verification that DragStarting only fires on drag, not on tap.
  • Introduced an automated UI test in TestCases.Shared.Tests to assert that DragStarting does not fire on a quick tap but does fire on an actual drag, preventing regressions.

Regression details

The regression was introduced by PR 21547

Issues Fixed

Fixes #35752

Tested the behaviour in the following platforms

  • - Windows
  • - Android
  • - iOS
  • - Mac
Before After
Before-35752.mov
After-35752.mov

PureWeen and others added 6 commits June 2, 2026 16:26
… it) (dotnet#35714)

<!-- 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!

Follow-up to @kubaflo's review on dotnet#35687.

## What

In `eng/pipelines/ci-copilot.yml`, when `parameters.Platform ==
'android'`, the Android AVD was being created twice:

1. **First**, via `common/provision.yml` running the
`ProvisionAndroidSdkAvdCreateAvds` MSBuild target — because
`skipAndroidCreateAvds: ${{ ne(parameters.Platform, 'android') }}`
evaluated to `false` for Android. That target invokes `dotnet android
avd create --name "Emulator_30" … --force`.
2. **Then**, the inline `Create AVD and boot Android Emulator` script
ran `avdmanager create avd -n Emulator_30 -k
"system-images;android-30;google_apis_playstore;x86_64" --device "Nexus
5X" --force`.

Both create the same AVD name with `--force`, so the second silently
overwrites the first — no error, just ~30–60s wasted on every Copilot
review pipeline run for Android.

The inline script is the canonical source of truth: it pins the
`google_apis_playstore` image variant, the `Nexus 5X` device profile,
the `disk.dataPartition.size=2048m` shrink, and ADB key pre-auth. None
of those are applied by `ProvisionAndroidSdkAvdCreateAvds`. So the right
fix is to skip the provision step entirely and let the inline script own
AVD creation.

## Change

Pinned `skipAndroidCreateAvds: true` (with an explanatory comment) at
both call sites of `common/provision.yml` in `ci-copilot.yml` (the
ReviewPR stage and the Deep stage). The inline `avdmanager` blocks are
untouched.

This is the AVD-creation portion of dotnet#35376 being reverted — the inline
script that same PR added already handles AVD creation, so the
provision-step AVD creation was redundant.

## Scope

This change is scoped to **`ci-copilot.yml`** only — the Copilot review
pipeline. It does **not** touch the required gating pipelines:
- `maui-pr`
- `maui-pr-devicetests`
- `maui-pr-uitests`

## Follow-up

Needs to be ported to `net11.0` afterward via the automated
`merge/main-to-net11.0` flow.

Co-authored-by: bot <bot@test>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…authors (dotnet#35751)

<!-- 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!

## Problem

Two issues with the daily "PR Review Queue" automation:

### 1. Workflow runs in forks and creates duplicate tracking issues

`.github/workflows/pr-review-queue.yml` had no fork guard, so the daily
scheduled run (`cron: "0 8 * * 1-5"`) was firing in active forks of
dotnet/maui as well as in upstream. The `gh issue create --repo ${{
github.repository }}` step targets the running repo, so each fork ended
up with its own `[PR Review Queue]` issue alongside the legitimate
dotnet/maui one — e.g. `PureWeen#105` and `dotnet#35732` for
the same day, both pinging the fork owner.

### 2. Every PR author gets @-mentioned daily

The markdown table emitted each PR author handle as a raw `@username`,
which GitHub parses as a real mention. Each daily run created a new
issue and notified every PR author — community contributors, partner
devs, bots — even though the issue is purely an internal MAUI-team
triage artifact.

## Changes

**`.github/workflows/pr-review-queue.yml`** — Add the same
`github.repository_owner == 'dotnet'` guard already used by 8 other
dotnet-owned workflows in this repo (`backport.yml`, `rebase.yml`,
`locker.yml`, `dogfood-comment.yml`, `maestro-changelog.yml`,
`inclusive-heat-sensor.yml`, `dotnet-format-daily.yml`,
`dotnet-autoformat-pr-push.yml`). Applied to both jobs:
  - `generate-report` — stops the scheduled issue creation on forks.
- `validate` — stops fork-internal PRs from spending CI on the dry-run.


**`.github/skills/find-reviewable-pr/scripts/query-reviewable-prs.ps1`**
— Wrap author handles in backticks (`` `@username` ``) in all four
table-row formats. GitHub does not parse mentions inside code spans, so
no notifications fire, but the table still reads naturally as a list of
author handles.

## Verification

- YAML parses cleanly (`python3 -c "import yaml; yaml.safe_load(...)"`).
- PowerShell escape verified — `"``@$($pr.Author)``"` renders as ``
`@PureWeen` ``.
- Existing renderer logic for `$showMilestone` / `$showTurn` variants
left intact; only the author cell changed.

## Out of scope

Other automation in this repo was checked and is already fork-safe:
- The gh-aw `*.lock.yml` files (`agentic-labeler`, `daily-repo-status`,
`copilot-evaluate-tests`) and `agentics-maintenance.yml` are
auto-generated (`DO NOT EDIT`) and have their own pre-activation role
checks.
- `review-trigger.yml` is implicitly fork-safe via its actor-permission
check.

---------

Co-authored-by: bot <bot@test>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tartup crashes (dotnet#35724)

<!-- 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!

## Description

PowerShell 7.4.x intermittently crashes at startup on macOS with:

```
Call to 'procargs' failed with errno 5
Unhandled exception. Microsoft.PowerShell.ManagedPSEntry+StartupException
   at Microsoft.PowerShell.ManagedPSEntry.ThrowOnFailure(String call, Int32 code)
   at Microsoft.PowerShell.ManagedPSEntry.AttemptExecPwshLogin(String[] args)
   at Microsoft.PowerShell.ManagedPSEntry.Main(String[] args)
```

This is
[PowerShell/PowerShell#20802](PowerShell/PowerShell#20802)
— open and untriaged since November 2023, **0 comments, no linked PRs,
no fix shipped**. The crash happens in `AttemptExecPwshLogin`, which on
macOS calls `sysctl(KERN_PROCARGS2)` to inspect its own `argv[0]` and
decide whether pwsh was launched as a login shell. That `sysctl` call
races intermittently and returns `errno 5`, aborting startup before pwsh
reads any user script.

## Repro / impact

Hit on the internal `dotnet-maui` pipeline (definition 1095), build
**2990586**, during release-prep for `release/11.0.1xx-preview5`:

> `##[section]Starting: Provision Android SDK - Common Packages` → `Call
to 'procargs' failed with errno 5` → task fails with exit code `null`.

A rerun on a different agent passed, but every macOS `pwsh:` /
`PowerShell@2` step in the pipeline is a flake risk going forward.
Inventory: ~57 pwsh tasks across 15 pipeline YAMLs, ~45 of them on
macOS-eligible jobs.

## Fix

PowerShell itself ships a short-circuit for this code path. From
[`src/powershell/Program.cs`](https://github.com/PowerShell/PowerShell/blob/master/src/powershell/Program.cs):

```csharp
private const string LOGIN_ENV_VAR_NAME = "__PWSH_LOGIN_CHECKED";

if (Environment.GetEnvironmentVariable(LOGIN_ENV_VAR_NAME) != null)
{
    Environment.SetEnvironmentVariable(LOGIN_ENV_VAR_NAME, null);
    return;   // ← skips the racy sysctl(KERN_PROCARGS2) entirely
}
```

Setting `__PWSH_LOGIN_CHECKED=1` makes pwsh skip the racy syscall. This
PR declares it as a pipeline-level variable in
`eng/pipelines/common/variables.yml` (included by every macOS-touching
pipeline — `ci.yml`, `ci-official.yml`, `ci-device-tests.yml`,
`ci-copilot.yml`, `ci-uitests.yml`, `device-tests.yml`, `ui-tests.yml`,
`handlers.yml`, etc.). Per [AzDO
docs](https://learn.microsoft.com/azure/devops/pipelines/process/variables),
user-defined variables are injected as environment variables for every
task — so every `pwsh:` / `PowerShell@2` step in every job inherits it.

## Safety analysis

This is safe because:

- **CI never wants login-shell semantics.** No `-Login` flag, no leading
`-` in `argv[0]`. The skipped code's `IsLogin()` check would have
returned `false` and the function would have returned without doing
anything visible. We just avoid the broken syscall that precedes that
decision.
- **The env var is single-use per process.** `AttemptExecPwshLogin`
consumes it via `SetEnvironmentVariable(LOGIN_ENV_VAR_NAME, null)`, so
nested `pwsh-launches-pwsh` scenarios still work normally (each new task
gets a fresh AzDO-injected value).
- **Windows is unaffected.** The read is inside a `#if UNIX` block;
Windows pwsh ignores the var entirely. Setting it on Windows agents is a
no-op.
- **Safe even under `-Login`.** If a future step explicitly adds
`-Login`, the short-circuit still routes around the broken syscall — the
same syscall the login-shell *detection* relies on. So there's no
regression even in that (currently nonexistent) use case.

## Empirical validation

Verified locally on `pwsh 7.4.5` / macOS 26.5 (same 7.4.x minor as the
CI agent that crashed):

| Run | Command | Result |
| --- | --- | --- |
| Control | `pwsh ... -Command 'Write-Output
"envvar=[$Env:__PWSH_LOGIN_CHECKED]"'` | `envvar=[]` (var unset; nothing
to clear) |
| Test | `__PWSH_LOGIN_CHECKED=1 pwsh ... -Command 'Write-Output
"envvar=[$Env:__PWSH_LOGIN_CHECKED]"'` | `envvar=[]` ← **proves the
short-circuit ran** (the only line that clears the var is inside the
skip-path; if it cleared, the racy syscall was never reached) |
| Soak | 100 × `__PWSH_LOGIN_CHECKED=1 pwsh ... -Command 'exit 0'` |
**0/100 failures**; no behavioral change |

## Backport plan

- This PR: `main` (.NET 10 SR dev). Drives the fix into ongoing
servicing builds.
- Will cherry-pick to `net11.0` afterward (where build 2990586 crashed
during preview5 prep). Diff is verified to apply cleanly to both
branches.

## Related macOS-agent flakes (out of scope)

The same dnceng macOS agent pool has produced other, *unrelated* flakes
during this release-prep window (e.g.
`IDEDownloadableMetalToolchainCoordinator: Failed to remount` and `iOS
26.2 platform not installed` on builds 2990581 and 2990384). Those are
agent-image issues and need agent-side fixes — they're not addressed
here. This PR fixes one specific class of macOS flake (pwsh startup
race), not all of them.

Co-authored-by: bot <bot@test>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🚀 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 -- 35778

Or

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

@NirmalKumarYuvaraj NirmalKumarYuvaraj added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Jun 5, 2026
@kubaflo

kubaflo commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 1 findings

See inline comments for details.

Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs Outdated
@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jun 5, 2026
MauiBot

This comment was marked as outdated.

@kubaflo kubaflo 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.

Looks good; is this pr ready?

@kubaflo

kubaflo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer -p android

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 2 findings

See inline comments for details.

Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs Outdated
Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs Outdated
@MauiBot MauiBot added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-fix-win AI found a better alternative fix than the PR labels Jun 7, 2026
MauiBot

This comment was marked as outdated.

@kubaflo kubaflo 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.

Could you please check the ai's suggestions?

@HarishwaranVijayakumar HarishwaranVijayakumar changed the title [WIP] [Android] Fix DragGestureRecognizer DragStarting Command/Event fired prematurely [Android] Fix DragGestureRecognizer DragStarting Command/Event fired prematurely Jun 8, 2026
@kubaflo

kubaflo commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

@HarishwaranVijayakumar

Copy link
Copy Markdown
Contributor Author

Could you please check the ai's suggestions?

Addressed the suggestions

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@HarishwaranVijayakumar — new AI review results are available based on this last commit: d19b1d9.
Update shared file To request a fresh review after new comments or commits, comment /review rerun.

Gate Passed Code Review In Review Confidence High Platform Android

Review Sessions — click to expand
Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: e904e900

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue35752 Issue35752 ✅ FAIL — 2337s ✅ PASS — 531s
🔴 Without fix — 🖥️ Issue35752: FAIL ✅ · 2337s

(truncated to last 15,000 chars)

.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.RunInstall() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]

Build FAILED.

/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: Mono.AndroidTools.InstallFailedException: Unexpected install output: cmd: Failure calling service package: Broken pipe (32) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:  [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Mono.AndroidTools.AndroidDevice.<>c__DisplayClass105_0.<InstallPackage>b__0(Task`1 t) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.RunInstall() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:17:15.11
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:08:28.62
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 2.44 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 7 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 5.64 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 6 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 3 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 13 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 3.24 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 1.78 sec).
  5 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.12]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.51]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 06/08/2026 12:15:35 FixtureSetup for Issue35752(Android)
>>>>> 06/08/2026 12:15:37 DragStartingShouldNotFireOnTapButShouldFireOnDrag Start
>>>>> 06/08/2026 12:15:41 DragStartingShouldNotFireOnTapButShouldFireOnDrag Stop
>>>>> 06/08/2026 12:15:41 Log types: logcat, bugreport, server
  Failed DragStartingShouldNotFireOnTapButShouldFireOnDrag [4 s]
  Error Message:
     DragStarting should not fire on a quick tap
Assert.That(dragCount, Is.EqualTo("0"))
  String lengths are both 1. Strings differ at index 0.
  Expected: "0"
  But was:  "1"
  -----------^

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.Issues.Issue35752.DragStartingShouldNotFireOnTapButShouldFireOnDrag() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs:line 26

1)    at Microsoft.Maui.TestCases.Tests.Issues.Issue35752.DragStartingShouldNotFireOnTapButShouldFireOnDrag() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs:line 26


NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35752.trx

Test Run Failed.
Total tests: 1
     Failed: 1
 Total time: 28.2989 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35752.trx

🟢 With fix — 🖥️ Issue35752: PASS ✅ · 531s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:07:03.08
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14309771
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.12]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.55]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 06/08/2026 12:34:00 FixtureSetup for Issue35752(Android)
>>>>> 06/08/2026 12:34:02 DragStartingShouldNotFireOnTapButShouldFireOnDrag Start
>>>>> 06/08/2026 12:34:08 DragStartingShouldNotFireOnTapButShouldFireOnDrag Stop
  Passed DragStartingShouldNotFireOnTapButShouldFireOnDrag [6 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35752.trx

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 19.4316 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35752.trx

📁 Fix files reverted (3 files)
  • eng/pipelines/ci-copilot.yml
  • eng/pipelines/common/variables.yml
  • src/Controls/src/Core/Platform/Android/InnerGestureListener.cs

UI Tests — DragAndDrop

Detected UI test categories: DragAndDrop

Deep UI tests — 3 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
DragAndDrop 3/3 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

Pre-Flight — Context & Validation

Issue: #35752 - [Android] DragGestureRecognizer DragStarting Command/Event fired prematurely
PR: #35778 - [Android] Fix DragGestureRecognizer DragStarting Command/Event fired prematurely
Platforms Affected: Android
Files Changed: 1 implementation, 2 test

Key Findings

  • Android drag-only views did not consume ACTION_DOWN after the regression from PR #21547, so Android stopped delivering the touch sequence and ACTION_UP never reached GestureDetector to cancel the long-press timer.
  • The PR fix adds drag recognizer presence to InnerGestureListener.HasAnyGestures(), causing OnDown() to return true for drag-only views and restoring ACTION_UP delivery.
  • Added UI coverage exercises both quick tap (must not fire DragStarting) and real drag (must fire once) on Android.
  • Prior inline review feedback about the test category and wait duration was addressed in the latest PR files: the test uses UITestCategories.DragAndDrop and waits 600ms after tap.

Code Review Summary

Verdict: LGTM
Confidence: high
Errors: 0 | Warnings: 1 | Suggestions: 1

Key code review findings:

  • ⚠️ src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs:23 uses Thread.Sleep(600) without documenting that it intentionally waits beyond Android's long-press threshold; functionally justified, but worth documenting for maintainability.
  • 💡 The UI test is Android-specific in behavior but has no explicit non-Android guard; likely acceptable because the issue page is marked PlatformAffected.Android, but worth considering if the test runs broadly.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35778 Include drag gestures in InnerGestureListener.HasAnyGestures() so OnDown() consumes drag-only touch sequences. ✅ PASSED (Gate) src/Controls/src/Core/Platform/Android/InnerGestureListener.cs Original PR

Code Review — Deep Analysis

Code Review — PR #35778

Independent Assessment

What this changes: InnerGestureListener.HasAnyGestures() is extended to also return true when the view has a DragGestureRecognizer, by adding || (_dragAndDropGestureHandler?.HasAnyDragGestures() ?? false) to the existing Pan/Tap/Swipe check. Two test files are added: a HostApp page demonstrating the bug and a NUnit UI test asserting the fixed behavior.

Inferred motivation: HasAnyGestures() is called from OnDown() (GestureDetector.IOnGestureListener.OnDown). When that method returns true, Android marks the ACTION_DOWN event as consumed and keeps delivering subsequent events (ACTION_MOVE, ACTION_UP) to the view. When it returns false, Android stops delivering — meaning ACTION_UP never arrives, the GestureDetector's internal long-press timer is never cancelled, and DragStarting fires spuriously after any tap. Adding drag to the check restores the expected consume-on-down behavior for drag-only views.

Reconciliation with PR Narrative

Author claims: Regression introduced by PR #21547 changed e.Handled = true to e.Handled = OnTouchEvent(e.Event), where OnTouchEvent returns InnerGestureListener.OnDown(). Since OnDown() calls HasAnyGestures() which did not include drag, drag-only views returned false, suppressing subsequent events and letting the long-press timer fire on every tap.

Agreement/disagreement: The root cause analysis is correct and maps to the code. The implementation is the minimal direct fix for the missing drag predicate.

Findings

⚠️ Warning — Thread.Sleep(600) in UI test is undocumented

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs:23 waits after a tap before asserting that DragStarting did not fire. This is functionally justified because the test is verifying absence of a delayed long-press callback, but the reason should be documented so future maintainers do not shorten or remove the wait.

💡 Suggestion — No explicit platform guard on the UI test

The bug is Android-specific and the HostApp page is marked PlatformAffected.Android. The test may still be discovered in broader runs; consider aligning the executable test scope with the Android-specific issue if non-Android drag semantics create false positives later.

Devil's Advocate

Returning true from OnDown() for drag-only views means the view consumes the touch sequence, but that matches pre-regression behavior and is required so ACTION_UP reaches GestureDetector and cancels the long-press timer. EnableLongPressGestures already depends on drag gesture presence, so the PR is consistent with the existing long-press activation path. The null-conditional pattern in HasAnyGestures() also matches the surrounding helper checks.

Verdict: LGTM

Confidence: high
Summary: The one-line implementation fix is correct, minimal, and Android-scoped. It restores delivery of ACTION_UP for drag-only views and the added UI test covers both tap and drag behavior. The only concerns are test maintainability refinements, not blockers.


Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Use EnableLongPressGestures in InnerGestureListener.OnDown() so drag-only views consume ACTION_DOWN. ✅ PASS 1 file Passed Android Issue35752 UI test; close variant of PR fix.
2 try-fix Force-consume unhandled drag ACTION_DOWN in GesturePlatformManager.Android.OnTouchEvent() and add ViewHasDragGestures(). ✅ PASS 1 file Passed Android Issue35752 UI test; structurally different but broader/less direct than PR.
PR PR #35778 Add drag recognizer presence to InnerGestureListener.HasAnyGestures(). ✅ PASSED (Gate) 1 file Original PR; direct missing-predicate fix.

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer 2 No Remaining possible approach (TapAndPanGestureDetector.OnTouchEvent returning true on down when long-press is enabled) is a trivial reformulation of try-fix-2, not meaningfully different. No later layer can cancel the long-press timer once ACTION_UP is lost.

Exhausted: Yes
Selected Fix: PR's fix — It is the simplest and most semantically direct fix: HasAnyGestures() was the missing predicate used by OnDown(), and adding drag there restores the correct Android touch-stream ownership with fewer moving parts than the platform-manager candidate.


Report — Final Recommendation

Comparative Fix Report — PR #35778

Candidates evaluated

Candidate Approach Regression result Assessment
pr Add DragGestureRecognizer presence to InnerGestureListener.HasAnyGestures(), so OnDown() consumes drag-only Android touch sequences. Passed Directly fixes the missing predicate at the decision point that controls Android touch-stream ownership.
pr-plus-reviewer Raw PR plus expert-reviewer feedback. Passed Expert reviewer returned no actionable findings, so this is identical to pr.
try-fix-1 Change InnerGestureListener.OnDown() to return true when `HasAnyGestures() EnableLongPressGestures`.
try-fix-2 Post-process unconsumed ACTION_DOWN in GesturePlatformManager.Android.OnTouchEvent() when the view has drag gestures. Passed Correct but broader and less direct; duplicates drag recognizer detection in the platform manager instead of fixing the listener predicate used by OnDown().

Ranking

  1. pr / pr-plus-reviewer
  2. try-fix-1
  3. try-fix-2

No candidate failed regression tests, so ranking is based on correctness, locality, maintainability, and consistency with existing Android gesture abstractions.

Winner

pr is the winning candidate. It is tied behaviorally with pr-plus-reviewer because the expert reviewer produced no actionable changes, and it is preferable to both try-fix candidates because it repairs the missing drag predicate exactly where OnDown() decides whether the view is interested in the remainder of the Android touch sequence.

Rationale

The regression occurred because drag-only views were not represented in InnerGestureListener.HasAnyGestures(), even though the same listener enables long-press handling for drag gestures. Adding drag to that helper makes the listener's gesture-presence predicate complete and restores the pre-regression event flow with a one-line, low-risk Android-scoped change.

try-fix-1 also passes because EnableLongPressGestures currently maps to HasAnyDragGestures(), but it leaves HasAnyGestures() inaccurate and couples OnDown() to the implementation detail that drag uses long press. try-fix-2 also passes, but it adds another drag scan and fixes the symptom one layer higher after the detector has already reported OnDown() as unconsumed.


Future Action — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@kubaflo kubaflo 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.

Looks good! Is it ready to merge?

@sheiksyedm
sheiksyedm marked this pull request as ready for review June 8, 2026 19:05
@sheiksyedm

Copy link
Copy Markdown
Contributor

Looks good! Is it ready to merge?

Yes, its ready.

@kubaflo
kubaflo changed the base branch from main to inflight/current June 8, 2026 19:09
@kubaflo
kubaflo merged commit 583a22a into dotnet:inflight/current Jun 8, 2026
24 of 33 checks passed
@github-actions github-actions Bot added this to the .NET 10.0 SR8 milestone Jun 8, 2026
PureWeen pushed a commit that referenced this pull request Jun 11, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
@sheiksyedm sheiksyedm modified the milestones: .NET 10 SR8, .NET 10 SR9 Jun 18, 2026
PureWeen pushed a commit that referenced this pull request Jun 22, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
kubaflo pushed a commit that referenced this pull request Jun 25, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
kubaflo pushed a commit that referenced this pull request Jul 3, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
@kubaflo kubaflo mentioned this pull request Jul 6, 2026
kubaflo pushed a commit that referenced this pull request Jul 6, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
PureWeen pushed a commit that referenced this pull request Jul 7, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
PureWeen pushed a commit that referenced this pull request Jul 7, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
kubaflo pushed a commit that referenced this pull request Jul 10, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
kubaflo pushed a commit that referenced this pull request Jul 15, 2026
…prematurely (#35778)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- On Android, a view that has only a DragGestureRecognizer incorrectly
raises DragStarting on a quick tap. The drag event fires after the user
lifts their finger, even though no long-press or drag motion occurred.

### Root Cause of the issue

- Android's native `GestureDetector` starts an internal long-press timer
(~400ms) on `ACTION_DOWN`. It relies on receiving `ACTION_UP` to cancel
that timer when the user lifts their finger quickly (tap).
 
- **Key fact:** If a view does NOT consume `ACTION_DOWN` (returns
`e.Handled = false`), Android stops delivering subsequent touch events
(`ACTION_MOVE`, `ACTION_UP`) to that view.
 
 ### Before Regression
 
`e.Handled` was always `true` in `OnPlatformViewTouched`, so drag-only
views always consumed `ACTION_DOWN`.
 
- **Tap:** `ACTION_DOWN` consumed → timer starts → finger lifts →
`ACTION_UP` delivered → timer cancelled → no DragStarting
- **Long press:** `ACTION_DOWN` consumed → timer starts → finger stays →
timer expires → `OnLongPress()` → DragStarting fires
 
 ### After Regression
 
PR [#21547](#21547) changed to
`e.Handled = OnTouchEvent(e.Event)`, which returns the result of
`InnerGestureListener.OnDown()`. The `OnDown()` method checks
`HasAnyGestures()` which only considers Pan/Tap/Swipe — **not Drag**. So
for drag-only views, `OnDown()` returns `false` → `e.Handled = false`.
 
- **Tap:** `ACTION_DOWN` NOT consumed → timer starts → finger lifts →
`ACTION_UP` **never delivered** → timer never cancelled → timer expires
→ DragStarting fires
- **Long press:** Same as tap — DragStarting fires regardless of touch
duration
 

### Description of Change
**Gesture Handling Improvements:**

- Updated the `HasAnyGestures()` method in `InnerGestureListener` to
include a check for drag gestures using the
`_dragAndDropGestureHandler?.HasAnyDragGestures()` condition, ensuring
drag gestures are properly distinguished from taps.

**Testing and Verification:**

- Added a new test page (`Issue35752`) in `TestCases.HostApp` that
visually and programmatically demonstrates the issue and its resolution,
allowing manual verification that `DragStarting` only fires on drag, not
on tap.
- Introduced an automated UI test in `TestCases.Shared.Tests` to assert
that `DragStarting` does not fire on a quick tap but does fire on an
actual drag, preventing regressions.
<!-- Enter description of the fix in this section -->

### Regression details
The regression was introduced by PR
[21547](#21547)
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35752

### Tested the behaviour in the following platforms

- [x] - Windows 
- [x] - Android
- [x] - iOS
- [x] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5ada0f41-2532-4a35-8377-44c49f4d3a71">
| <video
src="https://github.com/user-attachments/assets/4a0cd419-1269-4cd6-a9a6-ad533581326b">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-gestures Gesture types community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android 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] DragGestureRecognizer DragStarting Command/Event fired prematurely

6 participants