1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-06 18:43:43 +00:00

Merge branch 'feature/maui-migration' of https://github.com/bitwarden/mobile into feature/maui-migration

# Conflicts:
#	src/Core/Pages/Settings/OtherSettingsPageViewModel.cs
This commit is contained in:
Federico Maccaroni
2023-11-20 10:49:42 -03:00
148 changed files with 1755 additions and 942 deletions

View File

@@ -1,13 +1,10 @@
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using Bit.App.Utilities;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -29,32 +26,32 @@ namespace Bit.App.Pages
var environmentService = ServiceContainer.Resolve<IEnvironmentService>();
var clipboardService = ServiceContainer.Resolve<IClipboardService>();
ToggleSubmitCrashLogsCommand = CreateDefaultAsyncCommnad(ToggleSubmitCrashLogsAsync);
ToggleSubmitCrashLogsCommand = CreateDefaultAsyncRelayCommand(ToggleSubmitCrashLogsAsync, allowsMultipleExecutions: false);
GoToHelpCenterCommand = CreateDefaultAsyncCommnad(
GoToHelpCenterCommand = CreateDefaultAsyncRelayCommand(
() => LaunchUriAsync(AppResources.LearnMoreAboutHowToUseBitwardenOnTheHelpCenter,
AppResources.ContinueToHelpCenter,
ExternalLinksConstants.HELP_CENTER));
ExternalLinksConstants.HELP_CENTER), allowsMultipleExecutions: false);
ContactBitwardenSupportCommand = CreateDefaultAsyncCommnad(
ContactBitwardenSupportCommand = CreateDefaultAsyncRelayCommand(
() => LaunchUriAsync(AppResources.ContactSupportDescriptionLong,
AppResources.ContinueToContactSupport,
ExternalLinksConstants.CONTACT_SUPPORT));
ExternalLinksConstants.CONTACT_SUPPORT), allowsMultipleExecutions: false);
GoToWebVaultCommand = CreateDefaultAsyncCommnad(
GoToWebVaultCommand = CreateDefaultAsyncRelayCommand(
() => LaunchUriAsync(AppResources.ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp,
AppResources.ContinueToWebApp,
environmentService.GetWebVaultUrl()));
environmentService.GetWebVaultUrl()), allowsMultipleExecutions: false);
GoToLearnAboutOrgsCommand = CreateDefaultAsyncCommnad(
GoToLearnAboutOrgsCommand = CreateDefaultAsyncRelayCommand(
() => LaunchUriAsync(AppResources.LearnAboutOrganizationsDescriptionLong,
string.Format(AppResources.ContinueToX, ExternalLinksConstants.BITWARDEN_WEBSITE),
ExternalLinksConstants.HELP_ABOUT_ORGANIZATIONS));
ExternalLinksConstants.HELP_ABOUT_ORGANIZATIONS), allowsMultipleExecutions: false);
RateTheAppCommand = CreateDefaultAsyncCommnad(RateAppAsync);
RateTheAppCommand = CreateDefaultAsyncRelayCommand(RateAppAsync, allowsMultipleExecutions: false);
CopyAppInfoCommand = CreateDefaultAsyncCommnad(
() => clipboardService.CopyTextAsync(AppInfo));
CopyAppInfoCommand = CreateDefaultAsyncRelayCommand(
() => clipboardService.CopyTextAsync(AppInfo), allowsMultipleExecutions: false);
}
public bool ShouldSubmitCrashLogs
@@ -80,7 +77,7 @@ namespace Bit.App.Pages
}
}
public AsyncCommand ToggleSubmitCrashLogsCommand { get; }
public AsyncRelayCommand ToggleSubmitCrashLogsCommand { get; }
public ICommand GoToHelpCenterCommand { get; }
public ICommand ContactBitwardenSupportCommand { get; }
public ICommand GoToWebVaultCommand { get; }
@@ -97,7 +94,7 @@ namespace Bit.App.Pages
MainThread.BeginInvokeOnMainThread(() =>
{
TriggerPropertyChanged(nameof(ShouldSubmitCrashLogs));
ToggleSubmitCrashLogsCommand.RaiseCanExecuteChanged();
ToggleSubmitCrashLogsCommand.NotifyCanExecuteChanged();
});
}

View File

@@ -1,16 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -64,7 +58,7 @@ namespace Bit.App.Pages
() => _inited,
ex => HandleException(ex));
ToggleShowWebsiteIconsCommand = CreateDefaultAsyncCommnad(ToggleShowWebsiteIconsAsync, () => _inited);
ToggleShowWebsiteIconsCommand = CreateDefaultAsyncRelayCommand(ToggleShowWebsiteIconsAsync, () => _inited, allowsMultipleExecutions: false);
}
public PickerViewModel<string> LanguagePickerViewModel { get; }
@@ -87,7 +81,7 @@ namespace Bit.App.Pages
public bool IsShowWebsiteIconsEnabled => ToggleShowWebsiteIconsCommand.CanExecute(null);
public AsyncCommand ToggleShowWebsiteIconsCommand { get; }
public AsyncRelayCommand ToggleShowWebsiteIconsCommand { get; }
public async Task InitAsync()
{
@@ -102,10 +96,10 @@ namespace Bit.App.Pages
MainThread.BeginInvokeOnMainThread(() =>
{
ToggleShowWebsiteIconsCommand.RaiseCanExecuteChanged();
LanguagePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
ThemePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
DefaultDarkThemePickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
ToggleShowWebsiteIconsCommand.NotifyCanExecuteChanged();
LanguagePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
ThemePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
DefaultDarkThemePickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
});
}

View File

@@ -1,10 +1,6 @@
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.Core.Resources.Localization;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using Bit.App.Utilities;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -16,8 +12,7 @@ namespace Bit.App.Pages
private bool _useDrawOver;
private bool _askToAddLogin;
public bool SupportsAndroidAutofillServices => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
Device.RuntimePlatform == Device.Android && _deviceActionService.SupportsAutofillServices();
public bool SupportsAndroidAutofillServices => DeviceInfo.Platform == DevicePlatform.Android && _deviceActionService.SupportsAutofillServices();
public bool UseAutofillServices
{
@@ -45,8 +40,7 @@ Device.RuntimePlatform == Device.Android && _deviceActionService.SupportsAutofil
}
}
public bool ShowUseAccessibilityToggle => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
Device.RuntimePlatform == Device.Android;
public bool ShowUseAccessibilityToggle => DeviceInfo.Platform == DevicePlatform.Android;
public string UseAccessibilityDescription => _deviceActionService.GetAutofillAccessibilityDescription();
@@ -90,21 +84,21 @@ Device.RuntimePlatform == Device.Android;
}
}
public AsyncCommand ToggleUseAutofillServicesCommand { get; private set; }
public AsyncCommand ToggleUseInlineAutofillCommand { get; private set; }
public AsyncCommand ToggleUseAccessibilityCommand { get; private set; }
public AsyncCommand ToggleUseDrawOverCommand { get; private set; }
public AsyncCommand ToggleAskToAddLoginCommand { get; private set; }
public AsyncRelayCommand ToggleUseAutofillServicesCommand { get; private set; }
public AsyncRelayCommand ToggleUseInlineAutofillCommand { get; private set; }
public AsyncRelayCommand ToggleUseAccessibilityCommand { get; private set; }
public AsyncRelayCommand ToggleUseDrawOverCommand { get; private set; }
public AsyncRelayCommand ToggleAskToAddLoginCommand { get; private set; }
public ICommand GoToBlockAutofillUrisCommand { get; private set; }
private void InitAndroidCommands()
{
ToggleUseAutofillServicesCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseAutofillServices()), () => _inited);
ToggleUseInlineAutofillCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseInlineAutofillEnabledAsync()), () => _inited);
ToggleUseAccessibilityCommand = CreateDefaultAsyncCommnad(ToggleUseAccessibilityAsync, () => _inited);
ToggleUseDrawOverCommand = CreateDefaultAsyncCommnad(() => MainThread.InvokeOnMainThreadAsync(() => ToggleDrawOver()), () => _inited);
ToggleAskToAddLoginCommand = CreateDefaultAsyncCommnad(ToggleAskToAddLoginAsync, () => _inited);
GoToBlockAutofillUrisCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushAsync(new BlockAutofillUrisPage()));
ToggleUseAutofillServicesCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseAutofillServices()), () => _inited, allowsMultipleExecutions: false);
ToggleUseInlineAutofillCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseInlineAutofillEnabledAsync()), () => _inited, allowsMultipleExecutions: false);
ToggleUseAccessibilityCommand = CreateDefaultAsyncRelayCommand(ToggleUseAccessibilityAsync, () => _inited, allowsMultipleExecutions: false);
ToggleUseDrawOverCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleDrawOver()), () => _inited, allowsMultipleExecutions: false);
ToggleAskToAddLoginCommand = CreateDefaultAsyncRelayCommand(ToggleAskToAddLoginAsync, () => _inited, allowsMultipleExecutions: false);
GoToBlockAutofillUrisCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushAsync(new BlockAutofillUrisPage()), allowsMultipleExecutions: false);
}
private async Task InitAndroidAutofillSettingsAsync()

View File

@@ -1,13 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using Bit.App.Utilities;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -36,7 +33,7 @@ namespace Bit.App.Pages
() => _inited,
ex => HandleException(ex));
ToggleCopyTotpAutomaticallyCommand = CreateDefaultAsyncCommnad(ToggleCopyTotpAutomaticallyAsync, () => _inited);
ToggleCopyTotpAutomaticallyCommand = CreateDefaultAsyncRelayCommand(ToggleCopyTotpAutomaticallyAsync, () => _inited, allowsMultipleExecutions: false);
InitAndroidCommands();
InitIOSCommands();
@@ -56,7 +53,7 @@ namespace Bit.App.Pages
public PickerViewModel<UriMatchType> DefaultUriMatchDetectionPickerViewModel { get; }
public AsyncCommand ToggleCopyTotpAutomaticallyCommand { get; private set; }
public AsyncRelayCommand ToggleCopyTotpAutomaticallyCommand { get; private set; }
public async Task InitAsync()
{
@@ -72,11 +69,11 @@ namespace Bit.App.Pages
{
TriggerPropertyChanged(nameof(CopyTotpAutomatically));
ToggleUseAutofillServicesCommand.RaiseCanExecuteChanged();
ToggleUseInlineAutofillCommand.RaiseCanExecuteChanged();
ToggleUseAccessibilityCommand.RaiseCanExecuteChanged();
ToggleUseDrawOverCommand.RaiseCanExecuteChanged();
DefaultUriMatchDetectionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
ToggleUseAutofillServicesCommand.NotifyCanExecuteChanged();
ToggleUseInlineAutofillCommand.NotifyCanExecuteChanged();
ToggleUseAccessibilityCommand.NotifyCanExecuteChanged();
ToggleUseDrawOverCommand.NotifyCanExecuteChanged();
DefaultUriMatchDetectionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
});
}

View File

@@ -1,21 +1,18 @@
using System.Windows.Input;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
public partial class AutofillSettingsPageViewModel
{
public bool SupportsiOSAutofill => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
Device.RuntimePlatform == Device.iOS && _deviceActionService.SupportsAutofillServices();
public bool SupportsiOSAutofill => DeviceInfo.Platform == DevicePlatform.iOS && _deviceActionService.SupportsAutofillServices();
public ICommand GoToPasswordAutofillCommand { get; private set; }
public ICommand GoToAppExtensionCommand { get; private set; }
private void InitIOSCommands()
{
GoToPasswordAutofillCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())));
GoToAppExtensionCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())));
GoToPasswordAutofillCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new AutofillPage())), allowsMultipleExecutions: false);
GoToAppExtensionCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExtensionPage())), allowsMultipleExecutions: false);
}
}
}

View File

@@ -28,11 +28,11 @@ namespace Bit.App.Pages
_stateService = ServiceContainer.Resolve<IStateService>();
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>();
AddUriCommand = new AsyncCommand(AddUriAsync,
AddUriCommand = CreateDefaultAsyncRelayCommand(AddUriAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
EditUriCommand = new AsyncCommand<BlockAutofillUriItemViewModel>(EditUriAsync,
EditUriCommand = CreateDefaultAsyncRelayCommand<BlockAutofillUriItemViewModel>(EditUriAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
}

View File

@@ -13,6 +13,7 @@ using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using Bit.App.Utilities;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -34,11 +35,11 @@ namespace Bit.App.Pages
PageTitle = AppResources.PendingLogInRequests;
LoginRequests = new ObservableRangeCollection<PasswordlessLoginResponse>();
AnswerRequestCommand = new AsyncCommand<PasswordlessLoginResponse>(PasswordlessLoginAsync,
AnswerRequestCommand = CreateDefaultAsyncRelayCommand<PasswordlessLoginResponse>(PasswordlessLoginAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
DeclineAllRequestsCommand = new AsyncCommand(DeclineAllRequestsAsync,
DeclineAllRequestsCommand = CreateDefaultAsyncRelayCommand(DeclineAllRequestsAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
@@ -47,9 +48,9 @@ namespace Bit.App.Pages
public ICommand RefreshCommand { get; }
public AsyncCommand<PasswordlessLoginResponse> AnswerRequestCommand { get; }
public AsyncRelayCommand<PasswordlessLoginResponse> AnswerRequestCommand { get; }
public AsyncCommand DeclineAllRequestsCommand { get; }
public AsyncRelayCommand DeclineAllRequestsCommand { get; }
public ObservableRangeCollection<PasswordlessLoginResponse> LoginRequests { get; }

View File

@@ -53,6 +53,7 @@
Title="{u:I18n AllowScreenCapture}"
IsToggled="{Binding IsScreenCaptureAllowed}"
IsEnabled="{Binding CanToggleeScreenCaptureAllowed}"
IsVisible="{OnPlatform Android=True, iOS=False}"
AutomationId="AllowScreenCaptureSwitch"
StyleClass="settings-item-view"
HorizontalOptions="FillAndExpand" />

View File

@@ -1,16 +1,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using Bit.App.Utilities;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
@@ -43,19 +35,21 @@ namespace Bit.App.Pages
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
_logger = ServiceContainer.Resolve<ILogger>();
SyncCommand = CreateDefaultAsyncCommnad(SyncAsync, () => _inited);
ToggleIsScreenCaptureAllowedCommand = CreateDefaultAsyncCommnad(ToggleIsScreenCaptureAllowedAsync, () => _inited);
ToggleShouldConnectToWatchCommand = CreateDefaultAsyncCommnad(ToggleShouldConnectToWatchAsync, () => _inited);
SyncCommand = CreateDefaultAsyncRelayCommand(SyncAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
ToggleIsScreenCaptureAllowedCommand = CreateDefaultAsyncRelayCommand(ToggleIsScreenCaptureAllowedAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
ToggleShouldConnectToWatchCommand = CreateDefaultAsyncRelayCommand(ToggleShouldConnectToWatchAsync, CanExecuteIfInited, allowsMultipleExecutions: false);
ClearClipboardPickerViewModel = new PickerViewModel<int>(
_deviceActionService,
_logger,
OnClearClipboardChangingAsync,
AppResources.ClearClipboard,
() => _inited,
CanExecuteIfInited,
ex => HandleException(ex));
}
private bool CanExecuteIfInited() => _inited;
public bool EnableSyncOnRefresh
{
get => _syncOnRefresh;
@@ -104,9 +98,9 @@ namespace Bit.App.Pages
public bool CanToggleShouldConnectToWatch => ToggleShouldConnectToWatchCommand.CanExecute(null);
public AsyncCommand SyncCommand { get; }
public AsyncCommand ToggleIsScreenCaptureAllowedCommand { get; }
public AsyncCommand ToggleShouldConnectToWatchCommand { get; }
public AsyncRelayCommand SyncCommand { get; }
public AsyncRelayCommand ToggleIsScreenCaptureAllowedCommand { get; }
public AsyncRelayCommand ToggleShouldConnectToWatchCommand { get; }
public async Task InitAsync()
{
@@ -125,10 +119,10 @@ namespace Bit.App.Pages
{
TriggerPropertyChanged(nameof(IsScreenCaptureAllowed));
TriggerPropertyChanged(nameof(ShouldConnectToWatch));
SyncCommand.RaiseCanExecuteChanged();
ClearClipboardPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
ToggleIsScreenCaptureAllowedCommand.RaiseCanExecuteChanged();
ToggleShouldConnectToWatchCommand.RaiseCanExecuteChanged();
SyncCommand.NotifyCanExecuteChanged();
ClearClipboardPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
ToggleIsScreenCaptureAllowedCommand.NotifyCanExecuteChanged();
ToggleShouldConnectToWatchCommand.NotifyCanExecuteChanged();
});
}
@@ -142,8 +136,7 @@ namespace Bit.App.Pages
[30] = AppResources.ThirtySeconds,
[60] = AppResources.OneMinute
};
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform != Device.iOS)
if (DeviceInfo.Platform != DevicePlatform.iOS)
{
clearClipboardOptions.Add(120, AppResources.TwoMinutes);
clearClipboardOptions.Add(300, AppResources.FiveMinutes);

View File

@@ -67,7 +67,7 @@
<controls:CustomLabel
Text="{u:I18n SessionTimeout}"
StyleClass="settings-header" />
<Frame
IsVisible="{Binding ShowVaultTimeoutPolicyInfo}"
Padding="10"
@@ -109,8 +109,10 @@
<controls:SettingChooserItemView
Title="{u:I18n SessionTimeoutAction}"
Subtitle="{Binding SetUpUnlockMethodLabel}"
DisplayValue="{Binding VaultTimeoutActionPickerViewModel.SelectedValue}"
ChooseCommand="{Binding VaultTimeoutActionPickerViewModel.SelectOptionCommand}"
IsEnabled="{Binding IsVaultTimeoutActionLockAllowed}"
AutomationId="VaultTimeoutActionChooser"
StyleClass="settings-item-view"
HorizontalOptions="FillAndExpand"/>
@@ -147,6 +149,7 @@
<controls:CustomLabel
Text="{u:I18n LockNow}"
IsVisible="{Binding IsVaultTimeoutActionLockAllowed}"
StyleClass="settings-navigatable-label"
AutomationId="LockNowLabel">
<Label.GestureRecognizers>

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Pages.Accounts;
using Bit.Core.Resources.Localization;
@@ -12,10 +8,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -77,19 +70,19 @@ namespace Bit.App.Pages
_logger,
OnVaultTimeoutActionChangingAsync,
AppResources.SessionTimeoutAction,
() => _inited && !HasVaultTimeoutActionPolicy,
() => _inited && !HasVaultTimeoutActionPolicy && IsVaultTimeoutActionLockAllowed,
ex => HandleException(ex));
ToggleUseThisDeviceToApproveLoginRequestsCommand = CreateDefaultAsyncCommnad(ToggleUseThisDeviceToApproveLoginRequestsAsync, () => _inited);
GoToPendingLogInRequestsCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())));
ToggleCanUnlockWithBiometricsCommand = CreateDefaultAsyncCommnad(ToggleCanUnlockWithBiometricsAsync, () => _inited);
ToggleCanUnlockWithPinCommand = CreateDefaultAsyncCommnad(ToggleCanUnlockWithPinAsync, () => _inited);
ShowAccountFingerprintPhraseCommand = CreateDefaultAsyncCommnad(ShowAccountFingerprintPhraseAsync);
GoToTwoStepLoginCommand = CreateDefaultAsyncCommnad(() => GoToWebVaultSettingsAsync(AppResources.TwoStepLoginDescriptionLong, AppResources.ContinueToWebApp));
GoToChangeMasterPasswordCommand = CreateDefaultAsyncCommnad(() => GoToWebVaultSettingsAsync(AppResources.ChangeMasterPasswordDescriptionLong, AppResources.ContinueToWebApp));
LockCommand = CreateDefaultAsyncCommnad(() => _vaultTimeoutService.LockAsync(true, true));
LogOutCommand = CreateDefaultAsyncCommnad(LogOutAsync);
DeleteAccountCommand = CreateDefaultAsyncCommnad(() => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())));
ToggleUseThisDeviceToApproveLoginRequestsCommand = CreateDefaultAsyncRelayCommand(ToggleUseThisDeviceToApproveLoginRequestsAsync, () => _inited, allowsMultipleExecutions: false);
GoToPendingLogInRequestsCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new LoginPasswordlessRequestsListPage())), allowsMultipleExecutions: false);
ToggleCanUnlockWithBiometricsCommand = CreateDefaultAsyncRelayCommand(ToggleCanUnlockWithBiometricsAsync, () => _inited, allowsMultipleExecutions: false);
ToggleCanUnlockWithPinCommand = CreateDefaultAsyncRelayCommand(ToggleCanUnlockWithPinAsync, () => _inited, allowsMultipleExecutions: false);
ShowAccountFingerprintPhraseCommand = CreateDefaultAsyncRelayCommand(ShowAccountFingerprintPhraseAsync, allowsMultipleExecutions: false);
GoToTwoStepLoginCommand = CreateDefaultAsyncRelayCommand(() => GoToWebVaultSettingsAsync(AppResources.TwoStepLoginDescriptionLong, AppResources.ContinueToWebApp), allowsMultipleExecutions: false);
GoToChangeMasterPasswordCommand = CreateDefaultAsyncRelayCommand(() => GoToWebVaultSettingsAsync(AppResources.ChangeMasterPasswordDescriptionLong, AppResources.ContinueToWebApp), allowsMultipleExecutions: false);
LockCommand = CreateDefaultAsyncRelayCommand(() => _vaultTimeoutService.LockAsync(true, true), allowsMultipleExecutions: false);
LogOutCommand = CreateDefaultAsyncRelayCommand(LogOutAsync, allowsMultipleExecutions: false);
DeleteAccountCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage())), allowsMultipleExecutions: false);
}
public bool UseThisDeviceToApproveLoginRequests
@@ -114,8 +107,7 @@ namespace Bit.App.Pages
}
var biometricName = AppResources.Biometrics;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS)
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
biometricName = _deviceActionService.SupportsFaceBiometric()
? AppResources.FaceID
@@ -131,6 +123,7 @@ namespace Bit.App.Pages
get => _canUnlockWithBiometrics;
set
{
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
if (SetProperty(ref _canUnlockWithBiometrics, value))
{
((ICommand)ToggleCanUnlockWithBiometricsCommand).Execute(null);
@@ -143,6 +136,7 @@ namespace Bit.App.Pages
get => _canUnlockWithPin;
set
{
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
if (SetProperty(ref _canUnlockWithPin, value))
{
((ICommand)ToggleCanUnlockWithPinCommand).Execute(null);
@@ -150,6 +144,10 @@ namespace Bit.App.Pages
}
}
public bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _canUnlockWithBiometrics || _canUnlockWithPin;
public string SetUpUnlockMethodLabel => IsVaultTimeoutActionLockAllowed ? null : AppResources.SetUpAnUnlockOptionToChangeYourVaultTimeoutAction;
public TimeSpan? CustomVaultTimeoutTime
{
get => _customVaultTimeoutTime;
@@ -166,6 +164,7 @@ namespace Bit.App.Pages
MainThread.BeginInvokeOnMainThread(() => SetProperty(ref _customVaultTimeoutTime, oldValue));
});
}
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
}
}
@@ -205,22 +204,19 @@ namespace Bit.App.Pages
public bool ShowChangeMasterPassword { get; private set; }
private bool IsVaultTimeoutActionLockAllowed => _hasMasterPassword || _canUnlockWithBiometrics || _canUnlockWithPin;
private int? CurrentVaultTimeout => GetRawVaultTimeoutFrom(VaultTimeoutPickerViewModel.SelectedKey);
private bool IncludeLinksWithSubscriptionInfo => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
Device.RuntimePlatform != Device.iOS;
private bool IncludeLinksWithSubscriptionInfo => DeviceInfo.Platform != DevicePlatform.iOS;
private bool HasVaultTimeoutActionPolicy => !string.IsNullOrEmpty(_vaultTimeoutActionPolicy);
public PickerViewModel<int> VaultTimeoutPickerViewModel { get; }
public PickerViewModel<VaultTimeoutAction> VaultTimeoutActionPickerViewModel { get; }
public AsyncCommand ToggleUseThisDeviceToApproveLoginRequestsCommand { get; }
public AsyncRelayCommand ToggleUseThisDeviceToApproveLoginRequestsCommand { get; }
public ICommand GoToPendingLogInRequestsCommand { get; }
public AsyncCommand ToggleCanUnlockWithBiometricsCommand { get; }
public AsyncCommand ToggleCanUnlockWithPinCommand { get; }
public AsyncRelayCommand ToggleCanUnlockWithBiometricsCommand { get; }
public AsyncRelayCommand ToggleCanUnlockWithPinCommand { get; }
public ICommand ShowAccountFingerprintPhraseCommand { get; }
public ICommand GoToTwoStepLoginCommand { get; }
public ICommand GoToChangeMasterPasswordCommand { get; }
@@ -256,11 +252,12 @@ Device.RuntimePlatform != Device.iOS;
TriggerPropertyChanged(nameof(VaultTimeoutPolicyDescription));
TriggerPropertyChanged(nameof(ShowChangeMasterPassword));
TriggerUpdateCustomVaultTimeoutPicker();
ToggleUseThisDeviceToApproveLoginRequestsCommand.RaiseCanExecuteChanged();
ToggleCanUnlockWithBiometricsCommand.RaiseCanExecuteChanged();
ToggleCanUnlockWithPinCommand.RaiseCanExecuteChanged();
VaultTimeoutPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
VaultTimeoutActionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged();
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
ToggleUseThisDeviceToApproveLoginRequestsCommand.NotifyCanExecuteChanged();
ToggleCanUnlockWithBiometricsCommand.NotifyCanExecuteChanged();
ToggleCanUnlockWithPinCommand.NotifyCanExecuteChanged();
VaultTimeoutPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
});
}
@@ -275,7 +272,7 @@ Device.RuntimePlatform != Device.iOS;
_maximumVaultTimeoutPolicy = maximumVaultTimeoutPolicy?.GetInt(Policy.MINUTES_KEY);
_vaultTimeoutActionPolicy = maximumVaultTimeoutPolicy?.GetString(Policy.ACTION_KEY);
MainThread.BeginInvokeOnMainThread(VaultTimeoutActionPickerViewModel.SelectOptionCommand.RaiseCanExecuteChanged);
MainThread.BeginInvokeOnMainThread(VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged);
}
private async Task InitVaultTimeoutPickerAsync()
@@ -308,6 +305,7 @@ Device.RuntimePlatform != Device.iOS;
{
_customVaultTimeoutTime = TimeSpan.FromMinutes(vaultTimeout);
}
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
}
private async Task InitVaultTimeoutActionPickerAsync()
@@ -327,6 +325,7 @@ Device.RuntimePlatform != Device.iOS;
}
VaultTimeoutActionPickerViewModel.Init(options, timeoutAction, IsVaultTimeoutActionLockAllowed ? VaultTimeoutAction.Lock : VaultTimeoutAction.Logout);
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
}
private async Task ToggleUseThisDeviceToApproveLoginRequestsAsync()
@@ -363,15 +362,15 @@ Device.RuntimePlatform != Device.iOS;
{
if (!_canUnlockWithBiometrics)
{
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(CanUnlockWithBiometrics)));
await UpdateVaultTimeoutActionIfNeededAsync();
await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics);
return;
}
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (!_supportsBiometric
||
!await _platformUtilsService.AuthenticateBiometricAsync(null, Device.RuntimePlatform == Device.Android ? "." : null))
!await _platformUtilsService.AuthenticateBiometricAsync(null, DeviceInfo.Platform == DevicePlatform.Android ? "." : null))
{
_canUnlockWithBiometrics = false;
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(CanUnlockWithBiometrics)));
@@ -379,11 +378,12 @@ Device.RuntimePlatform != Device.iOS;
}
await _biometricsService.SetCanUnlockWithBiometricsAsync(CanUnlockWithBiometrics);
await InitVaultTimeoutActionPickerAsync();
}
public async Task ToggleCanUnlockWithPinAsync()
{
if (!CanUnlockWithPin)
if (!_canUnlockWithPin)
{
await _vaultTimeoutService.ClearAsync();
await UpdateVaultTimeoutActionIfNeededAsync();
@@ -407,10 +407,12 @@ Device.RuntimePlatform != Device.iOS;
AppResources.No);
await _userPinService.SetupPinAsync(newPin, requireMasterPasswordOnRestart);
await InitVaultTimeoutActionPickerAsync();
}
private async Task UpdateVaultTimeoutActionIfNeededAsync()
{
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
if (IsVaultTimeoutActionLockAllowed)
{
return;
@@ -471,6 +473,16 @@ Device.RuntimePlatform != Device.iOS;
TriggerPropertyChanged(nameof(CustomVaultTimeoutTime));
}
private void TriggerVaultTimeoutActionLockAllowedPropertyChanged()
{
MainThread.BeginInvokeOnMainThread(() =>
{
TriggerPropertyChanged(nameof(IsVaultTimeoutActionLockAllowed));
TriggerPropertyChanged(nameof(SetUpUnlockMethodLabel));
VaultTimeoutActionPickerViewModel.SelectOptionCommand.NotifyCanExecuteChanged();
});
}
private int? GetRawVaultTimeoutFrom(int vaultTimeoutPickerKey)
{
if (vaultTimeoutPickerKey == NEVER_SESSION_TIMEOUT_VALUE)
@@ -505,7 +517,7 @@ Device.RuntimePlatform != Device.iOS;
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(CurrentVaultTimeout, timeoutActionKey);
_messagingService.Send(AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND);
TriggerVaultTimeoutActionLockAllowedPropertyChanged();
return true;
}

View File

@@ -1,5 +1,6 @@
using Bit.App.Utilities;
using Bit.Core.Resources.Localization;
using CommunityToolkit.Mvvm.Input;
namespace Bit.App.Pages
{
@@ -7,7 +8,7 @@ namespace Bit.App.Pages
{
public SettingsPageViewModel()
{
ExecuteSettingItemCommand = new AsyncCommand<SettingsPageListItem>(item => item.ExecuteAsync(),
ExecuteSettingItemCommand = CreateDefaultAsyncRelayCommand<SettingsPageListItem>(item => item.ExecuteAsync(),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
@@ -24,7 +25,7 @@ namespace Bit.App.Pages
public List<SettingsPageListItem> SettingsItems { get; }
public AsyncCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
public AsyncRelayCommand<SettingsPageListItem> ExecuteSettingItemCommand { get; }
private async Task NavigateToAsync(Page page)
{

View File

@@ -22,15 +22,15 @@ namespace Bit.App.Pages
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
_environmentService = ServiceContainer.Resolve<IEnvironmentService>();
GoToFoldersCommand = new AsyncCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage())),
GoToFoldersCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new FoldersPage())),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
GoToExportVaultCommand = new AsyncCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())),
GoToExportVaultCommand = CreateDefaultAsyncRelayCommand(() => Page.Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage())),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
GoToImportItemsCommand = new AsyncCommand(GoToImportItemsAsync,
GoToImportItemsCommand = CreateDefaultAsyncRelayCommand(GoToImportItemsAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
}