PM-3350 Improved code safety adding a lot of try...catch and logging throughout the app. Also made the invoking on main thread safer on several places of the app. Additionally, on the GroupingsPageViewModel changed the code removing the old Xamarin hack and just using Replace directly instead of Clearing first to see if that fixes the crash we're having sometimes on the app.

This commit is contained in:
Federico Maccaroni 2024-01-12 13:54:34 -03:00
parent 27fa79e0bd
commit 1949a450fd
No known key found for this signature in database
GPG key ID: 5D233F8F2B034536
20 changed files with 745 additions and 512 deletions

View file

@ -54,10 +54,6 @@ namespace Bit.iOS
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
//LoadApplication(new App.App(null));
//iOSCoreHelpers.AppearanceAdjustments();
//ZXing.Net.Mobile.Forms.iOS.Platform.Init();
ConnectToWatchIfNeededAsync().FireAndForget();
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
@ -219,7 +215,11 @@ namespace Bit.iOS
public override void DidEnterBackground(UIApplication uiApplication)
{
_stateService?.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
if (_stateService != null && _deviceActionService != null)
{
_stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
_messagingService?.Send("slept");
base.DidEnterBackground(uiApplication);
}

View file

@ -35,6 +35,8 @@ namespace Bit.App
private readonly IAccountsManager _accountsManager;
private readonly IPushNotificationService _pushNotificationService;
private readonly IConfigService _configService;
private readonly ILogger _logger;
private static bool _isResumed;
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
private static bool _pendingCheckPasswordlessLoginRequests;
@ -155,6 +157,7 @@ namespace Bit.App
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_configService = ServiceContainer.Resolve<IConfigService>();
_logger = ServiceContainer.Resolve<ILogger>();
_accountsManager.Init(() => Options, this);
@ -169,7 +172,7 @@ namespace Bit.App
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (!string.IsNullOrWhiteSpace(details.CancelText))
{
@ -183,20 +186,16 @@ namespace Bit.App
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
#if IOS
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
{
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
ResumedAsync().FireAndForget();
}
}
else if (message.Command == "slept")
{
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await SleptAsync();
}
}
#endif
else if (message.Command == "migrated")
{
await Task.Delay(1000);
@ -213,7 +212,7 @@ namespace Bit.App
Options.OtpData = new OtpData((string)message.Data);
}
MainThread.InvokeOnMainThreadAsync(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (MainPage is TabsPage tabsPage)
{
@ -249,7 +248,7 @@ namespace Bit.App
}
else if (message.Command == "convertAccountToKeyConnector")
{
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
@ -257,7 +256,7 @@ namespace Bit.App
}
else if (message.Command == Constants.ForceUpdatePassword)
{
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
@ -372,6 +371,8 @@ namespace Bit.App
public AppOptions Options { get; private set; }
protected override async void OnStart()
{
try
{
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
_isResumed = true;
@ -390,23 +391,33 @@ namespace Bit.App
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (DeviceInfo.Platform == DevicePlatform.Android)
{
#if ANDROID
await _vaultTimeoutService.CheckVaultTimeoutAsync();
// Reset delay on every start
_vaultTimeoutService.DelayLockAndLogoutMs = null;
}
#endif
await _configService.GetAsync();
_messagingService.Send("startEventTimer");
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
#if ANDROID
protected override async void OnSleep()
#else
protected override void OnSleep()
#endif
{
try
{
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
_isResumed = false;
if (DeviceInfo.Platform == DevicePlatform.Android)
{
#if ANDROID
var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked)
{
@ -417,10 +428,18 @@ namespace Bit.App
ClearAutofillUri();
}
await SleptAsync();
#endif
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
protected override void OnResume()
{
try
{
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_isResumed = true;
@ -428,9 +447,15 @@ namespace Bit.App
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (DeviceInfo.Platform == DevicePlatform.Android)
{
#if ANDROID
ResumedAsync().FireAndForget();
#endif
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
@ -516,6 +541,8 @@ namespace Bit.App
Task.Run(() =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
try
{
Options.Uri = null;
if (isLocked)
@ -526,6 +553,12 @@ namespace Bit.App
{
App.MainPage = new TabsPage();
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});
return true;
@ -549,7 +582,7 @@ namespace Bit.App
ThemeManager.SetTheme(Resources);
RequestedThemeChanged += (s, a) =>
{
UpdateThemeAsync();
UpdateThemeAsync().FireAndForget();
};
_isResumed = true;
#if IOS
@ -567,6 +600,8 @@ namespace Bit.App
return;
}
Task.Run(async () =>
{
try
{
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
@ -574,6 +609,11 @@ namespace Bit.App
await Task.Delay(1000);
await _syncService.FullSyncAsync(false);
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
});
}

View file

@ -4,6 +4,7 @@ using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -26,16 +27,24 @@ namespace Bit.App.Pages
_vm = BindingContext as LockPageViewModel;
_vm.CheckPendingAuthRequests = checkPendingAuthRequests;
_vm.Page = this;
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () => await UnlockedAsync());
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () =>
{
try
{
await UnlockedAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
#if IOS
ToolbarItems.Add(_moreItem);
}
else
{
#else
ToolbarItems.Add(_logOut);
}
#endif
}
public Entry SecretEntry
@ -64,6 +73,8 @@ namespace Bit.App.Pages
}
protected override async void OnAppearing()
{
try
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LockPage), message =>
@ -108,11 +119,17 @@ namespace Bit.App.Pages
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
MainThread.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync());
await MainThread.InvokeOnMainThreadAsync(async () => await _vm.PromptBiometricAsync());
});
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private void PerformFocusSecretEntry(int? cursorPosition)
{
@ -166,14 +183,25 @@ namespace Bit.App.Pages
}
private async void Biometric_Clicked(object sender, EventArgs e)
{
try
{
if (DoOnce())
{
await _vm.PromptBiometricAsync();
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
try
{
await _accountListOverlay.HideAsync();
@ -190,6 +218,12 @@ namespace Bit.App.Pages
await _vm.LogOutAsync();
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async Task UnlockedAsync()
{

View file

@ -14,6 +14,7 @@ namespace Bit.App.Pages
{
private readonly LazyResolve<IStateService> _stateService = new LazyResolve<IStateService>();
private readonly LazyResolve<IDeviceActionService> _deviceActionService = new LazyResolve<IDeviceActionService>();
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
@ -47,12 +48,22 @@ namespace Bit.App.Pages
protected virtual bool ShouldCheckToPreventOnNavigatedToCalledTwice => false;
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
try
{
base.OnNavigatedTo(args);
if (IsThemeDirty)
{
UpdateOnThemeChanged();
try
{
await UpdateOnThemeChanged();
}
catch (Exception ex)
{
Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex);
// Don't rethrow on theme changed so the user can still continue on the app.
}
}
await SaveActivityAsync();
@ -65,6 +76,12 @@ namespace Bit.App.Pages
await InitOnNavigatedToAsync();
}
catch (Exception ex)
{
Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
protected virtual Task InitOnNavigatedToAsync() => Task.CompletedTask;
@ -115,6 +132,8 @@ namespace Bit.App.Pages
ContentView targetView = null)
{
async Task DoWorkAsync()
{
try
{
await workFunction.Invoke();
if (sourceView != null)
@ -129,16 +148,22 @@ namespace Bit.App.Pages
}
}
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
catch (Exception ex)
{
await DoWorkAsync();
return;
_logger.Value.Exception(ex);
throw;
}
}
#if IOS
await DoWorkAsync();
#else
await Task.Run(async () =>
{
await Task.Delay(fromModal ? ShowModalAnimationDelay : ShowPageAnimationDelay);
MainThread.BeginInvokeOnMainThread(async () => await DoWorkAsync());
});
#endif
}
protected void RequestFocus(InputView input)

View file

@ -42,7 +42,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
var history = await _passwordGenerationService.GetHistoryAsync();
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
ShowNoData = History.Count == 0;
@ -66,7 +66,7 @@ namespace Bit.App.Pages
{
try
{
await Device.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List<GeneratedPasswordHistory>()));
await MainThread.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List<GeneratedPasswordHistory>()));
await InitAsync();
}

View file

@ -15,6 +15,7 @@ namespace Bit.App.Pages
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
{
private readonly IBroadcasterService _broadcasterService;
private readonly ILogger _logger;
private GeneratorPageViewModel _vm;
private readonly bool _fromTabPage;
@ -26,6 +27,8 @@ namespace Bit.App.Pages
_tabsPage = tabsPage;
InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as GeneratorPageViewModel;
_vm.Page = this;
_fromTabPage = fromTabPage;
@ -35,26 +38,21 @@ namespace Bit.App.Pages
_vm.EmailWebsite = emailWebsite;
_vm.EditMode = editMode;
_vm.IosExtension = appOptions?.IosExtension ?? false;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
var isIos = Device.RuntimePlatform == Device.iOS;
if (selectAction != null)
{
if (isIos)
{
#if IOS
ToolbarItems.Add(_closeItem);
}
#endif
ToolbarItems.Add(_selectItem);
}
else
{
if (isIos)
{
#if IOS
ToolbarItems.Add(_moreItem);
}
else
{
#else
ToolbarItems.Add(_historyItem);
}
#endif
}
_typePicker.On<Microsoft.Maui.Controls.PlatformConfiguration.iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_passwordTypePicker.On<Microsoft.Maui.Controls.PlatformConfiguration.iOS>().SetUpdateMode(UpdateMode.WhenFinished);
@ -70,6 +68,8 @@ namespace Bit.App.Pages
}
protected async override void OnAppearing()
{
try
{
base.OnAppearing();
@ -84,10 +84,16 @@ namespace Bit.App.Pages
{
if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
{
Device.BeginInvokeOnMainThread(() => _vm.RedrawPassword());
MainThread.BeginInvokeOnMainThread(() => _vm.RedrawPassword());
}
});
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
protected override void OnDisappearing()
{
@ -100,16 +106,19 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed()
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && _tabsPage != null)
#if ANDROID
if (_tabsPage != null)
{
_tabsPage.ResetToVaultPage();
return true;
}
#endif
return base.OnBackButtonPressed();
}
private async void More_Clicked(object sender, EventArgs e)
{
try
{
if (!DoOnce())
{
@ -123,6 +132,11 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new Microsoft.Maui.Controls.NavigationPage(page));
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private void Select_Clicked(object sender, EventArgs e)
{
@ -144,7 +158,7 @@ namespace Bit.App.Pages
{
await base.UpdateOnThemeChanged();
await Device.InvokeOnMainThreadAsync(() =>
await MainThread.InvokeOnMainThreadAsync(() =>
{
if (_vm != null)
{

View file

@ -18,6 +18,7 @@ namespace Bit.App.Pages
private readonly ISyncService _syncService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISendService _sendService;
private readonly ILogger _logger;
private readonly SendGroupingsPageViewModel _vm;
private readonly string _pageName;
@ -33,6 +34,8 @@ namespace Bit.App.Pages
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as SendGroupingsPageViewModel;
_vm.Page = this;
_vm.MainPage = mainPage;
@ -43,25 +46,23 @@ namespace Bit.App.Pages
_vm.PageTitle = pageTitle;
}
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS)
{
#if IOS
_absLayout.Children.Remove(_fab);
if (type == null)
{
ToolbarItems.Add(_aboutIconItem);
}
ToolbarItems.Add(_addItem);
}
else
{
#else
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_aboutTextItem);
}
#endif
}
protected override async void OnAppearing()
{
try
{
base.OnAppearing();
if (_syncService.SyncInProgress)
@ -75,12 +76,12 @@ namespace Bit.App.Pages
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted" || message.Command == "sendUpdated")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
await MainThread.InvokeOnMainThreadAsync(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
@ -123,6 +124,12 @@ namespace Bit.App.Pages
await CheckAddRequest();
}, _mainContent);
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
protected override void OnDisappearing()
{

View file

@ -1,22 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.App.Abstractions;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Resources.Localization;
using Bit.Core.Utilities;
using DeviceType = Bit.Core.Enums.DeviceType;
using Microsoft.Maui.Networking;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
public class SendGroupingsPageViewModel : BaseViewModel
@ -117,6 +106,9 @@ namespace Bit.App.Pages
{
return;
}
try
{
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
@ -139,6 +131,13 @@ namespace Bit.App.Pages
Loading = true;
ShowList = false;
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
var groupedSends = new List<SendGroupingsPageListGroup>();
var page = Page as SendGroupingsPage;
@ -146,8 +145,11 @@ namespace Bit.App.Pages
{
await LoadDataAsync();
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
var uppercaseGroupNames = Device.RuntimePlatform == Device.iOS;
#if IOS
var uppercaseGroupNames = true;
#else
var uppercaseGroupNames = false;
#endif
if (MainPage)
{
groupedSends.Add(new SendGroupingsPageListGroup(
@ -208,6 +210,11 @@ namespace Bit.App.Pages
}
}
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
finally
{
_doingLoad = false;
@ -314,6 +321,8 @@ namespace Bit.App.Pages
}
private async void SendOptionsAsync(SendView send)
{
try
{
if ((Page as BaseContentPage).DoOnce())
{
@ -324,5 +333,11 @@ namespace Bit.App.Pages
}
}
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
}
}
}

View file

@ -29,7 +29,7 @@ namespace Bit.App.Pages
{
if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)

View file

@ -49,12 +49,10 @@ namespace Bit.App.Pages
private void UpdatePlaceholder()
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android)
{
#if ANDROID
MainThread.BeginInvokeOnMainThread(() =>
_emptyPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_login_requests" : "empty_login_requests_dark"));
}
#endif
}
}
}

View file

@ -10,6 +10,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -86,6 +87,8 @@ namespace Bit.App.Pages
}
protected override async void OnAppearing()
{
try
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
@ -103,6 +106,12 @@ namespace Bit.App.Pages
await ForcePasswordResetIfNeededAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async Task ForcePasswordResetIfNeededAsync()
{
@ -173,6 +182,8 @@ namespace Bit.App.Pages
}
protected override async void OnCurrentPageChanged()
{
try
{
if (CurrentPage is NavigationPage navPage)
{
@ -192,6 +203,12 @@ namespace Bit.App.Pages
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
public void OnPageReselected()
{

View file

@ -33,7 +33,7 @@ namespace Bit.App.Pages
{
if (message.Command == "selectFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<byte[], string>;
_vm.FileData = data.Item1;

View file

@ -7,6 +7,7 @@ using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -262,9 +263,17 @@ namespace Bit.App.Pages
var page = new ScanPage(key =>
{
MainThread.BeginInvokeOnMainThread(async () =>
{
try
{
await Navigation.PopModalAsync();
await _vm.UpdateTotpKeyAsync(key);
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});

View file

@ -63,12 +63,12 @@ namespace Bit.App.Pages
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
@ -83,7 +83,7 @@ namespace Bit.App.Pages
}
else if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)

View file

@ -108,6 +108,8 @@ namespace Bit.App.Pages
var previousCts = _searchCancellationTokenSource;
var cts = new CancellationTokenSource();
Task.Run(async () =>
{
try
{
List<CipherView> ciphers = null;
var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1;
@ -155,6 +157,11 @@ namespace Bit.App.Pages
ShowNoData = !shouldShowAllWhenEmpty && searchable && Ciphers.Count == 0;
ShowList = (searchable || shouldShowAllWhenEmpty) && !ShowNoData;
});
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}, cts.Token);
_searchCancellationTokenSource = cts;
}

View file

@ -1,9 +1,9 @@
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Resources.Localization;
using Bit.Core.Services;
using Bit.Core.Utilities;
@ -19,6 +19,7 @@ namespace Bit.App.Pages
private readonly ICipherService _cipherService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly ILogger _logger;
private readonly GroupingsPageViewModel _vm;
private readonly string _pageName;
@ -39,6 +40,8 @@ namespace Bit.App.Pages
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as GroupingsPageViewModel;
_vm.Page = this;
_vm.MainPage = mainPage;
@ -57,17 +60,14 @@ namespace Bit.App.Pages
_vm.VaultFilterDescription = vaultFilterSelection;
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
#if IOS
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_addItem);
}
else
{
#else
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_exitItem);
}
#endif
if (deleted || showTotp)
{
_absLayout.Children.Remove(_fab);
@ -80,6 +80,8 @@ namespace Bit.App.Pages
}
protected override async void OnAppearing()
{
try
{
base.OnAppearing();
if (_syncService.SyncInProgress)
@ -184,6 +186,12 @@ namespace Bit.App.Pages
}
}
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
protected override bool OnBackButtonPressed()
{
@ -195,7 +203,9 @@ namespace Bit.App.Pages
return false;
}
protected override async void OnDisappearing()
protected override void OnDisappearing()
{
try
{
base.OnDisappearing();
IsBusy = false;
@ -204,6 +214,12 @@ namespace Bit.App.Pages
_vm.DisableRefreshing();
_accountAvatar?.OnDisappearing();
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
private async void RowSelected(object sender, SelectionChangedEventArgs e)
{
@ -263,6 +279,8 @@ namespace Bit.App.Pages
}
private async void Search_Clicked(object sender, EventArgs e)
{
try
{
await _accountListOverlay.HideAsync();
if (DoOnce())
@ -271,26 +289,54 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Sync_Clicked(object sender, EventArgs e)
{
try
{
await _accountListOverlay.HideAsync();
await _vm.SyncAsync();
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Lock_Clicked(object sender, EventArgs e)
{
try
{
await _accountListOverlay.HideAsync();
await _vaultTimeoutService.LockAsync(true, true);
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Exit_Clicked(object sender, EventArgs e)
{
try
{
await _accountListOverlay.HideAsync();
await _vm.ExitAsync();
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void AddButton_Clicked(object sender, EventArgs e)
{
try
{
var skipAction = _accountListOverlay.IsVisible && DeviceInfo.Platform == DevicePlatform.Android;
await _accountListOverlay.HideAsync();
@ -305,6 +351,11 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async Task ShowPreviousPageAsync()
{

View file

@ -166,6 +166,9 @@ namespace Bit.App.Pages
{
return;
}
try
{
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
@ -190,6 +193,7 @@ namespace Bit.App.Pages
{
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
@ -197,10 +201,17 @@ namespace Bit.App.Pages
ShowList = false;
ShowAddCipherButton = !Deleted;
_websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true;
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
var groupedItems = new List<GroupingsPageListGroup>();
var page = Page as GroupingsPage;
_websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true;
try
{
await LoadDataAsync();
@ -307,11 +318,6 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(() =>
{
#if IOS
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
// because of update to XF v5.0.0.2401
GroupedItems.Clear();
#endif
GroupedItems.ReplaceRange(items);
});
}
@ -335,23 +341,22 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(() =>
{
if (groupedItems.Any())
if (!groupedItems.Any())
{
#if IOS
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
// because of update to XF v5.0.0.2401
GroupedItems.Clear();
#endif
return;
}
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
GroupedItems.AddRange(items);
}
else
{
GroupedItems.Clear();
}
});
}
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
finally
{
_doingLoad = false;

View file

@ -42,7 +42,7 @@ namespace Bit.App.Pages
{
var cipher = await _cipherService.GetAsync(CipherId);
var decCipher = await cipher.DecryptAsync();
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
ShowNoData = History.Count == 0;

View file

@ -204,7 +204,18 @@ namespace Bit.iOS.Core.Controllers
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
NSRunLoop.Main.BeginInvokeOnMainThread(async () => await PromptBiometricAsync());
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
{
try
{
await PromptBiometricAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});
}
}

View file

@ -31,7 +31,7 @@ namespace Bit.iOS.Core.Services
var dictArr = new NSDictionary<NSString, NSObject>[1];
dictArr[0] = new NSDictionary<NSString, NSObject>(new NSString(UTType.UTF8PlainText), new NSString(text));
Device.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions
MainThread.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions
{
LocalOnly = true,
ExpirationDate = clearSeconds > 0 ? NSDate.FromTimeIntervalSinceNow(clearSeconds) : null