account switching in autofill UI (Android) (#1882)

This commit is contained in:
mp-bw 2022-04-19 20:38:17 -04:00 committed by GitHub
parent 14d4b2ee29
commit cfbbea59e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 10 deletions

View File

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

View File

@ -15,7 +15,18 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="search.png" Clicked="Search_Clicked" />
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
@ -58,7 +69,7 @@
VerticalOptions="CenterAndExpand"
Padding="20, 0"
Spacing="20"
IsVisible="{Binding ShowList, Converter={StaticResource inverseBool}}">
IsVisible="{Binding ShowNoData}">
<Label
Text="{Binding NoDataText}"
HorizontalTextAlignment="Center"></Label>
@ -88,6 +99,8 @@
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<!-- Android FAB -->
<Button
x:Name="_fab"
Image="plus.png"
@ -99,6 +112,14 @@
<effects:FabShadowEffect />
</Button.Effects>
</Button>
<controls:AccountSwitchingOverlayView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
MainPage="{Binding Source={x:Reference _page}}"
MainFab="{Binding Source={x:Reference _fab}, Path=.}"
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@ -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<IPlatformUtilsService>("platformUtilsService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("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;

View File

@ -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<IDeviceActionService>("deviceActionService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
CipherOptionsCommand = new Command<CipherView>(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<IGroupingsPageListItem> 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<GroupingsPageListGroup>();
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)