Bitwarden-app-android-iphon.../src/App/Services/MobilePlatformUtilsService.cs

265 lines
8.6 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Abstractions;
2019-04-10 05:24:03 +02:00
using Bit.App.Models;
2019-05-16 22:31:58 +02:00
using Bit.App.Resources;
2019-04-10 16:49:24 +02:00
using Bit.Core.Abstractions;
2022-02-08 17:43:40 +01:00
using Bit.Core.Enums;
2019-05-16 22:31:58 +02:00
using Plugin.Fingerprint;
using Plugin.Fingerprint.Abstractions;
2019-04-10 05:24:03 +02:00
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Services
{
2019-04-10 16:49:24 +02:00
public class MobilePlatformUtilsService : IPlatformUtilsService
{
2019-04-10 05:24:03 +02:00
private static readonly Random _random = new Random();
private const int DialogPromiseExpiration = 600000; // 10 minutes
private readonly IDeviceActionService _deviceActionService;
private readonly IMessagingService _messagingService;
private readonly IBroadcasterService _broadcasterService;
2019-04-10 05:24:03 +02:00
private readonly Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>> _showDialogResolves =
new Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>>();
public MobilePlatformUtilsService(
IDeviceActionService deviceActionService,
IMessagingService messagingService,
IBroadcasterService broadcasterService)
2019-04-10 05:24:03 +02:00
{
_deviceActionService = deviceActionService;
_messagingService = messagingService;
_broadcasterService = broadcasterService;
2019-04-11 20:28:38 +02:00
}
public void Init()
{
2019-05-10 19:22:25 +02:00
_broadcasterService.Subscribe(nameof(MobilePlatformUtilsService), (message) =>
{
if (message.Command == "showDialogResolve")
2019-04-10 05:24:03 +02:00
{
2019-04-19 18:33:57 +02:00
var details = message.Data as Tuple<int, bool>;
var dialogId = details.Item1;
var confirmed = details.Item2;
if (_showDialogResolves.ContainsKey(dialogId))
2019-04-19 18:33:57 +02:00
{
var resolveObj = _showDialogResolves[dialogId].Item1;
resolveObj.TrySetResult(confirmed);
}
2019-04-10 05:24:03 +02:00
2019-04-19 18:33:57 +02:00
// Clean up old tasks
var deleteIds = new HashSet<int>();
foreach (var item in _showDialogResolves)
2019-04-10 05:24:03 +02:00
{
2019-04-19 18:33:57 +02:00
var age = DateTime.UtcNow - item.Value.Item2;
if (age.TotalMilliseconds > DialogPromiseExpiration)
2019-04-19 18:33:57 +02:00
{
deleteIds.Add(item.Key);
}
}
foreach (var id in deleteIds)
2019-04-19 18:33:57 +02:00
{
_showDialogResolves.Remove(id);
2019-04-10 05:24:03 +02:00
}
}
});
2019-04-10 05:24:03 +02:00
}
public Core.Enums.DeviceType GetDevice()
{
2019-04-19 15:11:17 +02:00
return _deviceActionService.DeviceType;
2019-04-10 05:24:03 +02:00
}
public string GetDeviceString()
{
return DeviceInfo.Model;
}
2022-02-08 17:43:40 +01:00
public ClientType GetClientType()
{
return ClientType.Mobile;
}
2019-04-10 05:24:03 +02:00
public bool IsViewOpen()
{
return false;
}
public void LaunchUri(string uri, Dictionary<string, object> options = null)
{
if ((uri.StartsWith("http://") || uri.StartsWith("https://")) &&
2019-06-11 17:00:34 +02:00
Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri))
2019-04-10 05:24:03 +02:00
{
2019-06-14 14:05:28 +02:00
try
{
Browser.OpenAsync(uri, BrowserLaunchMode.External);
}
catch (FeatureNotSupportedException) { }
2019-04-10 05:24:03 +02:00
}
else
{
var launched = false;
if (GetDevice() == Core.Enums.DeviceType.Android && uri.StartsWith("androidapp://"))
2019-04-10 05:24:03 +02:00
{
launched = _deviceActionService.LaunchApp(uri);
}
if (!launched && (options?.ContainsKey("page") ?? false))
2019-04-10 05:24:03 +02:00
{
(options["page"] as Page).DisplayAlert(null, "", ""); // TODO
}
}
}
public void SaveFile()
{
// TODO
}
public string GetApplicationVersion()
{
return AppInfo.VersionString;
}
2019-04-18 15:45:31 +02:00
public bool SupportsDuo()
{
return true;
}
public bool SupportsFido2()
2019-04-10 05:24:03 +02:00
{
return _deviceActionService.SupportsFido2();
2019-04-10 05:24:03 +02:00
}
public void ShowToast(string type, string title, string text, Dictionary<string, object> options = null)
{
ShowToast(type, title, new string[] { text }, options);
}
public void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null)
{
if (text.Length > 0)
2019-04-10 05:24:03 +02:00
{
var longDuration = options != null && options.ContainsKey("longDuration") ?
(bool)options["longDuration"] : false;
_deviceActionService.Toast(text[0], longDuration);
}
}
public Task<bool> ShowDialogAsync(string text, string title = null, string confirmText = null,
string cancelText = null, string type = null)
{
var dialogId = 0;
lock (_random)
2019-04-10 05:24:03 +02:00
{
dialogId = _random.Next(0, int.MaxValue);
}
_messagingService.Send("showDialog", new DialogDetails
2019-04-10 05:24:03 +02:00
{
Text = text,
Title = title,
ConfirmText = confirmText,
CancelText = cancelText,
Type = type,
DialogId = dialogId
});
var tcs = new TaskCompletionSource<bool>();
_showDialogResolves.Add(dialogId, new Tuple<TaskCompletionSource<bool>, DateTime>(tcs, DateTime.UtcNow));
return tcs.Task;
}
public async Task<bool> ShowPasswordDialogAsync(string title, string body, Func<string, Task<bool>> validator)
{
return (await ShowPasswordDialogAndGetItAsync(title, body, validator)).valid;
}
public async Task<(string password, bool valid)> ShowPasswordDialogAndGetItAsync(string title, string body, Func<string, Task<bool>> validator)
{
var password = await _deviceActionService.DisplayPromptAync(AppResources.PasswordConfirmation,
AppResources.PasswordConfirmationDesc, null, AppResources.Submit, AppResources.Cancel, password: true);
2021-06-10 17:57:18 +02:00
if (password == null)
{
return (password, false);
2021-06-10 17:57:18 +02:00
}
var valid = await validator(password);
if (!valid)
{
await ShowDialogAsync(AppResources.InvalidMasterPassword, null, AppResources.Ok);
}
return (password, valid);
}
2019-04-10 05:24:03 +02:00
public bool IsDev()
{
return Core.Utilities.CoreHelpers.InDebugMode();
}
public bool IsSelfHost()
{
return false;
}
public async Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null)
{
return await Clipboard.GetTextAsync();
}
2019-05-16 22:31:58 +02:00
2019-10-23 15:11:48 +02:00
public async Task<bool> SupportsBiometricAsync()
2019-05-16 22:31:58 +02:00
{
2020-06-08 14:25:13 +02:00
try
{
return await CrossFingerprint.Current.IsAvailableAsync();
}
catch
{
return false;
}
2019-05-16 22:31:58 +02:00
}
2019-10-23 15:11:48 +02:00
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
2019-05-17 15:42:20 +02:00
Action fallback = null)
2019-05-16 22:31:58 +02:00
{
2020-06-08 14:25:13 +02:00
try
2019-05-16 22:31:58 +02:00
{
2020-06-08 14:25:13 +02:00
if (text == null)
2019-05-17 15:42:20 +02:00
{
2020-06-08 14:25:13 +02:00
text = AppResources.BiometricsDirection;
if (Device.RuntimePlatform == Device.iOS)
2019-10-23 15:11:48 +02:00
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
text = supportsFace ? AppResources.FaceIDDirection : AppResources.FingerprintDirection;
}
2019-05-17 15:42:20 +02:00
}
2020-06-08 14:25:13 +02:00
var biometricRequest = new AuthenticationRequestConfiguration(AppResources.Bitwarden, text)
{
CancelTitle = AppResources.Cancel,
FallbackTitle = fallbackText
};
var result = await CrossFingerprint.Current.AuthenticateAsync(biometricRequest);
if (result.Authenticated)
{
return true;
}
if (result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{
fallback?.Invoke();
}
2019-05-16 22:31:58 +02:00
}
2020-06-08 14:25:13 +02:00
catch { }
return false;
2019-05-16 22:31:58 +02:00
}
public long GetActiveTime()
{
return _deviceActionService.GetActiveTime();
}
2019-04-10 05:24:03 +02:00
}
}