allow autofill (#9) for apps/websites without explicit autofill hints by detecting password fields and falling back to filling username/password if no autofill hint is available

This commit is contained in:
Philipp Crocoll
2017-12-30 03:41:02 +01:00
parent d87b8f7652
commit 6c69119d09
4 changed files with 76 additions and 10 deletions

View File

@@ -22,13 +22,20 @@ namespace keepass2android.services.AutofillBase
string[] AutofillOptions { get; }
public bool Focused { get; }
public AutofillFieldMetadata(AssistStructure.ViewNode view)
public AutofillFieldMetadata(AssistStructure.ViewNode view)
: this(view, view.GetAutofillHints())
{
}
public AutofillFieldMetadata(AssistStructure.ViewNode view, string[] autofillHints)
{
AutofillId = view.AutofillId;
AutofillType = view.AutofillType;
AutofillOptions = view.GetAutofillOptions();
Focused = view.IsFocused;
var supportedHints = AutofillHintsHelper.FilterForSupportedHints(view.GetAutofillHints());
var supportedHints = AutofillHintsHelper.FilterForSupportedHints(autofillHints);
var canonicalHints = AutofillHintsHelper.ConvertToCanonicalHints(supportedHints);
SetHints(canonicalHints.ToArray());

View File

@@ -20,7 +20,12 @@ namespace keepass2android.services.AutofillBase
int Size = 0;
public SaveDataType SaveType { get; set; }
public AutofillFieldMetadataCollection()
public bool Empty
{
get { return Size == 0; }
}
public AutofillFieldMetadataCollection()
{
SaveType = 0;
FocusedAutofillCanonicalHints = new List<string>();

View File

@@ -31,13 +31,13 @@ namespace keepass2android.services.AutofillBase
public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
{
Log.Debug(CommonUtil.Tag, "onFillRequest");
CommonUtil.logd( "onFillRequest");
var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;
//TODO package signature verification?
var clientState = request.ClientState;
Log.Debug(CommonUtil.Tag, "onFillRequest(): data=" + CommonUtil.BundleToString(clientState));
CommonUtil.logd( "onFillRequest(): data=" + CommonUtil.BundleToString(clientState));
cancellationSignal.CancelEvent += (sender, e) => {
@@ -62,7 +62,7 @@ namespace keepass2android.services.AutofillBase
bool responseAuth = true;
var autofillIds = autofillFields.GetAutofillIds();
if (responseAuth && autofillIds.Length != 0)
if (responseAuth && autofillIds.Length != 0 && CanAutofill(query))
{
var responseBuilder = new FillResponse.Builder();
@@ -89,6 +89,11 @@ namespace keepass2android.services.AutofillBase
}
}
private bool CanAutofill(string query)
{
return !(query == "androidapp://android" || query == "androidapp://"+this.PackageName);
}
public override void OnSaveRequest(SaveRequest request, SaveCallback callback)
{
//TODO implement
@@ -98,12 +103,12 @@ namespace keepass2android.services.AutofillBase
public override void OnConnected()
{
Log.Debug(CommonUtil.Tag, "onConnected");
CommonUtil.logd( "onConnected");
}
public override void OnDisconnected()
{
Log.Debug(CommonUtil.Tag, "onDisconnected");
CommonUtil.logd( "onDisconnected");
}
public abstract IAutofillIntentBuilder IntentBuilder{get;}

View File

@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App.Assist;
using Android.Content;
using Android.Text;
using Android.Util;
using Android.Views;
using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
namespace keepass2android.services.AutofillBase
@@ -16,7 +20,8 @@ namespace keepass2android.services.AutofillBase
public Context mContext { get; }
public AutofillFieldMetadataCollection AutofillFields { get; set; }
AssistStructure Structure;
public FilledAutofillFieldCollection ClientFormData { get; set; }
private List<AssistStructure.ViewNode> _editTextsWithoutHint = new List<AssistStructure.ViewNode>();
public FilledAutofillFieldCollection ClientFormData { get; set; }
public StructureParser(Context context, AssistStructure structure)
{
@@ -46,12 +51,52 @@ namespace keepass2android.services.AutofillBase
var nodes = Structure.WindowNodeCount;
ClientFormData = new FilledAutofillFieldCollection();
String webDomain = null;
for (int i = 0; i < nodes; i++)
_editTextsWithoutHint.Clear();
for (int i = 0; i < nodes; i++)
{
var node = Structure.GetWindowNodeAt(i);
var view = node.RootViewNode;
ParseLocked(forFill, view, ref webDomain);
}
if (AutofillFields.Empty)
{
var passwordFields = _editTextsWithoutHint
.Where(f =>
(!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) &&
(!f.Hint?.ToLowerInvariant().Contains("search") ?? true) &&
(
f.InputType.HasFlag(InputTypes.TextVariationPassword) ||
f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) ||
f.InputType.HasFlag(InputTypes.TextVariationWebPassword) ||
(f.HtmlInfo?.Attributes.Any(p => p.First.ToString() == "type" && p.Second.ToString() == "password") ?? false)
)
).ToList();
if (!_editTextsWithoutHint.Any())
{
passwordFields = _editTextsWithoutHint.Where(f =>
(f.IdEntry?.ToLowerInvariant().Contains("password") ?? false)
|| (f.Hint?.ToLowerInvariant().Contains("password") ?? false)).ToList();
}
foreach (var passwordField in passwordFields)
{
AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword }));
var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
if (usernameField != null)
{
AutofillFields.Add(new AutofillFieldMetadata(usernameField, new[] {View.AutofillHintUsername}));
}
}
}
String packageName = Structure.ActivityComponent.PackageName;
if (!string.IsNullOrEmpty(webDomain))
{
@@ -103,6 +148,10 @@ namespace keepass2android.services.AutofillBase
//ClientFormData.Add(new FilledAutofillField(viewNode));
}
}
else if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input")
{
_editTextsWithoutHint.Add(viewNode);
}
var childrenSize = viewNode.ChildCount;
if (childrenSize > 0)
{