Provide autofill response data even if focused field is not fillable (#3598)
Some checks failed
Crowdin Push / Crowdin Push (push) Waiting to run
Scan / Check PR run (push) Failing after 0s
Scan / SAST scan (push) Has been skipped
Scan / Quality scan (push) Has been skipped
Test / Check PR run (push) Failing after 0s
Test / Test (push) Has been skipped

This commit is contained in:
David Perez 2024-07-22 17:10:48 -05:00 committed by GitHub
parent 2475bf5a41
commit 21c1fa7131
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 78 additions and 5 deletions

View file

@ -64,6 +64,7 @@ class AutofillParserImpl(
/**
* Parse the [AssistStructure] into an [AutofillRequest].
*/
@Suppress("LongMethod")
private fun parseInternal(
assistStructure: AssistStructure,
autofillAppInfo: AutofillAppInfo,
@ -71,13 +72,24 @@ class AutofillParserImpl(
): AutofillRequest {
// Parse the `assistStructure` into internal models.
val traversalDataList = assistStructure.traverse()
// Flatten the autofill views for processing.
val autofillViews = traversalDataList
.map { it.autofillViews }
// Take only the autofill views from the node that currently has focus.
// Then remove all the fields that cannot be filled with data.
// We fallback to taking all the fillable views if nothing has focus.
val autofillViewsList = traversalDataList.map { it.autofillViews }
val autofillViews = autofillViewsList
.filter { views -> views.any { it.data.isFocused } }
.flatten()
.filter { it !is AutofillView.Unused }
.takeUnless { it.isEmpty() }
?: autofillViewsList
.flatten()
.filter { it !is AutofillView.Unused }
// Find the focused view.
val focusedView = autofillViews.firstOrNull { it.data.isFocused }
// Find the focused view, or fallback to the first fillable item on the screen (so
// we at least have something to hook into)
val focusedView = autofillViews
.firstOrNull { it.data.isFocused }
?: autofillViews.firstOrNull()
val packageName = traversalDataList.buildPackageNameOrNull(
assistStructure = assistStructure,
@ -108,6 +120,7 @@ class AutofillParserImpl(
is AutofillView.Unused -> {
// The view is unfillable since the field is not meant to be used for autofill.
// This will never happen since we filter out all unused views above.
return AutofillRequest.Unfillable
}
}

View file

@ -536,6 +536,66 @@ class AutofillParserTests {
}
}
@Suppress("MaxLineLength")
@Test
fun `parse should choose first fillable AutofillView for partition when there is no focused view`() {
// Setup
setupAssistStructureWithAllAutofillViewTypes()
val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth(
data = AutofillView.Data(
autofillId = cardAutofillId,
autofillOptions = emptyList(),
autofillType = AUTOFILL_TYPE,
isFocused = false,
textValue = null,
),
monthValue = null,
)
val loginAutofillView: AutofillView.Login = AutofillView.Login.Username(
data = AutofillView.Data(
autofillId = loginAutofillId,
autofillOptions = emptyList(),
autofillType = AUTOFILL_TYPE,
isFocused = false,
textValue = null,
),
)
val autofillPartition = AutofillPartition.Card(
views = listOf(cardAutofillView),
)
val expected = AutofillRequest.Fillable(
ignoreAutofillIds = emptyList(),
inlinePresentationSpecs = inlinePresentationSpecs,
maxInlineSuggestionsCount = MAX_INLINE_SUGGESTION_COUNT,
packageName = PACKAGE_NAME,
partition = autofillPartition,
uri = URI,
)
every { cardViewNode.toAutofillView() } returns cardAutofillView
every { loginViewNode.toAutofillView() } returns loginAutofillView
// Test
val actual = parser.parse(
autofillAppInfo = autofillAppInfo,
fillRequest = fillRequest,
)
// Verify
assertEquals(expected, actual)
verify(exactly = 1) {
fillRequest.getInlinePresentationSpecs(
autofillAppInfo = autofillAppInfo,
isInlineAutofillEnabled = true,
)
fillRequest.getMaxInlineSuggestionsCount(
autofillAppInfo = autofillAppInfo,
isInlineAutofillEnabled = true,
)
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
}
}
@Test
fun `parse should return empty inline suggestions when inline autofill is disabled`() {
// Setup