bitwarden-android/src/App/App.xaml.cs

441 lines
18 KiB
C#
Raw Normal View History

2019-05-17 19:03:35 +03:00
using Bit.App.Abstractions;
using Bit.App.Models;
2019-04-10 06:24:03 +03:00
using Bit.App.Pages;
2019-04-11 22:33:10 +03:00
using Bit.App.Resources;
using Bit.App.Services;
2019-03-30 04:23:34 +03:00
using Bit.App.Utilities;
2019-05-16 19:30:20 +03:00
using Bit.Core;
2019-04-11 22:33:10 +03:00
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
2019-03-29 00:10:10 +03:00
using System;
2019-04-19 23:40:20 +03:00
using System.Threading.Tasks;
2019-03-28 03:12:44 +03:00
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Bit.App
{
public partial class App : Application
{
2019-04-11 22:33:10 +03:00
private readonly MobileI18nService _i18nService;
2019-04-19 16:42:55 +03:00
private readonly IUserService _userService;
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
2019-05-15 22:47:50 +03:00
private readonly IStateService _stateService;
2019-05-16 00:37:59 +03:00
private readonly ILockService _lockService;
private readonly ISyncService _syncService;
private readonly ITokenService _tokenService;
private readonly ICryptoService _cryptoService;
private readonly ICipherService _cipherService;
private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService;
private readonly ISettingsService _settingsService;
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly ISearchService _searchService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuthService _authService;
2019-05-16 19:30:20 +03:00
private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService;
2019-05-17 19:03:35 +03:00
private readonly IDeviceActionService _deviceActionService;
private readonly AppOptions _appOptions;
2019-04-11 22:33:10 +03:00
2019-05-17 19:03:35 +03:00
public App(AppOptions appOptions)
2019-03-28 03:12:44 +03:00
{
2019-05-17 19:03:35 +03:00
_appOptions = appOptions ?? new AppOptions();
2019-04-19 16:42:55 +03:00
_userService = ServiceContainer.Resolve<IUserService>("userService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
2019-05-15 22:47:50 +03:00
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
2019-05-16 00:37:59 +03:00
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
_settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
2019-05-16 19:30:20 +03:00
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
2019-05-16 00:37:59 +03:00
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService");
2019-04-11 22:33:10 +03:00
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
2019-05-17 19:03:35 +03:00
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
2019-03-30 00:54:03 +03:00
2019-05-29 22:50:20 +03:00
Bootstrap();
2019-04-19 19:29:37 +03:00
_broadcasterService.Subscribe(nameof(App), async (message) =>
2019-04-10 06:24:03 +03:00
{
2019-04-19 19:29:37 +03:00
if(message.Command == "showDialog")
2019-04-10 06:24:03 +03:00
{
2019-04-19 19:29:37 +03:00
var details = message.Data as DialogDetails;
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
2019-05-30 18:40:33 +03:00
Device.BeginInvokeOnMainThread(async () =>
2019-04-19 19:29:37 +03:00
{
2019-05-30 18:40:33 +03:00
if(!string.IsNullOrWhiteSpace(details.CancelText))
{
2019-05-31 01:27:57 +03:00
confirmed = await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText,
2019-05-30 18:40:33 +03:00
details.CancelText);
}
else
{
2019-05-31 01:27:57 +03:00
await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText);
2019-05-30 18:40:33 +03:00
}
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
2019-04-10 06:24:03 +03:00
}
2019-05-15 22:47:50 +03:00
else if(message.Command == "locked")
{
await LockedAsync(!(message.Data as bool?).GetValueOrDefault());
2019-05-16 00:37:59 +03:00
}
else if(message.Command == "lockVault")
{
await _lockService.LockAsync(true);
}
else if(message.Command == "logout")
{
2019-10-22 23:30:28 +03:00
Device.BeginInvokeOnMainThread(async () =>
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
2019-05-16 00:37:59 +03:00
}
else if(message.Command == "loggedOut")
{
// Clean up old migrated key if they ever log out.
await _secureStorageService.RemoveAsync("oldKey");
2019-05-16 00:37:59 +03:00
}
2019-05-30 21:13:02 +03:00
else if(message.Command == "resumed")
{
if(Device.RuntimePlatform == Device.iOS)
{
2019-06-25 00:02:05 +03:00
ResumedAsync();
2019-05-30 21:13:02 +03:00
}
}
2019-07-14 03:25:31 +03:00
else if(message.Command == "slept")
{
if(Device.RuntimePlatform == Device.iOS)
{
await SleptAsync();
}
}
2019-06-02 06:33:36 +03:00
else if(message.Command == "migrated")
{
await Task.Delay(1000);
await SetMainPageAsync();
}
else if(message.Command == "popAllAndGoToTabGenerator" ||
message.Command == "popAllAndGoToTabMyVault")
{
Device.BeginInvokeOnMainThread(async () =>
{
if(Current.MainPage is TabsPage tabsPage)
{
while(tabsPage.Navigation.ModalStack.Count > 0)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if(message.Command == "popAllAndGoToTabMyVault")
{
2019-06-07 03:34:59 +03:00
_appOptions.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else
{
2019-06-07 03:34:59 +03:00
_appOptions.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
}
});
}
2019-04-10 06:24:03 +03:00
});
2019-03-28 03:12:44 +03:00
}
2019-05-17 19:03:35 +03:00
protected async override void OnStart()
2019-03-28 03:12:44 +03:00
{
2019-05-16 19:30:20 +03:00
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
2019-05-17 19:03:35 +03:00
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
2019-05-29 00:18:27 +03:00
Prime();
2019-05-30 21:13:02 +03:00
if(string.IsNullOrWhiteSpace(_appOptions.Uri))
{
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_storageService);
if(!updated)
{
SyncIfNeeded();
}
}
2019-07-13 00:29:40 +03:00
_messagingService.Send("startEventTimer");
2019-03-28 03:12:44 +03:00
}
2019-05-16 19:30:20 +03:00
protected async override void OnSleep()
2019-03-28 03:12:44 +03:00
{
2019-05-16 19:30:20 +03:00
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
2019-05-31 03:24:30 +03:00
if(Device.RuntimePlatform == Device.Android)
{
2019-07-22 15:37:06 +03:00
var isLocked = await _lockService.IsLockedAsync();
if(!isLocked)
{
await _storageService.SaveAsync(Constants.LastActiveKey, DateTime.UtcNow);
}
SetTabsPageFromAutofill(isLocked);
2019-07-14 03:25:31 +03:00
await SleptAsync();
2019-05-31 03:24:30 +03:00
}
2019-03-28 03:12:44 +03:00
}
protected override void OnResume()
2019-03-28 03:12:44 +03:00
{
2019-05-16 19:30:20 +03:00
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
2019-05-30 21:13:02 +03:00
if(Device.RuntimePlatform == Device.Android)
{
2019-06-25 00:02:05 +03:00
ResumedAsync();
2019-05-30 21:13:02 +03:00
}
2019-06-25 00:02:05 +03:00
}
2019-07-14 03:25:31 +03:00
private async Task SleptAsync()
{
await HandleLockingAsync();
_messagingService.Send("stopEventTimer");
}
2019-06-25 00:02:05 +03:00
private async void ResumedAsync()
{
_messagingService.Send("cancelLockTimer");
2019-07-13 00:29:40 +03:00
_messagingService.Send("startEventTimer");
2019-06-25 00:02:05 +03:00
await ClearCacheIfNeededAsync();
await TryClearCiphersCacheAsync();
2019-06-25 00:02:05 +03:00
Prime();
SyncIfNeeded();
if(Current.MainPage is NavigationPage navPage && navPage.CurrentPage is LockPage lockPage)
{
2019-12-16 17:43:14 +03:00
if(Device.RuntimePlatform == Device.Android)
{
// Workaround for https://github.com/xamarin/Xamarin.Forms/issues/7478
await Task.Delay(100);
Current.MainPage = new NavigationPage(lockPage);
// End workaround
}
await lockPage.PromptFingerprintAfterResumeAsync();
}
2019-03-28 03:12:44 +03:00
}
2019-04-11 22:33:10 +03:00
private void SetCulture()
{
// Calendars are removed by linker. ref https://bugzilla.xamarin.com/show_bug.cgi?id=59077
new System.Globalization.ThaiBuddhistCalendar();
new System.Globalization.HijriCalendar();
new System.Globalization.UmAlQuraCalendar();
}
2019-04-19 23:40:20 +03:00
2019-05-16 00:37:59 +03:00
private async Task LogOutAsync(bool expired)
{
var userId = await _userService.GetUserIdAsync();
await Task.WhenAll(
_syncService.SetLastSyncAsync(DateTime.MinValue),
_tokenService.ClearTokenAsync(),
_cryptoService.ClearKeysAsync(),
_userService.ClearAsync(),
_settingsService.ClearAsync(userId),
_cipherService.ClearAsync(userId),
_folderService.ClearAsync(userId),
_collectionService.ClearAsync(userId),
_passwordGenerationService.ClearAsync(),
_lockService.ClearAsync(),
_stateService.PurgeAsync());
2019-05-17 00:30:07 +03:00
_lockService.FingerprintLocked = true;
2019-05-16 00:37:59 +03:00
_searchService.ClearIndex();
_authService.LogOut(() =>
{
2019-05-31 01:27:57 +03:00
Current.MainPage = new HomePage();
2019-10-22 23:30:28 +03:00
if(expired)
{
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
}
2019-05-16 00:37:59 +03:00
});
}
2019-04-19 23:40:20 +03:00
private async Task SetMainPageAsync()
{
var authed = await _userService.IsAuthenticatedAsync();
if(authed)
{
2019-05-17 18:02:50 +03:00
if(await _lockService.IsLockedAsync())
2019-05-16 00:37:59 +03:00
{
2019-05-17 23:36:29 +03:00
Current.MainPage = new NavigationPage(new LockPage(_appOptions));
2019-05-16 00:37:59 +03:00
}
2019-05-17 19:03:35 +03:00
else if(_appOptions.FromAutofillFramework && _appOptions.SaveType.HasValue)
{
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: _appOptions));
}
2019-05-17 20:14:26 +03:00
else if(_appOptions.Uri != null)
{
Current.MainPage = new NavigationPage(new AutofillCiphersPage(_appOptions));
}
2019-05-16 00:37:59 +03:00
else
{
Current.MainPage = new TabsPage(_appOptions);
2019-05-16 00:37:59 +03:00
}
2019-04-19 23:40:20 +03:00
}
else
{
Current.MainPage = new HomePage();
}
}
2019-05-16 19:30:20 +03:00
private async Task HandleLockingAsync()
{
if(await _lockService.IsLockedAsync())
{
return;
}
var authed = await _userService.IsAuthenticatedAsync();
if(!authed)
{
return;
}
2019-05-16 19:30:20 +03:00
var lockOption = _platformUtilsService.LockTimeout();
if(lockOption == null)
{
lockOption = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
}
lockOption = lockOption.GetValueOrDefault(-1);
if(lockOption > 0)
{
_messagingService.Send("scheduleLockTimer", lockOption.Value);
}
else if(lockOption == 0)
{
2019-05-16 19:31:00 +03:00
await _lockService.LockAsync(true);
2019-05-16 19:30:20 +03:00
}
}
2019-05-17 19:03:35 +03:00
private async Task ClearCacheIfNeededAsync()
{
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
if((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
{
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
}
}
2019-07-22 15:37:06 +03:00
private void SetTabsPageFromAutofill(bool isLocked)
2019-05-17 19:03:35 +03:00
{
if(Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_appOptions.Uri) &&
!_appOptions.FromAutofillFramework)
{
Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
_appOptions.Uri = null;
2019-07-22 15:37:06 +03:00
if(isLocked)
{
Current.MainPage = new NavigationPage(new LockPage());
}
else
{
Current.MainPage = new TabsPage();
}
2019-05-17 19:03:35 +03:00
});
});
}
}
2019-05-29 00:18:27 +03:00
private void Prime()
{
Task.Run(() =>
{
var word = EEFLongWordList.Instance.List[1];
var parsedDomain = DomainName.TryParse("https://bitwarden.com", out var domainName);
});
}
2019-05-29 22:50:20 +03:00
private void Bootstrap()
{
InitializeComponent();
SetCulture();
2019-06-24 21:29:23 +03:00
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android);
2019-05-31 01:27:57 +03:00
Current.MainPage = new HomePage();
2019-05-29 22:50:20 +03:00
var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
}
2019-05-30 21:13:02 +03:00
private void SyncIfNeeded()
{
2019-06-04 06:12:54 +03:00
if(Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
return;
}
2019-05-30 21:13:02 +03:00
Task.Run(async () =>
{
var lastSync = await _syncService.GetLastSyncAsync();
2019-08-12 15:51:49 +03:00
if(lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
2019-05-30 21:13:02 +03:00
{
2019-08-12 16:35:18 +03:00
await Task.Delay(1000);
2019-05-30 21:13:02 +03:00
await _syncService.FullSyncAsync(false);
}
});
}
private async Task TryClearCiphersCacheAsync()
{
if(Device.RuntimePlatform != Device.iOS)
{
return;
}
var clearCache = await _storageService.GetAsync<bool?>(Constants.ClearCiphersCacheKey);
if(clearCache.GetValueOrDefault())
{
_cipherService.ClearCache();
await _storageService.RemoveAsync(Constants.ClearCiphersCacheKey);
}
}
private async Task LockedAsync(bool autoPromptFingerprint)
{
await _stateService.PurgeAsync();
if(autoPromptFingerprint && Device.RuntimePlatform == Device.iOS)
{
var lockOptions = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
if(lockOptions == 0)
{
autoPromptFingerprint = false;
}
}
else if(autoPromptFingerprint && Device.RuntimePlatform == Device.Android &&
_deviceActionService.UseNativeBiometric())
{
autoPromptFingerprint = false;
}
PreviousPageInfo lastPageBeforeLock = null;
if(Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
{
var topPage = tabbedPage.Navigation.ModalStack[tabbedPage.Navigation.ModalStack.Count - 1];
if(topPage is NavigationPage navPage)
{
if(navPage.CurrentPage is ViewPage viewPage)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "view",
CipherId = viewPage.ViewModel.CipherId
};
}
else if(navPage.CurrentPage is AddEditPage addEditPage && addEditPage.ViewModel.EditMode)
{
lastPageBeforeLock = new PreviousPageInfo
{
Page = "edit",
CipherId = addEditPage.ViewModel.CipherId
};
}
}
}
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
var lockPage = new LockPage(_appOptions, autoPromptFingerprint);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
}
2019-03-28 03:12:44 +03:00
}
}