From 21466d3b7290b6b2204f32613ed5b357a97231ac Mon Sep 17 00:00:00 2001 From: Ryan Morov Date: Sun, 14 Jun 2026 21:46:23 -0400 Subject: [PATCH 1/2] Color digits in revealed passwords Render numeric digits in a distinct blue color when a password is revealed in CopyTextItem, so visually similar characters (e.g. 0 vs O, 1 vs l) can be told apart. Coloring is applied only while the password is shown - not while masked - so it never reveals digit positions behind the masking dots. Covers both the main password field and password-type custom fields, since both use CopyTextItem. Gated by a new COLOR_PASSWORD_DIGITS preference that defaults to on. Signed-off-by: Ryan Morov --- .../es/wolfi/app/passman/CopyTextItem.java | 40 ++++++++++++++++++- .../es/wolfi/app/passman/SettingValues.java | 3 +- app/src/main/res/values/colors.xml | 1 + 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/es/wolfi/app/passman/CopyTextItem.java b/app/src/main/java/es/wolfi/app/passman/CopyTextItem.java index c5930172b..7178b6de1 100644 --- a/app/src/main/java/es/wolfi/app/passman/CopyTextItem.java +++ b/app/src/main/java/es/wolfi/app/passman/CopyTextItem.java @@ -26,8 +26,13 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.SharedPreferences; import android.graphics.Canvas; +import android.preference.PreferenceManager; import android.text.InputType; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -50,6 +55,9 @@ public class CopyTextItem extends LinearLayout { ImageButton toggle; ImageButton open_url_toggle; + private String rawText = ""; + private boolean passwordMode = false; + public CopyTextItem(Context context) { super(context); initView(); @@ -113,7 +121,8 @@ protected void onDraw(Canvas canvas) { } public void setText(String text) { - this.text.setText(text); + this.rawText = text != null ? text : ""; + refreshDisplayedText(); } public TextView getTextView() { @@ -121,18 +130,22 @@ public TextView getTextView() { } public void setModePassword() { + passwordMode = true; text.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); toggle.setVisibility(View.VISIBLE); open_url_toggle.setVisibility(View.GONE); + refreshDisplayedText(); } public void setModeText() { + passwordMode = false; text.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL); toggle.setVisibility(View.GONE); open_url_toggle.setVisibility(View.GONE); } public void setModeEmail() { + passwordMode = false; text.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); toggle.setVisibility(View.GONE); open_url_toggle.setVisibility(View.GONE); @@ -158,6 +171,31 @@ public void toggleVisibility() { toggle.setImageDrawable(getResources().getDrawable(R.drawable.ic_eye_grey)); break; } + refreshDisplayedText(); + } + + private void refreshDisplayedText() { + if (passwordMode && isPasswordRevealed() && isColorDigitsEnabled()) { + SpannableString spannable = new SpannableString(rawText); + int color = getResources().getColor(R.color.password_digit); + for (int i = 0; i < rawText.length(); i++) { + if (Character.isDigit(rawText.charAt(i))) { + spannable.setSpan(new ForegroundColorSpan(color), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + text.setText(spannable); + } else { + text.setText(rawText); + } + } + + private boolean isPasswordRevealed() { + return text.getInputType() == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + } + + private boolean isColorDigitsEnabled() { + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); + return settings.getBoolean(SettingValues.COLOR_PASSWORD_DIGITS.toString(), true); } public void copyTextToClipboard() { diff --git a/app/src/main/java/es/wolfi/app/passman/SettingValues.java b/app/src/main/java/es/wolfi/app/passman/SettingValues.java index 6f3af7a8f..b54a5faa6 100644 --- a/app/src/main/java/es/wolfi/app/passman/SettingValues.java +++ b/app/src/main/java/es/wolfi/app/passman/SettingValues.java @@ -43,7 +43,8 @@ public enum SettingValues { KEY_STORE_ENCRYPTION_KEY("key_store_encryption_key"), CREDENTIAL_LABEL_SORT("credential_label_sort"), CASE_INSENSITIVE_CREDENTIAL_LABEL_SORT("case_insensitive_credential_label_sort"), - RESTORE_CUSTOM_CREDENTIAL_SORT_ORDER("restore_custom_credential_sort_order"); + RESTORE_CUSTOM_CREDENTIAL_SORT_ORDER("restore_custom_credential_sort_order"), + COLOR_PASSWORD_DIGITS("color_password_digits"); private final String name; diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f78d3166f..6b745f709 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -20,4 +20,5 @@ #FF0000 #36FF0000 #7E4C819F + #1565C0 From 9b1b88fc1b07b908b9a617f0a39ed9e47b2bd5c1 Mon Sep 17 00:00:00 2001 From: Ryan Morov Date: Sun, 14 Jun 2026 22:29:51 -0400 Subject: [PATCH 2/2] Add settings toggle for password digit highlighting Add a checkbox in settings to enable or disable highlighting of digits in revealed passwords. Defaults to enabled. Wires the COLOR_PASSWORD_DIGITS preference through SettingsFragment and adds the corresponding strings. Signed-off-by: Ryan Morov --- .../app/passman/fragments/SettingsFragment.java | 4 ++++ app/src/main/res/layout/fragment_settings.xml | 14 ++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 20 insertions(+) diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java index f97ddd7ee..3bcc64ab3 100644 --- a/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java +++ b/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java @@ -98,6 +98,7 @@ public class SettingsFragment extends Fragment { EditText settings_password_generator_length_value; MaterialCheckBox enable_credential_list_icons_switch; + MaterialCheckBox settings_color_password_digits_switch; MaterialCheckBox enable_offline_cache_switch; TextView default_autofill_vault_title; @@ -163,6 +164,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, settings_password_generator_length_value = view.findViewById(R.id.settings_password_generator_length_value); enable_credential_list_icons_switch = view.findViewById(R.id.enable_credential_list_icons_switch); + settings_color_password_digits_switch = view.findViewById(R.id.settings_color_password_digits_switch); enable_offline_cache_switch = view.findViewById(R.id.enable_offline_cache_switch); default_autofill_vault_title = view.findViewById(R.id.default_autofill_vault_title); @@ -236,6 +238,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { } enable_credential_list_icons_switch.setChecked(settings.getBoolean(SettingValues.ENABLE_CREDENTIAL_LIST_ICONS.toString(), true)); + settings_color_password_digits_switch.setChecked(settings.getBoolean(SettingValues.COLOR_PASSWORD_DIGITS.toString(), true)); enable_offline_cache_switch.setChecked(settings.getBoolean(SettingValues.ENABLE_OFFLINE_CACHE.toString(), true)); Set> vaults = getVaultsEntrySet(); @@ -359,6 +362,7 @@ public void onClick(View view) { passwordGenerator.applyChanges(); settings.edit().putBoolean(SettingValues.ENABLE_CREDENTIAL_LIST_ICONS.toString(), enable_credential_list_icons_switch.isChecked()).commit(); + settings.edit().putBoolean(SettingValues.COLOR_PASSWORD_DIGITS.toString(), settings_color_password_digits_switch.isChecked()).commit(); settings.edit().putBoolean(SettingValues.ENABLE_OFFLINE_CACHE.toString(), enable_offline_cache_switch.isChecked()).commit(); settings.edit().putInt(SettingValues.CLEAR_CLIPBOARD_DELAY.toString(), Integer.parseInt(clear_clipboard_delay_value.getText().toString())).commit(); diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 7ef08b727..5b134ea2d 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -169,6 +169,20 @@ android:checked="false" android:text="@string/enable_credential_list_icons" /> + + + + Cancel Enable credential list icons Credential icon + Highlight password digits + Show digits in a revealed password in a different color to tell apart characters like 0 and O Clear clipboard (after seconds) Generate password Generate a random password and copy to clipboard