several changes to fix https://github.com/PhilippC/keepass2android/issues/1399:
* adding a lock to avoid flickering/disappearing prompt * returning a fill-response instead of a dataset from authentication activity * immediately present one search result (if there is a match) as a dataset item in the autofill prompt
This commit is contained in:
@@ -28,6 +28,7 @@ namespace keepass2android.services.AutofillBase
|
||||
if (datasetName != null)
|
||||
{
|
||||
var datasetBuilder = new Dataset.Builder(NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource));
|
||||
datasetBuilder.SetId(datasetName);
|
||||
|
||||
var setValueAtLeastOnce = filledAutofillFieldCollection.ApplyToFields(autofillFields, datasetBuilder);
|
||||
if (setValueAtLeastOnce)
|
||||
|
||||
@@ -10,6 +10,7 @@ using Android.Service.Autofill;
|
||||
using Android.Util;
|
||||
using Android.Views.Autofill;
|
||||
using Android.Widget;
|
||||
using Java.Util.Concurrent.Atomic;
|
||||
using keepass2android.services.AutofillBase.model;
|
||||
|
||||
namespace keepass2android.services.AutofillBase
|
||||
@@ -30,6 +31,10 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
public abstract class AutofillServiceBase: AutofillService
|
||||
{
|
||||
//use a lock to avoid returning a response several times in buggy Firefox during one connection: this avoids flickering
|
||||
//and disappearing of the autofill prompt.
|
||||
private AtomicBoolean _lock = new AtomicBoolean();
|
||||
|
||||
public AutofillServiceBase()
|
||||
{
|
||||
|
||||
@@ -83,15 +88,20 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0;
|
||||
CommonUtil.logd( "onFillRequest " + (isManual ? "manual" : "auto"));
|
||||
var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;
|
||||
var structure = request.FillContexts.Last().Structure;
|
||||
|
||||
if (!_lock.Get())
|
||||
{
|
||||
_lock.Set(true);
|
||||
|
||||
//TODO support package signature verification as soon as this is supported in Keepass storage
|
||||
|
||||
var clientState = request.ClientState;
|
||||
CommonUtil.logd( "onFillRequest(): data=" + CommonUtil.BundleToString(clientState));
|
||||
CommonUtil.logd("onFillRequest(): data=" + CommonUtil.BundleToString(clientState));
|
||||
|
||||
|
||||
cancellationSignal.CancelEvent += (sender, e) => {
|
||||
cancellationSignal.CancelEvent += (sender, e) =>
|
||||
{
|
||||
Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet.");
|
||||
};
|
||||
// Parse AutoFill data in Activity
|
||||
@@ -117,31 +127,36 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
|
||||
Dataset entryDataset = null;
|
||||
bool hasEntryDataset = false;
|
||||
|
||||
if (query.IncompatiblePackageAndDomain == false)
|
||||
{
|
||||
//domain and package are compatible. Use Domain if available and package otherwise. Can fill without warning.
|
||||
entryDataset = BuildEntryDataset(query.DomainOrPackage, query.WebDomain, query.PackageName, autofillIds, parser, DisplayWarning.None);
|
||||
}
|
||||
else
|
||||
foreach (var entryDataset in BuildEntryDatasets(query.DomainOrPackage, query.WebDomain,
|
||||
query.PackageName,
|
||||
autofillIds, parser, DisplayWarning.None)
|
||||
)
|
||||
{
|
||||
|
||||
//domain or package are incompatible. Don't show the entry. (Tried to do so first but behavior was not consistent)
|
||||
//entryDataset = BuildEntryDataset(query.WebDomain, query.WebDomain, query.PackageName, autofillIds, parser, DisplayWarning.FillDomainInUntrustedApp);
|
||||
}
|
||||
bool hasEntryDataset = entryDataset != null;
|
||||
if (entryDataset != null)
|
||||
responseBuilder.AddDataset(entryDataset);
|
||||
hasEntryDataset = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
if (query.WebDomain != null)
|
||||
AddQueryDataset(query.WebDomain,
|
||||
query.WebDomain, query.PackageName,
|
||||
isManual, autofillIds, responseBuilder, !hasEntryDataset, query.IncompatiblePackageAndDomain ? DisplayWarning.FillDomainInUntrustedApp : DisplayWarning.None);
|
||||
isManual, autofillIds, responseBuilder, !hasEntryDataset,
|
||||
query.IncompatiblePackageAndDomain
|
||||
? DisplayWarning.FillDomainInUntrustedApp
|
||||
: DisplayWarning.None);
|
||||
else
|
||||
AddQueryDataset(query.PackageNameWithPseudoSchema,
|
||||
query.WebDomain, query.PackageName,
|
||||
isManual, autofillIds, responseBuilder, !hasEntryDataset, DisplayWarning.None);
|
||||
|
||||
}
|
||||
|
||||
AddDisableDataset(query.DomainOrPackage, autofillIds, responseBuilder, isManual);
|
||||
|
||||
@@ -163,31 +178,41 @@ namespace keepass2android.services.AutofillBase
|
||||
callback.OnSuccess(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dataset BuildEntryDataset(string query, string queryDomain, string queryPackage, AutofillId[] autofillIds, StructureParser parser,
|
||||
private List<Dataset> BuildEntryDatasets(string query, string queryDomain, string queryPackage, AutofillId[] autofillIds, StructureParser parser,
|
||||
DisplayWarning warning)
|
||||
{
|
||||
var filledAutofillFieldCollection = GetSuggestedEntry(query);
|
||||
List<Dataset> result = new List<Dataset>();
|
||||
var suggestedEntries = GetSuggestedEntries(query).ToDictionary(e => e.DatasetName, e => e);
|
||||
foreach (var filledAutofillFieldCollection in suggestedEntries.Values)
|
||||
{
|
||||
|
||||
if (filledAutofillFieldCollection == null)
|
||||
return null;
|
||||
continue;
|
||||
|
||||
if (warning == DisplayWarning.None)
|
||||
{
|
||||
//can return an actual dataset
|
||||
int partitionIndex = AutofillHintsHelper.GetPartitionIndex(parser.AutofillFields.FocusedAutofillCanonicalHints.FirstOrDefault());
|
||||
FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex);
|
||||
int partitionIndex =
|
||||
AutofillHintsHelper.GetPartitionIndex(parser.AutofillFields.FocusedAutofillCanonicalHints
|
||||
.FirstOrDefault());
|
||||
FilledAutofillFieldCollection partitionData =
|
||||
AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex);
|
||||
|
||||
return AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder);
|
||||
result.Add(AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder));
|
||||
}
|
||||
else
|
||||
{
|
||||
//return an "auth" dataset (actually for just warning the user in case domain/package dont match)
|
||||
var sender = IntentBuilder.GetAuthIntentSenderForWarning(this, query, queryDomain, queryPackage, warning);
|
||||
var sender =
|
||||
IntentBuilder.GetAuthIntentSenderForWarning(this, query, queryDomain, queryPackage, warning);
|
||||
var datasetName = filledAutofillFieldCollection.DatasetName;
|
||||
if (datasetName == null)
|
||||
return null;
|
||||
|
||||
RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, datasetName, AppNames.LauncherIcon);
|
||||
RemoteViews presentation =
|
||||
AutofillHelper.NewRemoteViews(PackageName, datasetName, AppNames.LauncherIcon);
|
||||
|
||||
var datasetBuilder = new Dataset.Builder(presentation);
|
||||
datasetBuilder.SetAuthentication(sender);
|
||||
@@ -197,13 +222,16 @@ namespace keepass2android.services.AutofillBase
|
||||
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
|
||||
}
|
||||
|
||||
return datasetBuilder.Build();
|
||||
result.Add(datasetBuilder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected abstract FilledAutofillFieldCollection GetSuggestedEntry(string query);
|
||||
protected abstract List<FilledAutofillFieldCollection> GetSuggestedEntries(string query);
|
||||
|
||||
public enum DisplayWarning
|
||||
{
|
||||
@@ -335,6 +363,8 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
public override void OnDisconnected()
|
||||
{
|
||||
|
||||
_lock.Set(false);
|
||||
CommonUtil.logd( "onDisconnected");
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ namespace keepass2android.services.AutofillBase
|
||||
FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex);
|
||||
ReplyIntent = new Intent();
|
||||
SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, IntentBuilder));
|
||||
SetResult(Result.Ok, ReplyIntent);
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
@@ -229,7 +230,9 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
protected void SetDatasetIntent(Dataset dataset)
|
||||
{
|
||||
ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset);
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
responseBuilder.AddDataset(dataset);
|
||||
ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, responseBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
@@ -9,6 +10,7 @@ using keepass2android.services.AutofillBase.model;
|
||||
using keepass2android.services.Kp2aAutofill;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Collections;
|
||||
using KeePassLib.Utility;
|
||||
using Org.Json;
|
||||
using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase;
|
||||
@@ -31,12 +33,22 @@ namespace keepass2android.services
|
||||
{
|
||||
}
|
||||
|
||||
protected override FilledAutofillFieldCollection GetSuggestedEntry(string query)
|
||||
protected override List<FilledAutofillFieldCollection> GetSuggestedEntries(string query)
|
||||
{
|
||||
if (App.Kp2a.LastOpenedEntry?.SearchUrl == query)
|
||||
return ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(
|
||||
App.Kp2a.LastOpenedEntry, this);
|
||||
return null;
|
||||
var foundEntries = (ShareUrlResults.GetSearchResultsForUrl(query)?.Entries ?? new PwObjectList<PwEntry>())
|
||||
.Select(e => new PwEntryOutput(e, App.Kp2a.FindDatabaseForElement(e)))
|
||||
.ToList();
|
||||
|
||||
if ((App.Kp2a.LastOpenedEntry?.SearchUrl == query) && !foundEntries.Any(e => e.Uuid.Equals(App.Kp2a.LastOpenedEntry?.Uuid)))
|
||||
{
|
||||
foundEntries.Clear();
|
||||
foundEntries.Add(App.Kp2a.LastOpenedEntry);
|
||||
}
|
||||
|
||||
//it seems like at least with Firefox we can have at most 3 datasets. Reserve space for the disable/enable dataset and the "fill with KP2A" which allows to select another item
|
||||
//so take only 1:
|
||||
return foundEntries.Take(1).Select(e => ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(e, this))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
protected override void HandleSaveRequest(StructureParser parser, StructureParser.AutofillTargetId query)
|
||||
|
||||
Reference in New Issue
Block a user