app options for add/edit page

This commit is contained in:
Kyle Spearrin 2019-05-17 12:03:35 -04:00
parent c77d4b795a
commit 9d491a3636
9 changed files with 197 additions and 11 deletions

View file

@ -12,6 +12,9 @@ using System;
using Android.Content; using Android.Content;
using Bit.Droid.Utilities; using Bit.Droid.Utilities;
using Bit.Droid.Receivers; using Bit.Droid.Receivers;
using Bit.App.Models;
using Bit.Core.Enums;
using Android.Nfc;
namespace Bit.Droid namespace Bit.Droid
{ {
@ -28,6 +31,7 @@ namespace Bit.Droid
private IMessagingService _messagingService; private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService; private IBroadcasterService _broadcasterService;
private PendingIntent _lockAlarmPendingIntent; private PendingIntent _lockAlarmPendingIntent;
private AppOptions _appOptions;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
@ -45,7 +49,8 @@ namespace Bit.Droid
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState); Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App.App()); _appOptions = GetOptions();
LoadApplication(new App.App(_appOptions));
_broadcasterService.Subscribe(nameof(MainActivity), (message) => _broadcasterService.Subscribe(nameof(MainActivity), (message) =>
{ {
@ -122,5 +127,63 @@ namespace Bit.Droid
} }
} }
} }
private void ListenYubiKey(bool listen)
{
if(!_deviceActionService.SupportsNfc())
{
return;
}
var adapter = NfcAdapter.GetDefaultAdapter(this);
if(listen)
{
var intent = new Intent(this, Class);
intent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
// register for all NDEF tags starting with http och https
var ndef = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
ndef.AddDataScheme("http");
ndef.AddDataScheme("https");
var filters = new IntentFilter[] { ndef };
try
{
// register for foreground dispatch so we'll receive tags according to our intent filters
adapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
}
catch { }
}
else
{
adapter.DisableForegroundDispatch(this);
}
}
private AppOptions GetOptions()
{
var options = new AppOptions
{
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
};
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
if(fillType > 0)
{
options.FillType = (CipherType)fillType;
}
if(Intent.GetBooleanExtra("autofillFrameworkSave", false))
{
options.SaveType = (CipherType)Intent.GetIntExtra("autofillFrameworkType", 0);
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername");
options.SavePassword = Intent.GetStringExtra("autofillFrameworkPassword");
options.SaveCardName = Intent.GetStringExtra("autofillFrameworkCardName");
options.SaveCardNumber = Intent.GetStringExtra("autofillFrameworkCardNumber");
options.SaveCardExpMonth = Intent.GetStringExtra("autofillFrameworkCardExpMonth");
options.SaveCardExpYear = Intent.GetStringExtra("autofillFrameworkCardExpYear");
options.SaveCardCode = Intent.GetStringExtra("autofillFrameworkCardCode");
}
return options;
}
} }
} }

View file

@ -6,12 +6,14 @@ using Android;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Nfc;
using Android.OS; using Android.OS;
using Android.Provider; using Android.Provider;
using Android.Support.V4.App; using Android.Support.V4.App;
using Android.Support.V4.Content; using Android.Support.V4.Content;
using Android.Text; using Android.Text;
using Android.Text.Method; using Android.Text.Method;
using Android.Views.Autofill;
using Android.Webkit; using Android.Webkit;
using Android.Widget; using Android.Widget;
using Bit.App.Abstractions; using Bit.App.Abstractions;
@ -291,6 +293,31 @@ namespace Bit.Droid.Services
return false; return false;
} }
public bool SupportsNfc()
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var manager = activity.GetSystemService(Context.NfcService) as NfcManager;
return manager.DefaultAdapter?.IsEnabled ?? false;
}
public bool SupportsCamera()
{
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
return activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera);
}
public bool SupportsAutofillService()
{
if(Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return false;
}
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
var type = Java.Lang.Class.FromType(typeof(AutofillManager));
var manager = activity.GetSystemService(type) as AutofillManager;
return manager.IsAutofillSupported;
}
private bool DeleteDir(Java.IO.File dir) private bool DeleteDir(Java.IO.File dir)
{ {
if(dir != null && dir.IsDirectory) if(dir != null && dir.IsDirectory)

View file

@ -18,5 +18,8 @@ namespace Bit.App.Abstractions
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false); string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false);
void RateApp(); void RateApp();
bool SupportsFaceId(); bool SupportsFaceId();
bool SupportsNfc();
bool SupportsCamera();
bool SupportsAutofillService();
} }
} }

View file

@ -1,4 +1,5 @@
using Bit.App.Models; using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Pages; using Bit.App.Pages;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Services; using Bit.App.Services;
@ -34,9 +35,12 @@ namespace Bit.App
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly IStorageService _storageService; private readonly IStorageService _storageService;
private readonly IDeviceActionService _deviceActionService;
private readonly AppOptions _appOptions;
public App() public App(AppOptions appOptions)
{ {
_appOptions = appOptions ?? new AppOptions();
_userService = ServiceContainer.Resolve<IUserService>("userService"); _userService = ServiceContainer.Resolve<IUserService>("userService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
@ -56,6 +60,7 @@ namespace Bit.App
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>( _passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService"); "passwordGenerationService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService; _i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
InitializeComponent(); InitializeComponent();
SetCulture(); SetCulture();
@ -107,21 +112,24 @@ namespace Bit.App
}); });
} }
protected override void OnStart() protected async override void OnStart()
{ {
System.Diagnostics.Debug.WriteLine("XF App: OnStart"); System.Diagnostics.Debug.WriteLine("XF App: OnStart");
await ClearCacheIfNeededAsync();
} }
protected async override void OnSleep() protected async override void OnSleep()
{ {
System.Diagnostics.Debug.WriteLine("XF App: OnSleep"); System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
await HandleLockingAsync(); await HandleLockingAsync();
SetTabsPageFromAutofill();
} }
protected override void OnResume() protected async override void OnResume()
{ {
System.Diagnostics.Debug.WriteLine("XF App: OnResume"); System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_messagingService.Send("cancelLockTimer"); _messagingService.Send("cancelLockTimer");
await ClearCacheIfNeededAsync();
} }
private void SetCulture() private void SetCulture()
@ -168,6 +176,10 @@ namespace Bit.App
{ {
Current.MainPage = new NavigationPage(new LockPage()); Current.MainPage = new NavigationPage(new LockPage());
} }
else if(_appOptions.FromAutofillFramework && _appOptions.SaveType.HasValue)
{
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: _appOptions));
}
else else
{ {
Current.MainPage = new TabsPage(); Current.MainPage = new TabsPage();
@ -205,5 +217,30 @@ namespace Bit.App
await _lockService.LockAsync(true); await _lockService.LockAsync(true);
} }
} }
private async Task ClearCacheIfNeededAsync()
{
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
if((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
{
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
}
}
private void SetTabsPageFromAutofill()
{
if(Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(_appOptions.Uri) &&
!_appOptions.FromAutofillFramework)
{
Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
Current.MainPage = new TabsPage();
_appOptions.Uri = null;
});
});
}
}
} }
} }

View file

@ -0,0 +1,21 @@
using Bit.Core.Enums;
namespace Bit.App.Models
{
public class AppOptions
{
public bool MyVaultTile { get; set; }
public bool FromAutofillFramework { get; set; }
public CipherType? FillType { get; set; }
public string Uri { get; set; }
public CipherType? SaveType { get; set; }
public string SaveName { get; set; }
public string SaveUsername { get; set; }
public string SavePassword { get; set; }
public string SaveCardName { get; set; }
public string SaveCardNumber { get; set; }
public string SaveCardExpMonth { get; set; }
public string SaveCardExpYear { get; set; }
public string SaveCardCode { get; set; }
}
}

View file

@ -1,4 +1,5 @@
using Bit.Core.Enums; using Bit.App.Models;
using Bit.Core.Enums;
using System.Collections.Generic; using System.Collections.Generic;
using Xamarin.Forms; using Xamarin.Forms;
@ -7,13 +8,16 @@ namespace Bit.App.Pages
public partial class AddEditPage : BaseContentPage public partial class AddEditPage : BaseContentPage
{ {
private AddEditPageViewModel _vm; private AddEditPageViewModel _vm;
private readonly AppOptions _appOptions;
public AddEditPage( public AddEditPage(
string cipherId = null, string cipherId = null,
CipherType? type = null, CipherType? type = null,
string folderId = null, string folderId = null,
string collectionId = null) string collectionId = null,
AppOptions appOptions = null)
{ {
_appOptions = appOptions;
InitializeComponent(); InitializeComponent();
_vm = BindingContext as AddEditPageViewModel; _vm = BindingContext as AddEditPageViewModel;
_vm.Page = this; _vm.Page = this;
@ -40,7 +44,7 @@ namespace Bit.App.Pages
protected override async void OnAppearing() protected override async void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
await LoadOnAppearedAsync(_scrollView, true, () => _vm.LoadAsync()); await LoadOnAppearedAsync(_scrollView, true, () => _vm.LoadAsync(_appOptions));
if(_vm.EditMode && Device.RuntimePlatform == Device.Android) if(_vm.EditMode && Device.RuntimePlatform == Device.Android)
{ {
if(_vm.Cipher.OrganizationId == null) if(_vm.Cipher.OrganizationId == null)

View file

@ -1,4 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -264,7 +265,7 @@ namespace Bit.App.Pages
PageTitle = EditMode ? AppResources.EditItem : AppResources.AddItem; PageTitle = EditMode ? AppResources.EditItem : AppResources.AddItem;
} }
public async Task LoadAsync() public async Task LoadAsync(AppOptions appOptions = null)
{ {
var myEmail = await _userService.GetEmailAsync(); var myEmail = await _userService.GetEmailAsync();
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null)); OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
@ -310,6 +311,21 @@ namespace Bit.App.Pages
Cipher.Login.Uris = new List<LoginUriView> { new LoginUriView() }; Cipher.Login.Uris = new List<LoginUriView> { new LoginUriView() };
Cipher.SecureNote.Type = SecureNoteType.Generic; Cipher.SecureNote.Type = SecureNoteType.Generic;
TypeSelectedIndex = TypeOptions.FindIndex(k => k.Value == Cipher.Type); TypeSelectedIndex = TypeOptions.FindIndex(k => k.Value == Cipher.Type);
if(appOptions != null)
{
Cipher.Type = appOptions.SaveType.GetValueOrDefault(Cipher.Type);
Cipher.Login.Username = appOptions.SaveUsername;
Cipher.Login.Password = appOptions.SavePassword;
Cipher.Card.Code = appOptions.SaveCardCode;
if(int.TryParse(appOptions.SaveCardExpMonth, out int month) && month <= 12 && month >= 1)
{
Cipher.Card.ExpMonth = month.ToString();
}
Cipher.Card.ExpYear = appOptions.SaveCardExpYear;
Cipher.Card.CardholderName = appOptions.SaveCardName;
Cipher.Card.Number = appOptions.SaveCardNumber;
}
} }
FolderSelectedIndex = string.IsNullOrWhiteSpace(Cipher.FolderId) ? FolderOptions.Count - 1 : FolderSelectedIndex = string.IsNullOrWhiteSpace(Cipher.FolderId) ? FolderOptions.Count - 1 :

View file

@ -26,7 +26,7 @@ namespace Bit.iOS
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
LoadApplication(new App.App()); LoadApplication(new App.App(null));
return base.FinishedLaunching(app, options); return base.FinishedLaunching(app, options);
} }

View file

@ -233,6 +233,21 @@ namespace Bit.iOS.Services
return context.BiometryType == LABiometryType.FaceId; return context.BiometryType == LABiometryType.FaceId;
} }
public bool SupportsNfc()
{
return CoreNFC.NFCNdefReaderSession.ReadingAvailable;
}
public bool SupportsCamera()
{
return true;
}
public bool SupportsAutofillService()
{
return true;
}
private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e) private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{ {
if(sender is UIImagePickerController picker) if(sender is UIImagePickerController picker)