diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj index 24143f879..358ac775a 100644 --- a/src/Android/Android.csproj +++ b/src/Android/Android.csproj @@ -149,6 +149,7 @@ + diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index df1ca078a..459a3033c 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -34,9 +34,7 @@ namespace Bit.Droid private IBroadcasterService _broadcasterService; private IUserService _userService; private IAppIdService _appIdService; - private IStorageService _storageService; private IEventService _eventService; - private PendingIntent _clearClipboardPendingIntent; private PendingIntent _eventUploadPendingIntent; private AppOptions _appOptions; private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}"; @@ -48,9 +46,6 @@ namespace Bit.Droid var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver)); _eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent, PendingIntentFlags.UpdateCurrent); - var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver)); - _clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent, - PendingIntentFlags.UpdateCurrent); var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build(); StrictMode.SetThreadPolicy(policy); @@ -60,7 +55,6 @@ namespace Bit.Droid _broadcasterService = ServiceContainer.Resolve("broadcasterService"); _userService = ServiceContainer.Resolve("userService"); _appIdService = ServiceContainer.Resolve("appIdService"); - _storageService = ServiceContainer.Resolve("storageService"); _eventService = ServiceContainer.Resolve("eventService"); TabLayoutResource = Resource.Layout.Tabbar; @@ -108,10 +102,6 @@ namespace Bit.Droid { ExitApp(); } - else if (message.Command == "copiedToClipboard") - { - var task = ClearClipboardAlarmAsync(message.Data as Tuple); - } }); } @@ -388,30 +378,6 @@ namespace Bit.Droid Java.Lang.JavaSystem.Exit(0); } - private async Task ClearClipboardAlarmAsync(Tuple data) - { - if (data.Item3) - { - return; - } - var clearMs = data.Item2; - if (clearMs == null) - { - var clearSeconds = await _storageService.GetAsync(Constants.ClearClipboardKey); - if (clearSeconds != null) - { - clearMs = clearSeconds.Value * 1000; - } - } - if (clearMs == null) - { - return; - } - var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value; - var alarmManager = GetSystemService(AlarmService) as AlarmManager; - alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent); - } - private void StartEventAlarm() { var alarmManager = GetSystemService(AlarmService) as AlarmManager; diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index 32067c546..b2854aebe 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -113,6 +113,7 @@ namespace Bit.Droid ServiceContainer.Register("cryptoPrimitiveService", cryptoPrimitiveService); ServiceContainer.Register("storageService", mobileStorageService); ServiceContainer.Register("secureStorageService", secureStorageService); + ServiceContainer.Register("clipboardService", new ClipboardService(mobileStorageService)); ServiceContainer.Register("deviceActionService", deviceActionService); ServiceContainer.Register("platformUtilsService", platformUtilsService); ServiceContainer.Register("biometricService", biometricService); diff --git a/src/Android/Services/ClipboardService.cs b/src/Android/Services/ClipboardService.cs new file mode 100644 index 000000000..82f1c21db --- /dev/null +++ b/src/Android/Services/ClipboardService.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Android.App; +using Android.Content; +using Bit.Core; +using Bit.Core.Abstractions; +using Bit.Droid.Receivers; +using Plugin.CurrentActivity; +using Xamarin.Essentials; + +namespace Bit.Droid.Services +{ + public class ClipboardService : IClipboardService + { + private readonly IStorageService _storageService; + private readonly Lazy _clearClipboardPendingIntent; + + public ClipboardService(IStorageService storageService) + { + _storageService = storageService; + + _clearClipboardPendingIntent = new Lazy(() => + PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity, + 0, + new Intent(CrossCurrentActivity.Current.Activity, typeof(ClearClipboardAlarmReceiver)), + PendingIntentFlags.UpdateCurrent)); + } + + public async Task CopyTextAsync(string text, int expiresInMs = -1) + { + await Clipboard.SetTextAsync(text); + + await ClearClipboardAlarmAsync(expiresInMs); + } + + private async Task ClearClipboardAlarmAsync(int expiresInMs = -1) + { + var clearMs = expiresInMs; + if (clearMs < 0) + { + // if not set then we need to check if the user set this config + var clearSeconds = await _storageService.GetAsync(Constants.ClearClipboardKey); + if (clearSeconds != null) + { + clearMs = clearSeconds.Value * 1000; + } + } + if (clearMs < 0) + { + return; + } + var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs; + var alarmManager = CrossCurrentActivity.Current.Activity.GetSystemService(Context.AlarmService) as AlarmManager; + alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent.Value); + } + } +} diff --git a/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs b/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs index b3c88acb2..f4b78c5d4 100644 --- a/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs +++ b/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs @@ -12,6 +12,7 @@ namespace Bit.App.Pages { private readonly IPlatformUtilsService _platformUtilsService; private readonly IPasswordGenerationService _passwordGenerationService; + private readonly IClipboardService _clipboardService; private bool _showNoData; @@ -20,6 +21,7 @@ namespace Bit.App.Pages _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _passwordGenerationService = ServiceContainer.Resolve( "passwordGenerationService"); + _clipboardService = ServiceContainer.Resolve("clipboardService"); PageTitle = AppResources.PasswordHistory; History = new ExtendedObservableCollection(); @@ -51,7 +53,7 @@ namespace Bit.App.Pages private async void CopyAsync(GeneratedPasswordHistory ph) { - await _platformUtilsService.CopyToClipboardAsync(ph.Password); + await _clipboardService.CopyTextAsync(ph.Password); _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Password)); } diff --git a/src/App/Pages/Generator/GeneratorPageViewModel.cs b/src/App/Pages/Generator/GeneratorPageViewModel.cs index fb9350bc5..dc54f67b7 100644 --- a/src/App/Pages/Generator/GeneratorPageViewModel.cs +++ b/src/App/Pages/Generator/GeneratorPageViewModel.cs @@ -13,6 +13,7 @@ namespace Bit.App.Pages { private readonly IPasswordGenerationService _passwordGenerationService; private readonly IPlatformUtilsService _platformUtilsService; + private readonly IClipboardService _clipboardService; private PasswordGenerationOptions _options; private PasswordGeneratorPolicyOptions _enforcedPolicyOptions; @@ -38,6 +39,8 @@ namespace Bit.App.Pages _passwordGenerationService = ServiceContainer.Resolve( "passwordGenerationService"); _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); + _clipboardService = ServiceContainer.Resolve("clipboardService"); + PageTitle = AppResources.PasswordGenerator; TypeOptions = new List { AppResources.Password, AppResources.Passphrase }; } @@ -305,7 +308,7 @@ namespace Bit.App.Pages public async Task CopyAsync() { - await _platformUtilsService.CopyToClipboardAsync(Password); + await _clipboardService.CopyTextAsync(Password); _platformUtilsService.ShowToast("success", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Password)); } diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 2d079ad14..c6d55030d 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -28,6 +28,7 @@ namespace Bit.App.Pages private readonly IPolicyService _policyService; private readonly ILocalizeService _localizeService; private readonly IKeyConnectorService _keyConnectorService; + private readonly IClipboardService _clipboardService; private const int CustomVaultTimeoutValue = -100; @@ -78,6 +79,7 @@ namespace Bit.App.Pages _policyService = ServiceContainer.Resolve("policyService"); _localizeService = ServiceContainer.Resolve("localizeService"); _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); + _clipboardService = ServiceContainer.Resolve("clipboardService"); GroupedItems = new ExtendedObservableCollection(); PageTitle = AppResources.Settings; @@ -135,7 +137,7 @@ namespace Bit.App.Pages AppResources.Close); if (copy) { - await _platformUtilsService.CopyToClipboardAsync(debugText); + await _clipboardService.CopyTextAsync(debugText); } } diff --git a/src/App/Pages/Vault/PasswordHistoryPageViewModel.cs b/src/App/Pages/Vault/PasswordHistoryPageViewModel.cs index 49a25495e..2dc086322 100644 --- a/src/App/Pages/Vault/PasswordHistoryPageViewModel.cs +++ b/src/App/Pages/Vault/PasswordHistoryPageViewModel.cs @@ -12,6 +12,7 @@ namespace Bit.App.Pages { private readonly IPlatformUtilsService _platformUtilsService; private readonly ICipherService _cipherService; + private readonly IClipboardService _clipboardService; private bool _showNoData; @@ -19,6 +20,7 @@ namespace Bit.App.Pages { _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _cipherService = ServiceContainer.Resolve("cipherService"); + _clipboardService = ServiceContainer.Resolve("clipboardService"); PageTitle = AppResources.PasswordHistory; History = new ExtendedObservableCollection(); @@ -45,7 +47,7 @@ namespace Bit.App.Pages private async void CopyAsync(PasswordHistoryView ph) { - await _platformUtilsService.CopyToClipboardAsync(ph.Password); + await _clipboardService.CopyTextAsync(ph.Password); _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Password)); } diff --git a/src/App/Pages/Vault/ViewPageViewModel.cs b/src/App/Pages/Vault/ViewPageViewModel.cs index 0f48b323a..5ea45d1b9 100644 --- a/src/App/Pages/Vault/ViewPageViewModel.cs +++ b/src/App/Pages/Vault/ViewPageViewModel.cs @@ -1,4 +1,8 @@ -using Bit.App.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bit.App.Abstractions; using Bit.App.Resources; using Bit.App.Utilities; using Bit.Core.Abstractions; @@ -6,10 +10,6 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.View; using Bit.Core.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xamarin.Forms; namespace Bit.App.Pages @@ -26,6 +26,8 @@ namespace Bit.App.Pages private readonly IEventService _eventService; private readonly IPasswordRepromptService _passwordRepromptService; private readonly ILocalizeService _localizeService; + private readonly IClipboardService _clipboardService; + private CipherView _cipher; private List _fields; private bool _canAccessPremium; @@ -54,6 +56,8 @@ namespace Bit.App.Pages _eventService = ServiceContainer.Resolve("eventService"); _passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService"); _localizeService = ServiceContainer.Resolve("localizeService"); + _clipboardService = ServiceContainer.Resolve("clipboardService"); + CopyCommand = new Command((id) => CopyAsync(id, null)); CopyUriCommand = new Command(CopyUri); CopyFieldCommand = new Command(CopyField); @@ -653,7 +657,7 @@ namespace Bit.App.Pages if (text != null) { - await _platformUtilsService.CopyToClipboardAsync(text); + await _clipboardService.CopyTextAsync(text); if (!string.IsNullOrWhiteSpace(name)) { _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name)); diff --git a/src/App/Services/MobilePlatformUtilsService.cs b/src/App/Services/MobilePlatformUtilsService.cs index a9a4f1088..610b60fcd 100644 --- a/src/App/Services/MobilePlatformUtilsService.cs +++ b/src/App/Services/MobilePlatformUtilsService.cs @@ -1,19 +1,19 @@ -using Bit.App.Abstractions; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.App.Abstractions; using Bit.App.Models; using Bit.App.Resources; using Bit.Core.Abstractions; using Plugin.Fingerprint; using Plugin.Fingerprint.Abstractions; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using Xamarin.Essentials; using Xamarin.Forms; namespace Bit.App.Services { public class MobilePlatformUtilsService : IPlatformUtilsService - { + { private static readonly Random _random = new Random(); private const int DialogPromiseExpiration = 600000; // 10 minutes @@ -21,6 +21,7 @@ namespace Bit.App.Services private readonly IDeviceActionService _deviceActionService; private readonly IMessagingService _messagingService; private readonly IBroadcasterService _broadcasterService; + private readonly Dictionary, DateTime>> _showDialogResolves = new Dictionary, DateTime>>(); @@ -201,17 +202,6 @@ namespace Bit.App.Services return false; } - public async Task CopyToClipboardAsync(string text, Dictionary options = null) - { - var clearMs = options != null && options.ContainsKey("clearMs") ? (int?)options["clearMs"] : null; - var clearing = options != null && options.ContainsKey("clearing") ? (bool)options["clearing"] : false; - await Clipboard.SetTextAsync(text); - if (!clearing) - { - _messagingService.Send("copiedToClipboard", new Tuple(text, clearMs, clearing)); - } - } - public async Task ReadFromClipboardAsync(Dictionary options = null) { return await Clipboard.GetTextAsync(); diff --git a/src/App/Utilities/AppHelpers.cs b/src/App/Utilities/AppHelpers.cs index 6192fb9da..bf11e143a 100644 --- a/src/App/Utilities/AppHelpers.cs +++ b/src/App/Utilities/AppHelpers.cs @@ -27,6 +27,8 @@ namespace Bit.App.Utilities var platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); var eventService = ServiceContainer.Resolve("eventService"); var vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); + var clipboardService = ServiceContainer.Resolve("clipboardService"); + var options = new List { AppResources.View }; if (!cipher.IsDeleted) { @@ -92,7 +94,7 @@ namespace Bit.App.Utilities } else if (selection == AppResources.CopyUsername) { - await platformUtilsService.CopyToClipboardAsync(cipher.Login.Username); + await clipboardService.CopyTextAsync(cipher.Login.Username); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Username)); } @@ -100,7 +102,7 @@ namespace Bit.App.Utilities { if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) { - await platformUtilsService.CopyToClipboardAsync(cipher.Login.Password); + await clipboardService.CopyTextAsync(cipher.Login.Password); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Password)); var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id); @@ -114,7 +116,7 @@ namespace Bit.App.Utilities var totp = await totpService.GetCodeAsync(cipher.Login.Totp); if (!string.IsNullOrWhiteSpace(totp)) { - await platformUtilsService.CopyToClipboardAsync(totp); + await clipboardService.CopyTextAsync(totp); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp)); } @@ -128,7 +130,7 @@ namespace Bit.App.Utilities { if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) { - await platformUtilsService.CopyToClipboardAsync(cipher.Card.Number); + await clipboardService.CopyTextAsync(cipher.Card.Number); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Number)); } @@ -137,7 +139,7 @@ namespace Bit.App.Utilities { if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) { - await platformUtilsService.CopyToClipboardAsync(cipher.Card.Code); + await clipboardService.CopyTextAsync(cipher.Card.Code); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.SecurityCode)); var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id); @@ -145,7 +147,7 @@ namespace Bit.App.Utilities } else if (selection == AppResources.CopyNotes) { - await platformUtilsService.CopyToClipboardAsync(cipher.Notes); + await clipboardService.CopyTextAsync(cipher.Notes); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Notes)); } @@ -200,7 +202,8 @@ namespace Bit.App.Utilities return; } var platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); - await platformUtilsService.CopyToClipboardAsync(GetSendUrl(send)); + var clipboardService = ServiceContainer.Resolve("clipboardService"); + await clipboardService.CopyTextAsync(GetSendUrl(send)); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.SendLink)); } diff --git a/src/Core/Abstractions/IClipboardService.cs b/src/Core/Abstractions/IClipboardService.cs new file mode 100644 index 000000000..43b844433 --- /dev/null +++ b/src/Core/Abstractions/IClipboardService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Bit.Core.Abstractions +{ + public interface IClipboardService + { + /// + /// Copies the to the Clipboard. + /// If is set > 0 then the Clipboard will be cleared after this time in milliseconds. + /// if less than 0 then it takes the configuration that the user set in Options. + /// + /// Text to be copied to the Clipboard + /// Expiration time in milliseconds of the copied text + Task CopyTextAsync(string text, int expiresInMs = -1); + } +} diff --git a/src/Core/Abstractions/IPlatformUtilsService.cs b/src/Core/Abstractions/IPlatformUtilsService.cs index 12a31b71f..c03f3f87c 100644 --- a/src/Core/Abstractions/IPlatformUtilsService.cs +++ b/src/Core/Abstractions/IPlatformUtilsService.cs @@ -9,7 +9,6 @@ namespace Bit.Core.Abstractions { string IdentityClientId { get; } - Task CopyToClipboardAsync(string text, Dictionary options = null); string GetApplicationVersion(); DeviceType GetDevice(); string GetDeviceString(); diff --git a/src/iOS.Core/Services/ClipboardService.cs b/src/iOS.Core/Services/ClipboardService.cs new file mode 100644 index 000000000..6014da332 --- /dev/null +++ b/src/iOS.Core/Services/ClipboardService.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Bit.Core.Abstractions; +using Foundation; +using MobileCoreServices; +using UIKit; +using Xamarin.Forms; + +namespace Bit.iOS.Core.Services +{ + public class ClipboardService : IClipboardService + { + private readonly IStorageService _storageService; + + public ClipboardService(IStorageService storageService) + { + _storageService = storageService; + } + + public async Task CopyTextAsync(string text, int expiresInMs = -1) + { + int clearSeconds = -1; + if (expiresInMs < 0) + { + clearSeconds = await _storageService.GetAsync(Bit.Core.Constants.ClearClipboardKey) ?? -1; + } + else + { + clearSeconds = expiresInMs * 1000; + } + + var dictArr = new NSDictionary[1]; + dictArr[0] = new NSDictionary(new NSString(UTType.UTF8PlainText), new NSString(text)); + Device.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions + { + LocalOnly = true, + ExpirationDate = clearSeconds > 0 ? NSDate.FromTimeIntervalSinceNow(clearSeconds) : null + })); + } + } +} diff --git a/src/iOS.Core/Utilities/iOSCoreHelpers.cs b/src/iOS.Core/Utilities/iOSCoreHelpers.cs index 8238a659d..a8238c021 100644 --- a/src/iOS.Core/Utilities/iOSCoreHelpers.cs +++ b/src/iOS.Core/Utilities/iOSCoreHelpers.cs @@ -52,6 +52,7 @@ namespace Bit.iOS.Core.Utilities var cryptoPrimitiveService = new CryptoPrimitiveService(); var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage); var deviceActionService = new DeviceActionService(mobileStorageService, messagingService); + var clipboardService = new ClipboardService(mobileStorageService); var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService, broadcasterService); var biometricService = new BiometricService(mobileStorageService); @@ -67,6 +68,7 @@ namespace Bit.iOS.Core.Utilities ServiceContainer.Register("storageService", mobileStorageService); ServiceContainer.Register("secureStorageService", secureStorageService); ServiceContainer.Register("deviceActionService", deviceActionService); + ServiceContainer.Register("clipboardService", clipboardService); ServiceContainer.Register("platformUtilsService", platformUtilsService); ServiceContainer.Register("biometricService", biometricService); ServiceContainer.Register("cryptoFunctionService", cryptoFunctionService); diff --git a/src/iOS.Core/iOS.Core.csproj b/src/iOS.Core/iOS.Core.csproj index 219264ad5..a2353acf8 100644 --- a/src/iOS.Core/iOS.Core.csproj +++ b/src/iOS.Core/iOS.Core.csproj @@ -191,6 +191,7 @@ + diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index 78488a768..a6fe27541 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -71,14 +71,6 @@ namespace Bit.iOS iOSCoreHelpers.AppearanceAdjustments(); }); } - else if (message.Command == "copiedToClipboard") - { - - Device.BeginInvokeOnMainThread(() => - { - var task = ClearClipboardTimerAsync(message.Data as Tuple); - }); - } else if (message.Command == "listenYubiKeyOTP") { iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate); @@ -329,61 +321,6 @@ namespace Bit.iOS "pushNotificationService", iosPushNotificationService); } - private async Task ClearClipboardTimerAsync(Tuple data) - { - if (data.Item3) - { - return; - } - var clearMs = data.Item2; - if (clearMs == null) - { - var clearSeconds = await _storageService.GetAsync(Constants.ClearClipboardKey); - if (clearSeconds != null) - { - clearMs = clearSeconds.Value * 1000; - } - } - if (clearMs == null) - { - return; - } - if (_clipboardBackgroundTaskId > 0) - { - UIApplication.SharedApplication.EndBackgroundTask(_clipboardBackgroundTaskId); - _clipboardBackgroundTaskId = 0; - } - _clipboardBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() => - { - UIApplication.SharedApplication.EndBackgroundTask(_clipboardBackgroundTaskId); - _clipboardBackgroundTaskId = 0; - }); - _clipboardTimer?.Invalidate(); - _clipboardTimer?.Dispose(); - _clipboardTimer = null; - var lastClipboardChangeCount = UIPasteboard.General.ChangeCount; - var clearMsSpan = TimeSpan.FromMilliseconds(clearMs.Value); - _clipboardTimer = NSTimer.CreateScheduledTimer(clearMsSpan, timer => - { - Device.BeginInvokeOnMainThread(() => - { - var changeNow = UIPasteboard.General.ChangeCount; - if (changeNow == 0 || lastClipboardChangeCount == changeNow) - { - UIPasteboard.General.String = string.Empty; - } - _clipboardTimer?.Invalidate(); - _clipboardTimer?.Dispose(); - _clipboardTimer = null; - if (_clipboardBackgroundTaskId > 0) - { - UIApplication.SharedApplication.EndBackgroundTask(_clipboardBackgroundTaskId); - _clipboardBackgroundTaskId = 0; - } - }); - }); - } - private void ShowAppExtension(ExtensionPageViewModel extensionPageViewModel) { var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup);