mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
more updates to autofill flow
This commit is contained in:
parent
ddec7ab643
commit
36d4ce8718
6 changed files with 166 additions and 42 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
45
src/App/Resources/AppResources.Designer.cs
generated
45
src/App/Resources/AppResources.Designer.cs
generated
|
@ -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 "{0}"..
|
||||||
|
/// </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 "Ok" 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 "Ok" on the following prompt when asked to enable push notifications..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -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>
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue