diff --git a/feature/registration/src/main/java/org/signal/registration/screens/PinVisualTransformation.kt b/feature/registration/src/main/java/org/signal/registration/screens/PinVisualTransformation.kt new file mode 100644 index 0000000000..103a73ad04 --- /dev/null +++ b/feature/registration/src/main/java/org/signal/registration/screens/PinVisualTransformation.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.registration.screens + +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation + +/** + * Masks input with bullets exactly like [PasswordVisualTransformation], but is intentionally a + * distinct type. Compose flags a field with password semantics whenever its transformation is a + * [PasswordVisualTransformation], which makes the system autofill framework offer to save/fill a + * password. We never want that prompt for PIN entry. + */ +object PinVisualTransformation : VisualTransformation { + private const val MASK = '•' + + override fun filter(text: AnnotatedString): TransformedText { + return TransformedText( + AnnotatedString(MASK.toString().repeat(text.text.length)), + OffsetMapping.Identity + ) + } +} diff --git a/feature/registration/src/main/java/org/signal/registration/screens/pincreation/PinCreationScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/pincreation/PinCreationScreen.kt index 92f8c1a9a6..a1a0088cab 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/pincreation/PinCreationScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/pincreation/PinCreationScreen.kt @@ -57,7 +57,6 @@ import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle @@ -71,6 +70,7 @@ import org.signal.core.ui.compose.Previews import org.signal.core.ui.compose.Scaffolds import org.signal.core.ui.compose.SignalIcons import org.signal.registration.R +import org.signal.registration.screens.PinVisualTransformation import org.signal.registration.screens.RegistrationScaffold import org.signal.registration.screens.TwoPaneRegistrationScaffold import org.signal.registration.screens.attachDebugLogHelper @@ -399,7 +399,7 @@ private fun PinInputField( imeAction = ImeAction.Done ), keyboardActions = KeyboardActions(onDone = { if (canSubmitPin) onSubmit() }), - visualTransformation = PasswordVisualTransformation() + visualTransformation = PinVisualTransformation ) } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/pinentry/PinEntryScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/pinentry/PinEntryScreen.kt index c8b9d8e333..df43bd73ca 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/pinentry/PinEntryScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/pinentry/PinEntryScreen.kt @@ -43,7 +43,6 @@ import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import org.signal.core.ui.compose.AllDevicePreviews @@ -52,6 +51,7 @@ import org.signal.core.ui.compose.Dialogs import org.signal.core.ui.compose.Previews import org.signal.core.ui.compose.SignalIcons import org.signal.registration.R +import org.signal.registration.screens.PinVisualTransformation import org.signal.registration.screens.RegistrationScaffold import org.signal.registration.screens.TwoPaneRegistrationScaffold import org.signal.registration.screens.attachDebugLogHelper @@ -326,7 +326,7 @@ private fun PinInputField( ), keyboardActions = KeyboardActions(onDone = { if (canSubmitPin) onSubmit() }), isError = state.triesRemaining != null, - visualTransformation = PasswordVisualTransformation() + visualTransformation = PinVisualTransformation ) if (state.triesRemaining != null) {