display the last opened entry as an additional dataset, helps to fill paypal app and helps with partitioned data

This commit is contained in:
Philipp Crocoll
2018-01-02 13:59:24 +01:00
parent 11330b608b
commit 8287232866
9 changed files with 110 additions and 42 deletions

View File

@@ -289,6 +289,7 @@ namespace keepass2android
CanWrite = true;
_reloadRequested = false;
OtpAuxFileIoc = null;
LastOpenedEntry = null;
}
public void MarkAllGroupsAsDirty() {

View File

@@ -56,5 +56,10 @@ namespace keepass2android
{
get { return _entry; }
}
/// <summary>
/// if the entry was selected by searching for a URL, the query URL is returned here.
/// </summary>
public string SearchUrl { get; set; }
}
}

View File

@@ -80,8 +80,9 @@ namespace keepass2android
_db = App.Kp2a.GetDb();
if (App.Kp2a.DatabaseIsUnlocked)
{
String searchUrl = ((SearchUrlTask)AppTask).UrlToSearchFor;
Query(searchUrl);
var searchUrlTask = ((SearchUrlTask)AppTask);
String searchUrl = searchUrlTask.UrlToSearchFor;
Query(searchUrl, searchUrlTask.AutoReturnFromQuery);
}
// else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished.
@@ -93,7 +94,7 @@ namespace keepass2android
AppTask.ToBundle(outState);
}
private void Query(String url)
private void Query(string url, bool autoReturnFromQuery)
{
try
{
@@ -125,7 +126,7 @@ namespace keepass2android
}
//if there is exactly one match: open the entry
if (Group.Entries.Count() == 1)
if ((Group.Entries.Count() == 1) && autoReturnFromQuery)
{
LaunchActivityForEntry(Group.Entries.Single(),0);
return;

View File

@@ -361,6 +361,7 @@ namespace keepass2android
{
base.Setup(b);
UrlToSearchFor = b.GetString(UrlToSearchKey);
AutoReturnFromQuery = b.GetBoolean(AutoReturnFromQueryKey, true);
}
public override IEnumerable<IExtra> Extras
{
@@ -369,9 +370,15 @@ namespace keepass2android
foreach (IExtra e in base.Extras)
yield return e;
yield return new StringExtra { Key=UrlToSearchKey, Value = UrlToSearchFor };
}
yield return new BoolExtra { Key = AutoReturnFromQueryKey, Value = AutoReturnFromQuery };
}
}
public override void AfterUnlockDatabase(PasswordActivity act)
public const String AutoReturnFromQueryKey = "AutoReturnFromQuery";
public bool AutoReturnFromQuery { get; set; }
public override void AfterUnlockDatabase(PasswordActivity act)
{
if (String.IsNullOrEmpty(UrlToSearchFor))
{
@@ -403,6 +410,12 @@ namespace keepass2android
base.PopulatePasswordAccessServiceIntent(intent);
intent.PutExtra(UrlToSearchKey, UrlToSearchFor);
}
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor;
base.CompleteOnCreateEntryActivity(activity);
}
}
@@ -516,15 +529,18 @@ namespace keepass2android
public override void CompleteOnCreateEntryActivity(EntryActivity activity)
{
//if the database is readonly (or no URL exists), don't offer to modify the URL
if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor)))
App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor;
//if the database is readonly (or no URL exists), don't offer to modify the URL
if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor)))
{
base.CompleteOnCreateEntryActivity(activity);
return;
}
AskAddUrlThenCompleteCreate(activity, UrlToSearchFor);
AskAddUrlThenCompleteCreate(activity, UrlToSearchFor);
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Android.Content;
using Android.OS;
using Android.Runtime;
@@ -6,12 +7,13 @@ using Android.Service.Autofill;
using Android.Util;
using Android.Views.Autofill;
using Android.Widget;
using keepass2android.services.AutofillBase.model;
namespace keepass2android.services.AutofillBase
{
public interface IAutofillIntentBuilder
{
IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest);
IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest, bool autoReturnFromQuery);
Intent GetRestartAppIntent(Context context);
int AppIconResource { get; }
@@ -67,19 +69,13 @@ namespace keepass2android.services.AutofillBase
if (responseAuth && autofillIds.Length != 0 && CanAutofill(query))
{
var responseBuilder = new FillResponse.Builder();
var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual);
RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon);
var datasetBuilder = new Dataset.Builder(presentation);
datasetBuilder.SetAuthentication(sender);
//need to add placeholders so we can directly fill after ChooseActivity
foreach (var autofillId in autofillIds)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
}
var entryDataset = AddEntryDataset(query, parser);
bool hasEntryDataset = entryDataset != null;
if (entryDataset != null)
responseBuilder.AddDataset(entryDataset);
responseBuilder.AddDataset(datasetBuilder.Build());
AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset);
callback.OnSuccess(responseBuilder.Build());
}
@@ -91,6 +87,36 @@ namespace keepass2android.services.AutofillBase
}
}
private Dataset AddEntryDataset(string query, StructureParser parser)
{
var filledAutofillFieldCollection = GetSuggestedEntry(query);
if (filledAutofillFieldCollection == null)
return null;
int partitionIndex = AutofillHintsHelper.GetPartitionIndex(parser.AutofillFields.FocusedAutofillCanonicalHints.FirstOrDefault());
FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex);
return AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder);
}
protected abstract FilledAutofillFieldCollection GetSuggestedEntry(string query);
private void AddQueryDataset(string query, bool isManual, AutofillId[] autofillIds, FillResponse.Builder responseBuilder, bool autoReturnFromQuery)
{
var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual, autoReturnFromQuery);
RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName,
GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon);
var datasetBuilder = new Dataset.Builder(presentation);
datasetBuilder.SetAuthentication(sender);
//need to add placeholders so we can directly fill after ChooseActivity
foreach (var autofillId in autofillIds)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
}
responseBuilder.AddDataset(datasetBuilder.Build());
}
private bool CanAutofill(string query)
{
return !(query == "androidapp://android" || query == "androidapp://"+this.PackageName);

View File

@@ -23,6 +23,7 @@ namespace keepass2android.services.AutofillBase
public static string ExtraQueryString => "EXTRA_QUERY_STRING";
public static string ExtraIsManualRequest => "EXTRA_IS_MANUAL_REQUEST";
public static string ExtraAutoReturnFromQuery => "EXTRA_AUTO_RETURN_FROM_QUERY";
public int RequestCodeQuery => 6245;
@@ -46,11 +47,11 @@ namespace keepass2android.services.AutofillBase
return;
}
var i = GetQueryIntent(requestedUrl);
var i = GetQueryIntent(requestedUrl, Intent.GetBooleanExtra(ExtraAutoReturnFromQuery, true));
StartActivityForResult(i, RequestCodeQuery);
}
protected abstract Intent GetQueryIntent(string requestedUrl);
protected abstract Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery);
protected void RestartApp()
{
@@ -59,7 +60,7 @@ namespace keepass2android.services.AutofillBase
Finish();
}
/*
public override void Finish()
{
if (ReplyIntent != null)
@@ -71,7 +72,7 @@ namespace keepass2android.services.AutofillBase
SetResult(Result.Canceled);
}
base.Finish();
}*/
}
void OnFailure()
{
@@ -87,8 +88,7 @@ namespace keepass2android.services.AutofillBase
parser.ParseForFill(isManual);
AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFields.FocusedAutofillCanonicalHints.FirstOrDefault());
FilledAutofillFieldCollection partitionData =
AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex);
FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex);
ReplyIntent = new Intent();
SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, IntentBuilder));
}
@@ -121,6 +121,7 @@ namespace keepass2android.services.AutofillBase
public abstract IAutofillIntentBuilder IntentBuilder { get; }
protected void SetResponseIntent(FillResponse fillResponse)
{
ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, fillResponse);

View File

@@ -24,13 +24,13 @@ namespace keepass2android.services.Kp2aAutofill
Permission = "keepass2android." + AppNames.PackagePart + ".permission.Kp2aChooseAutofill")]
public class ChooseForAutofillActivity : ChooseForAutofillActivityBase
{
protected override Intent GetQueryIntent(string requestedUrl)
protected override Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery)
{
//launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task.
//will return the results later
Intent i = new Intent(this, typeof(FileSelectActivity));
//don't show user notifications when an entry is opened.
var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false };
var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false, AutoReturnFromQuery = autoReturnFromQuery };
task.ToIntent(i);
return i;
}
@@ -41,29 +41,37 @@ namespace keepass2android.services.Kp2aAutofill
{
if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked))
return null;
var entryOutput = App.Kp2a.GetDb().LastOpenedEntry;
return GetFilledAutofillFieldCollectionFromEntry(entryOutput, this);
}
public static FilledAutofillFieldCollection GetFilledAutofillFieldCollectionFromEntry(PwEntryOutput pwEntryOutput, Context context)
{
if (pwEntryOutput == null)
return null;
FilledAutofillFieldCollection fieldCollection = new FilledAutofillFieldCollection();
var pwEntry = pwEntryOutput.Entry;
var pwEntry = App.Kp2a.GetDb().LastOpenedEntry.Entry;
foreach (string key in pwEntry.Strings.GetKeys())
{
FilledAutofillField field =
new FilledAutofillField
{
AutofillHints = new[] { GetCanonicalHintFromKp2aField(pwEntry, key) },
AutofillHints = new[] {GetCanonicalHintFromKp2aField(pwEntry, key)},
TextValue = pwEntry.Strings.ReadSafe(key),
Protected = pwEntry.Strings.Get(key).IsProtected
};
fieldCollection.Add(field);
}
if (IsCreditCard(pwEntry) && pwEntry.Expires)
if (IsCreditCard(pwEntry, context) && pwEntry.Expires)
{
DateTime expTime = pwEntry.ExpiryTime;
FilledAutofillField field =
new FilledAutofillField
{
AutofillHints = new[] { View.AutofillHintCreditCardExpirationDate },
DateValue = (long)(1000*TimeUtil.SerializeUnix(expTime)),
AutofillHints = new[] {View.AutofillHintCreditCardExpirationDate},
DateValue = (long) (1000 * TimeUtil.SerializeUnix(expTime)),
Protected = false
};
fieldCollection.Add(field);
@@ -71,7 +79,7 @@ namespace keepass2android.services.Kp2aAutofill
field =
new FilledAutofillField
{
AutofillHints = new[] { View.AutofillHintCreditCardExpirationDay },
AutofillHints = new[] {View.AutofillHintCreditCardExpirationDay},
TextValue = expTime.Day.ToString(),
Protected = false
};
@@ -80,7 +88,7 @@ namespace keepass2android.services.Kp2aAutofill
field =
new FilledAutofillField
{
AutofillHints = new[] { View.AutofillHintCreditCardExpirationMonth },
AutofillHints = new[] {View.AutofillHintCreditCardExpirationMonth},
TextValue = expTime.Month.ToString(),
Protected = false
};
@@ -89,13 +97,12 @@ namespace keepass2android.services.Kp2aAutofill
field =
new FilledAutofillField
{
AutofillHints = new[] { View.AutofillHintCreditCardExpirationYear },
AutofillHints = new[] {View.AutofillHintCreditCardExpirationYear},
TextValue = expTime.Year.ToString(),
Protected = false
};
fieldCollection.Add(field);
}
fieldCollection.DatasetName = pwEntry.Strings.ReadSafe(PwDefs.TitleField);
@@ -103,11 +110,11 @@ namespace keepass2android.services.Kp2aAutofill
return fieldCollection;
}
private bool IsCreditCard(PwEntry pwEntry)
private static bool IsCreditCard(PwEntry pwEntry, Context context)
{
return pwEntry.Strings.Exists("cc-number")
|| pwEntry.Strings.Exists("cc-csc")
|| pwEntry.Strings.Exists(GetString(Resource.String.TemplateField_CreditCard_CVV));
|| pwEntry.Strings.Exists(context.GetString(Resource.String.TemplateField_CreditCard_CVV));
}
private static readonly Dictionary<string, string> keyToHint = BuildKeyToHint();
@@ -133,7 +140,7 @@ namespace keepass2android.services.Kp2aAutofill
return result;
}
private string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key)
private static string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key)
{
if (!keyToHint.TryGetValue(key, out string result))
result = key;

View File

@@ -4,6 +4,8 @@ using Android.App;
using Android.Content;
using Android.Runtime;
using keepass2android.services.AutofillBase;
using keepass2android.services.AutofillBase.model;
using keepass2android.services.Kp2aAutofill;
using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase;
namespace keepass2android.services
@@ -24,6 +26,14 @@ namespace keepass2android.services
{
}
protected override FilledAutofillFieldCollection GetSuggestedEntry(string query)
{
if (App.Kp2a.GetDb()?.LastOpenedEntry?.SearchUrl == query)
return ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry(
App.Kp2a.GetDb()?.LastOpenedEntry, this);
return null;
}
public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder();
}
}

View File

@@ -15,11 +15,12 @@ namespace keepass2android.services
class Kp2aAutofillIntentBuilder: IAutofillIntentBuilder
{
public IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest)
public IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest, bool autoReturnFromQuery)
{
Intent intent = new Intent(context, typeof(ChooseForAutofillActivity));
intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query);
intent.PutExtra(ChooseForAutofillActivityBase.ExtraIsManualRequest, isManualRequest);
intent.PutExtra(ChooseForAutofillActivityBase.ExtraAutoReturnFromQuery, autoReturnFromQuery);
return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender;
}