add autofill service support for web browsers

This commit is contained in:
Kyle Spearrin 2017-12-20 11:55:16 -05:00
parent 519fd212d9
commit e5d5d8b434
3 changed files with 57 additions and 8 deletions

View file

@ -5,6 +5,7 @@ using Android.Views;
using Android.Views.Autofill; using Android.Views.Autofill;
using static Android.App.Assist.AssistStructure; using static Android.App.Assist.AssistStructure;
using Android.Text; using Android.Text;
using static Android.Views.ViewStructure;
namespace Bit.Android.Autofill namespace Bit.Android.Autofill
{ {
@ -24,7 +25,9 @@ namespace Bit.Android.Autofill
Clickable = node.IsClickable; Clickable = node.IsClickable;
Visible = node.Visibility == ViewStates.Visible; Visible = node.Visibility == ViewStates.Visible;
Hints = FilterForSupportedHints(node.GetAutofillHints()); Hints = FilterForSupportedHints(node.GetAutofillHints());
Hint = node.Hint;
AutofillOptions = node.GetAutofillOptions()?.ToList(); AutofillOptions = node.GetAutofillOptions()?.ToList();
HtmlInfo = node.HtmlInfo;
Node = node; Node = node;
if(node.AutofillValue != null) if(node.AutofillValue != null)
@ -63,6 +66,7 @@ namespace Bit.Android.Autofill
UpdateSaveTypeFromHints(); UpdateSaveTypeFromHints();
} }
} }
public string Hint { get; set; }
public int Id { get; private set; } public int Id { get; private set; }
public string IdEntry { get; set; } public string IdEntry { get; set; }
public AutofillId AutofillId { get; private set; } public AutofillId AutofillId { get; private set; }
@ -77,6 +81,7 @@ namespace Bit.Android.Autofill
public long? DateValue { get; set; } public long? DateValue { get; set; }
public int? ListValue { get; set; } public int? ListValue { get; set; }
public bool? ToggleValue { get; set; } public bool? ToggleValue { get; set; }
public HtmlInfo HtmlInfo { get; private set; }
public ViewNode Node { get; private set; } public ViewNode Node { get; private set; }
private void UpdateSaveTypeFromHints() private void UpdateSaveTypeFromHints()

View file

@ -61,7 +61,7 @@ namespace Bit.Android.Autofill
_passwordFields = Fields _passwordFields = Fields
.Where(f => .Where(f =>
(!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) && (!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) &&
(!f.Node?.Hint?.ToLowerInvariant().Contains("search") ?? true) && (!f.Hint?.ToLowerInvariant().Contains("search") ?? true) &&
( (
f.InputType.HasFlag(InputTypes.TextVariationPassword) || f.InputType.HasFlag(InputTypes.TextVariationPassword) ||
f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) || f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) ||
@ -71,7 +71,8 @@ namespace Bit.Android.Autofill
if(!_passwordFields.Any()) if(!_passwordFields.Any())
{ {
_passwordFields = Fields.Where(f => _passwordFields = Fields.Where(f =>
f.IdEntry?.ToLowerInvariant().Contains("password") ?? false).ToList(); (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false)
|| (f.Hint?.ToLowerInvariant().Contains("password") ?? false)).ToList();
} }
} }
@ -104,7 +105,7 @@ namespace Bit.Android.Autofill
{ {
foreach(var passwordField in PasswordFields) foreach(var passwordField in PasswordFields)
{ {
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault(); var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
if(usernameField != null) if(usernameField != null)
{ {
_usernameFields.Add(usernameField); _usernameFields.Add(usernameField);
@ -137,10 +138,14 @@ namespace Bit.Android.Autofill
_passwordFields = _usernameFields = null; _passwordFields = _usernameFields = null;
Ids.Add(field.Id); if(field.Id > -1)
{
Ids.Add(field.Id);
IdToFieldMap.Add(field.Id, field);
}
Fields.Add(field); Fields.Add(field);
AutofillIds.Add(field.AutofillId); AutofillIds.Add(field.AutofillId);
IdToFieldMap.Add(field.Id, field);
if(field.Hints != null) if(field.Hints != null)
{ {
@ -181,7 +186,7 @@ namespace Bit.Android.Autofill
} }
}; };
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault(); var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
savedItem.Login.Username = GetFieldValue(usernameField); savedItem.Login.Username = GetFieldValue(usernameField);
return savedItem; return savedItem;

View file

@ -1,14 +1,26 @@
using static Android.App.Assist.AssistStructure; using static Android.App.Assist.AssistStructure;
using Android.App.Assist; using Android.App.Assist;
using Bit.App; using Bit.App;
using System.Collections.Generic;
namespace Bit.Android.Autofill namespace Bit.Android.Autofill
{ {
public class Parser public class Parser
{ {
public static HashSet<string> TrustedBrowsers = new HashSet<string>
{
"org.mozilla.focus","org.mozilla.firefox","org.mozilla.firefox_beta","com.microsoft.emmx",
"com.android.chrome","com.chrome.beta","com.android.browser","com.brave.browser","com.opera.browser",
"com.opera.browser.beta","com.opera.mini.native","com.chrome.dev","com.chrome.canary",
"com.google.android.apps.chrome","com.google.android.apps.chrome_dev","com.yandex.browser",
"com.sec.android.app.sbrowser","com.sec.android.app.sbrowser.beta","org.codeaurora.swe.browser",
"com.amazon.cloud9"
};
private readonly AssistStructure _structure; private readonly AssistStructure _structure;
private string _uri; private string _uri;
private string _packageName; private string _packageName;
private string _webDomain;
public Parser(AssistStructure structure) public Parser(AssistStructure structure)
{ {
@ -25,10 +37,14 @@ namespace Bit.Android.Autofill
return _uri; return _uri;
} }
if(string.IsNullOrWhiteSpace(PackageName)) if(string.IsNullOrWhiteSpace(WebDomain) && string.IsNullOrWhiteSpace(PackageName))
{ {
_uri = null; _uri = null;
} }
else if(!string.IsNullOrWhiteSpace(WebDomain))
{
_uri = string.Concat("http://", WebDomain);
}
else else
{ {
_uri = string.Concat(Constants.AndroidAppProtocol, PackageName); _uri = string.Concat(Constants.AndroidAppProtocol, PackageName);
@ -50,6 +66,19 @@ namespace Bit.Android.Autofill
_packageName = value; _packageName = value;
} }
} }
public string WebDomain
{
get => _webDomain;
set
{
if(string.IsNullOrWhiteSpace(value))
{
_webDomain = _uri = null;
}
_webDomain = value;
}
}
public void Parse() public void Parse()
{ {
@ -63,17 +92,27 @@ namespace Bit.Android.Autofill
private void ParseNode(ViewNode node) private void ParseNode(ViewNode node)
{ {
var hints = node.GetAutofillHints(); var hints = node.GetAutofillHints();
var isEditText = node.ClassName == "android.widget.EditText"; var isEditText = node.ClassName == "android.widget.EditText" || node?.HtmlInfo?.Tag == "input";
if(isEditText || (hints?.Length ?? 0) > 0) if(isEditText || (hints?.Length ?? 0) > 0)
{ {
if(PackageName == null) if(PackageName == null)
{ {
PackageName = node.IdPackage; PackageName = node.IdPackage;
} }
if(WebDomain == null && TrustedBrowsers.Contains(node.IdPackage))
{
WebDomain = node.WebDomain;
}
FieldCollection.Add(new Field(node)); FieldCollection.Add(new Field(node));
} }
else else
{ {
if(WebDomain == null && TrustedBrowsers.Contains(node.IdPackage))
{
WebDomain = node.WebDomain;
}
FieldCollection.IgnoreAutofillIds.Add(node.AutofillId); FieldCollection.IgnoreAutofillIds.Add(node.AutofillId);
} }