summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategy.kt25
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt38
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/Form.kt18
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt6
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt4
5 files changed, 74 insertions, 17 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategy.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategy.kt
index e1b157d5..07a38862 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategy.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategy.kt
@@ -88,8 +88,7 @@ val autofillStrategy = strategy {
breakTieOnPair { any { isFocused } }
}
username(optional = true) {
- takeSingle()
- breakTieOnSingle { usernameCertainty >= Likely }
+ takeSingle { usernameCertainty >= Likely }
breakTieOnSingle { usernameCertainty >= Certain }
breakTieOnSingle { alreadyMatched -> directlyPrecedes(alreadyMatched) }
breakTieOnSingle { isFocused }
@@ -104,8 +103,7 @@ val autofillStrategy = strategy {
breakTieOnSingle { isFocused }
}
username(optional = true) {
- takeSingle()
- breakTieOnSingle { usernameCertainty >= Likely }
+ takeSingle { usernameCertainty >= Likely }
breakTieOnSingle { usernameCertainty >= Certain }
breakTieOnSingle { alreadyMatched -> directlyPrecedes(alreadyMatched) }
breakTieOnSingle { isFocused }
@@ -176,4 +174,23 @@ val autofillStrategy = strategy {
breakTieOnSingle { hasAutocompleteHintUsername }
}
}
+
+ // Match any focused password field with optional username field on manual request.
+ rule(applyInSingleOriginMode = true, applyOnManualRequestOnly = true) {
+ genericPassword {
+ takeSingle { isFocused }
+ }
+ username(optional = true) {
+ takeSingle { alreadyMatched ->
+ usernameCertainty >= Likely && directlyPrecedes(alreadyMatched.singleOrNull())
+ }
+ }
+ }
+
+ // Match any focused username field on manual request.
+ rule(applyInSingleOriginMode = true, applyOnManualRequestOnly = true) {
+ username {
+ takeSingle { isFocused }
+ }
+ }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
index 32ffaa2a..eea5c337 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
@@ -149,6 +149,7 @@ private class PairOfFieldsMatcher(
class AutofillRule private constructor(
private val matchers: List<AutofillRuleMatcher>,
private val applyInSingleOriginMode: Boolean,
+ private val applyOnManualRequestOnly: Boolean,
private val name: String
) {
@@ -164,7 +165,10 @@ class AutofillRule private constructor(
}
@AutofillDsl
- class Builder(private val applyInSingleOriginMode: Boolean) {
+ class Builder(
+ private val applyInSingleOriginMode: Boolean,
+ private val applyOnManualRequestOnly: Boolean
+ ) {
companion object {
private var ruleId = 1
}
@@ -231,20 +235,25 @@ class AutofillRule private constructor(
require(matchers.none { it.matchHidden }) { "Rules with applyInSingleOriginMode set to true must not fill into hidden fields" }
}
return AutofillRule(
- matchers, applyInSingleOriginMode, name ?: "Rule #$ruleId"
+ matchers, applyInSingleOriginMode, applyOnManualRequestOnly, name ?: "Rule #$ruleId"
).also { ruleId++ }
}
}
- fun apply(
+ fun match(
allPassword: List<FormField>,
allUsername: List<FormField>,
- singleOriginMode: Boolean
+ singleOriginMode: Boolean,
+ isManualRequest: Boolean
): AutofillScenario<FormField>? {
if (singleOriginMode && !applyInSingleOriginMode) {
d { "$name: Skipped in single origin mode" }
return null
}
+ if (!isManualRequest && applyOnManualRequestOnly) {
+ d { "$name: Skipped since not a manual request" }
+ return null
+ }
d { "$name: Applying..." }
val scenarioBuilder = AutofillScenario.Builder<FormField>()
val alreadyMatched = mutableListOf<FormField>()
@@ -299,15 +308,25 @@ class AutofillStrategy private constructor(private val rules: List<AutofillRule>
fun rule(
applyInSingleOriginMode: Boolean = false,
+ applyOnManualRequestOnly: Boolean = false,
block: AutofillRule.Builder.() -> Unit
) {
- rules.add(AutofillRule.Builder(applyInSingleOriginMode).apply(block).build())
+ rules.add(
+ AutofillRule.Builder(
+ applyInSingleOriginMode = applyInSingleOriginMode,
+ applyOnManualRequestOnly = applyOnManualRequestOnly
+ ).apply(block).build()
+ )
}
fun build() = AutofillStrategy(rules)
}
- fun apply(fields: List<FormField>, multiOriginSupport: Boolean): AutofillScenario<FormField>? {
+ fun match(
+ fields: List<FormField>,
+ singleOriginMode: Boolean,
+ isManualRequest: Boolean
+ ): AutofillScenario<FormField>? {
val possiblePasswordFields =
fields.filter { it.passwordCertainty >= CertaintyLevel.Possible }
d { "Possible password fields: ${possiblePasswordFields.size}" }
@@ -317,7 +336,12 @@ class AutofillStrategy private constructor(private val rules: List<AutofillRule>
// Return the result of the first rule that matches
d { "Rules: ${rules.size}" }
for (rule in rules) {
- return rule.apply(possiblePasswordFields, possibleUsernameFields, multiOriginSupport)
+ return rule.match(
+ possiblePasswordFields,
+ possibleUsernameFields,
+ singleOriginMode = singleOriginMode,
+ isManualRequest = isManualRequest
+ )
?: continue
}
return null
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/Form.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/Form.kt
index 308391ec..ab21f00c 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/Form.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/Form.kt
@@ -69,7 +69,7 @@ sealed class FormOrigin(open val identifier: String) {
* Manages the detection of fields to fill in an [AssistStructure] and determines the [FormOrigin].
*/
@RequiresApi(Build.VERSION_CODES.O)
-private class Form(context: Context, structure: AssistStructure) {
+private class Form(context: Context, structure: AssistStructure, isManualRequest: Boolean) {
companion object {
private val SUPPORTED_SCHEMES = listOf("http", "https")
@@ -98,7 +98,7 @@ private class Form(context: Context, structure: AssistStructure) {
parseStructure(structure)
}
- val scenario = detectFieldsToFill()
+ val scenario = detectFieldsToFill(isManualRequest)
val formOrigin = determineFormOrigin(context)
init {
@@ -133,7 +133,11 @@ private class Form(context: Context, structure: AssistStructure) {
}
}
- private fun detectFieldsToFill() = autofillStrategy.apply(relevantFields, singleOriginMode)
+ private fun detectFieldsToFill(isManualRequest: Boolean) = autofillStrategy.match(
+ relevantFields,
+ singleOriginMode = singleOriginMode,
+ isManualRequest = isManualRequest
+ )
private fun trackOrigin(node: AssistStructure.ViewNode) {
if (!isTrustedBrowser) return
@@ -219,8 +223,12 @@ class FillableForm private constructor(
/**
* Returns a [FillableForm] if a login form could be detected in [structure].
*/
- fun parseAssistStructure(context: Context, structure: AssistStructure): FillableForm? {
- val form = Form(context, structure)
+ fun parseAssistStructure(
+ context: Context,
+ structure: AssistStructure,
+ isManualRequest: Boolean
+ ): FillableForm? {
+ val form = Form(context, structure, isManualRequest)
if (form.formOrigin == null || form.scenario == null) return null
return FillableForm(
form.formOrigin,
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
index 00fa3aa4..350d187b 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
@@ -18,6 +18,7 @@ import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.BuildConfig
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.autofill.oreo.ui.AutofillSaveActivity
+import com.zeapo.pwdstore.utils.hasFlag
@RequiresApi(Build.VERSION_CODES.O)
class OreoAutofillService : AutofillService() {
@@ -62,7 +63,10 @@ class OreoAutofillService : AutofillService() {
}
return
}
- val formToFill = FillableForm.parseAssistStructure(this, structure) ?: run {
+ val formToFill = FillableForm.parseAssistStructure(
+ this, structure,
+ isManualRequest = request.flags hasFlag FillRequest.FLAG_MANUAL_REQUEST
+ ) ?: run {
d { "Form cannot be filled" }
callback.onSuccess(null)
return
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
index 68f8df27..f7a637c3 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
@@ -10,6 +10,10 @@ import android.util.TypedValue
import android.view.autofill.AutofillManager
import androidx.annotation.RequiresApi
+infix fun Int.hasFlag(flag: Int): Boolean {
+ return this and flag == flag
+}
+
fun String.splitLines(): Array<String> {
return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
}