From 30a9d76d7017894a6895018a650367fdeebd4341 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 6 Dec 2023 15:36:42 -0600 Subject: [PATCH] Add visual transformation for password character colors (#337) --- .../components/BitwardenPasswordField.kt | 9 +-- .../NonLetterColorVisualTransformation.kt | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/components/util/NonLetterColorVisualTransformation.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenPasswordField.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenPasswordField.kt index 060a8314ff..c283e421ac 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenPasswordField.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/BitwardenPasswordField.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation /** * Represents a Bitwarden-styled password field that hoists show/hide password state to the caller. @@ -68,10 +69,10 @@ fun BitwardenPasswordField( label = { Text(text = label) }, value = value, onValueChange = onValueChange, - visualTransformation = if (showPassword) { - VisualTransformation.None - } else { - PasswordVisualTransformation() + visualTransformation = when { + !showPassword -> PasswordVisualTransformation() + readOnly -> nonLetterColorVisualTransformation() + else -> VisualTransformation.None }, singleLine = singleLine, readOnly = readOnly, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/util/NonLetterColorVisualTransformation.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/util/NonLetterColorVisualTransformation.kt new file mode 100644 index 0000000000..fed4824451 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/util/NonLetterColorVisualTransformation.kt @@ -0,0 +1,56 @@ +package com.x8bit.bitwarden.ui.platform.components.util + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.withStyle + +/** + * Returns the [VisualTransformation] that alters the output of the text in an input field by + * applying different colors to the digits and special characters, letters will remain unaffected. + */ +@Composable +fun nonLetterColorVisualTransformation(): VisualTransformation = + NonLetterColorVisualTransformation( + digitColor = MaterialTheme.colorScheme.primary, + specialCharacterColor = MaterialTheme.colorScheme.error, + ) + +/** + * Alters the visual output of the text in an input field. + * + * All numbers in the text will have the [digitColor] applied to it and special characters will + * have the [specialCharacterColor] applied. + */ +private class NonLetterColorVisualTransformation( + private val digitColor: Color, + private val specialCharacterColor: Color, +) : VisualTransformation { + + override fun filter(text: AnnotatedString): TransformedText = + TransformedText( + buildTransformedAnnotatedString(text.toString()), + OffsetMapping.Identity, + ) + + private fun buildTransformedAnnotatedString(text: String): AnnotatedString { + val builder = AnnotatedString.Builder() + text.toCharArray().forEach { char -> + when { + char.isDigit() -> builder.withStyle(SpanStyle(color = digitColor)) { append(char) } + + !char.isLetter() -> { + builder.withStyle(SpanStyle(color = specialCharacterColor)) { append(char) } + } + + else -> builder.append(char) + } + } + return builder.toAnnotatedString() + } +}