using Bit.App.Abstractions; using Bit.App.Resources; using Bit.App.Utilities; using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Utilities; using System.Collections.Generic; using System.Threading.Tasks; using Xamarin.Forms; namespace Bit.App.Pages { public class OptionsPageViewModel : BaseViewModel { private readonly IDeviceActionService _deviceActionService; private readonly IPlatformUtilsService _platformUtilsService; private readonly IStorageService _storageService; private readonly ITotpService _totpService; private readonly IStateService _stateService; private readonly IMessagingService _messagingService; private bool _autofillAlwaysScan; private bool _autofillPersistNotification; private bool _autofillPasswordField; private bool _autofillDisableSavePrompt; private string _autofillBlacklistedUris; private bool _disableFavicon; private bool _disableAutoTotpCopy; private int _clearClipboardSelectedIndex; private int _themeSelectedIndex; private int _uriMatchSelectedIndex; private bool _inited; private bool _updatingAutofill; private bool _showAndroidAccessibilitySettings; private bool _showAndroidAutofillSettings; public OptionsPageViewModel() { _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _storageService = ServiceContainer.Resolve("storageService"); _totpService = ServiceContainer.Resolve("totpService"); _stateService = ServiceContainer.Resolve("stateService"); _messagingService = ServiceContainer.Resolve("messagingService"); PageTitle = AppResources.Options; ClearClipboardOptions = new List> { new KeyValuePair(null, AppResources.Never), new KeyValuePair(10, AppResources.TenSeconds), new KeyValuePair(20, AppResources.TwentySeconds), new KeyValuePair(30, AppResources.ThirtySeconds), new KeyValuePair(60, AppResources.OneMinute), new KeyValuePair(120, AppResources.TwoMinutes), new KeyValuePair(300, AppResources.FiveMinutes), }; ThemeOptions = new List> { new KeyValuePair(null, AppResources.Default), new KeyValuePair("light", AppResources.Light), new KeyValuePair("dark", AppResources.Dark), }; if(Device.RuntimePlatform == Device.Android) { ThemeOptions.Add(new KeyValuePair("black", AppResources.Black)); } ThemeOptions.Add(new KeyValuePair("nord", "Nord")); UriMatchOptions = new List> { new KeyValuePair(UriMatchType.Domain, AppResources.BaseDomain), new KeyValuePair(UriMatchType.Host, AppResources.Host), new KeyValuePair(UriMatchType.StartsWith, AppResources.StartsWith), new KeyValuePair(UriMatchType.RegularExpression, AppResources.RegEx), new KeyValuePair(UriMatchType.Exact, AppResources.Exact), new KeyValuePair(UriMatchType.Never, AppResources.Never), }; } public List> ClearClipboardOptions { get; set; } public List> ThemeOptions { get; set; } public List> UriMatchOptions { get; set; } public int ClearClipboardSelectedIndex { get => _clearClipboardSelectedIndex; set { if(SetProperty(ref _clearClipboardSelectedIndex, value)) { var task = SaveClipboardChangedAsync(); } } } public int ThemeSelectedIndex { get => _themeSelectedIndex; set { if(SetProperty(ref _themeSelectedIndex, value)) { var task = SaveThemeAsync(); } } } public int UriMatchSelectedIndex { get => _uriMatchSelectedIndex; set { if(SetProperty(ref _uriMatchSelectedIndex, value)) { var task = SaveDefaultUriAsync(); } } } public bool DisableFavicon { get => _disableFavicon; set { if(SetProperty(ref _disableFavicon, value)) { var task = UpdateDisableFaviconAsync(); } } } public bool DisableAutoTotpCopy { get => _disableAutoTotpCopy; set { if(SetProperty(ref _disableAutoTotpCopy, value)) { var task = UpdateAutoTotpCopyAsync(); } } } public bool AutofillAlwaysScan { get => _autofillAlwaysScan; set { if(SetProperty(ref _autofillAlwaysScan, value)) { var task = UpdateAutofillAsync(false, false); } } } public bool AutofillPersistNotification { get => _autofillPersistNotification; set { if(SetProperty(ref _autofillPersistNotification, value)) { var task = UpdateAutofillAsync(value, false); } } } public bool AutofillPasswordField { get => _autofillPasswordField; set { if(SetProperty(ref _autofillPasswordField, value)) { var task = UpdateAutofillAsync(false, value); } } } public bool AutofillDisableSavePrompt { get => _autofillDisableSavePrompt; set { if(SetProperty(ref _autofillDisableSavePrompt, value)) { var task = UpdateAutofillDisableSavePromptAsync(); } } } public string AutofillBlacklistedUris { get => _autofillBlacklistedUris; set => SetProperty(ref _autofillBlacklistedUris, value); } public bool ShowAndroidAccessibilitySettings { get => _showAndroidAccessibilitySettings; set => SetProperty(ref _showAndroidAccessibilitySettings, value); } public bool ShowAndroidAutofillSettings { get => _showAndroidAutofillSettings; set => SetProperty(ref _showAndroidAutofillSettings, value); } public async Task InitAsync() { AutofillDisableSavePrompt = (await _storageService.GetAsync( Constants.AutofillDisableSavePromptKey)).GetValueOrDefault(); var blacklistedUrisList = await _storageService.GetAsync>( Constants.AutofillBlacklistedUrisKey); AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null; AutofillPersistNotification = (await _storageService.GetAsync( Constants.AccessibilityAutofillPersistNotificationKey)).GetValueOrDefault(); AutofillPasswordField = (await _storageService.GetAsync( Constants.AccessibilityAutofillPasswordFieldKey)).GetValueOrDefault(); AutofillAlwaysScan = !AutofillPersistNotification && !AutofillPasswordField; DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync()); DisableFavicon = (await _storageService.GetAsync(Constants.DisableFaviconKey)).GetValueOrDefault(); var theme = await _storageService.GetAsync(Constants.ThemeKey); ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme); var defaultUriMatch = await _storageService.GetAsync(Constants.DefaultUriMatch); UriMatchSelectedIndex = defaultUriMatch == null ? 0 : UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch); var clearClipboard = await _storageService.GetAsync(Constants.ClearClipboardKey); ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard); _inited = true; } private async Task UpdateAutofillAsync(bool persistNotification, bool passwordField) { if(_inited && !_updatingAutofill) { _updatingAutofill = true; if(persistNotification) { AutofillAlwaysScan = false; AutofillPasswordField = false; } else if(passwordField) { AutofillAlwaysScan = false; AutofillPersistNotification = false; } else { AutofillAlwaysScan = true; AutofillPersistNotification = false; AutofillPasswordField = false; } await _storageService.SaveAsync(Constants.AccessibilityAutofillPersistNotificationKey, AutofillPersistNotification); await _storageService.SaveAsync(Constants.AccessibilityAutofillPasswordFieldKey, AutofillPasswordField); _updatingAutofill = false; } } private async Task UpdateAutoTotpCopyAsync() { if(_inited) { await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy); } } private async Task UpdateDisableFaviconAsync() { if(_inited) { await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon); await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon); } } private async Task SaveClipboardChangedAsync() { if(_inited && ClearClipboardSelectedIndex > -1) { await _storageService.SaveAsync(Constants.ClearClipboardKey, ClearClipboardOptions[ClearClipboardSelectedIndex].Key); } } private async Task SaveThemeAsync() { if(_inited && ThemeSelectedIndex > -1) { var theme = ThemeOptions[ThemeSelectedIndex].Key; await _storageService.SaveAsync(Constants.ThemeKey, theme); if(Device.RuntimePlatform == Device.Android) { await _deviceActionService.ShowLoadingAsync(AppResources.Restarting); await Task.Delay(1000); } _messagingService.Send("updatedTheme", theme); } } private async Task SaveDefaultUriAsync() { if(_inited && UriMatchSelectedIndex > -1) { await _storageService.SaveAsync(Constants.DefaultUriMatch, (int?)UriMatchOptions[UriMatchSelectedIndex].Key); } } private async Task UpdateAutofillDisableSavePromptAsync() { if(_inited) { await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt); } } public async Task UpdateAutofillBlacklistedUris() { if(_inited) { if(string.IsNullOrWhiteSpace(AutofillBlacklistedUris)) { await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey); AutofillBlacklistedUris = null; return; } try { var csv = AutofillBlacklistedUris; var urisList = new List(); foreach(var uri in csv.Split(',')) { if(string.IsNullOrWhiteSpace(uri)) { continue; } var cleanedUri = uri.Replace(System.Environment.NewLine, string.Empty).Trim(); if(!cleanedUri.StartsWith("http://") && !cleanedUri.StartsWith("https://") && !cleanedUri.StartsWith(Constants.AndroidAppProtocol)) { continue; } urisList.Add(cleanedUri); } await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList); AutofillBlacklistedUris = string.Join(", ", urisList); } catch { } } } } }