mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
Fix Clipboard clear after time on iOS (#1679)
* Fixed Clipboard clear after x seconds depending on what the user set. Also refactored a bit to make the Clipboard a custom service to provide a better way to handle this situation #1464 * Clear some usings #1464
This commit is contained in:
parent
23a164b245
commit
705b8ac12b
17 changed files with 157 additions and 131 deletions
|
@ -149,6 +149,7 @@
|
||||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
|
<Compile Include="Services\ClipboardService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||||
|
|
|
@ -34,9 +34,7 @@ namespace Bit.Droid
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private IUserService _userService;
|
private IUserService _userService;
|
||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IStorageService _storageService;
|
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _clearClipboardPendingIntent;
|
|
||||||
private PendingIntent _eventUploadPendingIntent;
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
||||||
|
@ -48,9 +46,6 @@ namespace Bit.Droid
|
||||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
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();
|
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
|
||||||
StrictMode.SetThreadPolicy(policy);
|
StrictMode.SetThreadPolicy(policy);
|
||||||
|
@ -60,7 +55,6 @@ namespace Bit.Droid
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
|
||||||
TabLayoutResource = Resource.Layout.Tabbar;
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
|
@ -108,10 +102,6 @@ namespace Bit.Droid
|
||||||
{
|
{
|
||||||
ExitApp();
|
ExitApp();
|
||||||
}
|
}
|
||||||
else if (message.Command == "copiedToClipboard")
|
|
||||||
{
|
|
||||||
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,30 +378,6 @@ namespace Bit.Droid
|
||||||
Java.Lang.JavaSystem.Exit(0);
|
Java.Lang.JavaSystem.Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
|
|
||||||
{
|
|
||||||
if (data.Item3)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var clearMs = data.Item2;
|
|
||||||
if (clearMs == null)
|
|
||||||
{
|
|
||||||
var clearSeconds = await _storageService.GetAsync<int?>(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()
|
private void StartEventAlarm()
|
||||||
{
|
{
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||||
|
|
|
@ -113,6 +113,7 @@ namespace Bit.Droid
|
||||||
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
||||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
|
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(mobileStorageService));
|
||||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||||
|
|
57
src/Android/Services/ClipboardService.cs
Normal file
57
src/Android/Services/ClipboardService.cs
Normal file
|
@ -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<PendingIntent> _clearClipboardPendingIntent;
|
||||||
|
|
||||||
|
public ClipboardService(IStorageService storageService)
|
||||||
|
{
|
||||||
|
_storageService = storageService;
|
||||||
|
|
||||||
|
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||||
|
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<int?>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ namespace Bit.App.Pages
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||||
"passwordGenerationService");
|
"passwordGenerationService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordHistory;
|
PageTitle = AppResources.PasswordHistory;
|
||||||
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
||||||
|
@ -51,7 +53,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToast("info", null,
|
_platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private PasswordGenerationOptions _options;
|
private PasswordGenerationOptions _options;
|
||||||
private PasswordGeneratorPolicyOptions _enforcedPolicyOptions;
|
private PasswordGeneratorPolicyOptions _enforcedPolicyOptions;
|
||||||
|
@ -38,6 +39,8 @@ namespace Bit.App.Pages
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||||
"passwordGenerationService");
|
"passwordGenerationService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordGenerator;
|
PageTitle = AppResources.PasswordGenerator;
|
||||||
TypeOptions = new List<string> { AppResources.Password, AppResources.Passphrase };
|
TypeOptions = new List<string> { AppResources.Password, AppResources.Passphrase };
|
||||||
}
|
}
|
||||||
|
@ -305,7 +308,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
public async Task CopyAsync()
|
public async Task CopyAsync()
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(Password);
|
await _clipboardService.CopyTextAsync(Password);
|
||||||
_platformUtilsService.ShowToast("success", null,
|
_platformUtilsService.ShowToast("success", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace Bit.App.Pages
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private const int CustomVaultTimeoutValue = -100;
|
private const int CustomVaultTimeoutValue = -100;
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ namespace Bit.App.Pages
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
||||||
PageTitle = AppResources.Settings;
|
PageTitle = AppResources.Settings;
|
||||||
|
@ -135,7 +137,7 @@ namespace Bit.App.Pages
|
||||||
AppResources.Close);
|
AppResources.Close);
|
||||||
if (copy)
|
if (copy)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(debugText);
|
await _clipboardService.CopyTextAsync(debugText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordHistory;
|
PageTitle = AppResources.PasswordHistory;
|
||||||
History = new ExtendedObservableCollection<PasswordHistoryView>();
|
History = new ExtendedObservableCollection<PasswordHistoryView>();
|
||||||
|
@ -45,7 +47,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private async void CopyAsync(PasswordHistoryView ph)
|
private async void CopyAsync(PasswordHistoryView ph)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToast("info", null,
|
_platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
@ -6,10 +10,6 @@ using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
@ -26,6 +26,8 @@ namespace Bit.App.Pages
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private List<ViewPageFieldViewModel> _fields;
|
private List<ViewPageFieldViewModel> _fields;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
|
@ -54,6 +56,8 @@ namespace Bit.App.Pages
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
||||||
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
||||||
CopyFieldCommand = new Command<FieldView>(CopyField);
|
CopyFieldCommand = new Command<FieldView>(CopyField);
|
||||||
|
@ -653,7 +657,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
if (text != null)
|
if (text != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(text);
|
await _clipboardService.CopyTextAsync(text);
|
||||||
if (!string.IsNullOrWhiteSpace(name))
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
||||||
|
|
|
@ -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.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
using Plugin.Fingerprint.Abstractions;
|
using Plugin.Fingerprint.Abstractions;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
public class MobilePlatformUtilsService : IPlatformUtilsService
|
public class MobilePlatformUtilsService : IPlatformUtilsService
|
||||||
{
|
{
|
||||||
private static readonly Random _random = new Random();
|
private static readonly Random _random = new Random();
|
||||||
|
|
||||||
private const int DialogPromiseExpiration = 600000; // 10 minutes
|
private const int DialogPromiseExpiration = 600000; // 10 minutes
|
||||||
|
@ -21,6 +21,7 @@ namespace Bit.App.Services
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
private readonly Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>> _showDialogResolves =
|
private readonly Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>> _showDialogResolves =
|
||||||
new Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>>();
|
new Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>>();
|
||||||
|
|
||||||
|
@ -201,17 +202,6 @@ namespace Bit.App.Services
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CopyToClipboardAsync(string text, Dictionary<string, object> 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<string, int?, bool>(text, clearMs, clearing));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null)
|
public async Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null)
|
||||||
{
|
{
|
||||||
return await Clipboard.GetTextAsync();
|
return await Clipboard.GetTextAsync();
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace Bit.App.Utilities
|
||||||
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
var eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
var eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
|
var clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
var options = new List<string> { AppResources.View };
|
var options = new List<string> { AppResources.View };
|
||||||
if (!cipher.IsDeleted)
|
if (!cipher.IsDeleted)
|
||||||
{
|
{
|
||||||
|
@ -92,7 +94,7 @@ namespace Bit.App.Utilities
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyUsername)
|
else if (selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(cipher.Login.Username);
|
await clipboardService.CopyTextAsync(cipher.Login.Username);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Username));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Username));
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,7 @@ namespace Bit.App.Utilities
|
||||||
{
|
{
|
||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(cipher.Login.Password);
|
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
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);
|
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||||
if (!string.IsNullOrWhiteSpace(totp))
|
if (!string.IsNullOrWhiteSpace(totp))
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(totp);
|
await clipboardService.CopyTextAsync(totp);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp));
|
||||||
}
|
}
|
||||||
|
@ -128,7 +130,7 @@ namespace Bit.App.Utilities
|
||||||
{
|
{
|
||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(cipher.Card.Number);
|
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Number));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Number));
|
||||||
}
|
}
|
||||||
|
@ -137,7 +139,7 @@ namespace Bit.App.Utilities
|
||||||
{
|
{
|
||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(cipher.Card.Code);
|
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.SecurityCode));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.SecurityCode));
|
||||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
||||||
|
@ -145,7 +147,7 @@ namespace Bit.App.Utilities
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyNotes)
|
else if (selection == AppResources.CopyNotes)
|
||||||
{
|
{
|
||||||
await platformUtilsService.CopyToClipboardAsync(cipher.Notes);
|
await clipboardService.CopyTextAsync(cipher.Notes);
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Notes));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Notes));
|
||||||
}
|
}
|
||||||
|
@ -200,7 +202,8 @@ namespace Bit.App.Utilities
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
await platformUtilsService.CopyToClipboardAsync(GetSendUrl(send));
|
var clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
await clipboardService.CopyTextAsync(GetSendUrl(send));
|
||||||
platformUtilsService.ShowToast("info", null,
|
platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.SendLink));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.SendLink));
|
||||||
}
|
}
|
||||||
|
|
16
src/Core/Abstractions/IClipboardService.cs
Normal file
16
src/Core/Abstractions/IClipboardService.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Bit.Core.Abstractions
|
||||||
|
{
|
||||||
|
public interface IClipboardService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the <paramref name="text"/> to the Clipboard.
|
||||||
|
/// If <paramref name="expiresInMs"/> 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Text to be copied to the Clipboard</param>
|
||||||
|
/// <param name="expiresInMs">Expiration time in milliseconds of the copied text</param>
|
||||||
|
Task CopyTextAsync(string text, int expiresInMs = -1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,6 @@ namespace Bit.Core.Abstractions
|
||||||
{
|
{
|
||||||
string IdentityClientId { get; }
|
string IdentityClientId { get; }
|
||||||
|
|
||||||
Task CopyToClipboardAsync(string text, Dictionary<string, object> options = null);
|
|
||||||
string GetApplicationVersion();
|
string GetApplicationVersion();
|
||||||
DeviceType GetDevice();
|
DeviceType GetDevice();
|
||||||
string GetDeviceString();
|
string GetDeviceString();
|
||||||
|
|
40
src/iOS.Core/Services/ClipboardService.cs
Normal file
40
src/iOS.Core/Services/ClipboardService.cs
Normal file
|
@ -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<int?>(Bit.Core.Constants.ClearClipboardKey) ?? -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clearSeconds = expiresInMs * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
LocalOnly = true,
|
||||||
|
ExpirationDate = clearSeconds > 0 ? NSDate.FromTimeIntervalSinceNow(clearSeconds) : null
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ namespace Bit.iOS.Core.Utilities
|
||||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService);
|
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService);
|
||||||
|
var clipboardService = new ClipboardService(mobileStorageService);
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
var biometricService = new BiometricService(mobileStorageService);
|
var biometricService = new BiometricService(mobileStorageService);
|
||||||
|
@ -67,6 +68,7 @@ namespace Bit.iOS.Core.Utilities
|
||||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||||
|
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
|
||||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||||
ServiceContainer.Register<ICryptoFunctionService>("cryptoFunctionService", cryptoFunctionService);
|
ServiceContainer.Register<ICryptoFunctionService>("cryptoFunctionService", cryptoFunctionService);
|
||||||
|
|
|
@ -191,6 +191,7 @@
|
||||||
<Compile Include="Views\Toast.cs" />
|
<Compile Include="Views\Toast.cs" />
|
||||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
<Compile Include="Effects\ScrollEnabledEffect.cs" />
|
<Compile Include="Effects\ScrollEnabledEffect.cs" />
|
||||||
|
<Compile Include="Services\ClipboardService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\App\App.csproj">
|
<ProjectReference Include="..\App\App.csproj">
|
||||||
|
|
|
@ -71,14 +71,6 @@ namespace Bit.iOS
|
||||||
iOSCoreHelpers.AppearanceAdjustments();
|
iOSCoreHelpers.AppearanceAdjustments();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (message.Command == "copiedToClipboard")
|
|
||||||
{
|
|
||||||
|
|
||||||
Device.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
var task = ClearClipboardTimerAsync(message.Data as Tuple<string, int?, bool>);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (message.Command == "listenYubiKeyOTP")
|
else if (message.Command == "listenYubiKeyOTP")
|
||||||
{
|
{
|
||||||
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
|
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
|
||||||
|
@ -329,61 +321,6 @@ namespace Bit.iOS
|
||||||
"pushNotificationService", iosPushNotificationService);
|
"pushNotificationService", iosPushNotificationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearClipboardTimerAsync(Tuple<string, int?, bool> data)
|
|
||||||
{
|
|
||||||
if (data.Item3)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var clearMs = data.Item2;
|
|
||||||
if (clearMs == null)
|
|
||||||
{
|
|
||||||
var clearSeconds = await _storageService.GetAsync<int?>(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)
|
private void ShowAppExtension(ExtensionPageViewModel extensionPageViewModel)
|
||||||
{
|
{
|
||||||
var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup);
|
var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup);
|
||||||
|
|
Loading…
Reference in a new issue