1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-05 01:53:17 +00:00

[Auto Logout] Final review of feature (#932)

* Initial commit of LockService name refactor (#831)

* [Auto-Logout] Update Service layer logic (#835)

* Initial commit of service logic update

* Added default value for action

* Updated ToggleTokensAsync conditional

* Removed unused variables, updated action conditional

* Initial commit: lockOption/lock refactor app layer (#840)

* [Auto-Logout] Settings Refactor - Application Layer Part 2 (#844)

* Initial commit of app layer part 2

* Updated biometrics position

* Reverted resource name refactor

* LockOptions refactor revert

* Updated method casing :: Removed VaultTimeout prefix for timeouts

* Fixed dupe string resource (#854)

* Updated dependency to use VaultTimeoutService (#896)

* [Auto Logout] Xamarin Forms in AutoFill flow (iOS) (#902)

* fix typo in PINRequireMasterPasswordRestart (#900)

* initial commit for xf usage in autofill

* Fixed databinding for hint button

* Updated Two Factor page launch - removed unused imports

* First pass at broadcast/messenger implentation for autofill

* setting theme in extension using theme manager

* extension app resources

* App resources from main app

* fix ref to twoFactorPage

* apply resources to page

* load empty app for sytling in extension

* move ios renderers to ios core

* static ref to resources and GetResourceColor helper

* fix method ref

* move application.current.resources refs to helper

* switch login page alerts to device action dialogs

* run on main thread

* showDialog with device action service

* abstract action sheet to device action service

* add support for yubikey

* add yubikey iimages to extension

* support close button action

* add support to action extension

* remove empty lines

Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>

* [Auto Logout] Update lock option to be default value (#929)

* Initial commit - make lock action default

* Removed extra whitespace

Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
Vincent Salucci
2020-05-29 11:26:36 -05:00
committed by GitHub
parent 39e10ff01c
commit 4c3df2e1e1
80 changed files with 744 additions and 379 deletions

View File

@@ -32,6 +32,7 @@ namespace Bit.App.Abstractions
int SystemMajorVersion();
string SystemModel();
Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons);
Task<string> DisplayActionSheetAsync(string title, string cancel, string destruction, params string[] buttons);
void Autofill(CipherView cipher);
void CloseAutofill();
void Background();

View File

@@ -22,7 +22,7 @@ namespace Bit.App
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStateService _stateService;
private readonly ILockService _lockService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISyncService _syncService;
private readonly ITokenService _tokenService;
private readonly ICryptoService _cryptoService;
@@ -37,18 +37,22 @@ namespace Bit.App
private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService;
private readonly IDeviceActionService _deviceActionService;
private readonly AppOptions _appOptions;
private static bool _isResumed;
public App(AppOptions appOptions)
{
_appOptions = appOptions ?? new AppOptions();
Options = appOptions ?? new AppOptions();
if (Options.EmptyApp)
{
Current = this;
return;
}
_userService = ServiceContainer.Resolve<IUserService>("userService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
@@ -95,7 +99,7 @@ namespace Bit.App
}
else if (message.Command == "lockVault")
{
await _lockService.LockAsync(true);
await _vaultTimeoutService.LockAsync(true);
}
else if (message.Command == "logout")
{
@@ -139,12 +143,12 @@ namespace Bit.App
}
if (message.Command == "popAllAndGoToTabMyVault")
{
_appOptions.MyVaultTile = false;
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else
{
_appOptions.GeneratorTile = false;
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
}
@@ -152,14 +156,16 @@ namespace Bit.App
}
});
}
public AppOptions Options { get; private set; }
protected async override void OnStart()
{
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
Prime();
if (string.IsNullOrWhiteSpace(_appOptions.Uri))
if (string.IsNullOrWhiteSpace(Options.Uri))
{
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_storageService);
@@ -177,7 +183,7 @@ namespace Bit.App
_isResumed = false;
if (Device.RuntimePlatform == Device.Android)
{
var isLocked = await _lockService.IsLockedAsync();
var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked)
{
await _storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
@@ -199,13 +205,13 @@ namespace Bit.App
private async Task SleptAsync()
{
await HandleLockingAsync();
await HandleVaultTimeoutAsync();
_messagingService.Send("stopEventTimer");
}
private async void ResumedAsync()
{
_messagingService.Send("cancelLockTimer");
_messagingService.Send("cancelVaultTimeoutTimer");
_messagingService.Send("startEventTimer");
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
@@ -238,9 +244,9 @@ namespace Bit.App
_folderService.ClearAsync(userId),
_collectionService.ClearAsync(userId),
_passwordGenerationService.ClearAsync(),
_lockService.ClearAsync(),
_vaultTimeoutService.ClearAsync(),
_stateService.PurgeAsync());
_lockService.FingerprintLocked = true;
_vaultTimeoutService.FingerprintLocked = true;
_searchService.ClearIndex();
_authService.LogOut(() =>
{
@@ -257,32 +263,32 @@ namespace Bit.App
var authed = await _userService.IsAuthenticatedAsync();
if (authed)
{
if (await _lockService.IsLockedAsync())
if (await _vaultTimeoutService.IsLockedAsync())
{
Current.MainPage = new NavigationPage(new LockPage(_appOptions));
Current.MainPage = new NavigationPage(new LockPage(Options));
}
else if (_appOptions.FromAutofillFramework && _appOptions.SaveType.HasValue)
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
{
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: _appOptions));
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
}
else if (_appOptions.Uri != null)
else if (Options.Uri != null)
{
Current.MainPage = new NavigationPage(new AutofillCiphersPage(_appOptions));
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
}
else
{
Current.MainPage = new TabsPage(_appOptions);
Current.MainPage = new TabsPage(Options);
}
}
else
{
Current.MainPage = new HomePage(_appOptions);
Current.MainPage = new HomePage(Options);
}
}
private async Task HandleLockingAsync()
private async Task HandleVaultTimeoutAsync()
{
if (await _lockService.IsLockedAsync())
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
@@ -291,19 +297,28 @@ namespace Bit.App
{
return;
}
var lockOption = _platformUtilsService.LockTimeout();
if (lockOption == null)
// Will only ever be null - look to remove this in the future
var vaultTimeout = _platformUtilsService.LockTimeout();
if (vaultTimeout == null)
{
lockOption = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
}
lockOption = lockOption.GetValueOrDefault(-1);
if (lockOption > 0)
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
if (vaultTimeout > 0)
{
_messagingService.Send("scheduleLockTimer", lockOption.Value);
_messagingService.Send("scheduleVaultTimeoutTimer", vaultTimeout.Value);
}
else if (lockOption == 0)
else if (vaultTimeout == 0)
{
await _lockService.LockAsync(true);
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
if (action == "logOut")
{
await _vaultTimeoutService.LogOutAsync();
}
else
{
await _vaultTimeoutService.LockAsync(true);
}
}
}
@@ -318,14 +333,14 @@ namespace Bit.App
private void SetTabsPageFromAutofill(bool isLocked)
{
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_appOptions.Uri) &&
!_appOptions.FromAutofillFramework)
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
!Options.FromAutofillFramework)
{
Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
_appOptions.Uri = null;
Options.Uri = null;
if (isLocked)
{
Current.MainPage = new NavigationPage(new LockPage());
@@ -352,7 +367,7 @@ namespace Bit.App
{
InitializeComponent();
SetCulture();
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android);
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
Current.MainPage = new HomePage();
var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
@@ -394,8 +409,8 @@ namespace Bit.App
await _stateService.PurgeAsync();
if (autoPromptFingerprint && Device.RuntimePlatform == Device.iOS)
{
var lockOptions = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
if (lockOptions == 0)
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
if (vaultTimeout == 0)
{
autoPromptFingerprint = false;
}
@@ -430,7 +445,7 @@ namespace Bit.App
}
}
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
var lockPage = new LockPage(_appOptions, autoPromptFingerprint);
var lockPage = new LockPage(Options, autoPromptFingerprint);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
}
}

View File

@@ -18,5 +18,6 @@ namespace Bit.App.Models
public string SaveCardExpMonth { get; set; }
public string SaveCardExpYear { get; set; }
public string SaveCardCode { get; set; }
public bool EmptyApp { get; set; }
}
}

View File

@@ -16,7 +16,7 @@ namespace Bit.App.Pages
{
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IDeviceActionService _deviceActionService;
private readonly ILockService _lockService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICryptoService _cryptoService;
private readonly IStorageService _storageService;
private readonly IUserService _userService;
@@ -39,7 +39,7 @@ namespace Bit.App.Pages
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
@@ -102,9 +102,9 @@ namespace Bit.App.Pages
public async Task InitAsync(bool autoPromptFingerprint)
{
_pinSet = await _lockService.IsPinLockSetAsync();
PinLock = (_pinSet.Item1 && _lockService.PinProtectedKey != null) || _pinSet.Item2;
FingerprintLock = await _lockService.IsFingerprintLockSetAsync();
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
FingerprintLock = await _vaultTimeoutService.IsFingerprintLockSetAsync();
_email = await _userService.GetEmailAsync();
var webVault = _environmentService.GetWebVaultUrl();
if (string.IsNullOrWhiteSpace(webVault))
@@ -180,7 +180,7 @@ namespace Bit.App.Pages
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
_lockService.PinProtectedKey);
_vaultTimeoutService.PinProtectedKey);
var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
@@ -240,7 +240,7 @@ namespace Bit.App.Pages
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
_lockService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
}
MasterPassword = string.Empty;
await SetKeyAndContinueAsync(key);
@@ -290,7 +290,7 @@ namespace Bit.App.Pages
page.MasterPasswordEntry.Focus();
}
});
_lockService.FingerprintLocked = !success;
_vaultTimeoutService.FingerprintLocked = !success;
if (success)
{
await DoContinueAsync();
@@ -309,7 +309,7 @@ namespace Bit.App.Pages
private async Task DoContinueAsync()
{
_lockService.FingerprintLocked = false;
_vaultTimeoutService.FingerprintLocked = false;
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
_messagingService.Send("unlocked");

View File

@@ -73,8 +73,8 @@
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n GetPasswordHint}" Clicked="Hint_Clicked"></Button>
<StackLayout Padding="10, 0" IsVisible="{Binding HideHintButton, Converter={StaticResource inverseBool}}">
<Button Text="{u:I18n GetPasswordHint}" Clicked="Hint_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>

View File

@@ -26,6 +26,11 @@ namespace Bit.App.Pages
_vm.Page = this;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LoggedInAction = () => Device.BeginInvokeOnMainThread(async () => await LoggedInAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
_vm.Email = email;
MasterPasswordEntry = _masterPassword;
if (Device.RuntimePlatform == Device.Android)
@@ -73,8 +78,7 @@ namespace Bit.App.Pages
{
if (DoOnce())
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
_vm.CloseAction();
}
}
@@ -83,7 +87,7 @@ namespace Bit.App.Pages
var page = new TwoFactorPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task LoggedInAsync()
{
if (_appOptions != null)

View File

@@ -25,6 +25,7 @@ namespace Bit.App.Pages
private bool _showPassword;
private string _email;
private string _masterPassword;
private bool _hideHintButton;
public LoginPageViewModel()
{
@@ -68,7 +69,14 @@ namespace Bit.App.Pages
public bool RememberEmail { get; set; }
public Action StartTwoFactorAction { get; set; }
public Action LoggedInAction { get; set; }
public Action CloseAction { get; set; }
public bool HideHintButton
{
get => _hideHintButton;
set => SetProperty(ref _hideHintButton, value);
}
public async Task InitAsync()
{
if (string.IsNullOrWhiteSpace(Email))
@@ -89,20 +97,23 @@ namespace Bit.App.Pages
}
if (string.IsNullOrWhiteSpace(Email))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
AppResources.AnErrorHasOccurred,
AppResources.Ok);
return;
}
if (!Email.Contains("@"))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.InvalidEmail, AppResources.Ok);
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidEmail, AppResources.AnErrorHasOccurred,
AppResources.Ok);
return;
}
if (string.IsNullOrWhiteSpace(MasterPassword))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
AppResources.AnErrorHasOccurred,
AppResources.Ok);
return;
}

View File

@@ -30,6 +30,7 @@ namespace Bit.App.Pages
_vm = BindingContext as TwoFactorPageViewModel;
_vm.Page = this;
_vm.TwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await TwoFactorAuthAsync());
_vm.CloseAction = async () => await Navigation.PopModalAsync();
DuoWebView = _duoWebView;
if (Device.RuntimePlatform == Device.Android)
{
@@ -144,7 +145,7 @@ namespace Bit.App.Pages
{
if (DoOnce())
{
await Navigation.PopModalAsync();
_vm.CloseAction();
}
}

View File

@@ -90,6 +90,7 @@ namespace Bit.App.Pages
}
public Command SubmitCommand { get; }
public Action TwoFactorAction { get; set; }
public Action CloseAction { get; set; }
public void Init()
{
@@ -228,8 +229,8 @@ namespace Bit.App.Pages
var supportedProviders = _authService.GetSupportedTwoFactorProviders();
var options = supportedProviders.Select(p => p.Name).ToList();
options.Add(AppResources.RecoveryCodeTitle);
var method = await Page.DisplayActionSheet(AppResources.TwoStepLoginOptions, AppResources.Cancel,
null, options.ToArray());
var method = await _deviceActionService.DisplayActionSheetAsync(AppResources.TwoStepLoginOptions,
AppResources.Cancel, null, options.ToArray());
if (method == AppResources.RecoveryCodeTitle)
{
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");

View File

@@ -129,9 +129,13 @@ namespace Bit.App.Pages
{
await _vm.LockAsync();
}
else if (item.Name == AppResources.LockOptions)
else if (item.Name == AppResources.VaultTimeout)
{
await _vm.LockOptionsAsync();
await _vm.VaultTimeoutAsync();
}
else if (item.Name == AppResources.VaultTimeoutAction)
{
await _vm.VaultTimeoutActionAsync();
}
else if (item.Name == AppResources.UnlockWithPIN)
{

View File

@@ -1,4 +1,5 @@
using Bit.App.Resources;
using Bit.App.Utilities;
using System.Collections.Generic;
using Xamarin.Forms;
@@ -11,7 +12,7 @@ namespace Bit.App.Pages
public string SubLabel { get; set; }
public bool SubLabelTextEnabled => SubLabel == AppResources.Enabled;
public Color SubLabelColor => SubLabelTextEnabled ?
(Color)Application.Current.Resources["SuccessColor"] :
(Color)Application.Current.Resources["MutedColor"];
ThemeManager.GetResourceColor("SuccessColor") :
ThemeManager.GetResourceColor("MutedColor");
}
}

View File

@@ -19,7 +19,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService;
private readonly IEnvironmentService _environmentService;
private readonly IMessagingService _messagingService;
private readonly ILockService _lockService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IStorageService _storageService;
private readonly ISyncService _syncService;
@@ -27,20 +27,27 @@ namespace Bit.App.Pages
private bool _pin;
private bool _fingerprint;
private string _lastSyncDate;
private string _lockOptionValue;
private List<KeyValuePair<string, int?>> _lockOptions =
private string _vaultTimeoutDisplayValue;
private string _vaultTimeoutActionDisplayValue;
private List<KeyValuePair<string, int?>> _vaultTimeouts =
new List<KeyValuePair<string, int?>>
{
new KeyValuePair<string, int?>(AppResources.LockOptionImmediately, 0),
new KeyValuePair<string, int?>(AppResources.LockOption1Minute, 1),
new KeyValuePair<string, int?>(AppResources.LockOption5Minutes, 5),
new KeyValuePair<string, int?>(AppResources.LockOption15Minutes, 15),
new KeyValuePair<string, int?>(AppResources.LockOption30Minutes, 30),
new KeyValuePair<string, int?>(AppResources.LockOption1Hour, 60),
new KeyValuePair<string, int?>(AppResources.LockOption4Hours, 240),
new KeyValuePair<string, int?>(AppResources.LockOptionOnRestart, -1),
new KeyValuePair<string, int?>(AppResources.Immediately, 0),
new KeyValuePair<string, int?>(AppResources.OneMinute, 1),
new KeyValuePair<string, int?>(AppResources.FiveMinutes, 5),
new KeyValuePair<string, int?>(AppResources.FifteenMinutes, 15),
new KeyValuePair<string, int?>(AppResources.ThirtyMinutes, 30),
new KeyValuePair<string, int?>(AppResources.OneHour, 60),
new KeyValuePair<string, int?>(AppResources.FourHours, 240),
new KeyValuePair<string, int?>(AppResources.OnRestart, -1),
new KeyValuePair<string, int?>(AppResources.Never, null),
};
private List<KeyValuePair<string, string>> _vaultTimeoutActions =
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(AppResources.Lock, "lock"),
new KeyValuePair<string, string>(AppResources.LogOut, "logOut"),
};
public SettingsPageViewModel()
{
@@ -50,7 +57,7 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
@@ -70,11 +77,13 @@ namespace Bit.App.Pages
_lastSyncDate = string.Format("{0} {1}", lastSync.Value.ToShortDateString(),
lastSync.Value.ToShortTimeString());
}
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
_lockOptionValue = _lockOptions.FirstOrDefault(o => o.Value == option).Key;
var pinSet = await _lockService.IsPinLockSetAsync();
var timeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == timeout).Key;
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock";
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet.Item1 || pinSet.Item2;
_fingerprint = await _lockService.IsFingerprintLockSetAsync();
_fingerprint = await _vaultTimeoutService.IsFingerprintLockSetAsync();
BuildList();
}
@@ -179,21 +188,48 @@ namespace Bit.App.Pages
public async Task LockAsync()
{
await _lockService.LockAsync(true, true);
await _vaultTimeoutService.LockAsync(true, true);
}
public async Task LockOptionsAsync()
public async Task VaultTimeoutAsync()
{
var options = _lockOptions.Select(o => o.Key == _lockOptionValue ? $"✓ {o.Key}" : o.Key).ToArray();
var selection = await Page.DisplayActionSheet(AppResources.LockOptions, AppResources.Cancel, null, options);
var options = _vaultTimeouts.Select(o => o.Key == _vaultTimeoutDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeout, AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
var cleanSelection = selection.Replace("✓ ", string.Empty);
var selectionOption = _lockOptions.FirstOrDefault(o => o.Key == cleanSelection);
_lockOptionValue = selectionOption.Key;
await _lockService.SetLockOptionAsync(selectionOption.Value);
var selectionOption = _vaultTimeouts.FirstOrDefault(o => o.Key == cleanSelection);
_vaultTimeoutDisplayValue = selectionOption.Key;
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(selectionOption.Value,
GetVaultTimeoutActionFromKey(_vaultTimeoutActionDisplayValue));
BuildList();
}
public async Task VaultTimeoutActionAsync()
{
var options = _vaultTimeoutActions.Select(o => o.Key == _vaultTimeoutActionDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeoutAction, AppResources.Cancel, null, options);
if (selection == null || selection == AppResources.Cancel)
{
return;
}
var cleanSelection = selection.Replace("✓ ", string.Empty);
if (cleanSelection == AppResources.LogOut)
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.VaultTimeoutLogOutConfirmation,
AppResources.Warning, AppResources.Yes, AppResources.Cancel);
if (!confirmed)
{
// Reset to lock and continue process as if lock were selected
cleanSelection = AppResources.Lock;
}
}
var selectionOption = _vaultTimeoutActions.FirstOrDefault(o => o.Key == cleanSelection);
_vaultTimeoutActionDisplayValue = selectionOption.Key;
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(GetVaultTimeoutFromKey(_vaultTimeoutDisplayValue),
selectionOption.Value);
BuildList();
}
@@ -223,7 +259,7 @@ namespace Bit.App.Pages
{
var encPin = await _cryptoService.EncryptAsync(pin);
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
_lockService.PinProtectedKey = pinProtectedKey;
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
}
else
{
@@ -238,7 +274,7 @@ namespace Bit.App.Pages
if (!_pin)
{
await _cryptoService.ClearPinProtectedKeyAsync();
await _lockService.ClearAsync();
await _vaultTimeoutService.ClearAsync();
}
BuildList();
}
@@ -267,7 +303,7 @@ namespace Bit.App.Pages
{
await _storageService.RemoveAsync(Constants.FingerprintUnlockKey);
}
_lockService.FingerprintLocked = false;
_vaultTimeoutService.FingerprintLocked = false;
await _cryptoService.ToggleKeyAsync();
BuildList();
}
@@ -312,7 +348,8 @@ namespace Bit.App.Pages
};
var securityItems = new List<SettingsPageListItem>
{
new SettingsPageListItem { Name = AppResources.LockOptions, SubLabel = _lockOptionValue },
new SettingsPageListItem { Name = AppResources.VaultTimeout, SubLabel = _vaultTimeoutDisplayValue },
new SettingsPageListItem { Name = AppResources.VaultTimeoutAction, SubLabel = _vaultTimeoutActionDisplayValue },
new SettingsPageListItem
{
Name = AppResources.UnlockWithPIN,
@@ -338,7 +375,7 @@ namespace Bit.App.Pages
Name = string.Format(AppResources.UnlockWith, fingerprintName),
SubLabel = _fingerprint ? AppResources.Enabled : AppResources.Disabled
};
securityItems.Insert(1, item);
securityItems.Insert(2, item);
}
var accountItems = new List<SettingsPageListItem>
{
@@ -370,5 +407,15 @@ namespace Bit.App.Pages
new SettingsPageListGroup(otherItems, AppResources.Other, doUpper)
});
}
private string GetVaultTimeoutActionFromKey(string key)
{
return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
}
private int? GetVaultTimeoutFromKey(string key)
{
return _vaultTimeouts.FirstOrDefault(o => o.Key == key).Value;
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Bit.App.Pages
private readonly ISyncService _syncService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IStorageService _storageService;
private readonly ILockService _lockService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICipherService _cipherService;
private readonly IDeviceActionService _deviceActionService;
private readonly GroupingsPageViewModel _vm;
@@ -39,7 +39,7 @@ namespace Bit.App.Pages
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_vm = BindingContext as GroupingsPageViewModel;
@@ -247,7 +247,7 @@ namespace Bit.App.Pages
private async void Lock_Clicked(object sender, EventArgs e)
{
await _lockService.LockAsync(true, true);
await _vaultTimeoutService.LockAsync(true, true);
}
private async void Exit_Clicked(object sender, EventArgs e)

View File

@@ -39,7 +39,7 @@ namespace Bit.App.Pages
private readonly ICollectionService _collectionService;
private readonly ISyncService _syncService;
private readonly IUserService _userService;
private readonly ILockService _lockService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
@@ -52,7 +52,7 @@ namespace Bit.App.Pages
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
@@ -145,7 +145,7 @@ namespace Bit.App.Pages
{
return;
}
if (await _lockService.IsLockedAsync())
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}

View File

@@ -174,7 +174,7 @@ namespace Bit.App.Pages
fs.Spans.Add(new Span
{
Text = string.Format(" {0}", Cipher.PasswordHistory.Count.ToString()),
TextColor = (Color)Application.Current.Resources["PrimaryColor"]
TextColor = ThemeManager.GetResourceColor("PrimaryColor")
});
return fs;
}
@@ -209,7 +209,7 @@ namespace Bit.App.Pages
set
{
SetProperty(ref _totpLow, value);
Page.Resources["textTotp"] = Application.Current.Resources[value ? "text-danger" : "text-default"];
Page.Resources["textTotp"] = ThemeManager.Resources()[value ? "text-danger" : "text-default"];
}
}
public bool IsDeleted => Cipher.IsDeleted;

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -755,39 +754,51 @@ namespace Bit.App.Resources {
}
}
public static string LockOption15Minutes {
public static string FifteenMinutes {
get {
return ResourceManager.GetString("LockOption15Minutes", resourceCulture);
return ResourceManager.GetString("FifteenMinutes", resourceCulture);
}
}
public static string LockOption1Hour {
public static string OneHour {
get {
return ResourceManager.GetString("LockOption1Hour", resourceCulture);
return ResourceManager.GetString("OneHour", resourceCulture);
}
}
public static string LockOption1Minute {
public static string OneMinute {
get {
return ResourceManager.GetString("LockOption1Minute", resourceCulture);
return ResourceManager.GetString("OneMinute", resourceCulture);
}
}
public static string LockOption4Hours {
public static string FourHours {
get {
return ResourceManager.GetString("LockOption4Hours", resourceCulture);
return ResourceManager.GetString("FourHours", resourceCulture);
}
}
public static string LockOptionImmediately {
public static string Immediately {
get {
return ResourceManager.GetString("LockOptionImmediately", resourceCulture);
return ResourceManager.GetString("Immediately", resourceCulture);
}
}
public static string LockOptions {
public static string VaultTimeout {
get {
return ResourceManager.GetString("LockOptions", resourceCulture);
return ResourceManager.GetString("VaultTimeout", resourceCulture);
}
}
public static string VaultTimeoutAction {
get {
return ResourceManager.GetString("VaultTimeoutAction", resourceCulture);
}
}
public static string VaultTimeoutLogOutConfirmation {
get {
return ResourceManager.GetString("VaultTimeoutLogOutConfirmation", resourceCulture);
}
}
@@ -2501,15 +2512,9 @@ namespace Bit.App.Resources {
}
}
public static string LockOption30Minutes {
public static string ThirtyMinutes {
get {
return ResourceManager.GetString("LockOption30Minutes", resourceCulture);
}
}
public static string LockOption5Minutes {
get {
return ResourceManager.GetString("LockOption5Minutes", resourceCulture);
return ResourceManager.GetString("ThirtyMinutes", resourceCulture);
}
}
@@ -2555,12 +2560,6 @@ namespace Bit.App.Resources {
}
}
public static string OneMinute {
get {
return ResourceManager.GetString("OneMinute", resourceCulture);
}
}
public static string TenSeconds {
get {
return ResourceManager.GetString("TenSeconds", resourceCulture);
@@ -2687,9 +2686,9 @@ namespace Bit.App.Resources {
}
}
public static string LockOptionOnRestart {
public static string OnRestart {
get {
return ResourceManager.GetString("LockOptionOnRestart", resourceCulture);
return ResourceManager.GetString("OnRestart", resourceCulture);
}
}

View File

@@ -1517,7 +1517,7 @@
<value>Are you sure you want to exit Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>

View File

@@ -1517,7 +1517,7 @@
<value>Sind Sie sicher, dass Sie Bitwarden verlassen möchten?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Schwarz</value>

View File

@@ -1517,7 +1517,7 @@
<value>¿Estás seguro de que deseas salir de Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Negro</value>

View File

@@ -1517,7 +1517,7 @@
<value>Kas soovid tõesti Bitwardeni sulgeda?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Must</value>

View File

@@ -1517,7 +1517,7 @@
<value>آیا مطمئنید که می‌خواهید از Bitwarden خارج شوید؟</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>سیاه</value>

View File

@@ -1517,7 +1517,7 @@
<value>Biztos vagy benne, hogy kilépsz?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Fekete</value>

View File

@@ -1517,7 +1517,7 @@
<value>Apakah anda yakin ingin keluar dari Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Hitam</value>

View File

@@ -1517,7 +1517,7 @@
<value>Sei sicuro di voler uscire da Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Nero</value>

View File

@@ -1517,7 +1517,7 @@
<value>정말로 Bitwarden에서 나가시려는 건가요?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>검은 테마</value>

View File

@@ -1517,7 +1517,7 @@
<value>Er du sikker på at du vil lukke Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Svart</value>

View File

@@ -518,23 +518,29 @@
<data name="Lock" xml:space="preserve">
<value>Lock</value>
</data>
<data name="LockOption15Minutes" xml:space="preserve">
<data name="FifteenMinutes" xml:space="preserve">
<value>15 minutes</value>
</data>
<data name="LockOption1Hour" xml:space="preserve">
<data name="OneHour" xml:space="preserve">
<value>1 hour</value>
</data>
<data name="LockOption1Minute" xml:space="preserve">
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
</data>
<data name="LockOption4Hours" xml:space="preserve">
<data name="FourHours" xml:space="preserve">
<value>4 hours</value>
</data>
<data name="LockOptionImmediately" xml:space="preserve">
<data name="Immediately" xml:space="preserve">
<value>Immediately</value>
</data>
<data name="LockOptions" xml:space="preserve">
<value>Lock Options</value>
<data name="VaultTimeout" xml:space="preserve">
<value>Vault Timeout</value>
</data>
<data name="VaultTimeoutAction" xml:space="preserve">
<value>Vault Timeout Action</value>
</data>
<data name="VaultTimeoutLogOutConfirmation" xml:space="preserve">
<value>Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?</value>
</data>
<data name="LoggingIn" xml:space="preserve">
<value>Logging in...</value>
@@ -1433,12 +1439,9 @@
<data name="Unlock" xml:space="preserve">
<value>Unlock</value>
</data>
<data name="LockOption30Minutes" xml:space="preserve">
<data name="ThirtyMinutes" xml:space="preserve">
<value>30 minutes</value>
</data>
<data name="LockOption5Minutes" xml:space="preserve">
<value>5 minutes</value>
</data>
<data name="SetPINDescription" xml:space="preserve">
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
</data>
@@ -1463,9 +1466,6 @@
<data name="FiveMinutes" xml:space="preserve">
<value>5 minutes</value>
</data>
<data name="OneMinute" xml:space="preserve">
<value>1 minute</value>
</data>
<data name="TenSeconds" xml:space="preserve">
<value>10 seconds</value>
</data>
@@ -1535,7 +1535,7 @@
<data name="DisableSavePromptDescription" xml:space="preserve">
<value>The "Save Prompt" automatically prompts you to save new items to your vault whenever you enter them for the first time.</value>
</data>
<data name="LockOptionOnRestart" xml:space="preserve">
<data name="OnRestart" xml:space="preserve">
<value>On App Restart</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">

View File

@@ -1517,7 +1517,7 @@
<value>Sunteți sigur că doriți să ieșiți?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Neagră</value>

View File

@@ -1517,7 +1517,7 @@
<value>Naozaj chcete ukončiť Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Čierna</value>

View File

@@ -1517,7 +1517,7 @@
<value>Are you sure you want to exit Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>

View File

@@ -1517,7 +1517,7 @@
<value>Are you sure you want to exit Bitwarden?</value>
</data>
<data name="PINRequireMasterPasswordRestart" xml:space="preserve">
<value>You you want to require unlocking with your master password when the application is restarted?</value>
<value>Do you want to require unlocking with your master password when the application is restarted?</value>
</data>
<data name="Black" xml:space="preserve">
<value>Black</value>

View File

@@ -13,7 +13,8 @@ namespace Bit.App.Services
private readonly HashSet<string> _preferenceStorageKeys = new HashSet<string>
{
Constants.LockOptionKey,
Constants.VaultTimeoutKey,
Constants.VaultTimeoutActionKey,
Constants.ThemeKey,
Constants.DefaultUriMatch,
Constants.DisableAutoTotpCopyKey,

View File

@@ -18,7 +18,7 @@ namespace Bit.App.Utilities
{
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
var eventService = ServiceContainer.Resolve<IEventService>("eventService");
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
var options = new List<string> { AppResources.View };
if (!cipher.IsDeleted)
{
@@ -67,7 +67,7 @@ namespace Bit.App.Utilities
}
}
var selection = await page.DisplayActionSheet(cipher.Name, AppResources.Cancel, null, options.ToArray());
if (await lockService.IsLockedAsync())
if (await vaultTimeoutService.IsLockedAsync())
{
platformUtilsService.ShowToast("info", null, AppResources.VaultIsLocked);
}
@@ -137,10 +137,16 @@ namespace Bit.App.Utilities
if (lastBuild == null)
{
// Installed
var currentLock = await storageService.GetAsync<int?>(Constants.LockOptionKey);
if (currentLock == null)
var currentTimeout = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
if (currentTimeout == null)
{
await storageService.SaveAsync(Constants.LockOptionKey, 15);
await storageService.SaveAsync(Constants.VaultTimeoutKey, 15);
}
var currentAction = await storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
if (currentAction == null)
{
await storageService.SaveAsync(Constants.VaultTimeoutActionKey, "lock");
}
}
else if (lastBuild != currentBuild)

View File

@@ -31,9 +31,9 @@ namespace Bit.App.Utilities
// First two digits of returned hex code contains the alpha,
// which is not supported in HTML color, so we need to cut those out.
var normalColor = $"<span style=\"color:#{((Color)Application.Current.Resources["TextColor"]).ToHex().Substring(3)}\">";
var numberColor = $"<span style=\"color:#{((Color)Application.Current.Resources["PasswordNumberColor"]).ToHex().Substring(3)}\">";
var specialColor = $"<span style=\"color:#{((Color)Application.Current.Resources["PasswordSpecialColor"]).ToHex().Substring(3)}\">";
var normalColor = $"<span style=\"color:#{ThemeManager.GetResourceColor("TextColor").ToHex().Substring(3)}\">";
var numberColor = $"<span style=\"color:#{ThemeManager.GetResourceColor("PasswordNumberColor").ToHex().Substring(3)}\">";
var specialColor = $"<span style=\"color:#{ThemeManager.GetResourceColor("PasswordSpecialColor").ToHex().Substring(3)}\">";
var result = string.Empty;
// iOS won't hide the zero-width space char without these div attrs, but Android won't respect

View File

@@ -1,4 +1,5 @@
using Bit.App.Abstractions;
using System;
using Bit.App.Abstractions;
using Bit.App.Services;
using Bit.App.Styles;
using Bit.Core;
@@ -10,35 +11,38 @@ namespace Bit.App.Utilities
public static class ThemeManager
{
public static bool UsingLightTheme = true;
public static Func<ResourceDictionary> Resources = () => null;
public static void SetThemeStyle(string name)
public static void SetThemeStyle(string name, ResourceDictionary resources)
{
Resources = () => resources;
// Reset styles
Application.Current.Resources.Clear();
Application.Current.Resources.MergedDictionaries.Clear();
resources.Clear();
resources.MergedDictionaries.Clear();
// Variables
Application.Current.Resources.MergedDictionaries.Add(new Variables());
resources.MergedDictionaries.Add(new Variables());
// Themed variables
if (name == "dark")
{
Application.Current.Resources.MergedDictionaries.Add(new Dark());
resources.MergedDictionaries.Add(new Dark());
UsingLightTheme = false;
}
else if (name == "black")
{
Application.Current.Resources.MergedDictionaries.Add(new Black());
resources.MergedDictionaries.Add(new Black());
UsingLightTheme = false;
}
else if (name == "nord")
{
Application.Current.Resources.MergedDictionaries.Add(new Nord());
resources.MergedDictionaries.Add(new Nord());
UsingLightTheme = false;
}
else if (name == "light")
{
Application.Current.Resources.MergedDictionaries.Add(new Light());
resources.MergedDictionaries.Add(new Light());
UsingLightTheme = true;
}
else
@@ -46,33 +50,33 @@ namespace Bit.App.Utilities
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService", true);
if (deviceActionService?.UsingDarkTheme() ?? false)
{
Application.Current.Resources.MergedDictionaries.Add(new Dark());
resources.MergedDictionaries.Add(new Dark());
UsingLightTheme = false;
}
else
{
Application.Current.Resources.MergedDictionaries.Add(new Light());
resources.MergedDictionaries.Add(new Light());
UsingLightTheme = true;
}
}
// Base styles
Application.Current.Resources.MergedDictionaries.Add(new Base());
resources.MergedDictionaries.Add(new Base());
// Platform styles
if (Device.RuntimePlatform == Device.Android)
{
Application.Current.Resources.MergedDictionaries.Add(new Android());
resources.MergedDictionaries.Add(new Android());
}
else if (Device.RuntimePlatform == Device.iOS)
{
Application.Current.Resources.MergedDictionaries.Add(new iOS());
resources.MergedDictionaries.Add(new iOS());
}
}
public static void SetTheme(bool android)
public static void SetTheme(bool android, ResourceDictionary resources)
{
SetThemeStyle(GetTheme(android));
SetThemeStyle(GetTheme(android), resources);
}
public static string GetTheme(bool android)
@@ -81,5 +85,18 @@ namespace Bit.App.Utilities
string.Format(PreferencesStorageService.KeyFormat, Constants.ThemeKey), default(string),
!android ? "group.com.8bit.bitwarden" : default(string));
}
public static void ApplyResourcesToPage(ContentPage page)
{
foreach (var resourceDict in Resources().MergedDictionaries)
{
page.Resources.Add(resourceDict);
}
}
public static Color GetResourceColor(string color)
{
return (Color)Resources()[color];
}
}
}