mobile platform utils

This commit is contained in:
Kyle Spearrin 2019-04-09 23:24:03 -04:00
parent 36780c5ef8
commit 9e51c46522
11 changed files with 257 additions and 52 deletions

View File

@ -49,6 +49,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Plugin.CurrentActivity">
<Version>2.1.0.4</Version>
</PackageReference>
<PackageReference Include="Portable.BouncyCastle">
<Version>1.8.5</Version>
</PackageReference>
@ -76,6 +79,7 @@
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\DeviceActionService.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\FontAwesome.ttf" />

View File

@ -15,5 +15,11 @@ namespace Bit.Droid
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{ }
public override void OnCreate()
{
base.OnCreate();
Plugin.CurrentActivity.CrossCurrentActivity.Current.Init(this);
}
}
}

View File

@ -0,0 +1,35 @@
using Bit.App.Abstractions;
using Plugin.CurrentActivity;
namespace Bit.Droid.Services
{
public class DeviceActionService : IDeviceActionService
{
private Android.Widget.Toast _toast;
public void Toast(string text, bool longDuration = false)
{
if(_toast != null)
{
_toast.Cancel();
_toast.Dispose();
_toast = null;
}
_toast = Android.Widget.Toast.MakeText(CrossCurrentActivity.Current.Activity, text,
longDuration ? Android.Widget.ToastLength.Long : Android.Widget.ToastLength.Short);
_toast.Show();
}
public bool LaunchApp(string appName)
{
var activity = CrossCurrentActivity.Current.Activity;
appName = appName.Replace("androidapp://", string.Empty);
var launchIntent = activity.PackageManager.GetLaunchIntentForPackage(appName);
if(launchIntent != null)
{
activity.StartActivity(launchIntent);
}
return launchIntent != null;
}
}
}

View File

@ -0,0 +1,8 @@
namespace Bit.App.Abstractions
{
public interface IDeviceActionService
{
void Toast(string text, bool longDuration = false);
bool LaunchApp(string appName);
}
}

View File

@ -1,4 +1,5 @@
using Bit.App.Pages;
using Bit.App.Models;
using Bit.App.Pages;
using Bit.App.Utilities;
using System;
using System.Reflection;
@ -17,6 +18,23 @@ namespace Bit.App
ThemeManager.SetTheme("light");
MainPage = new TabsPage();
MessagingCenter.Subscribe<Application, DialogDetails>(Current, "ShowDialog", async (sender, details) =>
{
var confirmed = true;
// TODO: ok text
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ? "Ok" : details.ConfirmText;
if(!string.IsNullOrWhiteSpace(details.CancelText))
{
confirmed = await MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
else
{
await MainPage.DisplayAlert(details.Title, details.Text, details.ConfirmText);
}
MessagingCenter.Send(Current, "ShowDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
protected override void OnStart()

View File

@ -0,0 +1,12 @@
namespace Bit.App.Models
{
public class DialogDetails
{
public string Text { get; set; }
public string Title { get; set; }
public string ConfirmText { get; set; }
public string CancelText { get; set; }
public string Type { get; set; }
public int DialogId { get; set; }
}
}

View File

@ -0,0 +1,169 @@
using Bit.App.Abstractions;
using Bit.App.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Services
{
public class MobilePlatformUtilsService
{
private static readonly Random _random = new Random();
private const int DialogPromiseExpiration = 600000; // 10 minutes
private readonly IDeviceActionService _deviceActionService;
private readonly Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>> _showDialogResolves =
new Dictionary<int, Tuple<TaskCompletionSource<bool>, DateTime>>();
public MobilePlatformUtilsService(IDeviceActionService deviceActionService)
{
_deviceActionService = deviceActionService;
MessagingCenter.Subscribe<Xamarin.Forms.Application, Tuple<int, bool>>(
Xamarin.Forms.Application.Current, "ShowDialogResolve", (sender, details) =>
{
var dialogId = details.Item1;
var confirmed = details.Item2;
if(_showDialogResolves.ContainsKey(dialogId))
{
var resolveObj = _showDialogResolves[dialogId].Item1;
resolveObj.TrySetResult(confirmed);
}
// Clean up old tasks
var deleteIds = new HashSet<int>();
foreach(var item in _showDialogResolves)
{
var age = DateTime.UtcNow - item.Value.Item2;
if(age.TotalMilliseconds > DialogPromiseExpiration)
{
deleteIds.Add(item.Key);
}
}
foreach(var id in deleteIds)
{
_showDialogResolves.Remove(id);
}
});
}
public string IdentityClientId => "mobile";
public Core.Enums.DeviceType GetDevice()
{
return Device.RuntimePlatform == Device.iOS ? Core.Enums.DeviceType.iOS : Core.Enums.DeviceType.Android;
}
public string GetDeviceString()
{
return DeviceInfo.Model;
}
public bool IsViewOpen()
{
return false;
}
public int? LockTimeout()
{
return null;
}
public void LaunchUri(string uri, Dictionary<string, object> options = null)
{
if(uri.StartsWith("http://") || uri.StartsWith("https://"))
{
Browser.OpenAsync(uri, BrowserLaunchMode.External);
}
else
{
var launched = false;
if(Device.RuntimePlatform == Device.Android && uri.StartsWith("androidapp://"))
{
launched = _deviceActionService.LaunchApp(uri);
}
if(!launched && (options?.ContainsKey("page") ?? false))
{
(options["page"] as Page).DisplayAlert(null, "", ""); // TODO
}
}
}
public void SaveFile()
{
// TODO
}
public string GetApplicationVersion()
{
return AppInfo.VersionString;
}
public bool SupportsU2f()
{
return false;
}
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)
{
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)
{
dialogId = _random.Next(0, int.MaxValue);
}
MessagingCenter.Send(Xamarin.Forms.Application.Current, "ShowAlert", new DialogDetails
{
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 bool IsDev()
{
return Core.Utilities.CoreHelpers.InDebugMode();
}
public bool IsSelfHost()
{
return false;
}
public async Task CopyToClipboardAsync(string text, Dictionary<string, object> options = null)
{
var clearMs = options != null && options.ContainsKey("clearMs") ? (int?)options["clearMs"] : null;
await Clipboard.SetTextAsync(text);
// TODO: send message 'copiedToClipboard'
}
public async Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null)
{
return await Clipboard.GetTextAsync();
}
}
}

View File

@ -1,50 +1,27 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums
namespace Bit.Core.Enums
{
public enum DeviceType : byte
{
[Display(Name = "Android")]
Android = 0,
[Display(Name = "iOS")]
iOS = 1,
[Display(Name = "Chrome Extension")]
ChromeExtension = 2,
[Display(Name = "Firefox Extension")]
FirefoxExtension = 3,
[Display(Name = "Opera Extension")]
OperaExtension = 4,
[Display(Name = "Edge Extension")]
EdgeExtension = 5,
[Display(Name = "Windows")]
WindowsDesktop = 6,
[Display(Name = "macOS")]
MacOsDesktop = 7,
[Display(Name = "Linux")]
LinuxDesktop = 8,
[Display(Name = "Chrome")]
ChromeBrowser = 9,
[Display(Name = "Firefox")]
FirefoxBrowser = 10,
[Display(Name = "Opera")]
OperaBrowser = 11,
[Display(Name = "Edge")]
EdgeBrowser = 12,
[Display(Name = "Internet Explorer")]
IEBrowser = 13,
[Display(Name = "Unknown Browser")]
UnknownBrowser = 14,
[Display(Name = "Android")]
AndroidAmazon = 15,
[Display(Name = "UWP")]
UWP = 16,
[Display(Name = "Safari")]
SafariBrowser = 17,
[Display(Name = "Vivaldi")]
VivaldiBrowser = 18,
[Display(Name = "Vivaldi Extension")]
VivaldiExtension = 19,
[Display(Name = "Safari Extension")]
SafariExtension = 20
}
}

View File

@ -1,20 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums
namespace Bit.Core.Enums
{
public enum PaymentMethodType : byte
{
[Display(Name = "Card")]
Card = 0,
[Display(Name = "Bank Account")]
BankAccount = 1,
[Display(Name = "PayPal")]
PayPal = 2,
[Display(Name = "BitPay")]
BitPay = 3,
[Display(Name = "Credit")]
Credit = 4,
[Display(Name = "Wire Transfer")]
WireTransfer = 5,
}
}

View File

@ -1,22 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums
namespace Bit.Core.Enums
{
public enum PlanType : byte
{
[Display(Name = "Free")]
Free = 0,
[Display(Name = "Families")]
FamiliesAnnually = 1,
[Display(Name = "Teams (Monthly)")]
TeamsMonthly = 2,
[Display(Name = "Teams (Annually)")]
TeamsAnnually = 3,
[Display(Name = "Enterprise (Monthly)")]
EnterpriseMonthly = 4,
[Display(Name = "Enterprise (Annually)")]
EnterpriseAnnually = 5,
[Display(Name = "Custom")]
Custom = 6
}
}

View File

@ -1,18 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums
namespace Bit.Core.Enums
{
public enum TransactionType : byte
{
[Display(Name = "Charge")]
Charge = 0,
[Display(Name = "Credit")]
Credit = 1,
[Display(Name = "Promotional Credit")]
PromotionalCredit = 2,
[Display(Name = "Referral Credit")]
ReferralCredit = 3,
[Display(Name = "Refund")]
Refund = 4,
}
}