Fix Identity partition selection after merging real Identity partition support

This commit is contained in:
Andre Rosado committed 2026-09-17 16:21:57 +01:00
1 parent 663024e332
commit e8c07b36c4
2 files changed
+24 -11

No files matched your search

@@ -187,9 +187,7 @@ class AutofillParserImpl(
autofillViews
}
val effectiveFocusedView = effectiveViews
.filterNot { it is AutofillView.Identity }
.firstFocusedOrNull()
val effectiveFocusedView = effectiveViews.effectiveFocusedViewOrNull()
?: return AutofillRequest.Unfillable
// Choose the first focused partition of data for fulfillment.
@@ -284,8 +282,11 @@ class AutofillParserImpl(
is AutofillView.Login -> rule.category in loginCategories
is AutofillView.Identity -> rule.category in IDENTITY_FILL_ASSIST_CATEGORIES
is AutofillView.Unused -> {
val identityCoversRule = isIdentityAutofillEnabled &&
rule.category in IDENTITY_FILL_ASSIST_CATEGORIES
rule.category in loginCategories ||
rule.category in CARD_FILL_ASSIST_CATEGORIES
rule.category in CARD_FILL_ASSIST_CATEGORIES ||
identityCoversRule
}
}
}
@@ -352,6 +353,18 @@ private fun List<ViewNodeTraversalData>.selectCandidateAutofillViews(
private fun List<AutofillView>.firstFocusedOrNull(): AutofillView? =
firstOrNull { it.data.isFocused } ?: firstOrNull()
/**
* Returns the [AutofillView] that should win partition selection: a focused Login/Card view
* first, then any focused view (so a real Identity partition can still be built when Identity is
* the only classification available), then any Login/Card view, then any view at all.
*/
private fun List<AutofillView>.effectiveFocusedViewOrNull(): AutofillView? {
val focusedNonIdentity = firstOrNull { it.data.isFocused && it !is AutofillView.Identity }
val focusedAny = firstOrNull { it.data.isFocused }
val nonIdentity = firstOrNull { it !is AutofillView.Identity }
return focusedNonIdentity ?: focusedAny ?: nonIdentity ?: firstOrNull()
}
/**
* This helper function updates the [ViewNodeTraversalData] if necessary for missing password
* fields that were marked invalid because they contained a specific `hint` or `idEntry`. If the
@@ -2074,13 +2074,12 @@ class AutofillParserTests {
@Suppress("MaxLineLength")
@Test
fun `parse should choose AutofillPartition Login when an Identity view is focused but a Login view is fillable elsewhere`() {
fun `parse should choose AutofillPartition Identity when an Identity view is focused even though a Login view is fillable in another window`() {
// Setup: a focused field heuristics classify as Identity (e.g. "First name" on a signup
// form) sits in one window, while a fillable, unfocused Login.Username field exists in
// another window on the same screen. Before Phase D's Identity partition exists, a
// focused Identity view must not force the whole request to Unfillable when a fillable
// Login/Card partition exists elsewhere -- it should be excluded from candidates exactly
// like Unused, falling through to the other fillable view.
// another window on the same screen. The Login view never enters the candidate pool --
// selectCandidateAutofillViews only considers a window whose own views have a focused
// entry -- so the focused Identity view wins and builds a real Identity partition.
val identityAutofillId: AutofillId = mockk()
val identityViewNode: AssistStructure.ViewNode = mockk {
every { this@mockk.autofillHints } returns emptyArray()
@@ -2130,13 +2129,14 @@ class AutofillParserTests {
// Test
val actual = parser.parse(autofillAppInfo = autofillAppInfo, fillRequest = fillRequest)
// Verify: falls through to the fillable Login view instead of becoming Unfillable.
// Verify: the focused Identity view wins; the Login view in the other window is never a
// candidate.
val expected = AutofillRequest.Fillable(
ignoreAutofillIds = emptyList(),
inlinePresentationSpecs = inlinePresentationSpecs,
maxInlineSuggestionsCount = MAX_INLINE_SUGGESTION_COUNT,
packageName = PACKAGE_NAME,
partition = AutofillPartition.Login(views = listOf(loginAutofillView)),
partition = AutofillPartition.Identity(views = listOf(identityAutofillView)),
uri = URI,
)
assertEquals(expected, actual)