display the last opened entry as an additional dataset, helps to fill paypal app and helps with partitioned data
This commit is contained in:
@@ -289,6 +289,7 @@ namespace keepass2android
|
||||
CanWrite = true;
|
||||
_reloadRequested = false;
|
||||
OtpAuxFileIoc = null;
|
||||
LastOpenedEntry = null;
|
||||
}
|
||||
|
||||
public void MarkAllGroupsAsDirty() {
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user