first working (but still very rudimentary) version of Oreo Autofill (#9).
Not yet implemented and/or tested: partitioning, autofill fields without hints, saving, filling of other fields than username or password, package signature verification, DAL
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white"
|
||||
android:background="#eeeeff"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
@@ -24,6 +24,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="#738282"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
|
||||
@@ -142,8 +142,13 @@ namespace keepass2android
|
||||
FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);
|
||||
|
||||
View selectOtherEntry = FindViewById (Resource.Id.select_other_entry);
|
||||
selectOtherEntry.Click += (sender, e) => {
|
||||
GroupActivity.Launch (this, new SelectEntryForUrlTask(url));
|
||||
|
||||
var newTask = new SelectEntryForUrlTask(url);
|
||||
if (AppTask is SelectEntryTask currentSelectTask)
|
||||
newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications;
|
||||
|
||||
selectOtherEntry.Click += (sender, e) => {
|
||||
GroupActivity.Launch (this, newTask);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
<Compile Include="services\AutofillBase\AutofillFieldMetadata.cs" />
|
||||
<Compile Include="services\AutofillBase\AutofillFieldMetadataCollection.cs" />
|
||||
<Compile Include="services\AutofillBase\AutofillHelper.cs" />
|
||||
<Compile Include="services\AutofillBase\AutofillHintsHelper.cs" />
|
||||
<Compile Include="services\AutofillBase\AutofillServiceBase.cs" />
|
||||
<Compile Include="services\AutofillBase\ChooseForAutofillActivityBase.cs" />
|
||||
<Compile Include="services\AutofillBase\CommonUtil.cs" />
|
||||
|
||||
@@ -28,9 +28,11 @@ namespace keepass2android.services.AutofillBase
|
||||
AutofillType = view.AutofillType;
|
||||
AutofillOptions = view.GetAutofillOptions();
|
||||
Focused = view.IsFocused;
|
||||
//TODO port and use AutoFillHints
|
||||
SetHints(AutofillHelper.FilterForSupportedHints(view.GetAutofillHints()));
|
||||
}
|
||||
var supportedHints = AutofillHintsHelper.FilterForSupportedHints(view.GetAutofillHints());
|
||||
var storedHints = AutofillHintsHelper.ConvertToStoredHints(supportedHints);
|
||||
SetHints(storedHints.ToArray());
|
||||
|
||||
}
|
||||
|
||||
void SetHints(string[] value)
|
||||
{
|
||||
@@ -58,6 +60,7 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
void UpdateSaveTypeFromHints()
|
||||
{
|
||||
//TODO future add savetypes for W3cHints
|
||||
SaveType = 0;
|
||||
if (AutofillHints == null)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
if (!AutofillHintsToFieldsMap.ContainsKey(hint))
|
||||
{
|
||||
AutofillHintsToFieldsMap.Add(hint, new List<keepass2android.services.AutofillBase.AutofillFieldMetadata>());
|
||||
AutofillHintsToFieldsMap.Add(hint, new List<AutofillFieldMetadata>());
|
||||
}
|
||||
AutofillHintsToFieldsMap[hint].Add(autofillFieldMetadata);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace keepass2android.services.AutofillBase
|
||||
return AutofillIds.ToArray();
|
||||
}
|
||||
|
||||
public List<keepass2android.services.AutofillBase.AutofillFieldMetadata> GetFieldsForHint(String hint)
|
||||
public List<AutofillFieldMetadata> GetFieldsForHint(String hint)
|
||||
{
|
||||
return AutofillHintsToFieldsMap[hint];
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using Android.Views;
|
||||
using Android.Widget;
|
||||
using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
|
||||
|
||||
//TODO compare port
|
||||
namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -93,6 +92,7 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
if (autofillFields.SaveType != 0)
|
||||
{
|
||||
//TODO implement save
|
||||
var autofillIds = autofillFields.GetAutofillIds();
|
||||
responseBuilder.SetSaveInfo
|
||||
(new SaveInfo.Builder(autofillFields.SaveType, autofillIds).Build());
|
||||
@@ -105,47 +105,5 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] FilterForSupportedHints(string[] hints)
|
||||
{
|
||||
var filteredHints = new string[hints.Length];
|
||||
int i = 0;
|
||||
foreach (var hint in hints)
|
||||
{
|
||||
if (IsValidHint(hint))
|
||||
{
|
||||
filteredHints[i++] = hint;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug(CommonUtil.Tag, "Invalid autofill hint: " + hint);
|
||||
}
|
||||
}
|
||||
var finalFilteredHints = new string[i];
|
||||
Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
|
||||
return finalFilteredHints;
|
||||
}
|
||||
|
||||
public static bool IsValidHint(String hint)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case View.AutofillHintCreditCardExpirationDate:
|
||||
case View.AutofillHintCreditCardExpirationDay:
|
||||
case View.AutofillHintCreditCardExpirationMonth:
|
||||
case View.AutofillHintCreditCardExpirationYear:
|
||||
case View.AutofillHintCreditCardNumber:
|
||||
case View.AutofillHintCreditCardSecurityCode:
|
||||
case View.AutofillHintEmailAddress:
|
||||
case View.AutofillHintPhone:
|
||||
case View.AutofillHintName:
|
||||
case View.AutofillHintPassword:
|
||||
case View.AutofillHintPostalAddress:
|
||||
case View.AutofillHintPostalCode:
|
||||
case View.AutofillHintUsername:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.services.AutofillBase.model;
|
||||
|
||||
namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
class AutofillHintsHelper
|
||||
{
|
||||
private static readonly HashSet<string> validHints = new HashSet<string>()
|
||||
{
|
||||
View.AutofillHintUsername,
|
||||
View.AutofillHintPassword,
|
||||
W3cHints.USERNAME,
|
||||
W3cHints.CURRENT_PASSWORD,
|
||||
W3cHints.NEW_PASSWORD
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> hintReplacements= new Dictionary<string, string>()
|
||||
{
|
||||
{W3cHints.EMAIL, View.AutofillHintEmailAddress},
|
||||
{W3cHints.USERNAME, View.AutofillHintUsername},
|
||||
{W3cHints.CURRENT_PASSWORD, View.AutofillHintPassword},
|
||||
{W3cHints.NEW_PASSWORD, View.AutofillHintPassword},
|
||||
{W3cHints.CC_EXPIRATION_MONTH, View.AutofillHintCreditCardExpirationMonth },
|
||||
{W3cHints.CC_EXPIRATION_YEAR, View.AutofillHintCreditCardExpirationYear },
|
||||
{W3cHints.CC_EXPIRATION, View.AutofillHintCreditCardExpirationDate },
|
||||
{W3cHints.CC_NUMBER, View.AutofillHintCreditCardNumber },
|
||||
{W3cHints.CC_CSC, View.AutofillHintCreditCardSecurityCode },
|
||||
{W3cHints.POSTAL_CODE, View.AutofillHintPostalCode },
|
||||
|
||||
|
||||
};
|
||||
|
||||
public static bool IsValidHint(string hint)
|
||||
{
|
||||
return validHints.Contains(hint);
|
||||
}
|
||||
|
||||
|
||||
public static string[] FilterForSupportedHints(string[] hints)
|
||||
{
|
||||
var filteredHints = new string[hints.Length];
|
||||
int i = 0;
|
||||
foreach (var hint in hints)
|
||||
{
|
||||
if (IsValidHint(hint))
|
||||
{
|
||||
filteredHints[i++] = hint;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug(CommonUtil.Tag, "Invalid autofill hint: " + hint);
|
||||
}
|
||||
}
|
||||
var finalFilteredHints = new string[i];
|
||||
Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
|
||||
return finalFilteredHints;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static List<string> ConvertToStoredHints(string[] supportedHints)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
foreach (string hint in supportedHints)
|
||||
{
|
||||
string storedHint = hint;
|
||||
if (hintReplacements.ContainsKey(hint))
|
||||
storedHint = hintReplacements[hint];
|
||||
result.Add(storedHint);
|
||||
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Util;
|
||||
using Android.Views.Autofill;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
@@ -47,6 +49,7 @@ namespace keepass2android.services.AutofillBase
|
||||
try
|
||||
{
|
||||
query = parser.ParseForFill();
|
||||
|
||||
}
|
||||
catch (Java.Lang.SecurityException e)
|
||||
{
|
||||
@@ -56,20 +59,30 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
|
||||
AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
// Check user's settings for authenticating Responses and Datasets.
|
||||
|
||||
bool responseAuth = true;
|
||||
var autofillIds = autofillFields.GetAutofillIds();
|
||||
if (responseAuth && autofillIds.Length != 0)
|
||||
{
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
// If the entire Autofill Response is authenticated, AuthActivity is used
|
||||
// to generate Response.
|
||||
var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query);
|
||||
var presentation = keepass2android.services.AutofillBase.AutofillHelper
|
||||
RemoteViews presentation = keepass2android.services.AutofillBase.AutofillHelper
|
||||
.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt),
|
||||
Resource.Drawable.ic_launcher);
|
||||
responseBuilder
|
||||
.SetAuthentication(autofillIds, sender, presentation);
|
||||
|
||||
var datasetBuilder = new Dataset.Builder(presentation);
|
||||
|
||||
datasetBuilder
|
||||
.SetAuthentication(sender);
|
||||
foreach (var autofillId in autofillIds)
|
||||
{
|
||||
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
|
||||
}
|
||||
|
||||
responseBuilder.AddDataset(datasetBuilder.Build());
|
||||
|
||||
callback.OnSuccess(responseBuilder.Build());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
public static string ExtraQueryString => "EXTRA_QUERY_STRING";
|
||||
|
||||
public int RequestCodeQuery => 663245;
|
||||
public int RequestCodeQuery => 6245;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@@ -48,6 +48,11 @@ namespace keepass2android.services.AutofillBase
|
||||
StartActivityForResult(i, RequestCodeQuery);
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
}
|
||||
|
||||
protected abstract Intent GetQueryIntent(string requestedUrl);
|
||||
|
||||
protected void RestartApp()
|
||||
@@ -80,21 +85,12 @@ namespace keepass2android.services.AutofillBase
|
||||
protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap)
|
||||
{
|
||||
var intent = Intent;
|
||||
var forResponse = intent.GetBooleanExtra(CommonUtil.EXTRA_FOR_RESPONSE, true);
|
||||
AssistStructure structure = (AssistStructure)intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure);
|
||||
StructureParser parser = new StructureParser(this, structure);
|
||||
parser.ParseForFill();
|
||||
AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
|
||||
ReplyIntent = new Intent();
|
||||
if (forResponse)
|
||||
{
|
||||
Dictionary<string, FilledAutofillFieldCollection> dict = new Dictionary<string, FilledAutofillFieldCollection> { {clientFormDataMap.DatasetName, clientFormDataMap }};
|
||||
SetResponseIntent(AutofillHelper.NewResponse(this, false, autofillFields, dict, IntentBuilder));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder));
|
||||
}
|
||||
SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder));
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
@@ -103,16 +99,21 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
if (requestCode == RequestCodeQuery)
|
||||
{
|
||||
if (resultCode != Result.Ok)
|
||||
OnFailure();
|
||||
else
|
||||
if (resultCode == ExpectedActivityResult)
|
||||
OnSuccess(GetDataset(data));
|
||||
else
|
||||
OnFailure();
|
||||
Finish();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected virtual Result ExpectedActivityResult
|
||||
{
|
||||
get { return Result.Ok; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the FilledAutofillFieldCollection from the intent returned from the query activity
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Android.OS;
|
||||
using Android.Util;
|
||||
using Java.Util;
|
||||
|
||||
namespace keepass2android.services.AutofillBase
|
||||
@@ -9,8 +10,6 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
public const string Tag = "Kp2aAutofill";
|
||||
public const bool Debug = true;
|
||||
public const string EXTRA_DATASET_NAME = "dataset_name";
|
||||
public const string EXTRA_FOR_RESPONSE = "for_response";
|
||||
|
||||
static void BundleToString(StringBuilder builder, Bundle data)
|
||||
{
|
||||
@@ -43,5 +42,17 @@ namespace keepass2android.services.AutofillBase
|
||||
BundleToString(builder, data);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static void logd(string s)
|
||||
{
|
||||
#if DEBUG
|
||||
Log.Debug(Tag, s);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void loge(string s)
|
||||
{
|
||||
Kp2aLog.Log(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,20 +52,21 @@ namespace keepass2android.services.AutofillBase
|
||||
var view = node.RootViewNode;
|
||||
ParseLocked(forFill, view, ref webDomain);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(webDomain))
|
||||
String packageName = Structure.ActivityComponent.PackageName;
|
||||
if (!string.IsNullOrEmpty(webDomain))
|
||||
{
|
||||
String packageName = Structure.ActivityComponent.PackageName;
|
||||
bool valid = Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, packageName);
|
||||
if (!valid)
|
||||
{
|
||||
throw new Java.Lang.SecurityException(mContext.GetString(
|
||||
Resource.String.invalid_link_association, webDomain, packageName));
|
||||
}
|
||||
Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}");
|
||||
Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain");
|
||||
webDomain = "androidapp://" + packageName;
|
||||
Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain. Using package name.");
|
||||
}
|
||||
return webDomain;
|
||||
}
|
||||
@@ -93,7 +94,7 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
if (forFill)
|
||||
{
|
||||
AutofillFields.Add(new keepass2android.services.AutofillBase.AutofillFieldMetadata(viewNode));
|
||||
AutofillFields.Add(new AutofillFieldMetadata(viewNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -11,12 +11,13 @@ namespace keepass2android.services.AutofillBase.model
|
||||
|
||||
public string[] AutofillHints { get; set; }
|
||||
|
||||
|
||||
public FilledAutofillField()
|
||||
{}
|
||||
|
||||
public FilledAutofillField(AssistStructure.ViewNode viewNode)
|
||||
/*public FilledAutofillField(AssistStructure.ViewNode viewNode)
|
||||
{
|
||||
AutofillHints = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints());
|
||||
AutofillHintsHelper = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints());
|
||||
|
||||
//TODO port updated FilledAutofillField?
|
||||
AutofillValue autofillValue = viewNode.AutofillValue;
|
||||
@@ -40,7 +41,7 @@ namespace keepass2android.services.AutofillBase.model
|
||||
TextValue = autofillValue.TextValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
public bool IsNull()
|
||||
{
|
||||
|
||||
@@ -32,26 +32,114 @@ namespace keepass2android.services.AutofillBase.model
|
||||
public void Add(FilledAutofillField filledAutofillField)
|
||||
{
|
||||
string[] autofillHints = filledAutofillField.AutofillHints;
|
||||
//TODO apply W3C transformation
|
||||
foreach (string hint in autofillHints)
|
||||
{
|
||||
HintMap.Add(hint, filledAutofillField);
|
||||
}
|
||||
|
||||
string nextHint = null;
|
||||
for (int i = 0; i < autofillHints.Length; i++)
|
||||
{
|
||||
string hint = autofillHints[i];
|
||||
if (i < autofillHints.Length - 1)
|
||||
{
|
||||
nextHint = autofillHints[i + 1];
|
||||
}
|
||||
// First convert the compound W3C autofill hints
|
||||
if (isW3cSectionPrefix(hint) && i < autofillHints.Length - 1)
|
||||
{
|
||||
hint = autofillHints[++i];
|
||||
CommonUtil.logd($"Hint is a W3C section prefix; using {hint} instead");
|
||||
if (i < autofillHints.Length - 1)
|
||||
{
|
||||
nextHint = autofillHints[i + 1];
|
||||
}
|
||||
}
|
||||
if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint))
|
||||
{
|
||||
hint = nextHint;
|
||||
i++;
|
||||
CommonUtil.logd($"Hint is a W3C type prefix; using {hint} instead");
|
||||
}
|
||||
if (isW3cAddressType(hint) && nextHint != null)
|
||||
{
|
||||
hint = nextHint;
|
||||
i++;
|
||||
CommonUtil.logd($"Hint is a W3C address prefix; using {hint} instead");
|
||||
}
|
||||
|
||||
// Then check if the "actual" hint is supported.
|
||||
if (AutofillHintsHelper.IsValidHint(hint))
|
||||
{
|
||||
HintMap.Add(hint, filledAutofillField);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommonUtil.loge($"Invalid hint: {autofillHints[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates a Dataset.Builder with appropriate values for each AutofillId
|
||||
/// in a AutofillFieldMetadataCollection.
|
||||
///
|
||||
/// In other words, it constructs an autofill Dataset.Builder
|
||||
/// by applying saved values (from this FilledAutofillFieldCollection)
|
||||
/// to Views specified in a AutofillFieldMetadataCollection, which represents the current
|
||||
/// page the user is on.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if to fields was applyed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="autofillFieldMetadataCollection">Autofill field metadata collection.</param>
|
||||
/// <param name="datasetBuilder">Dataset builder.</param>
|
||||
public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,
|
||||
|
||||
private static bool isW3cSectionPrefix(string hint)
|
||||
{
|
||||
return hint.StartsWith(W3cHints.PREFIX_SECTION);
|
||||
}
|
||||
|
||||
private static bool isW3cAddressType(string hint)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case W3cHints.SHIPPING:
|
||||
case W3cHints.BILLING:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool isW3cTypePrefix(string hint)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case W3cHints.PREFIX_WORK:
|
||||
case W3cHints.PREFIX_FAX:
|
||||
case W3cHints.PREFIX_HOME:
|
||||
case W3cHints.PREFIX_PAGER:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool isW3cTypeHint(string hint)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case W3cHints.TEL:
|
||||
case W3cHints.TEL_COUNTRY_CODE:
|
||||
case W3cHints.TEL_NATIONAL:
|
||||
case W3cHints.TEL_AREA_CODE:
|
||||
case W3cHints.TEL_LOCAL:
|
||||
case W3cHints.TEL_LOCAL_PREFIX:
|
||||
case W3cHints.TEL_LOCAL_SUFFIX:
|
||||
case W3cHints.TEL_EXTENSION:
|
||||
case W3cHints.EMAIL:
|
||||
case W3cHints.IMPP:
|
||||
return true;
|
||||
}
|
||||
Log.Warn(CommonUtil.Tag, "Invalid W3C type hint: " + hint);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates a Dataset.Builder with appropriate values for each AutofillId
|
||||
/// in a AutofillFieldMetadataCollection.
|
||||
///
|
||||
/// In other words, it constructs an autofill Dataset.Builder
|
||||
/// by applying saved values (from this FilledAutofillFieldCollection)
|
||||
/// to Views specified in a AutofillFieldMetadataCollection, which represents the current
|
||||
/// page the user is on.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if to fields was applyed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="autofillFieldMetadataCollection">Autofill field metadata collection.</param>
|
||||
/// <param name="datasetBuilder">Dataset builder.</param>
|
||||
public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,
|
||||
Dataset.Builder datasetBuilder)
|
||||
{
|
||||
bool setValueAtLeastOnce = false;
|
||||
@@ -66,8 +154,8 @@ namespace keepass2android.services.AutofillBase.model
|
||||
}
|
||||
for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.Count; autofillFieldIndex++)
|
||||
{
|
||||
keepass2android.services.AutofillBase.model.FilledAutofillField filledAutofillField = HintMap[hint];
|
||||
if (filledAutofillField == null)
|
||||
FilledAutofillField filledAutofillField;
|
||||
if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4,68 +4,68 @@
|
||||
{
|
||||
|
||||
// Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill)
|
||||
public static string HONORIFIC_PREFIX = "honorific-prefix";
|
||||
public static string NAME = "name";
|
||||
public static string GIVEN_NAME = "given-name";
|
||||
public static string ADDITIONAL_NAME = "additional-name";
|
||||
public static string FAMILY_NAME = "family-name";
|
||||
public static string HONORIFIC_SUFFIX = "honorific-suffix";
|
||||
public static string USERNAME = "username";
|
||||
public static string NEW_PASSWORD = "new-password";
|
||||
public static string CURRENT_PASSWORD = "current-password";
|
||||
public static string ORGANIZATION_TITLE = "organization-title";
|
||||
public static string ORGANIZATION = "organization";
|
||||
public static string STREET_ADDRESS = "street-address";
|
||||
public static string ADDRESS_LINE1 = "address-line1";
|
||||
public static string ADDRESS_LINE2 = "address-line2";
|
||||
public static string ADDRESS_LINE3 = "address-line3";
|
||||
public static string ADDRESS_LEVEL4 = "address-level4";
|
||||
public static string ADDRESS_LEVEL3 = "address-level3";
|
||||
public static string ADDRESS_LEVEL2 = "address-level2";
|
||||
public static string ADDRESS_LEVEL1 = "address-level1";
|
||||
public static string COUNTRY = "country";
|
||||
public static string COUNTRY_NAME = "country-name";
|
||||
public static string POSTAL_CODE = "postal-code";
|
||||
public static string CC_NAME = "cc-name";
|
||||
public static string CC_GIVEN_NAME = "cc-given-name";
|
||||
public static string CC_ADDITIONAL_NAME = "cc-additional-name";
|
||||
public static string CC_FAMILY_NAME = "cc-family-name";
|
||||
public static string CC_NUMBER = "cc-number";
|
||||
public static string CC_EXPIRATION = "cc-exp";
|
||||
public static string CC_EXPIRATION_MONTH = "cc-exp-month";
|
||||
public static string CC_EXPIRATION_YEAR = "cc-exp-year";
|
||||
public static string CC_CSC = "cc-csc";
|
||||
public static string CC_TYPE = "cc-type";
|
||||
public static string TRANSACTION_CURRENCY = "transaction-currency";
|
||||
public static string TRANSACTION_AMOUNT = "transaction-amount";
|
||||
public static string LANGUAGE = "language";
|
||||
public static string BDAY = "bday";
|
||||
public static string BDAY_DAY = "bday-day";
|
||||
public static string BDAY_MONTH = "bday-month";
|
||||
public static string BDAY_YEAR = "bday-year";
|
||||
public static string SEX = "sex";
|
||||
public static string URL = "url";
|
||||
public static string PHOTO = "photo";
|
||||
public const string HONORIFIC_PREFIX = "honorific-prefix";
|
||||
public const string NAME = "name";
|
||||
public const string GIVEN_NAME = "given-name";
|
||||
public const string ADDITIONAL_NAME = "additional-name";
|
||||
public const string FAMILY_NAME = "family-name";
|
||||
public const string HONORIFIC_SUFFIX = "honorific-suffix";
|
||||
public const string USERNAME = "username";
|
||||
public const string NEW_PASSWORD = "new-password";
|
||||
public const string CURRENT_PASSWORD = "current-password";
|
||||
public const string ORGANIZATION_TITLE = "organization-title";
|
||||
public const string ORGANIZATION = "organization";
|
||||
public const string STREET_ADDRESS = "street-address";
|
||||
public const string ADDRESS_LINE1 = "address-line1";
|
||||
public const string ADDRESS_LINE2 = "address-line2";
|
||||
public const string ADDRESS_LINE3 = "address-line3";
|
||||
public const string ADDRESS_LEVEL4 = "address-level4";
|
||||
public const string ADDRESS_LEVEL3 = "address-level3";
|
||||
public const string ADDRESS_LEVEL2 = "address-level2";
|
||||
public const string ADDRESS_LEVEL1 = "address-level1";
|
||||
public const string COUNTRY = "country";
|
||||
public const string COUNTRY_NAME = "country-name";
|
||||
public const string POSTAL_CODE = "postal-code";
|
||||
public const string CC_NAME = "cc-name";
|
||||
public const string CC_GIVEN_NAME = "cc-given-name";
|
||||
public const string CC_ADDITIONAL_NAME = "cc-additional-name";
|
||||
public const string CC_FAMILY_NAME = "cc-family-name";
|
||||
public const string CC_NUMBER = "cc-number";
|
||||
public const string CC_EXPIRATION = "cc-exp";
|
||||
public const string CC_EXPIRATION_MONTH = "cc-exp-month";
|
||||
public const string CC_EXPIRATION_YEAR = "cc-exp-year";
|
||||
public const string CC_CSC = "cc-csc";
|
||||
public const string CC_TYPE = "cc-type";
|
||||
public const string TRANSACTION_CURRENCY = "transaction-currency";
|
||||
public const string TRANSACTION_AMOUNT = "transaction-amount";
|
||||
public const string LANGUAGE = "language";
|
||||
public const string BDAY = "bday";
|
||||
public const string BDAY_DAY = "bday-day";
|
||||
public const string BDAY_MONTH = "bday-month";
|
||||
public const string BDAY_YEAR = "bday-year";
|
||||
public const string SEX = "sex";
|
||||
public const string URL = "url";
|
||||
public const string PHOTO = "photo";
|
||||
// Optional W3C prefixes
|
||||
public static string PREFIX_SECTION = "section-";
|
||||
public static string SHIPPING = "shipping";
|
||||
public static string BILLING = "billing";
|
||||
public const string PREFIX_SECTION = "section-";
|
||||
public const string SHIPPING = "shipping";
|
||||
public const string BILLING = "billing";
|
||||
// W3C prefixes below...
|
||||
public static string PREFIX_HOME = "home";
|
||||
public static string PREFIX_WORK = "work";
|
||||
public static string PREFIX_FAX = "fax";
|
||||
public static string PREFIX_PAGER = "pager";
|
||||
public const string PREFIX_HOME = "home";
|
||||
public const string PREFIX_WORK = "work";
|
||||
public const string PREFIX_FAX = "fax";
|
||||
public const string PREFIX_PAGER = "pager";
|
||||
// ... require those suffix
|
||||
public static string TEL = "tel";
|
||||
public static string TEL_COUNTRY_CODE = "tel-country-code";
|
||||
public static string TEL_NATIONAL = "tel-national";
|
||||
public static string TEL_AREA_CODE = "tel-area-code";
|
||||
public static string TEL_LOCAL = "tel-local";
|
||||
public static string TEL_LOCAL_PREFIX = "tel-local-prefix";
|
||||
public static string TEL_LOCAL_SUFFIX = "tel-local-suffix";
|
||||
public static string TEL_EXTENSION = "tel_extension";
|
||||
public static string EMAIL = "email";
|
||||
public static string IMPP = "impp";
|
||||
public const string TEL = "tel";
|
||||
public const string TEL_COUNTRY_CODE = "tel-country-code";
|
||||
public const string TEL_NATIONAL = "tel-national";
|
||||
public const string TEL_AREA_CODE = "tel-area-code";
|
||||
public const string TEL_LOCAL = "tel-local";
|
||||
public const string TEL_LOCAL_PREFIX = "tel-local-prefix";
|
||||
public const string TEL_LOCAL_SUFFIX = "tel-local-suffix";
|
||||
public const string TEL_EXTENSION = "tel_extension";
|
||||
public const string EMAIL = "email";
|
||||
public const string IMPP = "impp";
|
||||
|
||||
private W3cHints()
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
return i;
|
||||
}
|
||||
|
||||
protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete;
|
||||
|
||||
protected override FilledAutofillFieldCollection GetDataset(Intent data)
|
||||
{
|
||||
if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked))
|
||||
@@ -45,13 +47,13 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
FilledAutofillField pwdField =
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {W3cHints.NAME, W3cHints.EMAIL},
|
||||
AutofillHints = new[] {View.AutofillHintPassword},
|
||||
TextValue = password
|
||||
};
|
||||
|
||||
FilledAutofillField userField = new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {W3cHints.NEW_PASSWORD, W3cHints.CURRENT_PASSWORD},
|
||||
AutofillHints = new[] {View.AutofillHintUsername},
|
||||
TextValue = username
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.services.AutofillBase;
|
||||
using keepass2android.services.Kp2aAutofill;
|
||||
|
||||
namespace keepass2android.services
|
||||
{
|
||||
@@ -17,7 +18,8 @@ namespace keepass2android.services
|
||||
|
||||
public IntentSender GetAuthIntentSenderForResponse(Context context, string query)
|
||||
{
|
||||
Intent intent = new Intent(context, typeof(KeePass));
|
||||
Intent intent = new Intent(context, typeof(ChooseForAutofillActivity));
|
||||
intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query);
|
||||
return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user