Skip to content

[Android] Fix for predictive back-to-home animation blocked by unconditional back callback registration#35223

Merged
kubaflo merged 10 commits into
dotnet:inflight/currentfrom
BagavathiPerumal:fix-34594
May 14, 2026
Merged

[Android] Fix for predictive back-to-home animation blocked by unconditional back callback registration#35223
kubaflo merged 10 commits into
dotnet:inflight/currentfrom
BagavathiPerumal:fix-34594

Conversation

@BagavathiPerumal
Copy link
Copy Markdown
Contributor

@BagavathiPerumal BagavathiPerumal commented Apr 29, 2026

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 16, the predictive back-to-home animation does not behave correctly in .NET MAUI applications because back handling is registered unconditionally through IOnBackInvokedCallback.

When the app is at the root navigation level and the system expects the default back-to-home gesture animation, MAUI still consumes the back callback even when no in-app navigation is available. As a result, the predictive back gesture animation is interrupted or does not transition smoothly to the home screen.

This issue occurs because the callback remains active regardless of whether the application can actually navigate back.

Root Cause

The issue occurs becasuse of MauiAppCompatActivity registering a predictive-back callback unconditionally on Android 13+ at startup and keeping it registered for the activity lifetime, which suppresses Android’s back-to-home predictive animation even when the app cannot consume back navigation (for example, on the root page).

Description of Change

The fix involves replacing the always-registered predictive-back callback in MauiAppCompatActivity with AndroidX OnBackPressedCallback and dynamically toggling its Enabled state based on whether MAUI can actually consume back navigation. A shared UpdatePredictiveBackRegistration() flow is triggered during navigation changes so the callback state stays in sync across Shell, NavigationPage, FlyoutPage, modal navigation, and window-level back state. This means MAUI intercepts back only when needed and lets the system handle back at root, restoring Android’s predictive back-to-home animation.

Related BlazorWebView Issue

The fix is scoped to the activity-level back callback in MauiAppCompatActivity. Created a separate issue report for the BlazorWebView scenario: #35397

Why Tests were not added:

Regarding the test case: This issue is specific to Android 16 (API 36), while the current automated device test coverage does not reliably reproduce this exact platform behavior in CI. Because of that limitation, a deterministic automated repro test could not be added at this time. Validation was performed through manual Android 16 scenario testing focused on root-page predictive back and navigated-page back handling.

Tested the behavior in the following platforms.

  • Android
  • Mac
  • iOS
  • Windows

Issues Fixed

Fixes #34594
Fixes #24752

Output

Before Issue Fix After Issue Fix
34594-BeforeFix-BackToAnimation.mov
34594-AfterFix-BackToAnimation.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 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 -- 35223

Or

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

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

Expert Review — 6 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues 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 Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 30, 2026
Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

Expert Review — 9 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-review-incomplete and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels Apr 30, 2026
Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

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

Could you please try ai's suggestions?

@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
@dotnet dotnet deleted a comment from MauiBot May 2, 2026
TamilarasanSF4853 and others added 5 commits May 11, 2026 12:25
…View2 is not connected in Appium. (dotnet#35335)

### Description of Changes

- Recently, the Appium driver has not been connecting properly to the
native WebView2 control on Windows. While running locally using Appium
Inspector with the WebView control, the inspector is unable to recognize
the WebView and displays an error.

- Due to this Appium driver issue, the WebView lane in CI takes a long
time to run (approximately 3 hours) and eventually gets cancelled. As a
temporary workaround, the WebView lane has been temporarily removed from
the Windows CI pipeline to allow the CI process to complete more
quickly.
<img width="649" height="294" alt="image"
src="https://github.com/user-attachments/assets/68df006b-56d6-4bfa-870a-a4184f5b18b7"
/>
<img width="576" height="430" alt="image"
src="https://github.com/user-attachments/assets/40c222e8-4935-450d-be7e-5ee9245e9eb1"
/>


**Issue:** dotnet#35334
…ion on Android 16 by replacing the unconditional IOnBackInvokedCallback with AndroidX OnBackPressedCallback and toggling Enabled based on the navigation state.
…s — thread safety, JNI cleanup, false positives, unit tests.
@dotnet dotnet deleted a comment from MauiBot May 11, 2026
@dotnet dotnet deleted a comment from BagavathiPerumal May 11, 2026
@MauiBot MauiBot added s/agent-review-incomplete and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels May 11, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 11, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 13, 2026

🤖 AI Summary

👋 @BagavathiPerumal — new AI review results are available. Please review the latest session below.

📊 Review Sessione1b6f18 · fix-34594-Optimized the code implementation. · 2026-05-13 21:51 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: 4f61d650

Test Without Fix (expect FAIL) With Fix (expect PASS)
🧪 WindowsTests WindowsTests 🛠️ BUILD ERROR ✅ PASS — 130s
🔴 Without fix — 🧪 WindowsTests: 🛠️ BUILD ERROR · 153s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 1.2 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/TestUtils/TestUtils.csproj (in 2.15 sec).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 4.37 sec).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 6.05 sec).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 3.48 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 250 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 70 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 63 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj (in 1.65 sec).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 55 ms).
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  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.14089010
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(960,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(968,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(976,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(986,23): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1000,23): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1014,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1030,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1047,23): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1056,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1072,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1080,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1089,23): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1099,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(1109,23): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]

🟢 With fix — 🧪 WindowsTests: PASS ✅ · 130s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.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.14089010
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089010
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.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.34]   Discovering: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:02.73]   Discovered:  Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:02.75]   Starting:    Microsoft.Maui.Controls.Core.UnitTests
  Passed MaxDimensionsArePassedToCoreCorrectly(inW: NaN, inH: NaN, outW: NaN, outH: NaN) [33 ms]
  Passed MaxDimensionsArePassedToCoreCorrectly(inW: 100, inH: 100, outW: 100, outH: 100) [< 1 ms]
  Passed MaxDimensionsArePassedToCoreCorrectly(inW: Infinity, inH: Infinity, outW: Infinity, outH: Infinity) [< 1 ms]
  Passed MaxDimensionsArePassedToCoreCorrectly(inW: -1, inH: -1, outW: NaN, outH: NaN) [< 1 ms]
  Passed MinDimensionsArePassedToCoreCorrectly(inW: -1, inH: -1, outW: NaN, outH: NaN) [< 1 ms]
  Passed MinDimensionsArePassedToCoreCorrectly(inW: NaN, inH: NaN, outW: NaN, outH: NaN) [< 1 ms]
  Passed MinDimensionsArePassedToCoreCorrectly(inW: 0, inH: 0, outW: 0, outH: 0) [< 1 ms]
  Passed MinDimensionsArePassedToCoreCorrectly(inW: 100, inH: 100, outW: 100, outH: 100) [< 1 ms]
  Passed ActivateWindowAndDeactivateWillSetIsActiveToFalse [50 ms]
  Passed WindowServiceScopeIsDisposedOnDestroying [51 ms]
  Passed BoundsArePassedToCoreCorrectly(inX: 0, inY: 0, inW: 100, inH: 100, outX: 0, outY: 0, outW: 100, outH: 100) [1 ms]
  Passed BoundsArePassedToCoreCorrectly(inX: -1, inY: -1, inW: -1, inH: -1, outX: -1, outY: -1, outW: NaN, outH: NaN) [< 1 ms]
  Passed BoundsArePassedToCoreCorrectly(inX: NaN, inY: NaN, inW: NaN, inH: NaN, outX: NaN, outY: NaN, outW: NaN, outH: NaN) [< 1 ms]
  Passed PoppingPageUnsetsWindow [30 ms]
  Passed PushingPageSetsWindow [3 ms]
  Passed RemovingControlsFromLayoutsUnsetsTheWindow [1 ms]
  Passed ListViewWindowIsInheritedByViewCells [24 ms]
  Passed SetMainPageTwice [6 ms]
  Passed PoppingPageUnSetsWindow [11 ms]
  Passed SettingSameCoreFrameDoesNothing [5 ms]
  Passed WindowIsStyleableWithStyleClass [8 ms]
  Passed UpdatingSingleCoordinateOnlyFiresSinglePropertyAndFrameEvent [6 ms]
  Passed DetachingThePageUnsetsTheWindow [< 1 ms]
  Passed RemovedPageFiresDisappearing [< 1 ms]
  Passed WindowFlowDirectionSetsOnPage [1 ms]
  Passed UpdatingSingleBoundOnlyFiresSingleProperty [< 1 ms]
  Passed SettingCoreFrameOnlyFiresEventOnce [< 1 ms]
  Passed NestedControlsAllHaveTheSameWindowWhenAddedLater [< 1 ms]
  Passed WindowIsStyleableWithStyleProperty [< 1 ms]
  Passed ReActivatedFiresCorrectActivatedEvent [< 1 ms]
  Passed ListViewWindowIsInheritedByLabelInViewCells [1 ms]
  Passed AddWindow [< 1 ms]
  Passed WindowDoesNotLeak [65 ms]
  Passed DefaultBoundsArePassedToCoreCorrectly [< 1 ms]
  Passed ContentPageFlowDirectionSetsOnIWindow [< 1 ms]
  Passed PreviousShellDisconnectsFromWindowPropertyChanged [19 ms]
  Passed ShellTitleChangePropagatesToWindow [1 ms]
  Passed CreateWindowKeepIsActivatedIsFalse [< 1 ms]
  Passed WindowCanRetrieveDisplayDensity [6 ms]
  Passed BindingIsActivatedProperty [8 ms]
  Passed AddingTabSetsWindow [< 1 ms]
  Passed ListViewWindowIsInheritedByLayoutsInViewCells [3 ms]
  Passed NestedControlsAllHaveTheSameWindow [< 1 ms]
  Passed ActivateWindowSendAppearingOnPage [3 ms]
  Passed DetachingInTheMiddleUnsetsTheWindow [< 1 ms]
  Passed MovingWindowDoNotTriggerSizeChanged [2 ms]
  Passed ApplicationIsSetOnWindowBeforeAppearingIsCalledOnPage [< 1 ms]
  Passed SetMainPage [< 1 ms]
  Passed PageHasTheSameWindowWhenAddedLater [< 1 ms]
  Passed WindowServiceScopeHandlesNullScope [1 ms]
  Passed WindowServiceScopeWorksWithWindowCreationFlow [17 ms]
  Passed SwappingPagesUpdatesTheWindow [< 1 ms]
  Passed WindowIsStyleableWithImplicitStyle [3 ms]
  Passed ActivateWindowWillSetIsActiveToTrue [< 1 ms]
[xUnit.net 00:00:03.30]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Passed DestroyedFiresDisappearingEvent [4 ms]
  Passed TwoKeysSameWindow [49 ms]
  Passed AddAndRemoveVisualDiagnosticAdorner [5 ms]
  Passed RemovingTabUnSetsWindow [< 1 ms]

Test Run Successful.
Total tests: 58
     Passed: 58
 Total time: 4.0583 Seconds

⚠️ Failure Details

  • 🛠️ WindowsTests without fix: build failed before tests could run
    • /home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/WindowsTests.cs(960,24): error CS0117: 'Window' does not contain a definition for 'CanConsumeBackNavigation' [/home/vsts/work/1/s/src/Controls/tes...
📁 Fix files reverted (9 files)
  • src/Controls/src/Core/FlyoutPage/FlyoutPage.cs
  • src/Controls/src/Core/NavigationPage/NavigationPage.cs
  • src/Controls/src/Core/Page/Page.cs
  • src/Controls/src/Core/Shell/Shell.cs
  • src/Controls/src/Core/Window/Window.Android.cs
  • src/Controls/src/Core/Window/Window.cs
  • src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs
  • src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
  • src/Core/src/Platform/Android/MauiAppCompatActivity.cs

New files (not reverted):

  • src/Core/src/Platform/Android/IBackNavigationState.cs

🧪 UI Tests — FlyoutPage,Navigation,Page,Shell,ViewBaseTests,Window

Detected UI test categories: FlyoutPage,Navigation,Page,Shell,ViewBaseTests,Window

Deep UI tests — 1313 passed, 6 failed across 6 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
controls-FlyoutPage 2/4 (2 ❌)
controls-Navigation 90/93 (2 ❌)
controls-Page 94/97 (2 ❌)
controls-Shell 297/297 ✓
controls-ViewBaseTests 415/416 ✓
controls-Window 415/416 ✓
controls-FlyoutPage — 2 failed tests
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...
controls-Navigation — 2 failed tests
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...
controls-Page — 2 failed tests
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...
ShouldKeepFlyoutLockedWhenSwitchingLandScapeToPortrait
OpenQA.Selenium.InvalidElementStateException : Screen rotation cannot be changed to ROTATION_0 after 2000ms. Is it locked programmatically?
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.set_Orientation(ScreenOrientation value)
   at UITest.Appium.AppiumOrientationActions.SetOrientationPortrait(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 43
   at UITest.Appium.AppiumOrientationActions.Execute(String commandName, IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumOrientationActions.cs:line 34
   at UITest.Appi
...

📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)


🔍 Pre-Flight — Context & Validation

Issue: #34594 - OnBackInvokedCallbacks block back-to-home animation
PR: #35223 - [Android] Fix for predictive back-to-home animation blocked by unconditional back callback registration
Platforms Affected: Android (API 33+, regression visible most clearly on Android 16)
Files Changed: 10 implementation, 1 test (Controls.Core.UnitTests)

Background

  • Regression introduced by the always-on IOnBackInvokedCallback registration in MauiAppCompatActivity.OnCreate (and a parallel one in BlazorWebViewHandler.Android.cs).
  • Once an IOnBackInvokedCallback is registered, Android suppresses the system back-to-home predictive animation regardless of whether the callback ultimately consumes back. So even at the root page MAUI was eating the gesture preview.
  • Fix scope: activity-level callback only. BlazorWebView regression tracked separately in [Android] BlazorWebView predictive back callback blocks Android back-to-home animation #35397.

PR Approach (current)

  1. Replace IOnBackInvokedCallback with AndroidX OnBackPressedCallback, whose Enabled flag actually toggles whether the system shows the back-to-home animation.
  2. Add MauiAppCompatActivity.UpdatePredictiveBackRegistration() that recomputes Enabled from IBackNavigationState.CanConsumeBackNavigation.
  3. Wire IBackNavigationState into Window.Android.cs (recursively walks Shell / NavigationPage / FlyoutPage / modal stack to decide whether MAUI can consume back).
  4. Add cross-platform Window.NotifyNavigationStateChanged() (no-op outside Android) and call it from MANY navigation transition sites: Page.SendAppearing, Shell.SendNavigated, Shell.OnPropertyChanged(FlyoutIsPresented), NavigationPage.OnCurrentPageChanged, NavigationPage.OnInsertPageBefore, NavigationPage.OnRemovePage, FlyoutPage.OnIsPresentedPropertyChanged, Window.OnModalPushed/Popped, Window.BackButtonClicked.
  5. Re-entrancy guard inside HandleBackNavigation — disable callback while propagating base.OnBackPressed() then re-evaluate in finally.
  6. Adds 14 xUnit cases for Window.CanConsumeBackNavigation(Page?).

Key Findings

  • Gate ✅ PASSED — tests fail without fix, pass with fix (per orchestrator input).
  • The cross-platform call-site sprinkling is the main complexity cost. It changes the contract of Page.SendAppearing, NavigationPage, FlyoutPage, Shell on EVERY platform (the call is no-op only at runtime).
  • Prior expert review noted that ShouldRegisterPredictiveBackCallback has a dead hasAnyHandler gate — HandleWindowBackButtonPressed is registered by the framework itself, so the gate is effectively always true. The actual filter is IBackNavigationState.CanConsumeBackNavigation. The hasAnyHandler foreach is dead code.
  • The recursive CanConsumeBackNavigation overlaps for Shell: shell.CurrentItem?.CurrentItem?.Stack.Count > 1 and recursing into shell.CurrentPage (which for legacy Shell is the top of the stack, possibly itself a NavigationPage) can both fire — author kept both as belt-and-braces.
  • FlyoutPage split-mode handled via IFlyoutPageController.CanChangeIsPresented check.
  • Re-entrancy guard in HandleBackNavigation is necessary because OnBackPressedDispatcher.OnBackPressed() re-walks enabled callbacks; without disable-during-propagation the same callback would re-fire.

Code Review Summary

Verdict: NEEDS_DISCUSSION (correctness OK, design surface area is large)
Confidence: medium
Errors: 0 | Warnings: 1 | Suggestions: 2

Key code-review observations from prior review threads (already addressed by the author or scoped out):

  • ⚠️ hasAnyHandler gate is dead code — author scoped this out (BlazorWebView fix is separate).
  • 💡 The foreach { hasAnyHandler = true; break; } pattern is services.GetLifecycleEventDelegates<…>().Any().
  • 💡 Stack.Count > 1 and recursive CanConsumeBackNavigation(shell.CurrentPage) overlap — author added a clarifying comment.
  • FlyoutBehavior.Disabled / split-mode edge cases were tightened (== FlyoutBehavior.Flyout, CanChangeIsPresented).
  • SendAppearing recursion-guard added so refresh fires once at the outermost page.
  • HasBackButtonPressedSubscribers false-positive replaced with structural navigation-state checks.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35223 OnBackPressedCallback + cross-platform NotifyNavigationStateChanged sprinkled across Page/NavigationPage/Shell/FlyoutPage/Window ✅ PASSED (Gate) 10 src + 1 test Wide blast radius; works
1 try-fix-1 Android-side event subscription in Window.Android.cs — listen to existing navigation events from a single platform-only place; revert all cross-platform call-site insertions ⏳ pending TBD Smaller cross-platform footprint
2 try-fix-2 Remove dead hasAnyHandler foreach; collapse ShouldRegisterPredictiveBackCallback to the single meaningful check ⏳ pending 1 src Dead-code cleanup atop PR
3 try-fix-3 No-callback approach — never register an unconditional callback at all; register/unregister OnBackPressedCallback lazily only when MAUI actually has consumable back state ⏳ pending TBD Even closer to system default behavior

🔧 Fix — Analysis & Comparison

Try-Fix Aggregate — PR #35223

Platform: android
Loop type: Iterative expert-review-and-test (maui-expert-reviewer agent + unit-test verification)
Stopping condition: Meaningfully different approaches exhausted (3 distinct lanes covered).

Fix Candidates

# Source / Model Approach Test Result Files Changed Notes
1 try-fix-1 (claude-opus-4.6) Subscriber-based: keep OnBackPressedCallback Enabled-toggle and IBackNavigationState/CanConsumeBackNavigation recursion, but revert all cross-platform NotifyNavigationStateChanged() call sites. Subscribe to existing public navigation events (NavigationPage.Pushed/Popped/PoppedToRoot, Shell.Navigated, Shell.PropertyChanged, FlyoutPage.IsPresentedChanged, MultiPage.CurrentPageChanged, Window.ModalPushed/Popped) from Window.Android.cs, wired/unwired via a new partial void OnPageChangedPlatform hook. ✅ PASSED (CanConsumeBackNavigationTests 14/14; broader Window/Page/Nav/Flyout/Shell suite 140/140) 5 shared (FlyoutPage, NavigationPage, Page, Shell, Window) + 1 Android (Window.Android.cs) Smaller cross-platform footprint; minor cosmetic gap on InsertPageBefore/RemovePage (no public event), self-corrects on next nav.
2 try-fix-2 (claude-sonnet-4.6) Dead-code cleanup: keep PR's overall design. Remove the dead hasAnyHandler foreach gate and unused using Microsoft.Maui.LifecycleEvents; from ShouldRegisterPredictiveBackCallback(). The framework always registers HandleWindowBackButtonPressed via IAndroidLifecycleBuilder.OnBackPressed, making the gate unreachable. Reduces the method to one expression-bodied line. ✅ PASSED (14/14; Android-target build not run locally — pure deletion + simplification, syntactically equivalent) 1 (MauiAppCompatActivity.cs) Strictly complementary — can be applied on top of the PR or merged into it. Net −17/+2.
3 try-fix-3 (gpt-5.3-codex) Lazy registration: dynamically AddCallback/Remove on OnBackPressedDispatcher instead of toggling Enabled. Track _isMauiOnBackPressedCallbackRegistered to avoid double-add. Re-entrancy guard removes the callback (instead of disabling) before base.OnBackPressed(). ✅ PASSED (14/14) 2 Android (MauiAppCompatActivity.cs + MauiAppCompatActivity.Lifecycle.cs) NOT RECOMMENDEDEnabled toggle is the documented AndroidX pattern; lazy registration adds state-coherence burden without empirical evidence of benefit. Reserve as targeted fallback only.
PR PR #35223 Replace IOnBackInvokedCallback with OnBackPressedCallback; toggle Enabled via IBackNavigationState.CanConsumeBackNavigation; sprinkle Window.NotifyNavigationStateChanged() across 8 cross-platform call sites in Page/NavigationPage/Shell/FlyoutPage/Window. ✅ PASSED (Gate, per orchestrator input) 10 src + 1 test Functionally correct; widest blast radius.

Iteration Notes

Round 1 — independent exploration

  • Candidate [Draft] Readme WIP #1 explored the largest design difference: eliminate cross-platform call-site sprinkling via subscriber wiring. Empirically equivalent to PR for all unit-test-covered scenarios; introduces a documented minor gap on InsertPageBefore/RemovePage that is cosmetic only because actual back-press handling re-evaluates state in HandleBackNavigation's finally block.
  • Candidate Update README.md #2 addressed a specific dead-code finding from prior expert review (the hasAnyHandler foreach is unreachable). Deliberately scoped to the smallest possible patch — complementary to whichever broader approach (PR or candidate [Draft] Readme WIP #1) is selected.
  • Candidate Third #3 explored a different Android mechanism (lazy registration vs. Enabled toggle). Builds cleanly and passes unit tests but does not improve on the PR; documented for completeness as a fallback if Android 16 OEM behavior contradicts the AndroidX spec.

Round 2 — cross-pollination

After round 1, each candidate was reviewed for new ideas:

  • Candidate [Draft] Readme WIP #1 + Update README.md #2 are orthogonal and could be combined (subscriber design + dead-code cleanup).
  • Candidate [Draft] Readme WIP #1 + Third #3 could also be combined (subscriber design + lazy registration), but Third #3 is not recommended on its own merits.
  • No further meaningfully-different approaches surfaced. Other variants (e.g., overriding getOnBackPressedDispatcher, polling, opt-in property AllowPredictiveBackToHomeAnimation) were considered but either degrade the user-visible behavior or duplicate the PR's mechanism trivially.

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 No Candidate #1's subscriber approach already explored the cleanest architectural lane.
claude-sonnet-4.6 2 No Suggested combining #1+#2; no additional independent fix idea.
gpt-5.3-codex 2 No Acknowledged that Enabled toggle is sufficient; #3 should not replace the PR.

Exhausted: Yes — three lanes (architectural / cleanup / mechanism) covered.

Selected Recommendation

Best path forward: Combine Candidate #1 (subscriber-based) + Candidate #2 (dead-code cleanup) as the preferred alternative to the PR's current approach. Together they:

  1. Remove cross-platform call-site sprinkling (Candidate [Draft] Readme WIP #1).
  2. Remove dead unreachable code in ShouldRegisterPredictiveBackCallback (Candidate Update README.md #2).
  3. Preserve all PR semantics (IBackNavigationState, CanConsumeBackNavigation, OnBackPressedCallback swap, re-entrancy guard).
  4. Keep all 14 new unit tests passing without modification.

If accepting the PR as-is is preferable for review velocity, Candidate #2 should still be applied — it is a strict cleanup with zero behavioral risk and 17-line deletion.

Test Verification Notes

Unit tests were exercised against each candidate via Controls.Core.UnitTests (filter CanConsumeBackNavigationTests):

  • All 14 new tests pass for every candidate.
  • Broader regression (140 tests in Window/Page/Nav/Flyout/Shell suites) verified for candidate [Draft] Readme WIP #1.

Limitation: Android-target build of Microsoft.Maui.dll (which is where MauiAppCompatActivity and Window.Android.cs actually compile) was attempted but blocked by transitive Essentials.csproj restore needing net10.0-android36.0 workload assets that aren't present in this CI environment. All candidate diffs use APIs already exercised by the PR's accepted Android code, so syntactic-build risk is minimal — but on-device validation of the predictive back-to-home animation on Android 16 is out of scope for this loop (matches the PR author's own statement that "deterministic automated repro test could not be added at this time").

Output Files Per Candidate

CustomAgentLogsTmp/PRState/35223/PRAgent/
├── pre-flight/
│   └── content.md             # Issue + PR summary, code-review observations
├── try-fix-1/
│   ├── content.md             # Subscriber approach analysis
│   ├── fix.diff               # 294-line diff
│   └── test-output.log        # 14/14 pass
├── try-fix-2/
│   ├── content.md             # Dead-code cleanup analysis
│   ├── fix.diff               # 40-line diff
│   └── test-output.log        # 14/14 pass
├── try-fix-3/
│   ├── content.md             # Lazy registration analysis (not recommended)
│   ├── fix.diff               # 112-line diff
│   └── test-output.log        # 14/14 pass
└── try-fix/
    └── content.md             # This file

📋 Report — Final Recommendation

Comparative Report — PR #35223

Issue: #34594 — Android 16 predictive back-to-home animation blocked by unconditional OnBackInvokedCallback registration.
Platform under test: android
Gate: ✅ PASSED (tests fail without fix, pass with fix).

Candidates Evaluated

Candidate Approach Tests Diff Size vs PR base Recommended?
pr OnBackPressedCallback + Enabled toggle + cross-platform NotifyNavigationStateChanged() sprinkled across 8 sites in Page / NavigationPage / Shell / FlyoutPage / Window. Plus dead hasAnyHandler foreach gate in ShouldRegisterPredictiveBackCallback. ✅ 14/14 (baseline) Functional but NEEDS_DISCUSSION
pr-plus-reviewer PR + reviewer-feedback edit: removes the dead hasAnyHandler foreach + unused using Microsoft.Maui.LifecycleEvents;. ShouldRegisterPredictiveBackCallback collapses to a comment + IBackNavigationState-only check. ✅ 14/14 (202 ms) −17 / +5 in MauiAppCompatActivity.cs ★ Recommended
try-fix-1 (claude-opus-4.6) Subscriber-based: revert all 8 cross-platform call sites, replace with single partial void OnPageChangedPlatform hook + Android-only event subscriptions to existing public navigation events. ✅ 14/14 (regression: 140/140) 294-line diff (−59 / +9 in shared, +~100 Android wiring) Strong alternative
try-fix-2 (claude-sonnet-4.6) Dead-code cleanup only atop PR — same intent as pr-plus-reviewer. ✅ 14/14 40-line diff in 1 file Strictly complementary to PR
try-fix-3 (gpt-5.3-codex) Lazy registration: dynamic AddCallback/Remove on dispatcher instead of Enabled toggle. ✅ 14/14 112-line diff, 2 Android files Author tagged NOT RECOMMENDED — speculative complexity vs. documented AndroidX pattern

All candidates passed the CanConsumeBackNavigationTests regression suite. No candidate failed tests, so the test-result tiebreaker does not eliminate any option.

Expert-Reviewer Findings on PR (full list in inline-findings.json)

Verdict: NEEDS_DISCUSSION (medium-high confidence) — 0 errors / 4 warnings / 8 suggestions.

Top 3 actionable findings:

  1. [major] MauiAppCompatActivity.cs:92 — dead hasAnyHandler foreach gate. MAUI itself always registers HandleWindowBackButtonPressed via IAndroidLifecycleBuilder.OnBackPressed in AppHostBuilderExtensions.Android.cs:OnConfigureLifeCycle, so the gate is unreachable. Allocates a service-locator lookup + enumerator on every navigation event for no purpose. → applied in pr-plus-reviewer.
  2. [moderate] Window.cs:808 — Shell branch overlap: shell.CurrentItem?.CurrentItem?.Stack.Count > 1 plus recursion into shell.CurrentPage can both fire. Author kept both as belt-and-braces; needs a comment but not behaviorally wrong.
  3. [moderate] Window.Android.cs:12 — fix only applies to apps using MauiAppCompatActivity. Custom Activity subclasses silently retain broken Android 16 behavior (out-of-PR-scope; documentation note acceptable).

All other findings are pure suggestions (style, comments, observability) and were not applied.

Decision Analysis

Why not try-fix-1 (subscriber-based)?

  • Architectural appeal: removes the 8 cross-platform call-site insertions, encapsulates the Android concern entirely in Window.Android.cs. Genuinely cleaner shared API.
  • But: it is a 294-line replacement of the PR, not an additive change. It re-wires OnPageChanged via a new partial void OnPageChangedPlatform hook and adds ~100 lines of subscriber wiring with a documented (minor) cosmetic gap on InsertPageBefore/RemovePage.
  • Self-correcting: the gap is masked by HandleBackNavigation's finally { UpdatePredictiveBackRegistration(); }, so back-press correctness is preserved — only the preview animation on the very next gesture might lag by one nav step.
  • Risk for review velocity: replacing the PR's already-discussed approach restarts the review cycle. The author and prior reviewers have already converged on the call-site approach with explicit comments addressing the design.
  • Verdict: viable follow-up refactor; not the right swap for this PR.

Why not try-fix-2 standalone?

  • It is the same edit as pr-plus-reviewer, just packaged as a standalone candidate. Choosing pr-plus-reviewer selects this same change while keeping the rest of the PR — equivalent outcome with a clearer name (PR + reviewer feedback) for the merge story.

Why not try-fix-3 (lazy registration)?

  • Author of the candidate explicitly recommended against it: "the Enabled-toggle pattern is the documented AndroidX idiom for predictive back; switching to add/remove introduces additional state (_isMauiOnBackPressedCallbackRegistered) and ordering concerns without empirical evidence that the simpler approach is insufficient."
  • No empirical Android 16 reproducer suggesting Enabled = false is inadequate.
  • Strictly more state-coherence burden for zero observed benefit.

Why pr-plus-reviewer wins

  • Preserves the PR's accepted design and review history.
  • Eliminates the only actionable major-severity finding (dead hasAnyHandler gate) — net code reduction with zero behavioral change.
  • 14/14 tests pass; reviewer-edit unified diff is trivially auditable (1 file, −17 / +5).
  • Other findings are either out-of-scope (BlazorWebView in [Android] BlazorWebView predictive back callback blocks Android back-to-home animation #35397; custom Activity subclasses) or comment-only (Shell branch overlap), and can be requested as follow-ups during PR review without blocking merge.

Winner

pr-plus-reviewer — accept the PR with the dead-code cleanup in MauiAppCompatActivity.ShouldRegisterPredictiveBackCallback applied as a follow-up commit (or merged into the PR before merge).

The cross-platform call-site sprinkling (the strongest design objection) is acknowledged but accepted: the PR has already been through review iterations on this point, the alternative (try-fix-1) is a wholesale replacement rather than a refinement, and the call sites are explicit and discoverable.

Inline-Comment Findings

See CustomAgentLogsTmp/PRState/35223/PRAgent/inline-findings.json (12 findings, schema [{path, line, body}]) for the file:line comments to post on the PR.


@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 14, 2026

/backport to release/10.0.1xx-sr7

@github-actions
Copy link
Copy Markdown
Contributor

Started backporting to release/10.0.1xx-sr7 (link to workflow run)

@lucacivale
Copy link
Copy Markdown

@kubaflo @BagavathiPerumal this should also fix #24752 right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) 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.

OnBackInvokedCallbacks block back-to-home animation

8 participants