mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
app options for add/edit page
This commit is contained in:
parent
c77d4b795a
commit
9d491a3636
9 changed files with 197 additions and 11 deletions
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/App/Models/AppOptions.cs
Normal file
21
src/App/Models/AppOptions.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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 :
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue