From 256b599a3d66615119b421eba5d2823dd6718795 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:52:54 +0000 Subject: [PATCH 1/8] Initial plan From 8b717fc526eed272fa36a7f5700b98eb709c9879 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:00:18 +0000 Subject: [PATCH 2/8] Enhance username field detection with expanded patterns and heuristics Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../service/PasswordAutofillService.kt | 104 +++++++++++++----- 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index 336b4c8..c31c8d6 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -295,13 +295,17 @@ class PasswordAutofillService : AutofillService() { // Parse fields from the view structure val fields = mutableListOf() + val allTextFields = mutableListOf() if (structure.windowNodeCount > 0) { structure.getWindowNodeAt(0)?.rootViewNode?.let { rootNode -> - parseNode(rootNode, fields) + parseNode(rootNode, fields, allTextFields) } } + // Apply heuristics to identify username fields from unidentified text fields + applyUsernameHeuristics(fields, allTextFields) + Log.d(TAG, "Parsed ${fields.size} autofill fields") return AutofillContext( @@ -311,12 +315,40 @@ class PasswordAutofillService : AutofillService() { ) } + /** + * Apply heuristics to identify username fields from unidentified text fields. + * If we have a password field but no username/email field, and there are unidentified + * text fields, we assume the first unidentified text field is the username field. + */ + private fun applyUsernameHeuristics( + identifiedFields: MutableList, + unidentifiedFields: List + ) { + // Check if we have a password field + val hasPasswordField = identifiedFields.any { it.fieldType == FieldType.PASSWORD } + + // Check if we already have a username or email field + val hasUsernameField = identifiedFields.any { + it.fieldType == FieldType.USERNAME || it.fieldType == FieldType.EMAIL + } + + // If we have a password but no username, and there are unidentified fields, + // assume the first unidentified field is the username field + if (hasPasswordField && !hasUsernameField && unidentifiedFields.isNotEmpty()) { + val usernameField = unidentifiedFields.first().copy(fieldType = FieldType.USERNAME) + identifiedFields.add(usernameField) + Log.d(TAG, "Applied heuristic: Identified unidentified text field as USERNAME (appears with password field)") + } + } + /** * Recursively parse view nodes to find autofillable fields. + * Stores both identified fields and potential unidentified text fields for heuristic analysis. */ private fun parseNode( node: android.app.assist.AssistStructure.ViewNode, - fields: MutableList + fields: MutableList, + allTextFields: MutableList = mutableListOf() ) { val autofillId = node.autofillId val autofillType = node.autofillType @@ -338,26 +370,29 @@ class PasswordAutofillService : AutofillService() { // Determine field type from multiple sources val fieldType = determineFieldTypeFromNode(hint, nodeHint, inputType, idEntry, htmlInfo) - // Only add if we can identify the field type - if (fieldType != FieldType.UNKNOWN) { - fields.add( - AutofillField( - autofillId = autofillId, - autofillType = autofillType, - hint = hint, - isFocused = node.isFocused, - fieldType = fieldType - ) - ) + val field = AutofillField( + autofillId = autofillId, + autofillType = autofillType, + hint = hint, + isFocused = node.isFocused, + fieldType = fieldType + ) + // Add to identified fields if we can determine the type + if (fieldType != FieldType.UNKNOWN) { + fields.add(field) Log.d(TAG, "Found autofill field - Hint: $hint, NodeHint: $nodeHint, InputType: $inputType, IdEntry: $idEntry, Type: $fieldType, Focused: ${node.isFocused}") + } else { + // Store unidentified text fields for potential heuristic analysis + allTextFields.add(field) + Log.d(TAG, "Found unidentified text field - Hint: $hint, NodeHint: $nodeHint, InputType: $inputType, IdEntry: $idEntry") } } } // Recursively parse child nodes for (i in 0 until node.childCount) { - node.getChildAt(i)?.let { parseNode(it, fields) } + node.getChildAt(i)?.let { parseNode(it, fields, allTextFields) } } } @@ -492,16 +527,21 @@ class PasswordAutofillService : AutofillService() { if (htmlType == "email") { return FieldType.EMAIL } + // Also check for text/tel types which might be used for username fields + if (htmlType == "text" || htmlType == "tel") { + // Continue checking other attributes to determine if it's a username field + } - // Check HTML name/id attributes + // Check HTML name/id attributes with expanded patterns val htmlName = html.attributes?.firstOrNull { it.first == "name" }?.second?.lowercase() val htmlId = html.attributes?.firstOrNull { it.first == "id" }?.second?.lowercase() htmlName?.let { name -> when { name.contains("password") || name.contains("pass") -> return FieldType.PASSWORD - name.contains("email") -> return FieldType.EMAIL - name.contains("user") || name.contains("login") -> return FieldType.USERNAME + name.contains("email") || name.contains("e-mail") || name.contains("e_mail") -> return FieldType.EMAIL + name.contains("user") || name.contains("login") || name.contains("account") || + name.contains("identifier") || (name.contains("id") && !name.contains("password")) -> return FieldType.USERNAME else -> Unit } } @@ -509,29 +549,43 @@ class PasswordAutofillService : AutofillService() { htmlId?.let { id -> when { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD - id.contains("email") -> return FieldType.EMAIL - id.contains("user") || id.contains("login") -> return FieldType.USERNAME + id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL + id.contains("user") || id.contains("login") || id.contains("account") || + id.contains("identifier") || (id.contains("id") && !id.contains("password")) -> return FieldType.USERNAME + else -> Unit + } + } + + // Check HTML autocomplete attribute (standard HTML5 attribute) + val htmlAutocomplete = html.attributes?.firstOrNull { it.first == "autocomplete" }?.second?.lowercase() + htmlAutocomplete?.let { autocomplete -> + when { + autocomplete.contains("current-password") || autocomplete.contains("new-password") -> return FieldType.PASSWORD + autocomplete.contains("email") -> return FieldType.EMAIL + autocomplete.contains("username") || autocomplete.contains("nickname") -> return FieldType.USERNAME else -> Unit } } } - // Check node hint + // Check node hint with expanded patterns nodeHint?.toString()?.lowercase()?.let { hint -> when { hint.contains("password") || hint.contains("pass") -> return FieldType.PASSWORD - hint.contains("email") -> return FieldType.EMAIL - hint.contains("user") || hint.contains("login") -> return FieldType.USERNAME + hint.contains("email") || hint.contains("e-mail") || hint.contains("e_mail") -> return FieldType.EMAIL + hint.contains("user") || hint.contains("login") || hint.contains("account") || + hint.contains("identifier") || (hint.contains("id") && !hint.contains("password")) -> return FieldType.USERNAME else -> Unit } } - // Check ID entry (resource name) + // Check ID entry (resource name) with expanded patterns idEntry?.lowercase()?.let { id -> when { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD - id.contains("email") -> return FieldType.EMAIL - id.contains("user") || id.contains("login") -> return FieldType.USERNAME + id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL + id.contains("user") || id.contains("login") || id.contains("account") || + id.contains("identifier") || (id.contains("id") && !id.contains("password") && !id.contains("edit")) -> return FieldType.USERNAME else -> Unit } } From 445bc1daa8ee806960bcfe0b76058fb4e6323ef7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:01:49 +0000 Subject: [PATCH 3/8] Refine username field detection patterns to avoid false positives Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../autofill/service/PasswordAutofillService.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index c31c8d6..26f9122 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -541,7 +541,7 @@ class PasswordAutofillService : AutofillService() { name.contains("password") || name.contains("pass") -> return FieldType.PASSWORD name.contains("email") || name.contains("e-mail") || name.contains("e_mail") -> return FieldType.EMAIL name.contains("user") || name.contains("login") || name.contains("account") || - name.contains("identifier") || (name.contains("id") && !name.contains("password")) -> return FieldType.USERNAME + name.contains("identifier") || name.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME else -> Unit } } @@ -551,7 +551,7 @@ class PasswordAutofillService : AutofillService() { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || (id.contains("id") && !id.contains("password")) -> return FieldType.USERNAME + id.contains("identifier") || id.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME else -> Unit } } @@ -574,7 +574,7 @@ class PasswordAutofillService : AutofillService() { hint.contains("password") || hint.contains("pass") -> return FieldType.PASSWORD hint.contains("email") || hint.contains("e-mail") || hint.contains("e_mail") -> return FieldType.EMAIL hint.contains("user") || hint.contains("login") || hint.contains("account") || - hint.contains("identifier") || (hint.contains("id") && !hint.contains("password")) -> return FieldType.USERNAME + hint.contains("identifier") || hint.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME else -> Unit } } @@ -585,7 +585,7 @@ class PasswordAutofillService : AutofillService() { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || (id.contains("id") && !id.contains("password") && !id.contains("edit")) -> return FieldType.USERNAME + id.contains("identifier") || id.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME else -> Unit } } From ff783a71b76ea5d4ae9c518b4ef825f93f34d610 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:03:38 +0000 Subject: [PATCH 4/8] Optimize regex compilation and fix parameter efficiency issues Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../autofill/service/PasswordAutofillService.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index 26f9122..8aff444 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -52,6 +52,9 @@ class PasswordAutofillService : AutofillService() { private const val NOTIFICATION_DELAY_MS = 8000L // Wait 8 seconds after field focus before showing notification private const val NEXPASS_PACKAGE_DEBUG = "com.nexpass.passwordmanager.debug" private const val NEXPASS_PACKAGE_RELEASE = "com.nexpass.passwordmanager" + + // Regex pattern for detecting username-related ID fields + private val USERNAME_ID_PATTERN = Regex(".*\\b(user_?id|login_?id|uid)\\b.*") } override fun onFillRequest( @@ -348,7 +351,7 @@ class PasswordAutofillService : AutofillService() { private fun parseNode( node: android.app.assist.AssistStructure.ViewNode, fields: MutableList, - allTextFields: MutableList = mutableListOf() + allTextFields: MutableList ) { val autofillId = node.autofillId val autofillType = node.autofillType @@ -541,7 +544,7 @@ class PasswordAutofillService : AutofillService() { name.contains("password") || name.contains("pass") -> return FieldType.PASSWORD name.contains("email") || name.contains("e-mail") || name.contains("e_mail") -> return FieldType.EMAIL name.contains("user") || name.contains("login") || name.contains("account") || - name.contains("identifier") || name.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME + name.contains("identifier") || name.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME else -> Unit } } @@ -551,7 +554,7 @@ class PasswordAutofillService : AutofillService() { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || id.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME + id.contains("identifier") || id.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME else -> Unit } } @@ -574,7 +577,7 @@ class PasswordAutofillService : AutofillService() { hint.contains("password") || hint.contains("pass") -> return FieldType.PASSWORD hint.contains("email") || hint.contains("e-mail") || hint.contains("e_mail") -> return FieldType.EMAIL hint.contains("user") || hint.contains("login") || hint.contains("account") || - hint.contains("identifier") || hint.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME + hint.contains("identifier") || hint.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME else -> Unit } } @@ -585,7 +588,7 @@ class PasswordAutofillService : AutofillService() { id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || id.matches(Regex(".*\\b(user_?id|login_?id|uid)\\b.*")) -> return FieldType.USERNAME + id.contains("identifier") || id.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME else -> Unit } } From 3e532d2b7726862a1cd9728a05a8c37941c4f493 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:05:40 +0000 Subject: [PATCH 5/8] Extract pattern matching logic into reusable helper functions Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../service/PasswordAutofillService.kt | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index 8aff444..b788336 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -440,6 +440,28 @@ class PasswordAutofillService : AutofillService() { return false } + /** + * Check if a text matches common username field patterns. + */ + private fun isUsernamePattern(text: String): Boolean { + return text.contains("user") || text.contains("login") || text.contains("account") || + text.contains("identifier") || text.matches(USERNAME_ID_PATTERN) + } + + /** + * Check if a text matches common email field patterns. + */ + private fun isEmailPattern(text: String): Boolean { + return text.contains("email") || text.contains("e-mail") || text.contains("e_mail") + } + + /** + * Check if a text matches common password field patterns. + */ + private fun isPasswordPattern(text: String): Boolean { + return text.contains("password") || text.contains("pass") + } + /** * Determine the field type from multiple sources. */ @@ -541,20 +563,18 @@ class PasswordAutofillService : AutofillService() { htmlName?.let { name -> when { - name.contains("password") || name.contains("pass") -> return FieldType.PASSWORD - name.contains("email") || name.contains("e-mail") || name.contains("e_mail") -> return FieldType.EMAIL - name.contains("user") || name.contains("login") || name.contains("account") || - name.contains("identifier") || name.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME + isPasswordPattern(name) -> return FieldType.PASSWORD + isEmailPattern(name) -> return FieldType.EMAIL + isUsernamePattern(name) -> return FieldType.USERNAME else -> Unit } } htmlId?.let { id -> when { - id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD - id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL - id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || id.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME + isPasswordPattern(id) -> return FieldType.PASSWORD + isEmailPattern(id) -> return FieldType.EMAIL + isUsernamePattern(id) -> return FieldType.USERNAME else -> Unit } } @@ -574,10 +594,9 @@ class PasswordAutofillService : AutofillService() { // Check node hint with expanded patterns nodeHint?.toString()?.lowercase()?.let { hint -> when { - hint.contains("password") || hint.contains("pass") -> return FieldType.PASSWORD - hint.contains("email") || hint.contains("e-mail") || hint.contains("e_mail") -> return FieldType.EMAIL - hint.contains("user") || hint.contains("login") || hint.contains("account") || - hint.contains("identifier") || hint.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME + isPasswordPattern(hint) -> return FieldType.PASSWORD + isEmailPattern(hint) -> return FieldType.EMAIL + isUsernamePattern(hint) -> return FieldType.USERNAME else -> Unit } } @@ -585,10 +604,9 @@ class PasswordAutofillService : AutofillService() { // Check ID entry (resource name) with expanded patterns idEntry?.lowercase()?.let { id -> when { - id.contains("password") || id.contains("pass") -> return FieldType.PASSWORD - id.contains("email") || id.contains("e-mail") || id.contains("e_mail") -> return FieldType.EMAIL - id.contains("user") || id.contains("login") || id.contains("account") || - id.contains("identifier") || id.matches(USERNAME_ID_PATTERN) -> return FieldType.USERNAME + isPasswordPattern(id) -> return FieldType.PASSWORD + isEmailPattern(id) -> return FieldType.EMAIL + isUsernamePattern(id) -> return FieldType.USERNAME else -> Unit } } From 28c36cc35f01836e9ae81265eca854ebd53b51a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:07:20 +0000 Subject: [PATCH 6/8] Use word boundary regex patterns to avoid false positives Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../service/PasswordAutofillService.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index b788336..de1e7d8 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -53,8 +53,12 @@ class PasswordAutofillService : AutofillService() { private const val NEXPASS_PACKAGE_DEBUG = "com.nexpass.passwordmanager.debug" private const val NEXPASS_PACKAGE_RELEASE = "com.nexpass.passwordmanager" - // Regex pattern for detecting username-related ID fields - private val USERNAME_ID_PATTERN = Regex(".*\\b(user_?id|login_?id|uid)\\b.*") + // Regex pattern for detecting username-related ID fields (with word boundaries) + private val USERNAME_ID_PATTERN = Regex("\\b(user_?id|login_?id|uid)\\b") + // Regex patterns for more precise matching with word boundaries + private val USERNAME_PATTERN = Regex("\\b(user|login|account|identifier)\\b") + private val EMAIL_PATTERN = Regex("\\b(email|e-mail|e_mail)\\b") + private val PASSWORD_PATTERN = Regex("\\bpassword\\b") } override fun onFillRequest( @@ -444,22 +448,24 @@ class PasswordAutofillService : AutofillService() { * Check if a text matches common username field patterns. */ private fun isUsernamePattern(text: String): Boolean { - return text.contains("user") || text.contains("login") || text.contains("account") || - text.contains("identifier") || text.matches(USERNAME_ID_PATTERN) + return USERNAME_PATTERN.containsMatchIn(text) || USERNAME_ID_PATTERN.containsMatchIn(text) } /** * Check if a text matches common email field patterns. */ private fun isEmailPattern(text: String): Boolean { - return text.contains("email") || text.contains("e-mail") || text.contains("e_mail") + return EMAIL_PATTERN.containsMatchIn(text) } /** * Check if a text matches common password field patterns. */ private fun isPasswordPattern(text: String): Boolean { - return text.contains("password") || text.contains("pass") + // Also check for "pass" as a standalone word or in specific contexts + return PASSWORD_PATTERN.containsMatchIn(text) || + text.contains(Regex("\\bpass\\b")) || + text.contains("passwd") } /** From b7968176f811a4d129a8ef7f3f94dc05f37b2cee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:09:34 +0000 Subject: [PATCH 7/8] Extract PASS_PATTERN constant and improve consistency Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../autofill/service/PasswordAutofillService.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index de1e7d8..fb84440 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -59,6 +59,7 @@ class PasswordAutofillService : AutofillService() { private val USERNAME_PATTERN = Regex("\\b(user|login|account|identifier)\\b") private val EMAIL_PATTERN = Regex("\\b(email|e-mail|e_mail)\\b") private val PASSWORD_PATTERN = Regex("\\bpassword\\b") + private val PASS_PATTERN = Regex("\\bpass\\b") } override fun onFillRequest( @@ -462,9 +463,9 @@ class PasswordAutofillService : AutofillService() { * Check if a text matches common password field patterns. */ private fun isPasswordPattern(text: String): Boolean { - // Also check for "pass" as a standalone word or in specific contexts + // Check for "password", "pass" as standalone words, or "passwd" return PASSWORD_PATTERN.containsMatchIn(text) || - text.contains(Regex("\\bpass\\b")) || + PASS_PATTERN.containsMatchIn(text) || text.contains("passwd") } @@ -526,9 +527,9 @@ class PasswordAutofillService : AutofillService() { // Check autofill hints first (most reliable) autofillHint?.lowercase()?.let { hint -> when { - hint.contains("password") -> return FieldType.PASSWORD - hint.contains("username") -> return FieldType.USERNAME - hint.contains("email") -> return FieldType.EMAIL + isPasswordPattern(hint) -> return FieldType.PASSWORD + isEmailPattern(hint) -> return FieldType.EMAIL + isUsernamePattern(hint) || hint.contains("username") -> return FieldType.USERNAME else -> Unit } } @@ -558,10 +559,6 @@ class PasswordAutofillService : AutofillService() { if (htmlType == "email") { return FieldType.EMAIL } - // Also check for text/tel types which might be used for username fields - if (htmlType == "text" || htmlType == "tel") { - // Continue checking other attributes to determine if it's a username field - } // Check HTML name/id attributes with expanded patterns val htmlName = html.attributes?.firstOrNull { it.first == "name" }?.second?.lowercase() From e4f5715f7017c17ba56cc1deac5baaeac9872b45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 01:11:15 +0000 Subject: [PATCH 8/8] Add username and passwd to regex patterns for consistency Co-authored-by: codegax <14095200+codegax@users.noreply.github.com> --- .../autofill/service/PasswordAutofillService.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt index fb84440..5a2bc72 100644 --- a/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt +++ b/app/src/main/java/com/nexpass/passwordmanager/autofill/service/PasswordAutofillService.kt @@ -56,9 +56,9 @@ class PasswordAutofillService : AutofillService() { // Regex pattern for detecting username-related ID fields (with word boundaries) private val USERNAME_ID_PATTERN = Regex("\\b(user_?id|login_?id|uid)\\b") // Regex patterns for more precise matching with word boundaries - private val USERNAME_PATTERN = Regex("\\b(user|login|account|identifier)\\b") + private val USERNAME_PATTERN = Regex("\\b(username|user|login|account|identifier)\\b") private val EMAIL_PATTERN = Regex("\\b(email|e-mail|e_mail)\\b") - private val PASSWORD_PATTERN = Regex("\\bpassword\\b") + private val PASSWORD_PATTERN = Regex("\\b(password|passwd)\\b") private val PASS_PATTERN = Regex("\\bpass\\b") } @@ -463,10 +463,9 @@ class PasswordAutofillService : AutofillService() { * Check if a text matches common password field patterns. */ private fun isPasswordPattern(text: String): Boolean { - // Check for "password", "pass" as standalone words, or "passwd" + // Check for "password", "passwd", or "pass" as standalone words return PASSWORD_PATTERN.containsMatchIn(text) || - PASS_PATTERN.containsMatchIn(text) || - text.contains("passwd") + PASS_PATTERN.containsMatchIn(text) } /** @@ -529,7 +528,7 @@ class PasswordAutofillService : AutofillService() { when { isPasswordPattern(hint) -> return FieldType.PASSWORD isEmailPattern(hint) -> return FieldType.EMAIL - isUsernamePattern(hint) || hint.contains("username") -> return FieldType.USERNAME + isUsernamePattern(hint) -> return FieldType.USERNAME else -> Unit } }