1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-06-30 11:14:51 +02:00

PM-3349 Added base structure for avoiding Android Autofill crash. This workaround works but it's not complete as it can't handle the entire workflow when showing CipherSelectionPAge (like checking if it should show LockPage)

This commit is contained in:
Dinis Vieira 2023-12-10 15:05:08 +00:00
parent 5803635f44
commit 17acb57732
No known key found for this signature in database
GPG Key ID: 9389160FF6C295F3
12 changed files with 107 additions and 26 deletions

View File

@ -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();

View File

@ -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<Action> _onResumeActions = new Queue<Action>();
#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<MobilePlatformUtilsService>("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;
}
}

View File

@ -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);
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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)