From cfbbea59e9a687a7d5bcc1da1d70acf85e1a45cf Mon Sep 17 00:00:00 2001 From: mp-bw <59324545+mp-bw@users.noreply.github.com> Date: Tue, 19 Apr 2022 20:38:17 -0400 Subject: [PATCH] account switching in autofill UI (Android) (#1882) --- src/App/App.xaml.cs | 17 ++++++- src/App/Pages/Vault/AutofillCiphersPage.xaml | 25 +++++++++- .../Pages/Vault/AutofillCiphersPage.xaml.cs | 48 +++++++++++++++++-- .../Vault/AutofillCiphersPageViewModel.cs | 26 ++++++++-- 4 files changed, 106 insertions(+), 10 deletions(-) diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index f51974df2..8391cbf4b 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -208,7 +208,10 @@ namespace Bit.App { await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime()); } - SetTabsPageFromAutofill(isLocked); + if (!SetTabsPageFromAutofill(isLocked)) + { + ClearAutofillUri(); + } await SleptAsync(); } } @@ -365,7 +368,15 @@ namespace Bit.App } } - private void SetTabsPageFromAutofill(bool isLocked) + private void ClearAutofillUri() + { + if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri)) + { + Options.Uri = null; + } + } + + private bool SetTabsPageFromAutofill(bool isLocked) { if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) && !Options.FromAutofillFramework) @@ -385,7 +396,9 @@ namespace Bit.App } }); }); + return true; } + return false; } private void Prime() diff --git a/src/App/Pages/Vault/AutofillCiphersPage.xaml b/src/App/Pages/Vault/AutofillCiphersPage.xaml index 5bb8b4004..4a99f7b0e 100644 --- a/src/App/Pages/Vault/AutofillCiphersPage.xaml +++ b/src/App/Pages/Vault/AutofillCiphersPage.xaml @@ -15,7 +15,18 @@ - + + @@ -58,7 +69,7 @@ VerticalOptions="CenterAndExpand" Padding="20, 0" Spacing="20" - IsVisible="{Binding ShowList, Converter={StaticResource inverseBool}}"> + IsVisible="{Binding ShowNoData}"> @@ -88,6 +99,8 @@ AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0, 0, 1, 1"> + + + + diff --git a/src/App/Pages/Vault/AutofillCiphersPage.xaml.cs b/src/App/Pages/Vault/AutofillCiphersPage.xaml.cs index 69d04cda5..3c680aa05 100644 --- a/src/App/Pages/Vault/AutofillCiphersPage.xaml.cs +++ b/src/App/Pages/Vault/AutofillCiphersPage.xaml.cs @@ -1,5 +1,4 @@ using Bit.App.Models; -using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Utilities; @@ -15,7 +14,8 @@ namespace Bit.App.Pages public partial class AutofillCiphersPage : BaseContentPage { private readonly AppOptions _appOptions; - private readonly IPlatformUtilsService _platformUtilsService; + private readonly IBroadcasterService _broadcasterService; + private readonly ISyncService _syncService; private readonly IVaultTimeoutService _vaultTimeoutService; private AutofillCiphersPageViewModel _vm; @@ -24,17 +24,23 @@ namespace Bit.App.Pages { _appOptions = appOptions; InitializeComponent(); + SetActivityIndicator(_mainContent); _vm = BindingContext as AutofillCiphersPageViewModel; _vm.Page = this; _vm.Init(appOptions); - _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); + _broadcasterService = ServiceContainer.Resolve("broadcasterService"); + _syncService = ServiceContainer.Resolve("syncService"); _vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); } protected async override void OnAppearing() { base.OnAppearing(); + if (_syncService.SyncInProgress) + { + IsBusy = true; + } if (!await AppHelpers.IsVaultTimeoutImmediateAsync()) { await _vaultTimeoutService.CheckVaultTimeoutAsync(); @@ -43,6 +49,30 @@ namespace Bit.App.Pages { return; } + + _accountAvatar?.OnAppearing(); + _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); + + _broadcasterService.Subscribe(nameof(AutofillCiphersPage), async (message) => + { + if (message.Command == "syncStarted") + { + Device.BeginInvokeOnMainThread(() => IsBusy = true); + } + else if (message.Command == "syncCompleted") + { + await Task.Delay(500); + Device.BeginInvokeOnMainThread(() => + { + IsBusy = false; + if (_vm.LoadedOnce) + { + var task = _vm.LoadAsync(); + } + }); + } + }); + await LoadOnAppearedAsync(_mainLayout, false, async () => { try @@ -59,6 +89,11 @@ namespace Bit.App.Pages protected override bool OnBackButtonPressed() { + if (_accountListOverlay.IsVisible) + { + _accountListOverlay.HideAsync().FireAndForget(); + return true; + } if (Device.RuntimePlatform == Device.Android) { _appOptions.Uri = null; @@ -66,6 +101,13 @@ namespace Bit.App.Pages return base.OnBackButtonPressed(); } + protected override void OnDisappearing() + { + base.OnDisappearing(); + IsBusy = false; + _accountAvatar?.OnDisappearing(); + } + private async void RowSelected(object sender, SelectionChangedEventArgs e) { ((ExtendedCollectionView)sender).SelectedItem = null; diff --git a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs index 360b5d225..77e0dea0a 100644 --- a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs +++ b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xamarin.CommunityToolkit.ObjectModel; +using Bit.App.Controls; using Xamarin.Forms; namespace Bit.App.Pages @@ -23,8 +24,10 @@ namespace Bit.App.Pages private readonly ICipherService _cipherService; private readonly IStateService _stateService; private readonly IPasswordRepromptService _passwordRepromptService; + private readonly IMessagingService _messagingService; + private readonly ILogger _logger; - private AppOptions _appOptions; + private bool _showNoData; private bool _showList; private string _noDataText; private bool _websiteIconsEnabled; @@ -36,15 +39,30 @@ namespace Bit.App.Pages _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _stateService = ServiceContainer.Resolve("stateService"); _passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService"); + _messagingService = ServiceContainer.Resolve("messagingService"); + _logger = ServiceContainer.Resolve("logger"); GroupedItems = new ObservableRangeCollection(); CipherOptionsCommand = new Command(CipherOptionsAsync); + + AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) + { + AllowAddAccountRow = false + }; } public string Name { get; set; } public string Uri { get; set; } public Command CipherOptionsCommand { get; set; } + public bool LoadedOnce { get; set; } public ObservableRangeCollection GroupedItems { get; set; } + public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } + + public bool ShowNoData + { + get => _showNoData; + set => SetProperty(ref _showNoData, value); + } public bool ShowList { @@ -65,7 +83,6 @@ namespace Bit.App.Pages public void Init(AppOptions appOptions) { - _appOptions = appOptions; Uri = appOptions?.Uri; string name = null; if (Uri?.StartsWith(Constants.AndroidAppProtocol) ?? false) @@ -87,8 +104,10 @@ namespace Bit.App.Pages public async Task LoadAsync() { - WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault(); + LoadedOnce = true; ShowList = false; + ShowNoData = false; + WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault(); var groupedItems = new List(); var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null); var matching = ciphers.Item1?.Select(c => new GroupingsPageListItem { Cipher = c }).ToList(); @@ -150,6 +169,7 @@ namespace Bit.App.Pages } } ShowList = groupedItems.Any(); + ShowNoData = !ShowList; } public async Task SelectCipherAsync(CipherView cipher, bool fuzzy)