autofill WIP into main activity. created login selection page

This commit is contained in:
Kyle Spearrin 2017-01-27 23:13:28 -05:00
parent 61e0379eb3
commit 26667c0a59
10 changed files with 266 additions and 51 deletions

View file

@ -298,7 +298,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AutofillSelectLoginActivity.cs" />
<Compile Include="AutofillActivity.cs" /> <Compile Include="AutofillActivity.cs" />
<Compile Include="Controls\CustomSearchBarRenderer.cs" /> <Compile Include="Controls\CustomSearchBarRenderer.cs" />
<Compile Include="Controls\CustomButtonRenderer.cs" /> <Compile Include="Controls\CustomButtonRenderer.cs" />

View file

@ -7,17 +7,17 @@ using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime; using Android.Runtime;
using Bit.App.Models; using Android.Views;
namespace Bit.Android namespace Bit.Android
{ {
[Activity(Label = "bitwarden Autofill", [Activity(Label = "bitwarden",
Icon = "@drawable/icon",
LaunchMode = global::Android.Content.PM.LaunchMode.SingleInstance, LaunchMode = global::Android.Content.PM.LaunchMode.SingleInstance,
Theme = "@style/android:Theme.Material.Light")] WindowSoftInputMode = SoftInput.StateHidden)]
public class AutofillActivity : Activity public class AutofillActivity : Activity
{ {
private string _lastQueriedUri; private string _lastQueriedUri;
public static Credentials LastCredentials; public static Credentials LastCredentials;
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle bundle)
@ -25,12 +25,11 @@ namespace Bit.Android
base.OnCreate(bundle); base.OnCreate(bundle);
_lastQueriedUri = Intent.GetStringExtra("uri"); _lastQueriedUri = Intent.GetStringExtra("uri");
var intent = new Intent(this, typeof(AutofillSelectLoginActivity)); var intent = new Intent(this, typeof(MainActivity));
intent.PutExtra("uri", _lastQueriedUri); intent.PutExtra("uri", _lastQueriedUri);
StartActivityForResult(intent, 123); StartActivityForResult(intent, 123);
} }
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{ {
base.OnActivityResult(requestCode, resultCode, data); base.OnActivityResult(requestCode, resultCode, data);

View file

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
namespace Bit.Android
{
[Activity(LaunchMode = global::Android.Content.PM.LaunchMode.SingleInstance)]
public class AutofillSelectLoginActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
var uri = Intent.GetStringExtra("uri");
Intent data = new Intent();
data.PutExtra("uri", uri);
data.PutExtra("username", "user123");
data.PutExtra("password", "pass123");
if(Parent == null)
{
SetResult(Result.Ok, data);
}
else
{
Parent.SetResult(Result.Ok, data);
}
Finish();
}
}
}

View file

@ -14,6 +14,7 @@ using System.Reflection;
using Xamarin.Forms.Platform.Android; using Xamarin.Forms.Platform.Android;
using Xamarin.Forms; using Xamarin.Forms;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models.Page;
namespace Bit.Android namespace Bit.Android
{ {
@ -27,6 +28,12 @@ namespace Bit.Android
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle bundle)
{ {
var uri = Intent.GetStringExtra("uri");
if(uri != null && !Resolver.IsSet)
{
MainApplication.SetIoc(Application);
}
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build(); var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
StrictMode.SetThreadPolicy(policy); StrictMode.SetThreadPolicy(policy);
@ -55,6 +62,7 @@ namespace Bit.Android
.SetValue(null, Color.FromHex("d2d6de")); .SetValue(null, Color.FromHex("d2d6de"));
LoadApplication(new App.App( LoadApplication(new App.App(
uri,
Resolver.Resolve<IAuthService>(), Resolver.Resolve<IAuthService>(),
Resolver.Resolve<IConnectivity>(), Resolver.Resolve<IConnectivity>(),
Resolver.Resolve<IUserDialogs>(), Resolver.Resolve<IUserDialogs>(),
@ -70,6 +78,31 @@ namespace Bit.Android
{ {
RateApp(); RateApp();
}); });
MessagingCenter.Subscribe<Xamarin.Forms.Application, VaultListPageModel.Login>(
Xamarin.Forms.Application.Current, "Autofill", (sender, args) =>
{
ReturnCredentials(args);
});
}
private void ReturnCredentials(VaultListPageModel.Login login)
{
Intent data = new Intent();
data.PutExtra("uri", login.Uri.Value);
data.PutExtra("username", login.Username);
data.PutExtra("password", login.Password.Value);
if(Parent == null)
{
SetResult(Result.Ok, data);
}
else
{
Parent.SetResult(Result.Ok, data);
}
Finish();
} }
protected override void OnPause() protected override void OnPause()

View file

@ -43,7 +43,7 @@ namespace Bit.Android
if(!Resolver.IsSet) if(!Resolver.IsSet)
{ {
SetIoc(); SetIoc(this);
} }
} }
@ -178,16 +178,16 @@ namespace Bit.Android
} }
} }
private void SetIoc() public static void SetIoc(Application application)
{ {
UserDialogs.Init(this); UserDialogs.Init(application);
var container = new UnityContainer(); var container = new UnityContainer();
container container
// Android Stuff // Android Stuff
.RegisterInstance(ApplicationContext) .RegisterInstance(application.ApplicationContext)
.RegisterInstance<Application>(this) .RegisterInstance<Application>(application)
// Services // Services
.RegisterType<IDatabaseService, DatabaseService>(new ContainerControlledLifetimeManager()) .RegisterType<IDatabaseService, DatabaseService>(new ContainerControlledLifetimeManager())
.RegisterType<ISqlService, SqlService>(new ContainerControlledLifetimeManager()) .RegisterType<ISqlService, SqlService>(new ContainerControlledLifetimeManager())

View file

@ -19,6 +19,7 @@ namespace Bit.App
{ {
public class App : Application public class App : Application
{ {
private readonly string _uri;
private readonly IDatabaseService _databaseService; private readonly IDatabaseService _databaseService;
private readonly IConnectivity _connectivity; private readonly IConnectivity _connectivity;
private readonly IUserDialogs _userDialogs; private readonly IUserDialogs _userDialogs;
@ -31,6 +32,7 @@ namespace Bit.App
private readonly ILocalizeService _localizeService; private readonly ILocalizeService _localizeService;
public App( public App(
string uri,
IAuthService authService, IAuthService authService,
IConnectivity connectivity, IConnectivity connectivity,
IUserDialogs userDialogs, IUserDialogs userDialogs,
@ -42,6 +44,7 @@ namespace Bit.App
IGoogleAnalyticsService googleAnalyticsService, IGoogleAnalyticsService googleAnalyticsService,
ILocalizeService localizeService) ILocalizeService localizeService)
{ {
_uri = uri;
_databaseService = databaseService; _databaseService = databaseService;
_connectivity = connectivity; _connectivity = connectivity;
_userDialogs = userDialogs; _userDialogs = userDialogs;
@ -56,7 +59,11 @@ namespace Bit.App
SetCulture(); SetCulture();
SetStyles(); SetStyles();
if(authService.IsAuthenticated) if(authService.IsAuthenticated && _uri != null)
{
MainPage = new ExtendedNavigationPage(new VaultAutofillListLoginsPage(_uri));
}
else if(authService.IsAuthenticated)
{ {
MainPage = new MainPage(); MainPage = new MainPage();
} }

View file

@ -134,6 +134,7 @@
<Compile Include="Pages\Settings\SettingsSyncPage.cs" /> <Compile Include="Pages\Settings\SettingsSyncPage.cs" />
<Compile Include="Pages\Settings\SettingsPage.cs" /> <Compile Include="Pages\Settings\SettingsPage.cs" />
<Compile Include="Pages\Settings\SettingsListFoldersPage.cs" /> <Compile Include="Pages\Settings\SettingsListFoldersPage.cs" />
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" /> <Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
<Compile Include="Repositories\ApiRepository.cs" /> <Compile Include="Repositories\ApiRepository.cs" />

View file

@ -8,6 +8,8 @@ namespace Bit.App.Models.Page
{ {
public class Login public class Login
{ {
private string _baseDomain;
public Login(Models.Login login) public Login(Models.Login login)
{ {
Id = login.Id; Id = login.Id;
@ -24,6 +26,37 @@ namespace Bit.App.Models.Page
public string Username { get; set; } public string Username { get; set; }
public Lazy<string> Password { get; set; } public Lazy<string> Password { get; set; }
public Lazy<string> Uri { get; set; } public Lazy<string> Uri { get; set; }
public string BaseDomain
{
get
{
if(_baseDomain != null)
{
return _baseDomain;
}
if(string.IsNullOrWhiteSpace(Uri.Value))
{
return null;
}
Uri uri;
if(!System.Uri.TryCreate(Uri.Value, UriKind.Absolute, out uri))
{
return null;
}
DomainName domain;
if(!DomainName.TryParse(uri.Host, out domain))
{
return null;
}
_baseDomain = domain.BaseDomain;
return _baseDomain;
}
}
} }
public class Folder : List<Login> public class Folder : List<Login>

View file

@ -0,0 +1,179 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Acr.UserDialogs;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Models.Page;
using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Bit.App.Utilities;
using PushNotification.Plugin.Abstractions;
using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
using System.Collections.Generic;
using System.Threading;
using Bit.App.Models;
namespace Bit.App.Pages
{
public class VaultAutofillListLoginsPage : ExtendedContentPage
{
private readonly IFolderService _folderService;
private readonly ILoginService _loginService;
private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity;
private readonly IClipboardService _clipboardService;
private readonly ISyncService _syncService;
private readonly IPushNotification _pushNotification;
private readonly IDeviceInfoService _deviceInfoService;
private readonly ISettings _settings;
private CancellationTokenSource _filterResultsCancellationTokenSource;
private readonly DomainName _domainName;
public VaultAutofillListLoginsPage(string uriString)
: base(true)
{
Uri uri;
if(Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri) &&
DomainName.TryParse(uri.Host, out _domainName)) { }
_folderService = Resolver.Resolve<IFolderService>();
_loginService = Resolver.Resolve<ILoginService>();
_connectivity = Resolver.Resolve<IConnectivity>();
_userDialogs = Resolver.Resolve<IUserDialogs>();
_clipboardService = Resolver.Resolve<IClipboardService>();
_syncService = Resolver.Resolve<ISyncService>();
_pushNotification = Resolver.Resolve<IPushNotification>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
_settings = Resolver.Resolve<ISettings>();
Init();
}
public ExtendedObservableCollection<VaultListPageModel.Login> PresentationLogins { get; private set; }
= new ExtendedObservableCollection<VaultListPageModel.Login>();
public ListView ListView { get; set; }
private void Init()
{
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", (sender, success) =>
{
if(success)
{
_filterResultsCancellationTokenSource = FetchAndLoadVault();
}
});
ToolbarItems.Add(new AddLoginToolBarItem(this));
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = PresentationLogins,
HasUnevenRows = true,
ItemTemplate = new DataTemplate(() => new VaultListViewCell(this))
};
if(Device.OS == TargetPlatform.iOS)
{
ListView.RowHeight = -1;
}
ListView.ItemSelected += LoginSelected;
Title = AppResources.Logins;
Content = ListView;
}
protected override void OnAppearing()
{
base.OnAppearing();
_filterResultsCancellationTokenSource = FetchAndLoadVault();
}
private CancellationTokenSource FetchAndLoadVault()
{
var cts = new CancellationTokenSource();
_settings.AddOrUpdateValue(Constants.FirstVaultLoad, false);
if(PresentationLogins.Count > 0 && _syncService.SyncInProgress)
{
return cts;
}
_filterResultsCancellationTokenSource?.Cancel();
Task.Run(async () =>
{
var logins = await _loginService.GetAllAsync();
var filteredLogins = logins
.Select(s => new VaultListPageModel.Login(s))
.Where(s => s.BaseDomain != null && s.BaseDomain == _domainName.BaseDomain)
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToArray();
PresentationLogins.ResetWithRange(filteredLogins);
}, cts.Token);
return cts;
}
private void LoginSelected(object sender, SelectedItemChangedEventArgs e)
{
var login = e.SelectedItem as VaultListPageModel.Login;
MessagingCenter.Send(Application.Current, "Autofill", login);
}
private async void AddLogin()
{
var page = new VaultAddLoginPage();
await Navigation.PushForDeviceAsync(page);
}
private class AddLoginToolBarItem : ToolbarItem
{
private readonly VaultAutofillListLoginsPage _page;
public AddLoginToolBarItem(VaultAutofillListLoginsPage page)
{
_page = page;
Text = AppResources.Add;
Icon = "plus";
Clicked += ClickedItem;
}
private void ClickedItem(object sender, EventArgs e)
{
_page.AddLogin();
}
}
private class VaultListViewCell : LabeledDetailCell
{
private VaultAutofillListLoginsPage _page;
public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
typeof(VaultListPageModel.Login), typeof(VaultListViewCell), null);
public VaultListViewCell(VaultAutofillListLoginsPage page)
{
_page = page;
SetBinding(LoginParameterProperty, new Binding("."));
Label.SetBinding<VaultListPageModel.Login>(Label.TextProperty, s => s.Name);
Detail.SetBinding<VaultListPageModel.Login>(Label.TextProperty, s => s.Username);
BackgroundColor = Color.White;
}
public VaultListPageModel.Login LoginParameter
{
get { return GetValue(LoginParameterProperty) as VaultListPageModel.Login; }
set { SetValue(LoginParameterProperty, value); }
}
}
}
}

View file

@ -56,6 +56,7 @@ namespace Bit.iOS
manager.DisableMetricsManager = manager.DisableFeedbackManager = manager.DisableUpdateManager = true; manager.DisableMetricsManager = manager.DisableFeedbackManager = manager.DisableUpdateManager = true;
LoadApplication(new App.App( LoadApplication(new App.App(
null,
Resolver.Resolve<IAuthService>(), Resolver.Resolve<IAuthService>(),
Resolver.Resolve<IConnectivity>(), Resolver.Resolve<IConnectivity>(),
Resolver.Resolve<IUserDialogs>(), Resolver.Resolve<IUserDialogs>(),