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 c5930172..7178b6de 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 6f3af7a8..b54a5faa 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/java/es/wolfi/app/passman/fragments/SettingsFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java index f97ddd7e..3bcc64ab 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 7ef08b72..5b134ea2 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" /> + + + + #FF0000 #36FF0000 #7E4C819F + #1565C0 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c57c683..38b97709 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,6 +66,8 @@ 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