Skip to content

[Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher #35154

Open
Dhivya-SF4094 wants to merge 131 commits into
dotnet:inflight/currentfrom
Dhivya-SF4094:fix-8680
Open

[Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher #35154
Dhivya-SF4094 wants to merge 131 commits into
dotnet:inflight/currentfrom
Dhivya-SF4094:fix-8680

Conversation

@Dhivya-SF4094
Copy link
Copy Markdown
Contributor

@Dhivya-SF4094 Dhivya-SF4094 commented Apr 27, 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!

Description

Fixes #8680

Replaces the deprecated OnBackPressed() override and the API 33+-only PredictiveBackCallback (registered via native OnBackInvokedDispatcher) with a single unified MauiOnBackPressedCallback registered on AndroidX OnBackPressedDispatcher.

What this fixes:

  • Eliminates the dual-fire bug on API 33+ where both OnBackPressed() and PredictiveBackCallback could fire
  • Unifies back navigation into a single AndroidX path for all API levels (21+)
  • Removes manual OnDestroy cleanup — AddCallback(LifecycleOwner, callback) is lifecycle-aware

What this does NOT change:

  • The system-rendered predictive back preview (OS swipe animation) continues to work on API 33+ as before
  • Gesture-progress hooks (onBackStarted/onBackProgressed/onBackCancelled) are not surfaced to MAUI lifecycle — this was also the case before and could be a future enhancement

Changes

  • MauiAppCompatActivity.cs — Added MauiOnBackPressedCallback : OnBackPressedCallback registered in OnCreate; removed PredictiveBackCallback, using Android.Window, and using System
  • MauiAppCompatActivity.Lifecycle.cs — Removed deprecated OnBackPressed() override; changed HandleBackNavigation() from void to internal bool

Key Design Decisions

  • Remove OnBackPressed() override? AppCompatActivity.OnBackPressed() internally calls OnBackPressedDispatcher.OnBackPressed() anyway. Once callbacks are registered with the dispatcher, the override is redundant and was creating a dual-path risk on API 33+.
  • try/finally around the dispatcher call? When MAUI doesn't handle back, the callback temporarily disables itself and re-invokes the dispatcher, then re-enables in finally. Without finally, an exception would leave Enabled = false permanently.
  • No manual OnDestroy cleanup? AddCallback(LifecycleOwner, callback) is lifecycle-aware — AndroidX removes the callback automatically at DESTROYED.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Apr 27, 2026
@Dhivya-SF4094 Dhivya-SF4094 changed the base branch from main to net11.0 April 27, 2026 12:39
@Dhivya-SF4094 Dhivya-SF4094 changed the title Fix 8680 [Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher Apr 27, 2026
@Dhivya-SF4094 Dhivya-SF4094 changed the title [Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher [WIP] Android: Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher Apr 27, 2026
@Dhivya-SF4094 Dhivya-SF4094 changed the title [WIP] Android: Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher [Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher Apr 28, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review April 28, 2026 13:25
Copilot AI review requested due to automatic review settings April 28, 2026 13:25
@sheiksyedm sheiksyedm added the community ✨ Community Contribution label Apr 28, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Android back navigation handling in MauiAppCompatActivity to restore reliable OnBackButtonPressed interception when using AndroidX OnBackPressedDispatcher, avoiding deprecated/dual-path back handling and adding a regression UI test.

Changes:

  • Register a single AndroidX OnBackPressedCallback (all API levels) and route back handling through MAUI lifecycle events.
  • Remove the deprecated OnBackPressed() override and adjust HandleBackNavigation() to return whether MAUI handled the back press.
  • Add Issue #8680 HostApp page + UI test, and adjust Shell toolbar back handling to use Shell’s back-press pipeline.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Records removal of OnBackPressed/OnDestroy overrides from the public API surface (but introduces a BOM inconsistency).
src/Core/src/Platform/Android/MauiAppCompatActivity.cs Registers MauiOnBackPressedCallback on OnBackPressedDispatcher and removes predictive-back callback plumbing.
src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs Removes OnBackPressed() override and changes HandleBackNavigation() to return a boolean.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs Adds a regression UI test for intercepting back navigation (but currently gated incorrectly and doesn’t simulate device back).
src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs Adds the HostApp reproduction page for Issue #8680 (needs formatting cleanup).
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs Routes toolbar back through _shell.SendBackButtonPressed() instead of the current Page directly.

Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs Outdated
Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs Outdated
Comment thread src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
Comment thread src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
Comment thread src/Core/src/Platform/Android/MauiAppCompatActivity.cs Outdated
Comment thread src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs Outdated
Comment thread src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Outdated
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 — 7 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-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Apr 28, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 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 Apr 29, 2026
@dotnet dotnet deleted a comment from github-actions Bot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 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 — 6 findings

See inline comments for details.

Comment thread src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
Comment thread src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs Outdated
Comment thread src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs
Comment thread src/Core/src/Platform/Android/MauiAppCompatActivity.cs Outdated
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 19, 2026

/review -b feature/regression-check -p android

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.

🤖 Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against #1 automatically generated candidates and selected try-fix-1 as the strongest fix.

Why: try-fix-1 unblocks the gate (resolves RS0017) by restoring the deprecated OnBackPressed() override as a one-line shim that calls base.OnBackPressed(), routing through OnBackPressedDispatcher to the new MauiOnBackPressedCallback. It preserves the PR's underlying design (single AndroidX callback) while avoiding the publicly visible breaking-API change introduced by both the PR and try-fix-2/pr-plus-reviewer.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (`try-fix-1`)
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
new file mode 100644
index 0000000000..0546cb2f3a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
@@ -0,0 +1,66 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 8680, "Rework OnBackButtonPressed to use onBackPressedDispatcher", PlatformAffected.Android)]
+public class Issue8680 : TestNavigationPage
+{
+	protected override void Init()
+	{
+		PushAsync(new Issue8680MainPage());
+	}
+}
+
+public class Issue8680MainPage : ContentPage
+{
+	public Issue8680MainPage()
+	{
+		var navigateButton = new Button
+		{
+			Text = "Go to Intercept Page",
+			AutomationId = "NavigateButton",
+		};
+		navigateButton.Clicked += async (s, e) =>
+		{
+			await Navigation.PushAsync(new Issue8680InterceptPage());
+		};
+
+		Content = new VerticalStackLayout
+		{
+			Children =
+			{
+				new Label { Text = "Main Page", AutomationId = "MainPageLabel" },
+				navigateButton,
+			}
+		};
+	}
+}
+
+public class Issue8680InterceptPage : ContentPage
+{
+	int _backPressCount;
+	readonly Label _statusLabel;
+
+	public Issue8680InterceptPage()
+	{
+		_statusLabel = new Label
+		{
+			Text = "Back not pressed yet",
+			AutomationId = "StatusLabel",
+		};
+
+		Content = new VerticalStackLayout
+		{
+			Children =
+			{
+				_statusLabel,
+				new Label { Text = "Press device back button — it should be intercepted", AutomationId = "InterceptPageLabel" },
+			}
+		};
+	}
+
+	protected override bool OnBackButtonPressed()
+	{
+		_backPressCount++;
+		_statusLabel.Text = $"Back intercepted: {_backPressCount}";
+		return true; // true = handled, prevents navigation back
+	}
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs
new file mode 100644
index 0000000000..d53179569c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs
@@ -0,0 +1,39 @@
+#if ANDROID // Android-specific: OnBackButtonPressed uses onBackPressedDispatcher
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue8680 : _IssuesUITest
+{
+	public Issue8680(TestDevice device) : base(device) { }
+
+	public override string Issue => "Rework OnBackButtonPressed to use onBackPressedDispatcher";
+
+	[Test]
+	[Category(UITestCategories.Navigation)]
+	public void BackButtonPressIsInterceptedByOnBackButtonPressed()
+	{
+		// Navigate to the intercept page
+		App.WaitForElement("NavigateButton");
+		App.Tap("NavigateButton");
+
+		// Confirm we are on the intercept page
+		App.WaitForElement("InterceptPageLabel");
+
+		// Press the device back button — should be intercepted (page stays)
+		App.Back();
+
+		// The page should still be visible because OnBackButtonPressed returned true
+		App.WaitForElement("StatusLabel");
+		Assert.That(
+		 App.FindElement("StatusLabel").GetText(),
+		 Is.EqualTo("Back intercepted: 1"),
+		 "OnBackButtonPressed should have been called exactly once per back press (detects dual-fire regression on API 33+).");
+
+		// The intercept page should still be displayed (not popped)
+		App.WaitForElement("InterceptPageLabel");
+	}
+}
+#endif
\ No newline at end of file
diff --git a/src/Core/src/Platform/Android/MauiAppCompatActivity.cs b/src/Core/src/Platform/Android/MauiAppCompatActivity.cs
index de7ae83171..62b9b4f72c 100644
--- a/src/Core/src/Platform/Android/MauiAppCompatActivity.cs
+++ b/src/Core/src/Platform/Android/MauiAppCompatActivity.cs
@@ -1,4 +1,3 @@
-using System;
 using Android.OS;
 using Android.Views;
 using AndroidX.Activity;
diff --git a/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs b/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
index 1f813c41cd..84ce333f7f 100644
--- a/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
+++ b/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
@@ -5,7 +5,6 @@ using Android.Content.PM;
 using Android.Content.Res;
 using Android.OS;
 using Android.Views;
-using Microsoft.Maui.Devices;
 using Microsoft.Maui.LifecycleEvents;
 
 namespace Microsoft.Maui
@@ -20,14 +19,19 @@ namespace Microsoft.Maui
 			IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnActivityResult>(del => del(this, requestCode, resultCode, data));
 		}
 
-		// TODO: Investigate whether the new AndroidX way is actually useful:
-		//       https://developer.android.com/reference/android/app/Activity#onBackPressed()
+		// Preserved as a thin compatibility shim for the shipped public API surface.
+		// Back navigation is fully driven by MauiOnBackPressedCallback (registered on
+		// OnBackPressedDispatcher in OnCreate). AppCompatActivity.OnBackPressed() forwards
+		// to the dispatcher, so calling base here routes through the same callback and
+		// avoids the dual-fire risk that existed when this override called
+		// HandleBackNavigation() directly while a separate PredictiveBackCallback was
+		// also registered.
 		[Obsolete]
 #pragma warning disable 809
 		public override void OnBackPressed()
 #pragma warning restore 809
 		{
-			HandleBackNavigation();
+			base.OnBackPressed();
 		}
 
 		public override void OnConfigurationChanged(Configuration newConfig)
@@ -136,10 +140,12 @@ namespace Microsoft.Maui
 		}
 
 		/// <summary>
-		/// Central handler used by both legacy <see cref="OnBackPressed"/> and the Android 13+ predictive back gesture callback.
-		/// Implements lifecycle event invocation and default back stack propagation unless explicitly prevented.
+		/// Central handler invoked by <see cref="MauiOnBackPressedCallback"/> when the back button is pressed.
+		/// This method only checks whether MAUI lifecycle handlers intercept the back press.
+		/// Callers are responsible for invoking <see cref="AndroidX.Activity.OnBackPressedDispatcher.OnBackPressed()"/> as a
+		/// fallback when this method returns <see langword="false"/>.
 		/// </summary>
-		void HandleBackNavigation()
+		internal void HandleBackNavigation()
 		{
 			var preventBackPropagation = false;
 			IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnBackPressed>(del =>

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 20, 2026

/review -b feature/regression-check -p android

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 20, 2026

/azp run maui-pr-devicetests, maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

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 verify the failing tests?

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 24, 2026

/review -b feature/refactor-copilot-yml

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 25, 2026

/review -b feature/regression-check -p android

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Jun 2, 2026

Test Failure Review

@Dhivya-SF4094 - new test-failure review results are available based on this last commit: a42e2f9.
To request a fresh review after new comments, commits, or CI runs, comment /review tests.

Overall Insufficient data Failures 3 Data Partial Platform android+multi

Test Failure Review - a42e2f9 - [Android] Restore OnBackButtonPressed interception support when using OnBackPressedDispatcher - 2026-06-02 13:14 UTC

Overall verdict: Insufficient data

The gathered context shows failing Build Analysis, maui-pr-devicetests, and maui-pr-uitests checks, but both referenced AzDO builds returned 404 and no failed records, logs, test results, or Helix summaries were available. The PR scope is Android navigation and includes Android platform code plus Issue8680 UI tests, so the Android device/UI failures cannot be responsibly attributed or dismissed without the missing build evidence.

Failure Verdict Evidence
Build Analysis Insufficient data The check is marked failed, but the context provides only the Build Analysis documentation link and no build-analysis matches, timeline issues, or log excerpts.
maui-pr-devicetests build 1428613 Insufficient data The rollup and Android CoreCLR, Android Mono, MacCatalyst Mono, Windows device-test build, and iOS Mono child checks failed, but build 1428613 was inaccessible with 404 (Not Found); failedRecords, logExcerpts, testResults, and Helix summaries are empty, so hidden device-test failures could not be verified.
maui-pr-uitests build 1428612 Insufficient data The rollup and sample-app build child checks failed, but build 1428612 was inaccessible with 404 (Not Found); no timeline records, logs, test results, or distinct extracted failures were available.

Recommended action

Regather the review context when builds 1428612 and 1428613 are accessible, then inspect the failed timelines/logs and Helix aggregate data before attributing these failures to the PR.

Evidence details

PR #35154 targets inflight/current from fix-8680 at a42e2f99997da32392b4bc9c76a8e8c9c29ddc7a. Labels and scope point to platform/android and area-navigation; inferred platform from changed files is android.

Changed files are src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs, src/Core/src/Platform/Android/MauiAppCompatActivity.cs, src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs, and src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs.

The gathered check rollup marks maui-pr build 1420508 successful. The failing or inconclusive checks are Build Analysis, maui-pr-devicetests, device-test child checks for Android CoreCLR, Android Mono, MacCatalyst Mono, Windows device-test build, iOS Mono, maui-pr-uitests, and UI sample-app build child checks.

Build 1428613 and build 1428612 both have accessible: false with error Response status code does not indicate success: 404 (Not Found). For both builds, failedRecords, timelineIssues, logExcerpts, testFailuresFromLogs, testResults, and recentBaseBuilds are empty. For device-test build 1428613, helix.checked is false, helix.jobIds is empty, and helix.summaries is empty.

No distinct test failures were extracted from accessible AzDO logs or test results. The context limitation notes that authenticated AzDO access used an Azure CLI bearer token for local-only data gathering, while the gh-aw workflow relies on public build/timeline/log APIs unless AZDO_TOKEN is provided by the runner environment.

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

Labels

area-navigation NavigationPage community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android 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)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rework OnBackButtonPressed to use onBackPressedDispatcher