Support other autofill types (#948)

This commit is contained in:
Lucas Kivi
2024-02-05 11:42:42 -06:00
committed by Álison Fernandes
parent 3e1674b9e3
commit da47e3fbbb
15 changed files with 451 additions and 41 deletions

View File

@@ -32,8 +32,7 @@ class FillResponseBuilderImpl : FillResponseBuilder {
filledData
.filledPartitions
.forEach { filledPartition ->
// It won't be empty but we really don't want to make an empty dataset,
// it causes a crash.
// We really don't want to make an empty dataset as it causes a crash.
if (filledPartition.filledItems.isNotEmpty()) {
// We build a dataset for each filled partition. A filled partition is a
// copy of all the views that we are going to fill, loaded with the data

View File

@@ -95,7 +95,7 @@ class FilledDataBuilderImpl(
inlinePresentationSpec: InlinePresentationSpec?,
): FilledPartition {
val filledItems = autofillViews
.map { autofillView ->
.mapNotNull { autofillView ->
val value = when (autofillView) {
is AutofillView.Card.ExpirationMonth -> autofillCipher.expirationMonth
is AutofillView.Card.ExpirationYear -> autofillCipher.expirationYear
@@ -124,7 +124,7 @@ class FilledDataBuilderImpl(
inlinePresentationSpec: InlinePresentationSpec?,
): FilledPartition {
val filledItems = autofillViews
.map { autofillView ->
.mapNotNull { autofillView ->
val value = when (autofillView) {
is AutofillView.Login.Username -> autofillCipher.username
is AutofillView.Login.Password -> autofillCipher.password

View File

@@ -11,11 +11,15 @@ sealed class AutofillView {
* The data important to a given [AutofillView].
*
* @param autofillId The [AutofillId] associated with this view.
* @param autofillOptions A list of autofill options that can be used to fill this view.
* @param autofillType The autofill field type. (ex: View.AUTOFILL_TYPE_TEXT)
* @param isFocused Whether the view is currently focused.
* @param textValue A text value that represents the input present in the field.
*/
data class Data(
val autofillId: AutofillId,
val autofillOptions: List<String>,
val autofillType: Int,
val isFocused: Boolean,
val textValue: String?,
)

View File

@@ -7,14 +7,14 @@ import android.view.autofill.AutofillValue
*/
@Suppress("MagicNumber")
fun AutofillValue.extractMonthValue(
autofillOptions: List<String>?,
autofillOptions: List<String>,
): String? =
when {
this.isList && autofillOptions?.size == 13 -> {
this.isList && autofillOptions.size == 13 -> {
this.listValue.toString()
}
this.isList && autofillOptions?.size == 12 -> {
this.isList && autofillOptions.size == 12 -> {
(this.listValue + 1).toString()
}

View File

@@ -1,17 +1,63 @@
package com.x8bit.bitwarden.data.autofill.util
import android.view.View
import android.view.autofill.AutofillValue
import com.x8bit.bitwarden.data.autofill.model.AutofillView
import com.x8bit.bitwarden.data.autofill.model.FilledItem
/**
* Convert this [AutofillView] into a [FilledItem].
* Convert this [AutofillView] into a [FilledItem]. Return null if not possible.
*/
fun AutofillView.buildFilledItemOrNull(
value: String,
): FilledItem =
// TODO: handle other autofill types (BIT-1457)
FilledItem(
autofillId = data.autofillId,
value = AutofillValue.forText(value),
)
): FilledItem? =
when (this.data.autofillType) {
View.AUTOFILL_TYPE_DATE -> {
value
.toLongOrNull()
?.let { AutofillValue.forDate(it) }
}
View.AUTOFILL_TYPE_LIST -> this.buildListAutofillValueOrNull(value = value)
View.AUTOFILL_TYPE_TEXT -> AutofillValue.forText(value)
View.AUTOFILL_TYPE_TOGGLE -> {
value
.toBooleanStrictOrNull()
?.let { AutofillValue.forToggle(it) }
}
else -> null
}
?.let { autofillValue ->
FilledItem(
autofillId = this.data.autofillId,
value = autofillValue,
)
}
/**
* Build a list [AutofillValue] out of [value] or return null if not possible.
*/
@Suppress("MagicNumber")
private fun AutofillView.buildListAutofillValueOrNull(
value: String,
): AutofillValue? =
if (this is AutofillView.Card.ExpirationMonth) {
val autofillOptionsSize = this.data.autofillOptions.size
// The idea here is that `value` is a numerical representation of a month.
val monthIndex = value.toIntOrNull()
when {
monthIndex == null -> null
// We expect there is some placeholder or empty space at the beginning of the list.
autofillOptionsSize == 13 -> AutofillValue.forList(monthIndex)
autofillOptionsSize >= monthIndex -> AutofillValue.forList(monthIndex - 1)
else -> null
}
} else {
this
.data
.autofillOptions
.indexOfFirst { it == value }
.takeIf { it != -1 }
?.let { AutofillValue.forList(it) }
}

View File

@@ -76,12 +76,20 @@ fun AssistStructure.ViewNode.toAutofillView(): AutofillView? =
?.firstOrNull { SUPPORTED_VIEW_HINTS.contains(it) }
if (supportedHint != null || this.isInputField) {
val autofillOptions = this
.autofillOptions
.orEmpty()
.map { it.toString() }
val autofillViewData = AutofillView.Data(
autofillId = nonNullAutofillId,
isFocused = isFocused,
autofillOptions = autofillOptions,
autofillType = this.autofillType,
isFocused = this.isFocused,
textValue = this.autofillValue?.extractTextValue(),
)
buildAutofillView(
autofillOptions = autofillOptions,
autofillViewData = autofillViewData,
supportedHint = supportedHint,
)
@@ -94,13 +102,11 @@ fun AssistStructure.ViewNode.toAutofillView(): AutofillView? =
* Attempt to convert this [AssistStructure.ViewNode] and [autofillViewData] into an [AutofillView].
*/
private fun AssistStructure.ViewNode.buildAutofillView(
autofillOptions: List<String>,
autofillViewData: AutofillView.Data,
supportedHint: String?,
): AutofillView? = when {
supportedHint == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH -> {
val autofillOptions = this
.autofillOptions
?.map { it.toString() }
val monthValue = this
.autofillValue
?.extractMonthValue(