@@ -1,4 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<autofill-service
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
/>
|
||||
>
|
||||
<compatibility-package
|
||||
android:name="com.android.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.chrome.canary"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.microsoft.emmx"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.browser.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.mini.native"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.opera.mini.native.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.sec.android.app.sbrowser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.sec.android.app.sbrowser.beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fennec_aurora"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fennec_fdroid"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.firefox"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.firefox_beta"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.fenix.nightly"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.reference.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.mozilla.rocket"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.brave.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.google.android.apps.chrome_dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.yandex.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.codeaurora.swe.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.amazon.cloud9"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="mark.via.gp"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.bromite.bromite"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="org.chromium.chrome"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.ecosia.android"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.vivaldi.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
|
||||
</autofill-service>
|
||||
|
||||
@@ -35,6 +35,44 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
}
|
||||
|
||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||
{
|
||||
"org.mozilla.firefox",
|
||||
"org.mozilla.firefox_beta",
|
||||
"com.microsoft.emmx",
|
||||
"com.android.chrome",
|
||||
"com.chrome.beta",
|
||||
"com.android.browser",
|
||||
"com.brave.browser",
|
||||
"com.opera.browser",
|
||||
"com.opera.browser.beta",
|
||||
"com.opera.mini.native",
|
||||
"com.chrome.dev",
|
||||
"com.chrome.canary",
|
||||
"com.google.android.apps.chrome",
|
||||
"com.google.android.apps.chrome_dev",
|
||||
"com.yandex.browser",
|
||||
"com.sec.android.app.sbrowser",
|
||||
"com.sec.android.app.sbrowser.beta",
|
||||
"org.codeaurora.swe.browser",
|
||||
"com.amazon.cloud9",
|
||||
"mark.via.gp",
|
||||
"org.bromite.bromite",
|
||||
"org.chromium.chrome",
|
||||
"com.kiwibrowser.browser",
|
||||
"com.ecosia.android",
|
||||
"com.opera.mini.native.beta",
|
||||
"org.mozilla.fennec_aurora",
|
||||
"org.mozilla.fennec_fdroid",
|
||||
"com.qwant.liberty",
|
||||
"com.opera.touch",
|
||||
"org.mozilla.fenix",
|
||||
"org.mozilla.fenix.nightly",
|
||||
"org.mozilla.reference.browser",
|
||||
"org.mozilla.rocket",
|
||||
"org.torproject.torbrowser",
|
||||
"com.vivaldi.browser",
|
||||
};
|
||||
|
||||
public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
|
||||
{
|
||||
@@ -81,9 +119,16 @@ namespace keepass2android.services.AutofillBase
|
||||
|
||||
AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset);
|
||||
AddDisableDataset(query, autofillIds, responseBuilder, isManual);
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
|
||||
responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
|
||||
parser.AutofillFields.GetAutofillIds()).Build());
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
|
||||
{
|
||||
if (!CompatBrowsers.Contains(parser.PackageId))
|
||||
{
|
||||
responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
|
||||
parser.AutofillFields.GetAutofillIds()).Build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
callback.OnSuccess(responseBuilder.Build());
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace keepass2android.services.AutofillBase
|
||||
private PublicSuffixRuleCache domainSuffixParserCache;
|
||||
public FilledAutofillFieldCollection ClientFormData { get; set; }
|
||||
|
||||
public string PackageId { get; set; }
|
||||
|
||||
public StructureParser(Context context, AssistStructure structure)
|
||||
{
|
||||
mContext = context;
|
||||
@@ -63,6 +65,7 @@ namespace keepass2android.services.AutofillBase
|
||||
for (int i = 0; i < nodes; i++)
|
||||
{
|
||||
var node = Structure.GetWindowNodeAt(i);
|
||||
|
||||
var view = node.RootViewNode;
|
||||
ParseLocked(forFill, isManualRequest, view, ref webDomain);
|
||||
}
|
||||
@@ -78,23 +81,33 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList();
|
||||
}
|
||||
|
||||
foreach (var passwordField in passwordFields)
|
||||
{
|
||||
var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
|
||||
if (usernameField != null)
|
||||
{
|
||||
usernameFields.Add(usernameField);
|
||||
}
|
||||
}
|
||||
//for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
|
||||
//let's assume it is a username field:
|
||||
if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
|
||||
{
|
||||
usernameFields.Add(_editTextsWithoutHint.First());
|
||||
|
||||
usernameFields = _editTextsWithoutHint.Where(HasUsernameHint).ToList();
|
||||
|
||||
if (usernameFields.Any() == false)
|
||||
{
|
||||
|
||||
foreach (var passwordField in passwordFields)
|
||||
{
|
||||
var usernameField = _editTextsWithoutHint
|
||||
.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
|
||||
if (usernameField != null)
|
||||
{
|
||||
usernameFields.Add(usernameField);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usernameFields.Any() == false)
|
||||
{
|
||||
//for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
|
||||
//let's assume it is a username field:
|
||||
if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
|
||||
{
|
||||
usernameFields.Add(_editTextsWithoutHint.First());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints)
|
||||
@@ -149,14 +162,31 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
return webDomain;
|
||||
}
|
||||
|
||||
private static bool HasPasswordHint(AssistStructure.ViewNode f)
|
||||
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password","passwort" };
|
||||
private static bool HasPasswordHint(AssistStructure.ViewNode f)
|
||||
{
|
||||
return (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false)
|
||||
|| (f.Hint?.ToLowerInvariant().Contains("password") ?? false);
|
||||
}
|
||||
return ContainsAny(f.IdEntry, _passwordHints) ||
|
||||
ContainsAny(f.Hint, _passwordHints);
|
||||
}
|
||||
|
||||
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
|
||||
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email","e-mail","username" };
|
||||
private static bool HasUsernameHint(AssistStructure.ViewNode f)
|
||||
{
|
||||
return ContainsAny(f.IdEntry, _usernameHints) ||
|
||||
ContainsAny(f.Hint, _usernameHints);
|
||||
}
|
||||
|
||||
private static bool ContainsAny(string value, IEnumerable<string> terms)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var lowerValue = value.ToLowerInvariant();
|
||||
return terms.Any(t => lowerValue.Contains(t));
|
||||
}
|
||||
|
||||
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
|
||||
{
|
||||
if (!InputTypes.MaskClass.HasFlag(inputTypeClass))
|
||||
throw new Exception("invalid inputTypeClas");
|
||||
@@ -198,8 +228,13 @@ namespace keepass2android.services.AutofillBase
|
||||
void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain)
|
||||
{
|
||||
String webDomain = viewNode.WebDomain;
|
||||
if ((PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
|
||||
(viewNode.IdPackage != "android"))
|
||||
{
|
||||
PackageId = viewNode.IdPackage;
|
||||
}
|
||||
|
||||
DomainName outDomain;
|
||||
DomainName outDomain;
|
||||
if (DomainName.TryParse(webDomain, domainSuffixParserCache, out outDomain))
|
||||
{
|
||||
webDomain = outDomain.RegisterableDomainName;
|
||||
@@ -224,9 +259,13 @@ namespace keepass2android.services.AutofillBase
|
||||
if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused &&
|
||||
isManualRequest)
|
||||
viewHints[0] = "on";
|
||||
CommonUtil.logd("viewHints=" + viewHints);
|
||||
CommonUtil.logd("class=" + viewNode.ClassName);
|
||||
CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
|
||||
if (viewHints != null && viewHints.Any())
|
||||
{
|
||||
CommonUtil.logd("viewHints=" + viewHints);
|
||||
CommonUtil.logd("class=" + viewNode.ClassName);
|
||||
CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
|
||||
}
|
||||
|
||||
if (viewNode?.HtmlInfo?.Tag == "input")
|
||||
{
|
||||
foreach (var p in viewNode.HtmlInfo.Attributes)
|
||||
|
||||
@@ -15,7 +15,7 @@ using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServic
|
||||
|
||||
namespace keepass2android.services
|
||||
{
|
||||
[Service(Label = AppNames.AppName, Permission=Manifest.Permission.BindAutofillService)]
|
||||
[Service(Label = AppNames.AppName, Permission=Manifest.Permission.BindAutofillService)]
|
||||
[IntentFilter(new [] {"android.service.autofill.AutofillService"})]
|
||||
[MetaData("android.autofill", Resource = "@xml/autofillservice")]
|
||||
[Register("keepass2android.services.Kp2aAutofillService")]
|
||||
@@ -41,8 +41,6 @@ namespace keepass2android.services
|
||||
|
||||
protected override void HandleSaveRequest(StructureParser parser, string query)
|
||||
{
|
||||
|
||||
|
||||
var intent = new Intent(this, typeof(SelectCurrentDbActivity));
|
||||
|
||||
Dictionary<string, string> outputFields = new Dictionary<string, string>();
|
||||
|
||||
Reference in New Issue
Block a user