diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs index c49bc0758..fe26186ad 100644 --- a/src/Android/Services/DeviceActionService.cs +++ b/src/Android/Services/DeviceActionService.cs @@ -83,7 +83,7 @@ namespace Bit.Droid.Services return launchIntentSender != null; } - public async Task ShowLoadingAsync(string text) + public async Task ShowLoadingAsync(string text, System.Threading.CancellationTokenSource cts = null, string cancelButtonText = null) { if (_progressDialog != null) { @@ -98,10 +98,16 @@ namespace Bit.Droid.Services txtLoading.Text = text; txtLoading.SetTextColor(ThemeHelpers.TextColor); - _progressDialog = new AlertDialog.Builder(activity) + var progressDialogBuilder = new AlertDialog.Builder(activity) .SetView(dialogView) - .SetCancelable(false) - .Create(); + .SetCancelable(cts != null); + + if (cts != null) + { + progressDialogBuilder.SetNegativeButton(cancelButtonText ?? AppResources.Cancel, (sender, args) => cts?.Cancel()); + } + + _progressDialog = progressDialogBuilder.Create(); _progressDialog.Show(); } diff --git a/src/App/Abstractions/IDeviceActionService.cs b/src/App/Abstractions/IDeviceActionService.cs index e539b156f..d91e43089 100644 --- a/src/App/Abstractions/IDeviceActionService.cs +++ b/src/App/Abstractions/IDeviceActionService.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using Bit.Core.Enums; using Bit.Core.Models; @@ -13,7 +14,7 @@ namespace Bit.App.Abstractions string GetBuildNumber(); void Toast(string text, bool longDuration = false); - Task ShowLoadingAsync(string text); + Task ShowLoadingAsync(string text, CancellationTokenSource cts = null, string cancelButtonText = null); Task HideLoadingAsync(); Task DisplayPromptAync(string title = null, string description = null, string text = null, string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false, diff --git a/src/App/Pages/Vault/AttachmentsPageViewModel.cs b/src/App/Pages/Vault/AttachmentsPageViewModel.cs index 02e9b2ae6..00f0b9ab0 100644 --- a/src/App/Pages/Vault/AttachmentsPageViewModel.cs +++ b/src/App/Pages/Vault/AttachmentsPageViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Bit.App.Abstractions; @@ -31,6 +32,7 @@ namespace Bit.App.Pages private bool _hasUpdatedKey; private bool _canAccessAttachments; private string _fileName; + private CancellationTokenSource _uploadCts; public AttachmentsPageViewModel() { @@ -119,11 +121,15 @@ namespace Bit.App.Pages AppResources.AnErrorHasOccurred); return false; } + + _uploadCts = new CancellationTokenSource(); + var uploadCts = _uploadCts; + try { - await _deviceActionService.ShowLoadingAsync(AppResources.Saving); + await _deviceActionService.ShowLoadingAsync(AppResources.Saving, uploadCts); _cipherDomain = await _cipherService.SaveAttachmentRawWithServerAsync( - _cipherDomain, FileName, FileData); + _cipherDomain, FileName, FileData, uploadCts.Token); Cipher = await _cipherDomain.DecryptAsync(); await _deviceActionService.HideLoadingAsync(); _platformUtilsService.ShowToast("success", null, AppResources.AttachementAdded); @@ -132,6 +138,11 @@ namespace Bit.App.Pages FileName = null; return true; } + catch (OperationCanceledException) + { + await _deviceActionService.HideLoadingAsync(); + await _platformUtilsService.ShowDialogAsync(AppResources.UploadHasBeenCanceled, AppResources.Attachments); + } catch (ApiException e) { _logger.Exception(e); diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 3810c9cf9..e30e87765 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -202,6 +202,24 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Biometric unlock for this account is disabled pending verification of master password.. + /// + public static string AccountBiometricInvalidated { + get { + return ResourceManager.GetString("AccountBiometricInvalidated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autofill biometric unlock for this account is disabled pending verification of master password.. + /// + public static string AccountBiometricInvalidatedExtension { + get { + return ResourceManager.GetString("AccountBiometricInvalidatedExtension", resourceCulture); + } + } + /// /// Looks up a localized string similar to Your new account has been created! You may now log in.. /// @@ -967,24 +985,6 @@ namespace Bit.App.Resources { } } - /// - /// Looks up a localized string similar to Biometric unlock disabled pending verification of master password.. - /// - public static string AccountBiometricInvalidated { - get { - return ResourceManager.GetString("AccountBiometricInvalidated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Biometric unlock for autofill disabled pending verification of master password.. - /// - public static string AccountBiometricInvalidatedExtension { - get { - return ResourceManager.GetString("AccountBiometricInvalidatedExtension", resourceCulture); - } - } - /// /// Looks up a localized string similar to Biometrics. /// @@ -6515,6 +6515,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Upload has been canceled. + /// + public static string UploadHasBeenCanceled { + get { + return ResourceManager.GetString("UploadHasBeenCanceled", resourceCulture); + } + } + /// /// Looks up a localized string similar to Uppercase (A to Z). /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index a94cdb2b4..0a3b7479f 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -2631,4 +2631,7 @@ Do you want to switch to this account? Current master password + + Upload has been canceled + diff --git a/src/Core/Services/ApiService.cs b/src/Core/Services/ApiService.cs index 1ca456383..c2ac96a93 100644 --- a/src/Core/Services/ApiService.cs +++ b/src/Core/Services/ApiService.cs @@ -699,6 +699,14 @@ namespace Bit.Core.Services { response = await _httpClient.SendAsync(requestMessage, cancellationToken); } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) when (ex.Message?.Contains("Socket closed") == true) + { + throw new OperationCanceledException(); + } catch (Exception e) { throw new ApiException(HandleWebError(e)); diff --git a/src/iOS.Core/Services/DeviceActionService.cs b/src/iOS.Core/Services/DeviceActionService.cs index 21de54abe..056a61afb 100644 --- a/src/iOS.Core/Services/DeviceActionService.cs +++ b/src/iOS.Core/Services/DeviceActionService.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Bit.App.Abstractions; using Bit.App.Resources; @@ -61,7 +62,7 @@ namespace Bit.iOS.Core.Services }; } - public Task ShowLoadingAsync(string text) + public Task ShowLoadingAsync(string text, CancellationTokenSource cts = null, string cancelButtonText = null) { if (_progressAlert != null) { @@ -84,6 +85,14 @@ namespace Bit.iOS.Core.Services _progressAlert = UIAlertController.Create(null, text, UIAlertControllerStyle.Alert); _progressAlert.View.TintColor = UIColor.Black; _progressAlert.View.Add(loadingIndicator); + if (cts != null) + { + _progressAlert.AddAction(UIAlertAction.Create(cancelButtonText ?? AppResources.Cancel, UIAlertActionStyle.Cancel, x => + { + cts.Cancel(); + result.TrySetResult(0); + })); + } vc.PresentViewController(_progressAlert, false, () => result.TrySetResult(0)); return result.Task;