From 5e549ff2a25aeb56b0f482ac6ffb527c7e8e2ae5 Mon Sep 17 00:00:00 2001 From: Federico Maccaroni Date: Tue, 5 Mar 2024 18:26:58 -0300 Subject: [PATCH] [PM-6552] Fix for Android Window issues when opening Autofill/Accessibility --- src/Core/App.xaml.cs | 54 ++----------------- .../AndroidNavigationRedirectPage.xaml.cs | 31 ++++++++--- .../AutofillSettingsPageViewModel.android.cs | 5 -- src/Core/ResumeWindow.cs | 10 ---- 4 files changed, 26 insertions(+), 74 deletions(-) diff --git a/src/Core/App.xaml.cs b/src/Core/App.xaml.cs index b94e95df6..2c63c252d 100644 --- a/src/Core/App.xaml.cs +++ b/src/Core/App.xaml.cs @@ -46,7 +46,6 @@ namespace Bit.App // This queue keeps those actions so that when the app has resumed they can still be executed. // Links: https://github.com/dotnet/maui/issues/11501 and https://bitwarden.atlassian.net/wiki/spaces/NMME/pages/664862722/MainPage+Assignments+not+working+on+Android+on+Background+or+App+resume private readonly Queue _onResumeActions = new Queue(); - private bool _hasNavigatedToAutofillWindow; #if ANDROID @@ -108,8 +107,6 @@ namespace Bit.App } } - public bool HasNavigatedToAccessibilitySettings { get; set; } - protected override Window CreateWindow(IActivationState activationState) { //When executing from AutofillExternalActivity we don't have "Options" so we need to filter "manually" @@ -122,53 +119,8 @@ namespace Bit.App return new Window(new NavigationPage()); //No actual page needed. Only used for auto-filling the fields directly (externally) } - //"Internal" Autofill and Uri/Otp/CreateSend. This is where we create the autofill specific Window - if (Options != null && (Options.FromAutofillFramework || Options.Uri != null || Options.OtpData != null || Options.CreateSend != null)) - { - _isResumed = true; //Specifically for the Autofill scenario we need to manually set the _isResumed here - _hasNavigatedToAutofillWindow = true; - return new AutoFillWindow(new NavigationPage(new AndroidNavigationRedirectPage())); - } - - var homePage = new HomePage(Options); - // WORKAROUND: If the user autofills with Accessibility Services enabled and goes back to the application then there is currently an issue - // where this method is called again - // thus it goes through here and the user goes to HomePage as we see here. - // So to solve this, the next flag check has been added which then turns on a flag on the home page - // that will trigger a navigation on the accounts manager when it loads; workarounding this behavior and navigating the user - // to the proper page depending on its state. - // WARNING: this doens't navigate the user to where they were but it acts as if the user had changed their account. - if(_hasNavigatedToAutofillWindow) - { - homePage.PerformNavigationOnAccountChangedOnLoad = true; - // this is needed because when coming back from AutofillWindow OnResume won't be called and we need this flag - // so that void Navigate(NavigationTarget navTarget, INavigationParams navParams) doesn't enqueue the navigation - // and it performs it directly. - _isResumed = true; - _hasNavigatedToAutofillWindow = false; - } - - // WORKAROUND: This workaround is similar to the one above (_hasNavigatedToAutofillWindow) related with Accessibility Services but this one specifically - // is due to trying to open the Accessibility Settings Page for enabled/disabling - if(HasNavigatedToAccessibilitySettings) - { - homePage.PerformNavigationOnAccountChangedOnLoad = true; - // this is needed because when coming back from AutofillWindow OnResume won't be called and we need this flag - // so that void Navigate(NavigationTarget navTarget, INavigationParams navParams) doesn't enqueue the navigation - // and it performs it directly. - _isResumed = true; - HasNavigatedToAccessibilitySettings = false; - } - - //If we have an existing MainAppWindow we can use that one - var mainAppWindow = Windows.OfType().FirstOrDefault(); - if (mainAppWindow != null) - { - mainAppWindow.PendingPage = new NavigationPage(homePage); - } - - //Create new main window - return new MainAppWindow(new NavigationPage(homePage)); + _isResumed = true; + return new ResumeWindow(new NavigationPage(new AndroidNavigationRedirectPage(Options))); } #else //iOS doesn't use the CreateWindow override used in Android so we just set the Application.Current.MainPage directly @@ -185,7 +137,7 @@ namespace Bit.App Application.Current.MainPage = value; } } - } + } #endif public App() : this(null) diff --git a/src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs b/src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs index 21fe1bd31..770e505a6 100644 --- a/src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs +++ b/src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs @@ -1,4 +1,6 @@ using Bit.App.Abstractions; +using Bit.App.Models; +using Bit.App.Pages; using Bit.Core.Abstractions; using Bit.Core.Utilities; @@ -6,19 +8,32 @@ namespace Bit.Core.Pages; public partial class AndroidNavigationRedirectPage : ContentPage { - private readonly IAccountsManager _accountsManager; - private readonly IConditionedAwaiterManager _conditionedAwaiterManager; - - public AndroidNavigationRedirectPage() + private AppOptions _options; + public AndroidNavigationRedirectPage(AppOptions options) { - _accountsManager = ServiceContainer.Resolve("accountsManager"); - _conditionedAwaiterManager = ServiceContainer.Resolve(); + _options = options ?? new AppOptions(); + InitializeComponent(); } private void AndroidNavigationRedirectPage_OnLoaded(object sender, EventArgs e) { - _accountsManager.NavigateOnAccountChangeAsync().FireAndForget(); - _conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated); + if (ServiceContainer.TryResolve(out var accountsManager)) + { + accountsManager.NavigateOnAccountChangeAsync().FireAndForget(); + } + else + { + Bit.App.App.MainPage = new NavigationPage(new HomePage(_options)); //Fallback scenario to load HomePage just in case something goes wrong when resolving IAccountsManager + } + + if (ServiceContainer.TryResolve(out var conditionedAwaiterManager)) + { + conditionedAwaiterManager?.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated); + } + else + { + LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("ConditionedAwaiterManager can't be resolved on Android Navigation redirection")); + } } } diff --git a/src/Core/Pages/Settings/AutofillSettingsPageViewModel.android.cs b/src/Core/Pages/Settings/AutofillSettingsPageViewModel.android.cs index 0d7ead1dd..90e3b42c3 100644 --- a/src/Core/Pages/Settings/AutofillSettingsPageViewModel.android.cs +++ b/src/Core/Pages/Settings/AutofillSettingsPageViewModel.android.cs @@ -158,11 +158,6 @@ namespace Bit.App.Pages await MainThread.InvokeOnMainThreadAsync(() => TriggerPropertyChanged(nameof(UseAccessibility))); return; } - -#if ANDROID - // WORKAROUND: Set workaround property to avoid an issue when launching the app after being in Accessibility Settings. More Info on App.xaml.cs - ((App)Application.Current).HasNavigatedToAccessibilitySettings = true; -#endif _deviceActionService.OpenAccessibilitySettings(); } diff --git a/src/Core/ResumeWindow.cs b/src/Core/ResumeWindow.cs index 643b7e98c..d124ed188 100644 --- a/src/Core/ResumeWindow.cs +++ b/src/Core/ResumeWindow.cs @@ -32,14 +32,4 @@ IsActive = false; } } - - public class MainAppWindow : ResumeWindow - { - public MainAppWindow(Page page) : base(page) { } - } - - public class AutoFillWindow : ResumeWindow - { - public AutoFillWindow(Page page) : base(page){ } - } }