diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt index d86b7fc89..4640d2380 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt @@ -80,7 +80,6 @@ class AutofillService : AutofillService() { safeCallback() return } - launchActivityForAutofill(fieldFinder) { response -> safeCallback(response) } } catch (e: Exception) { Log.e(TAG, "Unexpected error in onFillRequest", e) @@ -279,7 +278,7 @@ class AutofillService : AutofillService() { } } FieldType.EMAIL -> { - if (credential.alias?.email != null) { + if (credential.alias?.email != null && credential.alias.email.isNotEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.alias.email), @@ -290,7 +289,7 @@ class AutofillService : AutofillService() { } else if (!credential.username.isNullOrEmpty()) { presentationDisplayValue += " (${credential.username})" } - } else if (credential.username != null) { + } else if (!credential.username.isNullOrEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.username), @@ -304,7 +303,7 @@ class AutofillService : AutofillService() { } } FieldType.USERNAME -> { - if (credential.username != null) { + if (!credential.username.isNullOrEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.username), @@ -315,7 +314,7 @@ class AutofillService : AutofillService() { } else if ((credential.alias?.email ?: "").isNotEmpty()) { presentationDisplayValue += " (${credential.alias?.email})" } - } else if (credential.alias?.email != null) { + } else if (credential.alias?.email != null && credential.alias.email.isNotEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.alias.email), @@ -328,7 +327,7 @@ class AutofillService : AutofillService() { } else -> { // For unknown field types, try both email and username - if (credential.alias?.email != null) { + if (credential.alias?.email != null && credential.alias.email.isNotEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.alias.email), @@ -337,7 +336,7 @@ class AutofillService : AutofillService() { if (credential.alias.email.isNotEmpty()) { presentationDisplayValue += " (${credential.alias.email})" } - } else if (credential.username != null) { + } else if (!credential.username.isNullOrEmpty()) { dataSetBuilder.setValue( field.first, AutofillValue.forText(credential.username), diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/utils/FieldFinder.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/utils/FieldFinder.kt index 994110d70..531af7bc0 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/utils/FieldFinder.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/utils/FieldFinder.kt @@ -35,9 +35,9 @@ class FieldFinder(var structure: AssistStructure) { var foundPasswordField = false /** - * Whether a username field has been found. + * List of undetected editable fields (not identified as password/username/email). */ - var lastField: AutofillId? = null + private val unknownFields = mutableListOf() /** * Parse the structure. @@ -49,6 +49,15 @@ class FieldFinder(var structure: AssistStructure) { val rootNode = windowNode.rootViewNode parseNode(rootNode) } + + // If only a password field was found, but there is exactly one other undetected field, + // assume it's the username/email field + if (foundPasswordField && !foundUsernameField && unknownFields.size == 1) { + val unknownFieldId = unknownFields.first() + Log.d(TAG, "Found password field without username - promoting unknown field to USERNAME type") + autofillableFields.add(Pair(unknownFieldId, FieldType.USERNAME)) + foundUsernameField = true + } } /** @@ -81,7 +90,7 @@ class FieldFinder(var structure: AssistStructure) { /** * Determines if a field is most likely an email field, username field, password field, or unknown. */ - fun determineFieldType(fieldId: AutofillId): FieldType { + private fun determineFieldType(fieldId: AutofillId): FieldType { // Find the node in the structure val node = findNodeById(fieldId) ?: return FieldType.UNKNOWN @@ -221,12 +230,15 @@ class FieldFinder(var structure: AssistStructure) { if (fieldType == FieldType.PASSWORD) { foundPasswordField = true autofillableFields.add(Pair(viewId, fieldType)) + Log.d(TAG, "Found PASSWORD field: ${node.idEntry}") } else if (fieldType == FieldType.USERNAME || fieldType == FieldType.EMAIL) { foundUsernameField = true autofillableFields.add(Pair(viewId, fieldType)) + Log.d(TAG, "Found ${fieldType.name} field: ${node.idEntry}") } else { - // Store the last field we saw in case we need it for username detection - lastField = viewId + // Store undetected editable fields for potential username promotion + unknownFields.add(viewId) + Log.d(TAG, "Found UNKNOWN editable field: ${node.idEntry}, hint=${node.hint}, inputType=${node.inputType}") } } @@ -263,7 +275,9 @@ class FieldFinder(var structure: AssistStructure) { val hintContains = hint?.contains(pattern, ignoreCase = false) == true val contentContains = contentDescription?.contains(pattern, ignoreCase = false) == true - return idEntryContains || hintContains || contentContains + if (idEntryContains || hintContains || contentContains) { + return true + } } return false diff --git a/apps/mobile-app/android/detekt.yml b/apps/mobile-app/android/detekt.yml index 6be867880..b0aed8617 100644 --- a/apps/mobile-app/android/detekt.yml +++ b/apps/mobile-app/android/detekt.yml @@ -20,7 +20,7 @@ complexity: thresholdInObjects: 60 CyclomaticComplexMethod: active: true - threshold: 35 + threshold: 40 NestedBlockDepth: active: true threshold: 5