PrismLibrary / Prism

Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, and Uno / Win UI Applications..
Other
6.29k stars 1.64k forks source link

[BUG] Page.OnBackButtonPressed not called in MAUI Android #3174

Closed aqua-ix closed 2 months ago

aqua-ix commented 3 months ago

Description

I want to hook the event triggered by pressing the back key on an Android device, but the standard MAUI method, Page.OnBackButtonPressed, is not being called. https://learn.microsoft.com/ja-jp/dotnet/api/microsoft.maui.controls.page.onbackbuttonpressed?view=net-maui-8.0

Steps to Reproduce

It can be reproduce this issue in this repo: https://github.com/aqua-ix/MauiPlayground/tree/prism/onbackbutton

  1. Launch the app and navigate from the MainPage to the Third Page.
  2. Press the back key on the device while on the Third Page.
  3. Observe that ThirdPage.OnBackButtonPressed does not output any log.

If it were called, you would see a log like [ThirdPage] OnBackButtonPressed().

The Third Page was specifically created because the bug #2990 fix has not been included in the latest version 9.0.401-pre. This bug causes the app to exit when trying to navigate back using the device's back key from a page that includes a NavigationPage in the back stack.

Platform with bug

.NET MAUI

Affected platforms

Android

Did you find any workaround?

I haven't found it myself. If there is a workaround, please let me know.

Relevant log output

No response

aqua-ix commented 2 months ago
builder.AddAndroid(android =>
{
    android.OnBackPressed(_ =>
    {
        var mainPage = Application.Current?.MainPage;
        var currentPage = MvvmHelpers.GetCurrentPage(mainPage);
        // currentPage is previous page

        return currentPage?.SendBackButtonPressed() ?? false;
    });
});

By hooking the Android lifecycle event OnBackPressed as described above and explicitly calling back the SendBackButtonPressed of the currently displayed page, I confirmed that the OnBackPressed of the Page class is being called. However, there is one problem: the page retrieved by the GetCurrentPage method is always the previously displayed page. This seems to be related to the bug in https://github.com/dotnet/maui/issues/15296.

dansiegel commented 2 months ago

You're trying to do something goofy here and without any explanation of your goals it's hard to say exactly what you should be doing instead. That being said Prism.Maui has some breaking changes for those coming from Prism.Forms. This is to correct behaviors that weren't quite right in Prism.Forms. This is one of those cases. Where you have a NavigationPage and Press the BackButton we could only respond after the fact in Prism.Forms. In Prism.Maui we intercept this by default and prevent the native navigation from happening. This is to allow Prism's Navigation to take precedence and allow things like IConfirmNavigation to work as intended.

aqua-ix commented 2 months ago

@dansiegel

You're trying to do something goofy here and without any explanation of your goals it's hard to say exactly what you should be doing instead.

I apologize for the lack of clarity in my explanation.

In the app I am developing, there was a feature in the Prism.Forms era where the behavior differed when the user navigated using the "Up" button on the navigation bar versus the "Back" button on the device. Since transitioning to Prism.Maui, this behavior can no longer be implemented. Therefore, I am currently investigating other methods to distinguish whether the device's back button was pressed. Is there a way to do this that doesn't depend on whether OnBackButtonPressed is triggered?

dansiegel commented 2 months ago

I'm not sure what you mean by the "Up" button. Keep in that when the Back Button either Software or Hardware is pressed in Prism.Maui this is intercepted and the NavigationService.GoBackAsync is called without any parameters

brianlagunas commented 2 months ago

If you want to run code when the hardware or software back button is pressed, you should implement the INavigatedAware interface, or IConfirmNavigationAsync if you want to prevent navigation.

aqua-ix commented 2 months ago

The "Up" button referred to the software button enclosed in the red frame of the image. This is separate from the back operation using the blue-framed button or Android gestures.

I already implemented INavigatedAware, but in my app, there is an additional requirement to perform extra processing only when the Android hardware button is pressed. In Xamarin, I used Page.OnBackButtonPressed, which was called only when the Android or UWP hardware button was pressed. After introducing Prism.Maui, it seems that this is no longer called, making it difficult to distinguish whether the hardware button has been pressed.

brianlagunas commented 2 months ago

Currently, Prism prevents the OnBackButtonPressed to prevent you from trying to intermix the two approaches and enforce the loose coupling/MVVM approach to respond to navigation events.

Currently in MAUI, the OnBackButtonPressed is called when either the Software Back Button or the Hardware Back Button is pressed. That is a behavior change in MAUI as well. Which sounds like wouldn't work for you either.

aqua-ix commented 2 months ago

@brianlagunas

Currently, Prism prevents the OnBackButtonPressed to prevent you from trying to intermix the two approaches and enforce the loose coupling/MVVM approach to respond to navigation events.

I understand why OnBackButtonPressed was not being called when I install Prism. I will consider a different approach.

Thanks for the explanation.

thomasgalliker commented 1 month ago

@aqua-ix how did you finally solve this? I'm struggling with the same problem. In some pages I blocked hardware-back navigation by overriding OnBackButtonPressed directly in the affected page. @brianlagunas I can use IConfirmNavigation instead, but is there a ways to figure out if the originator of the back-navigation request was the hardware/software back button of the Android device?

brianlagunas commented 1 month ago

Internally we use the PageNavigationService.NavigationSource to check if the navigation was invoked by the device (hardware/software) or the nav service itself.

thomasgalliker commented 1 month ago

Yep, @brianlagunas I was thinking about that flag too. I managed to solve this problem, so it‘s no longer a show stopper for me. But what, if we pass this NavigationSource as navigation parameter along with all navigation calls, or at least with the go-back calls? I‘m just thinking louf 😊 If I knew the originator of the go-back call, I could return false if my ViewModel is asked about CanNavigate() 👍🏻

aqua-ix commented 1 month ago

@thomasgalliker

By creating a custom handler for the ToolBar, I was able to detect when the software back button is tapped using a listener. I stored it as a property in the ViewModel, and handled it within the CanNavigate() method.

Below is a simple sample I created. In this sample, navigation is disabled with the hardware back button, but it allows navigation with the software back button. https://github.com/aqua-ix/MauiPlayground/tree/prism/toolbarhandler

I hope this helps.