diff --git a/src/Android/Autofill/AutofillService.cs b/src/Android/Autofill/AutofillService.cs index 1062c2274..cbbcb2642 100644 --- a/src/Android/Autofill/AutofillService.cs +++ b/src/Android/Autofill/AutofillService.cs @@ -21,7 +21,7 @@ namespace Bit.Droid.Autofill public class AutofillService : Android.Service.Autofill.AutofillService { private ICipherService _cipherService; - private ILockService _lockService; + private IVaultTimeoutService _vaultTimeoutService; private IStorageService _storageService; public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, @@ -47,13 +47,13 @@ namespace Bit.Droid.Autofill return; } - if (_lockService == null) + if (_vaultTimeoutService == null) { - _lockService = ServiceContainer.Resolve("lockService"); + _vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); } List items = null; - var locked = await _lockService.IsLockedAsync(); + var locked = await _vaultTimeoutService.IsLockedAsync(); if (!locked) { if (_cipherService == null) diff --git a/src/Android/Effects/FabShadowEffect.cs b/src/Android/Effects/FabShadowEffect.cs index ad0083d37..11bda2e4f 100644 --- a/src/Android/Effects/FabShadowEffect.cs +++ b/src/Android/Effects/FabShadowEffect.cs @@ -1,4 +1,5 @@ using Android.Graphics.Drawables; +using Bit.App.Utilities; using Bit.Droid.Effects; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; @@ -13,7 +14,7 @@ namespace Bit.Droid.Effects if (Control is Android.Widget.Button button) { var gd = new GradientDrawable(); - gd.SetColor(((Color)Application.Current.Resources["FabColor"]).ToAndroid()); + gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid()); gd.SetCornerRadius(100); button.SetBackground(gd); diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 0155c3992..d36a6df89 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -37,7 +37,7 @@ namespace Bit.Droid private IAppIdService _appIdService; private IStorageService _storageService; private IEventService _eventService; - private PendingIntent _lockAlarmPendingIntent; + private PendingIntent _vaultTimeoutAlarmPendingIntent; private PendingIntent _clearClipboardPendingIntent; private PendingIntent _eventUploadPendingIntent; private AppOptions _appOptions; @@ -51,7 +51,7 @@ namespace Bit.Droid _eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent, PendingIntentFlags.UpdateCurrent); var alarmIntent = new Intent(this, typeof(LockAlarmReceiver)); - _lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent, + _vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent); var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver)); _clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent, @@ -90,18 +90,18 @@ namespace Bit.Droid _broadcasterService.Subscribe(_activityKey, (message) => { - if (message.Command == "scheduleLockTimer") + if (message.Command == "scheduleVaultTimeoutTimer") { var alarmManager = GetSystemService(AlarmService) as AlarmManager; - var lockOptionMinutes = (int)message.Data; - var lockOptionMs = lockOptionMinutes * 60000; - var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10; - alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent); + var vaultTimeoutMinutes = (int)message.Data; + var vaultTimeoutMs = vaultTimeoutMinutes * 60000; + var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10; + alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent); } - else if (message.Command == "cancelLockTimer") + else if (message.Command == "cancelVaultTimeoutTimer") { var alarmManager = GetSystemService(AlarmService) as AlarmManager; - alarmManager.Cancel(_lockAlarmPendingIntent); + alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent); } else if (message.Command == "startEventTimer") { diff --git a/src/Android/Receivers/LockAlarmReceiver.cs b/src/Android/Receivers/LockAlarmReceiver.cs index 95979c3bc..e774a367b 100644 --- a/src/Android/Receivers/LockAlarmReceiver.cs +++ b/src/Android/Receivers/LockAlarmReceiver.cs @@ -9,8 +9,8 @@ namespace Bit.Droid.Receivers { public async override void OnReceive(Context context, Intent intent) { - var lockService = ServiceContainer.Resolve("lockService"); - await lockService.CheckLockAsync(); + var vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); + await vaultTimeoutService.CheckVaultTimeoutAsync(); } } } diff --git a/src/Android/Renderers/CipherViewCellRenderer.cs b/src/Android/Renderers/CipherViewCellRenderer.cs index 0d4811cb1..80b26518c 100644 --- a/src/Android/Renderers/CipherViewCellRenderer.cs +++ b/src/Android/Renderers/CipherViewCellRenderer.cs @@ -7,6 +7,7 @@ using Android.Views; using Android.Views.InputMethods; using Android.Widget; using Bit.App.Controls; +using Bit.App.Utilities; using Bit.Droid.Renderers; using FFImageLoading; using FFImageLoading.Views; @@ -42,19 +43,15 @@ namespace Bit.Droid.Renderers } if (_textColor == default(Android.Graphics.Color)) { - _textColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["TextColor"]) - .ToAndroid(); + _textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid(); } if (_mutedColor == default(Android.Graphics.Color)) { - _mutedColor = ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MutedColor"]) - .ToAndroid(); + _mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid(); } if (_disabledIconColor == default(Android.Graphics.Color)) { - _disabledIconColor = - ((Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["DisabledIconColor"]) - .ToAndroid(); + _disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid(); } var cipherCell = item as CipherViewCell; diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs index 78390e741..57b8063f4 100644 --- a/src/Android/Services/DeviceActionService.cs +++ b/src/Android/Services/DeviceActionService.cs @@ -200,13 +200,13 @@ namespace Bit.Droid.Services catch { } return null; } - + public bool SaveFile(byte[] fileData, string id, string fileName, string contentUri) { try { var activity = (MainActivity)CrossCurrentActivity.Current.Activity; - + if (contentUri != null) { var uri = Android.Net.Uri.Parse(contentUri); @@ -219,7 +219,7 @@ namespace Bit.Droid.Services javaStream.Close(); return true; } - + // Prompt for location to save file var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower()); if (extension == null) @@ -238,7 +238,7 @@ namespace Bit.Droid.Services intent.SetType(mimeType); intent.AddCategory(Intent.CategoryOpenable); intent.PutExtra(Intent.ExtraTitle, fileName); - + activity.StartActivityForResult(intent, Constants.SaveFileRequestCode); return true; } @@ -569,6 +569,13 @@ namespace Bit.Droid.Services return result.Task; } + public async Task DisplayActionSheetAsync(string title, string cancel, string destruction, + params string[] buttons) + { + return await Xamarin.Forms.Application.Current.MainPage.DisplayActionSheet( + title, cancel, destruction, buttons); + } + public void Autofill(CipherView cipher) { var activity = (MainActivity)CrossCurrentActivity.Current.Activity; diff --git a/src/App/Abstractions/IDeviceActionService.cs b/src/App/Abstractions/IDeviceActionService.cs index 785402fd6..994943317 100644 --- a/src/App/Abstractions/IDeviceActionService.cs +++ b/src/App/Abstractions/IDeviceActionService.cs @@ -32,6 +32,7 @@ namespace Bit.App.Abstractions int SystemMajorVersion(); string SystemModel(); Task DisplayAlertAsync(string title, string message, string cancel, params string[] buttons); + Task DisplayActionSheetAsync(string title, string cancel, string destruction, params string[] buttons); void Autofill(CipherView cipher); void CloseAutofill(); void Background(); diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index e862ffa34..fb9482346 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -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("userService"); _broadcasterService = ServiceContainer.Resolve("broadcasterService"); _messagingService = ServiceContainer.Resolve("messagingService"); _stateService = ServiceContainer.Resolve("stateService"); - _lockService = ServiceContainer.Resolve("lockService"); + _vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); _syncService = ServiceContainer.Resolve("syncService"); _tokenService = ServiceContainer.Resolve("tokenService"); _cryptoService = ServiceContainer.Resolve("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(Constants.LockOptionKey); + vaultTimeout = await _storageService.GetAsync(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(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("platformUtilsService").Init(); @@ -394,8 +409,8 @@ namespace Bit.App await _stateService.PurgeAsync(); if (autoPromptFingerprint && Device.RuntimePlatform == Device.iOS) { - var lockOptions = await _storageService.GetAsync(Constants.LockOptionKey); - if (lockOptions == 0) + var vaultTimeout = await _storageService.GetAsync(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)); } } diff --git a/src/App/Models/AppOptions.cs b/src/App/Models/AppOptions.cs index 8469f4957..c772714b6 100644 --- a/src/App/Models/AppOptions.cs +++ b/src/App/Models/AppOptions.cs @@ -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; } } } diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 6d5dce8f7..1361cfc96 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -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("platformUtilsService"); _deviceActionService = ServiceContainer.Resolve("deviceActionService"); - _lockService = ServiceContainer.Resolve("lockService"); + _vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); _cryptoService = ServiceContainer.Resolve("cryptoService"); _storageService = ServiceContainer.Resolve("storageService"); _userService = ServiceContainer.Resolve("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(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(Constants.DisableFaviconKey); await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault()); _messagingService.Send("unlocked"); diff --git a/src/App/Pages/Accounts/LoginPage.xaml b/src/App/Pages/Accounts/LoginPage.xaml index 340fd0731..3c8677d1a 100644 --- a/src/App/Pages/Accounts/LoginPage.xaml +++ b/src/App/Pages/Accounts/LoginPage.xaml @@ -73,8 +73,8 @@ AutomationProperties.Name="{u:I18n ToggleVisibility}" /> - - + +