more updates to autofill flow

This commit is contained in:
Kyle Spearrin 2017-02-13 22:10:34 -05:00
parent ddec7ab643
commit 36d4ce8718
6 changed files with 166 additions and 42 deletions

View file

@ -8,8 +8,6 @@ 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;
@ -28,6 +26,17 @@ namespace Bit.App.Models.Page
public Lazy<string> Uri { get; set; } public Lazy<string> Uri { get; set; }
} }
public class AutofillLogin : Login
{
public AutofillLogin(Models.Login login, bool fuzzy = false)
: base(login)
{
Fuzzy = fuzzy;
}
public bool Fuzzy { get; set; }
}
public class Folder : List<Login> public class Folder : List<Login>
{ {
public Folder(Models.Folder folder) public Folder(Models.Folder folder)
@ -45,9 +54,9 @@ namespace Bit.App.Models.Page
public string Name { get; set; } = AppResources.FolderNone; public string Name { get; set; } = AppResources.FolderNone;
} }
public class AutofillGrouping : List<Login> public class AutofillGrouping : List<AutofillLogin>
{ {
public AutofillGrouping(List<Login> logins, string name) public AutofillGrouping(List<AutofillLogin> logins, string name)
{ {
AddRange(logins); AddRange(logins);
Name = name; Name = name;

View file

@ -152,25 +152,35 @@ namespace Bit.App.Pages
Task.Run(async () => Task.Run(async () =>
{ {
var autofillGroupings = new List<VaultListPageModel.AutofillGrouping>();
var logins = await _loginService.GetAllAsync(Uri); var logins = await _loginService.GetAllAsync(Uri);
var normalLogins = logins.Item1.Select(l => new VaultListPageModel.Login(l))
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToList();
var fuzzyLogins = logins.Item2.Select(l => new VaultListPageModel.Login(l))
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToList();
var autofillGroupings = new List<VaultListPageModel.AutofillGrouping> var normalLogins = logins?.Item1.Select(l => new VaultListPageModel.AutofillLogin(l, false))
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToList();
if(normalLogins?.Any() ?? false)
{ {
new VaultListPageModel.AutofillGrouping(normalLogins, "Matching"), autofillGroupings.Add(new VaultListPageModel.AutofillGrouping(normalLogins, AppResources.MatchingLogins));
new VaultListPageModel.AutofillGrouping(fuzzyLogins, "Possible Matches") }
};
var fuzzyLogins = logins?.Item2.Select(l => new VaultListPageModel.AutofillLogin(l, true))
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToList();
if(fuzzyLogins?.Any() ?? false)
{
autofillGroupings.Add(new VaultListPageModel.AutofillGrouping(fuzzyLogins,
AppResources.PossibleMatchingLogins));
}
Device.BeginInvokeOnMainThread(() => Device.BeginInvokeOnMainThread(() =>
{ {
PresentationLogins.ResetWithRange(autofillGroupings); if(autofillGroupings.Any())
{
PresentationLogins.ResetWithRange(autofillGroupings);
}
AdjustContent(); AdjustContent();
}); });
}, cts.Token); }, cts.Token);
@ -178,18 +188,36 @@ namespace Bit.App.Pages
return cts; return cts;
} }
private void LoginSelected(object sender, SelectedItemChangedEventArgs e) private async void LoginSelected(object sender, SelectedItemChangedEventArgs e)
{ {
var login = e.SelectedItem as VaultListPageModel.Login; var login = e.SelectedItem as VaultListPageModel.AutofillLogin;
if(login == null)
{
return;
}
if(Uri.StartsWith("http") && _deviceInfoService.Version < 21) if(Uri.StartsWith("http") && _deviceInfoService.Version < 21)
{ {
MoreClickedAsync(login); MoreClickedAsync(login);
return; }
else
{
bool doAutofill = true;
if(login.Fuzzy)
{
doAutofill = await _userDialogs.ConfirmAsync(
string.Format(AppResources.BitwardenAutofillServiceMatchConfirm, _name),
okText: AppResources.Yes, cancelText: AppResources.No);
}
if(doAutofill)
{
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", login as VaultListPageModel.Login);
}
} }
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App"); ((ListView)sender).SelectedItem = null;
MessagingCenter.Send(Application.Current, "Autofill", login);
} }
private async void AddLoginAsync() private async void AddLoginAsync()
@ -272,7 +300,8 @@ namespace Bit.App.Pages
private void ClickedItem(object sender, EventArgs e) private void ClickedItem(object sender, EventArgs e)
{ {
_page.GoogleAnalyticsService.TrackExtensionEvent("CloseToSearch", _page.Uri.StartsWith("http") ? "Website" : "App"); _page.GoogleAnalyticsService.TrackExtensionEvent("CloseToSearch",
_page.Uri.StartsWith("http") ? "Website" : "App");
Application.Current.MainPage = new MainPage(_page.Uri); Application.Current.MainPage = new MainPage(_page.Uri);
} }
} }

View file

@ -28,6 +28,7 @@ namespace Bit.App.Pages
private readonly IPushNotification _pushNotification; private readonly IPushNotification _pushNotification;
private readonly IDeviceInfoService _deviceInfoService; private readonly IDeviceInfoService _deviceInfoService;
private readonly ISettings _settings; private readonly ISettings _settings;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly bool _favorites; private readonly bool _favorites;
private bool _loadExistingData; private bool _loadExistingData;
private CancellationTokenSource _filterResultsCancellationTokenSource; private CancellationTokenSource _filterResultsCancellationTokenSource;
@ -45,6 +46,7 @@ namespace Bit.App.Pages
_pushNotification = Resolver.Resolve<IPushNotification>(); _pushNotification = Resolver.Resolve<IPushNotification>();
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>(); _deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
_settings = Resolver.Resolve<ISettings>(); _settings = Resolver.Resolve<ISettings>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
var cryptoService = Resolver.Resolve<ICryptoService>(); var cryptoService = Resolver.Resolve<ICryptoService>();
_loadExistingData = !_settings.GetValueOrDefault(Constants.FirstVaultLoad, true) || !cryptoService.KeyChanged; _loadExistingData = !_settings.GetValueOrDefault(Constants.FirstVaultLoad, true) || !cryptoService.KeyChanged;
@ -272,7 +274,7 @@ namespace Bit.App.Pages
return false; return false;
} }
//GoogleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App"); _googleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Login)null); MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Login)null);
return true; return true;
} }
@ -378,12 +380,16 @@ namespace Bit.App.Pages
private async void LoginSelected(object sender, SelectedItemChangedEventArgs e) private async void LoginSelected(object sender, SelectedItemChangedEventArgs e)
{ {
var login = e.SelectedItem as VaultListPageModel.Login; var login = e.SelectedItem as VaultListPageModel.Login;
if(login == null)
{
return;
}
string selection = null; string selection = null;
if(!string.IsNullOrWhiteSpace(Uri)) if(!string.IsNullOrWhiteSpace(Uri))
{ {
selection = await DisplayActionSheet("Auto-fill or view?", AppResources.Cancel, null, selection = await DisplayActionSheet(AppResources.AutofillOrView, AppResources.Cancel, null,
"Autofill", AppResources.View); AppResources.Autofill, AppResources.View);
} }
if(selection == AppResources.View || string.IsNullOrWhiteSpace(Uri)) if(selection == AppResources.View || string.IsNullOrWhiteSpace(Uri))
@ -391,17 +397,20 @@ namespace Bit.App.Pages
var page = new VaultViewLoginPage(login.Id); var page = new VaultViewLoginPage(login.Id);
await Navigation.PushForDeviceAsync(page); await Navigation.PushForDeviceAsync(page);
} }
else if(selection == "Autofill") else if(selection == AppResources.Autofill)
{ {
if(Uri.StartsWith("http") && _deviceInfoService.Version < 21) if(Uri.StartsWith("http") && _deviceInfoService.Version < 21)
{ {
MoreClickedAsync(login); MoreClickedAsync(login);
return;
} }
else
//GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App"); {
MessagingCenter.Send(Application.Current, "Autofill", login); _googleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
MessagingCenter.Send(Application.Current, "Autofill", login);
}
} }
((ListView)sender).SelectedItem = null;
} }
private async void MoreClickedAsync(VaultListPageModel.Login login) private async void MoreClickedAsync(VaultListPageModel.Login login)

View file

@ -142,6 +142,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Auto-fill.
/// </summary>
public static string Autofill {
get {
return ResourceManager.GetString("Autofill", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use the bitwarden accessibility service to auto-fill your logins across apps and the web.. /// Looks up a localized string similar to Use the bitwarden accessibility service to auto-fill your logins across apps and the web..
/// </summary> /// </summary>
@ -151,6 +160,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Do you want to auto-fill or view this login?.
/// </summary>
public static string AutofillOrView {
get {
return ResourceManager.GetString("AutofillOrView", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Auto-fill Service. /// Looks up a localized string similar to Auto-fill Service.
/// </summary> /// </summary>
@ -250,6 +268,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Are you sure you want to auto-fill this login? It is not a complete match for &quot;{0}&quot;..
/// </summary>
public static string BitwardenAutofillServiceMatchConfirm {
get {
return ResourceManager.GetString("BitwardenAutofillServiceMatchConfirm", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to When you see a bitwarden auto-fill notification, you can tap it to launch the auto-fill service.. /// Looks up a localized string similar to When you see a bitwarden auto-fill notification, you can tap it to launch the auto-fill service..
/// </summary> /// </summary>
@ -1204,6 +1231,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Matching Logins.
/// </summary>
public static string MatchingLogins {
get {
return ResourceManager.GetString("MatchingLogins", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Minimum Numbers. /// Looks up a localized string similar to Minimum Numbers.
/// </summary> /// </summary>
@ -1456,6 +1492,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Possible Matching Logins.
/// </summary>
public static string PossibleMatchingLogins {
get {
return ResourceManager.GetString("PossibleMatchingLogins", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to bitwarden keeps your vault automatically synced by using push notifications. For the best possible experience, please select &quot;Ok&quot; on the following prompt when asked to enable push notifications.. /// Looks up a localized string similar to bitwarden keeps your vault automatically synced by using push notifications. For the best possible experience, please select &quot;Ok&quot; on the following prompt when asked to enable push notifications..
/// </summary> /// </summary>

View file

@ -795,4 +795,19 @@
<data name="BitwardenAutofillServiceAlert" xml:space="preserve"> <data name="BitwardenAutofillServiceAlert" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the bitwarden Auto-fill Service. Learn more about using the bitwarden Auto-fill Service by navigating to the "Tools" screen.</value> <value>The easiest way to add new logins to your vault is from the bitwarden Auto-fill Service. Learn more about using the bitwarden Auto-fill Service by navigating to the "Tools" screen.</value>
</data> </data>
<data name="Autofill" xml:space="preserve">
<value>Auto-fill</value>
</data>
<data name="AutofillOrView" xml:space="preserve">
<value>Do you want to auto-fill or view this login?</value>
</data>
<data name="BitwardenAutofillServiceMatchConfirm" xml:space="preserve">
<value>Are you sure you want to auto-fill this login? It is not a complete match for "{0}".</value>
</data>
<data name="MatchingLogins" xml:space="preserve">
<value>Matching Logins</value>
</data>
<data name="PossibleMatchingLogins" xml:space="preserve">
<value>Possible Matching Logins</value>
</data>
</root> </root>

View file

@ -70,7 +70,7 @@ namespace Bit.App.Services
{ {
if(domainName == null) if(domainName == null)
{ {
androidApp = uriString.StartsWith(Constants.AndroidAppProtocol); androidApp = UriIsAndroidApp(uriString);
} }
} }
@ -79,16 +79,7 @@ namespace Bit.App.Services
return null; return null;
} }
string androidAppWebUriString = null; var androidAppWebUriString = WebUriFromAndroidAppUri(uriString);
if(androidApp)
{
var androidUriParts = uriString.Replace(Constants.AndroidAppProtocol, string.Empty).Split('.');
if(androidUriParts.Length >= 2)
{
androidAppWebUriString = string.Join(".", androidUriParts[1], androidUriParts[0]);
}
}
var eqDomains = (await _settingsService.GetEquivalentDomainsAsync()).Select(d => d.ToArray()); var eqDomains = (await _settingsService.GetEquivalentDomainsAsync()).Select(d => d.ToArray());
var matchingDomains = new List<string>(); var matchingDomains = new List<string>();
var matchingFuzzyDomains = new List<string>(); var matchingFuzzyDomains = new List<string>();
@ -150,6 +141,11 @@ namespace Bit.App.Services
matchingFuzzyLogins.Add(new Login(login)); matchingFuzzyLogins.Add(new Login(login));
continue; continue;
} }
else if(!androidApp && Array.IndexOf(matchingDomainsArray, WebUriFromAndroidAppUri(loginUriString)) >= 0)
{
matchingFuzzyLogins.Add(new Login(login));
continue;
}
Uri loginUri; Uri loginUri;
DomainName loginDomainName; DomainName loginDomainName;
@ -223,5 +219,26 @@ namespace Bit.App.Services
return response; return response;
} }
private string WebUriFromAndroidAppUri(string androidAppUriString)
{
if(!UriIsAndroidApp(androidAppUriString))
{
return null;
}
var androidUriParts = androidAppUriString.Replace(Constants.AndroidAppProtocol, string.Empty).Split('.');
if(androidUriParts.Length >= 2)
{
return string.Join(".", androidUriParts[1], androidUriParts[0]);
}
return null;
}
private bool UriIsAndroidApp(string uriString)
{
return uriString.StartsWith(Constants.AndroidAppProtocol);
}
} }
} }