From 941ac8bd05627711b7f062fd5de3c688abd58ca3 Mon Sep 17 00:00:00 2001 From: Damian Kaczmarek Date: Thu, 10 Oct 2024 15:31:55 +0200 Subject: [PATCH] fix: disable autocorrect for all email text fields --- .../create/common/handle/UsernameTextField.kt | 7 ++- .../details/CreateAccountDetailsScreen.kt | 7 ++- .../create/email/CreateAccountEmailScreen.kt | 5 +- .../login/email/LoginEmailScreen.kt | 17 ++++-- .../authentication/login/email/ProxyScreen.kt | 11 ++-- .../ui/common/textfield/WireTextField.kt | 58 ++++++++++++++++--- .../email/updateEmail/ChangeEmailScreen.kt | 21 +++++-- 7 files changed, 92 insertions(+), 34 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/create/common/handle/UsernameTextField.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/create/common/handle/UsernameTextField.kt index 07c8c8e93c0..b97552b9b8c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/create/common/handle/UsernameTextField.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/create/common/handle/UsernameTextField.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.res.stringResource import com.wire.android.R import com.wire.android.ui.common.ShakeAnimation import com.wire.android.ui.common.error.CoreFailureErrorDialog -import com.wire.android.ui.common.textfield.DefaultEmail +import com.wire.android.ui.common.textfield.DefaultEmailDone import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState import com.wire.android.ui.common.textfield.forceLowercase @@ -45,6 +45,7 @@ fun UsernameTextField( errorState: HandleUpdateErrorState, username: TextFieldState, onErrorDismiss: () -> Unit, + modifier: Modifier = Modifier, ) { if (errorState is HandleUpdateErrorState.DialogError.GenericError) { CoreFailureErrorDialog(errorState.coreFailure, onErrorDismiss) @@ -78,9 +79,9 @@ fun UsernameTextField( WireTextFieldState.Error(stringResource(id = R.string.create_account_username_description)) } else WireTextFieldState.Default, descriptionText = stringResource(id = R.string.create_account_username_description), - keyboardOptions = KeyboardOptions.DefaultEmail, + keyboardOptions = KeyboardOptions.DefaultEmailDone, onKeyboardAction = { keyboardController?.hide() }, - modifier = Modifier.padding(horizontal = MaterialTheme.wireDimensions.spacing16x) + modifier = modifier.then(Modifier.padding(horizontal = MaterialTheme.wireDimensions.spacing16x)) ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/create/details/CreateAccountDetailsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/create/details/CreateAccountDetailsScreen.kt index 5693e00a434..01b5f779af6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/create/details/CreateAccountDetailsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/create/details/CreateAccountDetailsScreen.kt @@ -147,7 +147,12 @@ private fun DetailsContent( .padding(internalPadding) .fillMaxHeight() ) { - val keyboardOptions = KeyboardOptions(KeyboardCapitalization.Words, true, KeyboardType.Text, ImeAction.Next) + val keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Words, + autoCorrectEnabled = true, + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next, + ) val keyboardController = LocalSoftwareKeyboardController.current val firstNameFocusRequester = remember { FocusRequester() } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/create/email/CreateAccountEmailScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/create/email/CreateAccountEmailScreen.kt index a67bfc92854..fa1fc809bfc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/create/email/CreateAccountEmailScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/create/email/CreateAccountEmailScreen.kt @@ -45,8 +45,6 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource 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.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle @@ -71,6 +69,7 @@ import com.wire.android.ui.common.button.WirePrimaryButton import com.wire.android.ui.common.button.WireSecondaryButton import com.wire.android.ui.common.error.CoreFailureErrorDialog import com.wire.android.ui.common.scaffold.WireScaffold +import com.wire.android.ui.common.textfield.DefaultEmailDone import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar @@ -173,7 +172,7 @@ private fun EmailContent( labelText = stringResource(R.string.create_account_email_label), state = if (state.error is CreateAccountEmailViewState.EmailError.None) WireTextFieldState.Default else WireTextFieldState.Error(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Done), + keyboardOptions = KeyboardOptions.DefaultEmailDone, onKeyboardAction = { keyboardController?.hide() }, modifier = Modifier .padding(horizontal = MaterialTheme.wireDimensions.spacing16x) diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt index d4fa8734ff1..5f1351dfc9b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt @@ -49,7 +49,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import com.wire.android.R @@ -61,6 +60,7 @@ import com.wire.android.ui.common.button.WireButtonState import com.wire.android.ui.common.button.WirePrimaryButton import com.wire.android.ui.common.colorsScheme import com.wire.android.ui.common.rememberBottomBarElevationState +import com.wire.android.ui.common.textfield.DefaultEmailNext import com.wire.android.ui.common.textfield.DefaultPassword import com.wire.android.ui.common.textfield.WireAutoFillType import com.wire.android.ui.common.textfield.WirePasswordTextField @@ -224,10 +224,10 @@ private fun LoginEmailContent( @Composable private fun UserIdentifierInput( - modifier: Modifier, userIdentifierState: TextFieldState, error: String?, isEnabled: Boolean, + modifier: Modifier = Modifier, ) { WireTextField( autoFillType = WireAutoFillType.Login, @@ -239,14 +239,14 @@ private fun UserIdentifierInput( error != null -> WireTextFieldState.Error(error) else -> WireTextFieldState.Default }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next), + keyboardOptions = KeyboardOptions.DefaultEmailNext, modifier = modifier.testTag("emailField"), testTag = "userIdentifierInput" ) } @Composable -private fun PasswordInput(modifier: Modifier, passwordState: TextFieldState) { +private fun PasswordInput(passwordState: TextFieldState, modifier: Modifier = Modifier) { val keyboardController = LocalSoftwareKeyboardController.current WirePasswordTextField( textState = passwordState, @@ -259,7 +259,7 @@ private fun PasswordInput(modifier: Modifier, passwordState: TextFieldState) { } @Composable -private fun ForgotPasswordLabel(modifier: Modifier, forgotPasswordUrl: String) { +private fun ForgotPasswordLabel(forgotPasswordUrl: String, modifier: Modifier = Modifier) { Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) { val context = LocalContext.current Text( @@ -287,7 +287,12 @@ private fun openForgotPasswordPage(context: Context, forgotPasswordUrl: String) } @Composable -private fun LoginButton(modifier: Modifier, loading: Boolean, enabled: Boolean, onClick: () -> Unit) { +private fun LoginButton( + loading: Boolean, + enabled: Boolean, + modifier: Modifier = Modifier, + onClick: () -> Unit, +) { val interactionSource = remember { MutableInteractionSource() } Column(modifier = modifier) { val text = if (loading) stringResource(R.string.label_logging_in) else stringResource(R.string.label_login) diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/ProxyScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/ProxyScreen.kt index 6fba32a7d1c..87d628b16bd 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/ProxyScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/ProxyScreen.kt @@ -33,11 +33,11 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.Dp import com.wire.android.R import com.wire.android.ui.authentication.login.LoginState import com.wire.android.ui.common.colorsScheme +import com.wire.android.ui.common.textfield.DefaultEmailNext import com.wire.android.ui.common.textfield.DefaultPassword import com.wire.android.ui.common.textfield.WirePasswordTextField import com.wire.android.ui.common.textfield.WireTextField @@ -107,25 +107,22 @@ fun ProxyScreen( @Composable private fun ProxyIdentifierInput( - modifier: Modifier, proxyIdentifierState: TextFieldState, error: String?, + modifier: Modifier = Modifier, ) { WireTextField( textState = proxyIdentifierState, placeholderText = stringResource(R.string.login_user_identifier_placeholder), labelText = stringResource(R.string.login_proxy_identifier_label), state = if (error != null) WireTextFieldState.Error(error) else WireTextFieldState.Default, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next), + keyboardOptions = KeyboardOptions.DefaultEmailNext, modifier = modifier.testTag("emailField") ) } @Composable -private fun ProxyPasswordInput( - modifier: Modifier, - proxyPasswordState: TextFieldState -) { +private fun ProxyPasswordInput(proxyPasswordState: TextFieldState, modifier: Modifier = Modifier) { val keyboardController = LocalSoftwareKeyboardController.current WirePasswordTextField( textState = proxyPasswordState, diff --git a/app/src/main/kotlin/com/wire/android/ui/common/textfield/WireTextField.kt b/app/src/main/kotlin/com/wire/android/ui/common/textfield/WireTextField.kt index 100d1793f6c..dfd4419ec31 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/textfield/WireTextField.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/textfield/WireTextField.kt @@ -91,8 +91,19 @@ internal fun WireTextField( onSelectedLineIndexChanged: (Int) -> Unit = { }, onLineBottomYCoordinateChanged: (Float) -> Unit = { }, onTap: ((Offset) -> Unit)? = null, - testTag: String = String.EMPTY + testTag: String = String.EMPTY, + validateKeyboardOptions: Boolean = true, ) { + if(validateKeyboardOptions){ + assert(keyboardOptions.keyboardType != KeyboardType.Email || keyboardOptions == KeyboardOptions.DefaultEmailDone || keyboardOptions == KeyboardOptions.DefaultEmailNext) { + "For email text fields use KeyboardOptions.DefaultEmailDone or KeyboardOptions.DefaultEmailNext. If you want to use a custom KeyboardOptions, set validateKeyboardOptions to false." + } + + assert(keyboardOptions.keyboardType != KeyboardType.Password) { + "Use WirePasswordTextField for passwords. If you want to use a custom KeyboardOptions, set validateKeyboardOptions to false." + } + } + WireTextFieldLayout( shouldShowPlaceholder = textState.text.isEmpty(), placeholderText = placeholderText, @@ -108,13 +119,21 @@ internal fun WireTextField( inputMinHeight = inputMinHeight, shape = shape, colors = colors, - modifier = modifier.then(autoFillModifier(autoFillType, textState::setTextAndPlaceCursorAtEnd)), + modifier = modifier.then( + autoFillModifier( + autoFillType, + textState::setTextAndPlaceCursorAtEnd + ) + ), onTap = onTap, testTag = testTag, innerBasicTextField = { decorator, textFieldModifier -> BasicTextField( state = textState, - textStyle = textStyle.copy(color = colors.textColor(state = state).value, textDirection = TextDirection.ContentOrLtr), + textStyle = textStyle.copy( + color = colors.textColor(state = state).value, + textDirection = TextDirection.ContentOrLtr + ), keyboardOptions = keyboardOptions, onKeyboardAction = onKeyboardAction, lineLimits = lineLimits, @@ -127,7 +146,11 @@ internal fun WireTextField( interactionSource = interactionSource, modifier = textFieldModifier, decorator = decorator, - onTextLayout = onTextLayout(textState, onSelectedLineIndexChanged, onLineBottomYCoordinateChanged) + onTextLayout = onTextLayout( + textState, + onSelectedLineIndexChanged, + onLineBottomYCoordinateChanged + ) ) } ) @@ -156,13 +179,22 @@ val KeyboardOptions.Companion.DefaultText: KeyboardOptions ) @Stable -val KeyboardOptions.Companion.DefaultEmail: KeyboardOptions - get() = Default.copy( +val KeyboardOptions.Companion.DefaultEmailDone: KeyboardOptions + get() = defaultEmail(ImeAction.Done) + +@Stable +val KeyboardOptions.Companion.DefaultEmailNext: KeyboardOptions + get() = defaultEmail(ImeAction.Next) + +@Stable +private fun KeyboardOptions.Companion.defaultEmail(imeAction: ImeAction): KeyboardOptions { + return Default.copy( keyboardType = KeyboardType.Email, - imeAction = ImeAction.Done, + imeAction = imeAction, autoCorrectEnabled = false, capitalization = KeyboardCapitalization.None, ) +} @PreviewMultipleThemes @Composable @@ -191,8 +223,16 @@ fun PreviewWireTextFieldDenseSearch() = WireTheme { WireTextField( textState = rememberTextFieldState("text"), placeholderText = "Search", - leadingIcon = { IconButton(modifier = Modifier.height(40.dp), onClick = {}) { Icon(Icons.Filled.Search, "") } }, - trailingIcon = { IconButton(modifier = Modifier.height(40.dp), onClick = {}) { Icon(Icons.Filled.Close, "") } }, + leadingIcon = { + IconButton( + modifier = Modifier.height(40.dp), + onClick = {}) { Icon(Icons.Filled.Search, "") } + }, + trailingIcon = { + IconButton( + modifier = Modifier.height(40.dp), + onClick = {}) { Icon(Icons.Filled.Close, "") } + }, inputMinHeight = 40.dp, modifier = Modifier.padding(16.dp) ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/account/email/updateEmail/ChangeEmailScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/account/email/updateEmail/ChangeEmailScreen.kt index 24f17c6ff98..a58a1e0a585 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/account/email/updateEmail/ChangeEmailScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/account/email/updateEmail/ChangeEmailScreen.kt @@ -52,7 +52,7 @@ import com.wire.android.ui.common.button.WirePrimaryButton import com.wire.android.ui.common.rememberBottomBarElevationState import com.wire.android.ui.common.rememberTopBarElevationState import com.wire.android.ui.common.scaffold.WireScaffold -import com.wire.android.ui.common.textfield.DefaultEmail +import com.wire.android.ui.common.textfield.DefaultEmailDone import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState import com.wire.android.ui.common.textfield.patternWithCallback @@ -75,8 +75,15 @@ fun ChangeEmailScreen( when (val flowState = viewModel.state.flowState) { is ChangeEmailState.FlowState.NoChange, is ChangeEmailState.FlowState.Error.SelfUserNotFound -> navigator.navigateBack() + is ChangeEmailState.FlowState.Success -> - navigator.navigate(NavigationCommand(VerifyEmailScreenDestination(flowState.newEmail), BackStackMode.REMOVE_CURRENT)) + navigator.navigate( + NavigationCommand( + VerifyEmailScreenDestination(flowState.newEmail), + BackStackMode.REMOVE_CURRENT + ) + ) + else -> ChangeEmailContent( textState = viewModel.textState, @@ -93,9 +100,10 @@ fun ChangeEmailContent( state: ChangeEmailState, onSaveClicked: () -> Unit, onBackPressed: () -> Unit, + modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() - WireScaffold(topBar = { + WireScaffold(modifier = modifier, topBar = { WireCenterAlignedTopAppBar( elevation = scrollState.rememberTopBarElevationState().value, onNavigationPressed = onBackPressed, @@ -133,9 +141,12 @@ fun ChangeEmailContent( WireTextField( textState = textState, labelText = stringResource(R.string.email_label).uppercase(), - inputTransformation = InputTransformation.patternWithCallback(Patterns.EMAIL_ADDRESS, animate), + inputTransformation = InputTransformation.patternWithCallback( + Patterns.EMAIL_ADDRESS, + animate + ), state = computeEmailErrorState(state.flowState), - keyboardOptions = KeyboardOptions.DefaultEmail, + keyboardOptions = KeyboardOptions.DefaultEmailDone, onKeyboardAction = { keyboardController?.hide() }, modifier = Modifier.padding( horizontal = MaterialTheme.wireDimensions.spacing16x