Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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
20 changes: 5 additions & 15 deletions src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System;
using Android.App;
using Android.Content;
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
Expand All @@ -20,16 +18,6 @@ protected override void OnActivityResult(int requestCode, Result resultCode, Int
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()
[Obsolete]
#pragma warning disable 809
public override void OnBackPressed()
#pragma warning restore 809
{
HandleBackNavigation();
}

public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
Expand Down Expand Up @@ -136,10 +124,12 @@ public override bool OnKeyUp(Keycode keyCode, KeyEvent? e)
}

/// <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 =>
Expand Down
1 change: 0 additions & 1 deletion src/Core/src/Platform/Android/MauiAppCompatActivity.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Android.OS;
using Android.Views;
using AndroidX.Activity;
Expand Down
Loading