diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPage.xaml.cs b/src/App/Pages/Settings/SettingsPage/SettingsPage.xaml.cs index ac0cfdf04..47a8a22c4 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPage.xaml.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPage.xaml.cs @@ -2,18 +2,13 @@ using System.ComponentModel; using System.Linq; using System.Threading.Tasks; -using Bit.App.Abstractions; using Bit.App.Controls; -using Bit.App.Pages.Accounts; -using Bit.App.Resources; -using Bit.Core.Utilities; using Xamarin.Forms; namespace Bit.App.Pages { public partial class SettingsPage : BaseContentPage { - private readonly IDeviceActionService _deviceActionService; private readonly TabsPage _tabsPage; private SettingsPageViewModel _vm; @@ -21,7 +16,6 @@ namespace Bit.App.Pages { _tabsPage = tabsPage; InitializeComponent(); - _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _vm = BindingContext as SettingsPageViewModel; _vm.Page = this; } @@ -67,122 +61,12 @@ namespace Bit.App.Pages } } - private async void RowSelected(object sender, SelectionChangedEventArgs e) + private void RowSelected(object sender, SelectionChangedEventArgs e) { ((ExtendedCollectionView)sender).SelectedItem = null; - if (!DoOnce()) + if (e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item) { - return; - } - if (!(e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item)) - { - return; - } - - if (item.Name == AppResources.Sync) - { - await Navigation.PushModalAsync(new NavigationPage(new SyncPage())); - } - else if (item.Name == AppResources.AutofillServices) - { - await Navigation.PushModalAsync(new NavigationPage(new AutofillServicesPage(this))); - } - else if (item.Name == AppResources.PasswordAutofill) - { - await Navigation.PushModalAsync(new NavigationPage(new AutofillPage())); - } - else if (item.Name == AppResources.AppExtension) - { - await Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())); - } - else if (item.Name == AppResources.Options) - { - await Navigation.PushModalAsync(new NavigationPage(new OptionsPage())); - } - else if (item.Name == AppResources.Folders) - { - await Navigation.PushModalAsync(new NavigationPage(new FoldersPage())); - } - else if (item.Name == AppResources.About) - { - await _vm.AboutAsync(); - } - else if (item.Name == AppResources.HelpAndFeedback) - { - _vm.Help(); - } - else if (item.Name == AppResources.FingerprintPhrase) - { - await _vm.FingerprintAsync(); - } - else if (item.Name == AppResources.RateTheApp) - { - _vm.Rate(); - } - else if (item.Name == AppResources.ImportItems) - { - _vm.Import(); - } - else if (item.Name == AppResources.ExportVault) - { - await Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())); - } - else if (item.Name == AppResources.LearnOrg) - { - await _vm.ShareAsync(); - } - else if (item.Name == AppResources.WebVault) - { - _vm.WebVault(); - } - else if (item.Name == AppResources.ChangeMasterPassword) - { - await _vm.ChangePasswordAsync(); - } - else if (item.Name == AppResources.TwoStepLogin) - { - await _vm.TwoStepAsync(); - } - else if (item.Name == AppResources.LogOut) - { - await _vm.LogOutAsync(); - } - else if (item.Name == AppResources.DeleteAccount) - { - await Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())); - } - else if (item.Name == AppResources.LockNow) - { - await _vm.LockAsync(); - } - else if (item.Name == AppResources.VaultTimeout) - { - await _vm.VaultTimeoutAsync(); - } - else if (item.Name == AppResources.VaultTimeoutAction) - { - await _vm.VaultTimeoutActionAsync(); - } - else if (item.Name == AppResources.UnlockWithPIN) - { - await _vm.UpdatePinAsync(); - } - else if (item.Name == AppResources.SubmitCrashLogs) - { - await _vm.LoggerReportingAsync(); - } - else - { - var biometricName = AppResources.Biometrics; - if (Device.RuntimePlatform == Device.iOS) - { - var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync(); - biometricName = supportsFace ? AppResources.FaceID : AppResources.TouchID; - } - if (item.Name == string.Format(AppResources.UnlockWith, biometricName)) - { - await _vm.UpdateBiometricAsync(); - } + _vm?.ExecuteSettingItemCommand.Execute(item); } } } diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageListItem.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageListItem.cs index c1e8878f4..a4d3e926d 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageListItem.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageListItem.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Bit.App.Resources; using Bit.App.Utilities; using Xamarin.Forms; @@ -12,6 +13,8 @@ namespace Bit.App.Pages public string SubLabel { get; set; } public TimeSpan? Time { get; set; } public bool UseFrame { get; set; } + public Func ExecuteAsync { get; set; } + public bool SubLabelTextEnabled => SubLabel == AppResources.Enabled; public string LineBreakMode => SubLabel == null ? "TailTruncation" : ""; public bool ShowSubLabel => SubLabel.Length != 0; diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 831be4979..59d76f762 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Bit.App.Abstractions; +using Bit.App.Pages.Accounts; using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Enums; @@ -84,10 +85,14 @@ namespace Bit.App.Pages GroupedItems = new ObservableRangeCollection(); PageTitle = AppResources.Settings; + + ExecuteSettingItemCommand = new AsyncCommand(item => item.ExecuteAsync(), onException: _loggerService.Exception, allowsMultipleExecutions: false); } public ObservableRangeCollection GroupedItems { get; set; } + public IAsyncCommand ExecuteSettingItemCommand { get; } + public async Task InitAsync() { _supportsBiometric = await _platformUtilsService.SupportsBiometricAsync(); @@ -434,6 +439,8 @@ namespace Bit.App.Pages public void BuildList() { + //TODO: Refactor this once navigation is abstracted so that it doesn't depend on Page, e.g. Page.Navigation.PushModalAsync... + var doUpper = Device.RuntimePlatform != Device.Android; var autofillItems = new List(); if (Device.RuntimePlatform == Device.Android) @@ -441,38 +448,69 @@ namespace Bit.App.Pages autofillItems.Add(new SettingsPageListItem { Name = AppResources.AutofillServices, - SubLabel = _deviceActionService.AutofillServicesEnabled() ? - AppResources.Enabled : AppResources.Disabled + SubLabel = _deviceActionService.AutofillServicesEnabled() ? AppResources.Enabled : AppResources.Disabled, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillServicesPage(Page as SettingsPage))) }); } else { if (_deviceActionService.SystemMajorVersion() >= 12) { - autofillItems.Add(new SettingsPageListItem { Name = AppResources.PasswordAutofill }); + autofillItems.Add(new SettingsPageListItem + { + Name = AppResources.PasswordAutofill, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())) + }); } - autofillItems.Add(new SettingsPageListItem { Name = AppResources.AppExtension }); + autofillItems.Add(new SettingsPageListItem + { + Name = AppResources.AppExtension, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())) + }); } var manageItems = new List { - new SettingsPageListItem { Name = AppResources.Folders }, - new SettingsPageListItem { Name = AppResources.Sync, SubLabel = _lastSyncDate } + new SettingsPageListItem + { + Name = AppResources.Folders, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage())) + }, + new SettingsPageListItem + { + Name = AppResources.Sync, + SubLabel = _lastSyncDate, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new SyncPage())) + } }; var securityItems = new List { - new SettingsPageListItem { Name = AppResources.VaultTimeout, SubLabel = _vaultTimeoutDisplayValue }, + new SettingsPageListItem + { + Name = AppResources.VaultTimeout, + SubLabel = _vaultTimeoutDisplayValue, + ExecuteAsync = () => VaultTimeoutAsync() }, new SettingsPageListItem { Name = AppResources.VaultTimeoutAction, - SubLabel = _vaultTimeoutActionDisplayValue + SubLabel = _vaultTimeoutActionDisplayValue, + ExecuteAsync = () => VaultTimeoutActionAsync() }, new SettingsPageListItem { Name = AppResources.UnlockWithPIN, - SubLabel = _pin ? AppResources.Enabled : AppResources.Disabled + SubLabel = _pin ? AppResources.Enabled : AppResources.Disabled, + ExecuteAsync = () => UpdatePinAsync() }, - new SettingsPageListItem { Name = AppResources.LockNow }, - new SettingsPageListItem { Name = AppResources.TwoStepLogin } + new SettingsPageListItem + { + Name = AppResources.LockNow, + ExecuteAsync = () => LockAsync() + }, + new SettingsPageListItem + { + Name = AppResources.TwoStepLogin, + ExecuteAsync = () => TwoStepAsync() + } }; if (_supportsBiometric || _biometric) { @@ -485,7 +523,8 @@ namespace Bit.App.Pages var item = new SettingsPageListItem { Name = string.Format(AppResources.UnlockWith, biometricName), - SubLabel = _biometric ? AppResources.Enabled : AppResources.Disabled + SubLabel = _biometric ? AppResources.Enabled : AppResources.Disabled, + ExecuteAsync = () => UpdateBiometricAsync() }; securityItems.Insert(2, item); } @@ -510,38 +549,87 @@ namespace Bit.App.Pages } var accountItems = new List { - new SettingsPageListItem { Name = AppResources.FingerprintPhrase }, - new SettingsPageListItem { Name = AppResources.LogOut } + new SettingsPageListItem + { + Name = AppResources.FingerprintPhrase, + ExecuteAsync = () => FingerprintAsync() + }, + new SettingsPageListItem + { + Name = AppResources.LogOut, + ExecuteAsync = () => LogOutAsync() + } }; if (_showChangeMasterPassword) { - accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword }); + accountItems.Insert(0, new SettingsPageListItem + { + Name = AppResources.ChangeMasterPassword, + ExecuteAsync = () => ChangePasswordAsync() + }); } var toolsItems = new List { - new SettingsPageListItem { Name = AppResources.ImportItems }, - new SettingsPageListItem { Name = AppResources.ExportVault } + new SettingsPageListItem + { + Name = AppResources.ImportItems, + ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Import()) + }, + new SettingsPageListItem + { + Name = AppResources.ExportVault, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())) + } }; if (IncludeLinksWithSubscriptionInfo()) { - toolsItems.Add(new SettingsPageListItem { Name = AppResources.LearnOrg }); - toolsItems.Add(new SettingsPageListItem { Name = AppResources.WebVault }); + toolsItems.Add(new SettingsPageListItem + { + Name = AppResources.LearnOrg, + ExecuteAsync = () => ShareAsync() + }); + toolsItems.Add(new SettingsPageListItem + { + Name = AppResources.WebVault, + ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => WebVault()) + }); } var otherItems = new List { - new SettingsPageListItem { Name = AppResources.Options }, - new SettingsPageListItem { Name = AppResources.About }, - new SettingsPageListItem { Name = AppResources.HelpAndFeedback }, + new SettingsPageListItem + { + Name = AppResources.Options, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new OptionsPage())) + }, + new SettingsPageListItem + { + Name = AppResources.About, + ExecuteAsync = () => AboutAsync() + }, + new SettingsPageListItem + { + Name = AppResources.HelpAndFeedback, + ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Help()) + }, #if !FDROID new SettingsPageListItem { Name = AppResources.SubmitCrashLogs, SubLabel = _reportLoggingEnabled ? AppResources.Enabled : AppResources.Disabled, + ExecuteAsync = () => LoggerReportingAsync() }, #endif - new SettingsPageListItem { Name = AppResources.RateTheApp }, - new SettingsPageListItem { Name = AppResources.DeleteAccount } + new SettingsPageListItem + { + Name = AppResources.RateTheApp, + ExecuteAsync = () => Device.InvokeOnMainThreadAsync(() => Rate()) + }, + new SettingsPageListItem + { + Name = AppResources.DeleteAccount, + ExecuteAsync = () => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())) + } }; // TODO: improve this. Leaving this as is to reduce error possibility on the hotfix.