[Android] Fix for predictive back-to-home animation blocked by unconditional back callback registration#35223
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35223Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35223" |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 6 findings
See inline comments for details.
3cb4dbc to
30bfb65
Compare
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 9 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please try ai's suggestions?
…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.
06bf7f5 to
4a58a0f
Compare
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
🤖 AI Summary
📊 Review Session —
|
| 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.cssrc/Controls/src/Core/NavigationPage/NavigationPage.cssrc/Controls/src/Core/Page/Page.cssrc/Controls/src/Core/Shell/Shell.cssrc/Controls/src/Core/Window/Window.Android.cssrc/Controls/src/Core/Window/Window.cssrc/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cssrc/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cssrc/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
IOnBackInvokedCallbackregistration inMauiAppCompatActivity.OnCreate(and a parallel one inBlazorWebViewHandler.Android.cs). - Once an
IOnBackInvokedCallbackis 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)
- Replace
IOnBackInvokedCallbackwith AndroidXOnBackPressedCallback, whoseEnabledflag actually toggles whether the system shows the back-to-home animation. - Add
MauiAppCompatActivity.UpdatePredictiveBackRegistration()that recomputesEnabledfromIBackNavigationState.CanConsumeBackNavigation. - Wire
IBackNavigationStateintoWindow.Android.cs(recursively walksShell/NavigationPage/FlyoutPage/ modal stack to decide whether MAUI can consume back). - 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. - Re-entrancy guard inside
HandleBackNavigation— disable callback while propagatingbase.OnBackPressed()then re-evaluate infinally. - 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,Shellon EVERY platform (the call is no-op only at runtime). - Prior expert review noted that
ShouldRegisterPredictiveBackCallbackhas a deadhasAnyHandlergate —HandleWindowBackButtonPressedis registered by the framework itself, so the gate is effectively alwaystrue. The actual filter isIBackNavigationState.CanConsumeBackNavigation. ThehasAnyHandlerforeach is dead code. - The recursive
CanConsumeBackNavigationoverlaps forShell:shell.CurrentItem?.CurrentItem?.Stack.Count > 1and recursing intoshell.CurrentPage(which for legacy Shell is the top of the stack, possibly itself aNavigationPage) can both fire — author kept both as belt-and-braces. FlyoutPagesplit-mode handled viaIFlyoutPageController.CanChangeIsPresentedcheck.- Re-entrancy guard in
HandleBackNavigationis necessary becauseOnBackPressedDispatcher.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):
⚠️ hasAnyHandlergate is dead code — author scoped this out (BlazorWebView fix is separate).- 💡 The
foreach { hasAnyHandler = true; break; }pattern isservices.GetLifecycleEventDelegates<…>().Any(). - 💡
Stack.Count > 1and recursiveCanConsumeBackNavigation(shell.CurrentPage)overlap — author added a clarifying comment. - ✅
FlyoutBehavior.Disabled/ split-mode edge cases were tightened (== FlyoutBehavior.Flyout,CanChangeIsPresented). - ✅
SendAppearingrecursion-guard added so refresh fires once at the outermost page. - ✅
HasBackButtonPressedSubscribersfalse-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 RECOMMENDED — Enabled 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/RemovePagethat is cosmetic only because actual back-press handling re-evaluates state inHandleBackNavigation'sfinallyblock. - Candidate Update README.md #2 addressed a specific dead-code finding from prior expert review (the
hasAnyHandlerforeach 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.
Enabledtoggle). 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 propertyAllowPredictiveBackToHomeAnimation) 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:
- Remove cross-platform call-site sprinkling (Candidate [Draft] Readme WIP #1).
- Remove dead unreachable code in
ShouldRegisterPredictiveBackCallback(Candidate Update README.md #2). - Preserve all PR semantics (
IBackNavigationState,CanConsumeBackNavigation,OnBackPressedCallbackswap, re-entrancy guard). - 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:
- [major]
MauiAppCompatActivity.cs:92— deadhasAnyHandlerforeach gate. MAUI itself always registersHandleWindowBackButtonPressedviaIAndroidLifecycleBuilder.OnBackPressedinAppHostBuilderExtensions.Android.cs:OnConfigureLifeCycle, so the gate is unreachable. Allocates a service-locator lookup + enumerator on every navigation event for no purpose. → applied inpr-plus-reviewer. - [moderate]
Window.cs:808— Shell branch overlap:shell.CurrentItem?.CurrentItem?.Stack.Count > 1plus recursion intoshell.CurrentPagecan both fire. Author kept both as belt-and-braces; needs a comment but not behaviorally wrong. - [moderate]
Window.Android.cs:12— fix only applies to apps usingMauiAppCompatActivity. CustomActivitysubclasses 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
OnPageChangedvia a newpartial void OnPageChangedPlatformhook and adds ~100 lines of subscriber wiring with a documented (minor) cosmetic gap onInsertPageBefore/RemovePage. - Self-correcting: the gap is masked by
HandleBackNavigation'sfinally { 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. Choosingpr-plus-reviewerselects 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 = falseis 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
hasAnyHandlergate) — 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
Activitysubclasses) 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.
|
/backport to release/10.0.1xx-sr7 |
|
Started backporting to |
|
@kubaflo @BagavathiPerumal this should also fix #24752 right? |
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.
Issues Fixed
Fixes #34594
Fixes #24752
Output
34594-BeforeFix-BackToAnimation.mov
34594-AfterFix-BackToAnimation.mov