diff --git a/src/App/Platforms/Android/MainActivity.cs b/src/App/Platforms/Android/MainActivity.cs index ea72a165c..88fbceb8c 100644 --- a/src/App/Platforms/Android/MainActivity.cs +++ b/src/App/Platforms/Android/MainActivity.cs @@ -74,6 +74,10 @@ namespace Bit.Droid // this needs to be called here before base.OnCreate(...) Intent?.Validate(); + //We need to get and set the Options before calling OnCreate as that will "trigger" CreateWindow on App.xaml.cs + _appOptions = GetOptions(); + ((Bit.App.App)Microsoft.Maui.Controls.Application.Current).SetOptions(_appOptions); + base.OnCreate(savedInstanceState); _deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(_ => @@ -89,7 +93,6 @@ namespace Bit.Droid toplayout.FilterTouchesWhenObscured = true; } - _appOptions = GetOptions(); CreateNotificationChannel(); DisableAndroidFontScale(); diff --git a/src/Core/App.xaml.cs b/src/Core/App.xaml.cs index 7c714c03a..a3d2d1406 100644 --- a/src/Core/App.xaml.cs +++ b/src/Core/App.xaml.cs @@ -44,6 +44,85 @@ namespace Bit.App // 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(); +#if ANDROID + + /* + * ** Workaround for our Android crashes when trying to use Autofill ** + * + * This workaround works by managing the "Window Creation" ourselves. When we get an Autofill initialization we should create a new window instead of reusing the "Main/Current Window". + * While this workaround works, it's hard to execute the "workflow" that devices where we should navigate to. Below are some of the things we tried: + * 1 - Tried creating "new Window(new NavigationPage)" and invoking the code for handling the Navigations afterward. Issue with this is that the code that handles the navigations doesn't know which "Window" to use and calls the default "Window.Page" + * 2 - Tried using CustomWindow implementations to track the "WindowCreated" event and to be able to distinguish the different Window types (Default Window or Window for Autofill for example). + * This solution had a bit of overhear work and still required the app to set something line "new Window(new NavigationPage)" before actually knowing where we wanted to navigate to. + * + * Ideally we could figure out the Navigation we want to do before CreateWindow (on MainActivity.OnCreate) for example. But this needs to be done in async anyway we can't do async in both CreateWindow and MainActivity.OnCreate + */ + + public new static Page MainPage + { + get + { + return CurrentWindow?.Page; + } + set + { + if (CurrentWindow != null) + { + CurrentWindow.Page = value; + } + } + } + + public static Window CurrentWindow { get; private set; } + + public void SetOptions(AppOptions appOptions) + { + Options = appOptions; + } + + protected override Window CreateWindow(IActivationState activationState) + { + if (activationState != null + && activationState.State.TryGetValue("autofillFramework", out string autofillFramework) + && autofillFramework == "true") //TODO: There are likely better ways to filter this. Maybe using Options. + { + if (activationState.State.ContainsKey("autofillFrameworkCipherId")) //TODO: There are likely better ways to filter this. Maybe using Options. + { + return new Window(new NavigationPage()); //No actual page needed. Only used for auto-filling the fields directly (externally) + } + else + { + //TODO: IMPORTANT Question: this works if we want to show the CipherSelection, but we are skipping all our ASYNC Navigation workflows where we (for example) check if the user is logged in and/or needs to enter auth again. + var autofillWindow = new Window(new NavigationPage(new CipherSelectionPage(Options))); + return autofillWindow; + } + } + else if(CurrentWindow != null) + { + //TODO: This likely crashes if we try to have two apps side-by-side on Android + //TODO: Question: In these scenarios should a new Window be created or can the same one be reused? + return CurrentWindow; + } + else + { + CurrentWindow = new Window(new NavigationPage(new HomePage(Options))); + return CurrentWindow; + } + } +#elif IOS + public new static Page MainPage + { + get + { + return Application.Current.MainPage; + } + set + { + Application.Current.MainPage = value; + } + } +#endif + public App() : this(null) { } @@ -432,11 +511,11 @@ namespace Bit.App Options.Uri = null; if (isLocked) { - MainPage = new NavigationPage(new LockPage()); + App.MainPage = new NavigationPage(new LockPage()); } else { - MainPage = new TabsPage(); + App.MainPage = new TabsPage(); } }); }); @@ -463,7 +542,6 @@ namespace Bit.App { UpdateThemeAsync(); }; - MainPage = new NavigationPage(new HomePage(Options)); _accountsManager.NavigateOnAccountChangeAsync().FireAndForget(); ServiceContainer.Resolve("platformUtilsService").Init(); } @@ -535,36 +613,36 @@ namespace Bit.App switch (navTarget) { case NavigationTarget.HomeLogin: - MainPage = new NavigationPage(new HomePage(Options)); + App.MainPage = new NavigationPage(new HomePage(Options)); break; case NavigationTarget.Login: if (navParams is LoginNavigationParams loginParams) { - MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options)); + App.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options)); } break; case NavigationTarget.Lock: if (navParams is LockNavigationParams lockParams) { - MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric)); + App.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric)); } else { - MainPage = new NavigationPage(new LockPage(Options)); + App.MainPage = new NavigationPage(new LockPage(Options)); } break; case NavigationTarget.Home: - MainPage = new TabsPage(Options); + App.MainPage = new TabsPage(Options); break; case NavigationTarget.AddEditCipher: - MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options)); + App.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options)); break; case NavigationTarget.AutofillCiphers: case NavigationTarget.OtpCipherSelection: - MainPage = new NavigationPage(new CipherSelectionPage(Options)); + App.MainPage = new NavigationPage(new CipherSelectionPage(Options)); break; case NavigationTarget.SendAddEdit: - MainPage = new NavigationPage(new SendAddEditPage(Options)); + App.MainPage = new NavigationPage(new SendAddEditPage(Options)); break; } } diff --git a/src/Core/Pages/Accounts/LockPage.xaml.cs b/src/Core/Pages/Accounts/LockPage.xaml.cs index d60820eb6..0cf421abe 100644 --- a/src/Core/Pages/Accounts/LockPage.xaml.cs +++ b/src/Core/Pages/Accounts/LockPage.xaml.cs @@ -199,7 +199,7 @@ namespace Bit.App.Pages } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } } } diff --git a/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs b/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs index ea8eb015f..65d212b5a 100644 --- a/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs @@ -36,7 +36,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } private async Task StartLogInWithMasterPasswordAsync() diff --git a/src/Core/Pages/Accounts/LoginPage.xaml.cs b/src/Core/Pages/Accounts/LoginPage.xaml.cs index 82125add3..84f62879f 100644 --- a/src/Core/Pages/Accounts/LoginPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPage.xaml.cs @@ -193,7 +193,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } private async Task UpdateTempPasswordAsync() diff --git a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs index ddc7addbc..ad5e3aba3 100644 --- a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs @@ -53,7 +53,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } private async Task UpdateTempPasswordAsync() diff --git a/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs b/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs index ad05b5a25..3760f961e 100644 --- a/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs @@ -120,11 +120,11 @@ namespace Bit.App.Pages if (await _vaultTimeoutService.IsLockedAsync()) { - Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions)); + App.MainPage = new NavigationPage(new LockPage(_appOptions)); } else { - Application.Current.MainPage = new TabsPage(_appOptions, null); + App.MainPage = new TabsPage(_appOptions, null); } } } diff --git a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs index 3fdf38f8e..5504b9d43 100644 --- a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs +++ b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs @@ -69,7 +69,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } } } diff --git a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs index ee92ca538..1b218dc3d 100644 --- a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs +++ b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs @@ -192,7 +192,7 @@ namespace Bit.App.Pages private void TwoFactorAuthSuccessWithSSOLocked() { - Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions)); + App.MainPage = new NavigationPage(new LockPage(_appOptions)); } private async Task TwoFactorAuthSuccessToMainAsync() @@ -202,7 +202,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new TabsPage(_appOptions, previousPage); + App.MainPage = new TabsPage(_appOptions, previousPage); } private void Token_TextChanged(object sender, TextChangedEventArgs e) diff --git a/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs b/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs index a8e0d5f66..792e4a63a 100644 --- a/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs +++ b/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs @@ -184,7 +184,7 @@ namespace Bit.App.Pages { if (FromAutofillFramework) { - Microsoft.Maui.Controls.Application.Current.MainPage = new TabsPage(); + App.MainPage = new TabsPage(); return true; } return base.OnBackButtonPressed(); diff --git a/src/Core/Utilities/AppHelpers.cs b/src/Core/Utilities/AppHelpers.cs index 07db2fb81..c16e97849 100644 --- a/src/Core/Utilities/AppHelpers.cs +++ b/src/Core/Utilities/AppHelpers.cs @@ -430,19 +430,19 @@ namespace Bit.App.Utilities { if (appOptions.FromAutofillFramework && appOptions.SaveType.HasValue) { - Application.Current.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: appOptions)); + App.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: appOptions)); return true; } if (appOptions.Uri != null || appOptions.OtpData != null) { - Application.Current.MainPage = new NavigationPage(new CipherSelectionPage(appOptions)); + App.MainPage = new NavigationPage(new CipherSelectionPage(appOptions)); return true; } if (appOptions.CreateSend != null) { - Application.Current.MainPage = new NavigationPage(new SendAddEditPage(appOptions)); + App.MainPage = new NavigationPage(new SendAddEditPage(appOptions)); return true; } } diff --git a/src/iOS.Core/Services/DeviceActionService.cs b/src/iOS.Core/Services/DeviceActionService.cs index 36f13679f..3b91a5da4 100644 --- a/src/iOS.Core/Services/DeviceActionService.cs +++ b/src/iOS.Core/Services/DeviceActionService.cs @@ -252,7 +252,7 @@ namespace Bit.iOS.Core.Services { if (Application.Current is App.App app && app.Options != null && !app.Options.IosExtension) { - return app.MainPage.DisplayActionSheet(title, cancel, destruction, buttons); + return Bit.App.App.MainPage.DisplayActionSheet(title, cancel, destruction, buttons); } var vc = GetPresentedViewController(); if (vc is null)