Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-6552] Fix for Android Window issues when opening Autofill/Accessibility #3051

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
54 changes: 3 additions & 51 deletions src/Core/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public partial class App : Application, IAccountsManagerHost
// 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<Action> _onResumeActions = new Queue<Action>();
private bool _hasNavigatedToAutofillWindow;

#if ANDROID

Expand Down Expand Up @@ -108,8 +107,6 @@ public void SetAndroidOptions(AppOptions appOptions)
}
}

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"
Expand All @@ -122,53 +119,8 @@ protected override Window CreateWindow(IActivationState activationState)
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<MainAppWindow>().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
Expand All @@ -185,7 +137,7 @@ protected override Window CreateWindow(IActivationState activationState)
Application.Current.MainPage = value;
}
}
}
}
#endif

public App() : this(null)
Expand Down
27 changes: 19 additions & 8 deletions src/Core/Pages/AndroidNavigationRedirectPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Pages;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;

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<IAccountsManager>("accountsManager");
_conditionedAwaiterManager = ServiceContainer.Resolve<IConditionedAwaiterManager>();
_options = options ?? new AppOptions();

InitializeComponent();
}

private void AndroidNavigationRedirectPage_OnLoaded(object sender, EventArgs e)
{
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
_conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated);
if (ServiceContainer.TryResolve<IAccountsManager>(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<IConditionedAwaiterManager>(out var conditionedAwaiterManager))
{
conditionedAwaiterManager?.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated);
}
dinisvieira marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,6 @@ private async Task ToggleUseAccessibilityAsync()
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();
}

Expand Down
10 changes: 0 additions & 10 deletions src/Core/ResumeWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,4 @@ protected override void OnDeactivated()
IsActive = false;
}
}

public class MainAppWindow : ResumeWindow
{
public MainAppWindow(Page page) : base(page) { }
}

public class AutoFillWindow : ResumeWindow
{
public AutoFillWindow(Page page) : base(page){ }
}
}
Loading