From 46a57b77464370086c5390de7f8ceb455228b3e4 Mon Sep 17 00:00:00 2001 From: Mohamad Jaara Date: Thu, 7 Sep 2023 11:24:59 +0200 Subject: [PATCH] fix: app crash when clicking next on create account screen (#2183) (#2186) --- .../create/common/handle/UsernameTextField.kt | 8 +- .../details/CreateAccountDetailsScreen.kt | 234 ++++++++---------- .../create/email/CreateAccountEmailScreen.kt | 8 +- .../devices/register/RegisterDeviceScreen.kt | 3 +- .../devices/remove/RemoveDeviceDialog.kt | 3 +- .../login/email/LoginEmailScreen.kt | 3 +- .../authentication/login/email/ProxyScreen.kt | 2 +- .../com/wire/android/ui/common/WireDialog.kt | 8 +- .../ui/common/textfield/AutoFillTextField.kt | 33 ++- .../common/textfield/WirePasswordTextField.kt | 127 ++++++---- .../CreatePasswordProtectedGuestLinkScreen.kt | 6 +- .../dialog/create/CreateBackupDialogs.kt | 5 +- .../dialog/restore/RestoreBackupDialogs.kt | 3 +- .../JoinConversationViaDeepLinkDialog.kt | 4 +- 14 files changed, 240 insertions(+), 207 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 315763e0970..7392115f00a 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 @@ -25,7 +25,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.autofill.AutofillType import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -35,9 +34,8 @@ import androidx.compose.ui.text.input.TextFieldValue 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.AutoFillTextField +import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState -import com.wire.android.ui.common.textfield.clearAutofillTree import com.wire.android.ui.theme.wireDimensions @OptIn(ExperimentalComposeUiApi::class) @@ -50,7 +48,6 @@ fun UsernameTextField( onUsernameChange: (TextFieldValue) -> Unit, onUsernameErrorAnimated: () -> Unit ) { - clearAutofillTree() if (errorState is HandleUpdateErrorState.DialogError.GenericError) { CoreFailureErrorDialog(errorState.coreFailure, onErrorDismiss) } @@ -61,8 +58,7 @@ fun UsernameTextField( animate() onUsernameErrorAnimated() } - AutoFillTextField( - autofillTypes = listOf(AutofillType.Username), + WireTextField( value = username, onValueChange = onUsernameChange, placeholderText = stringResource(R.string.create_account_username_placeholder), 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 787ded5cb93..08f85d6a0c6 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 @@ -25,11 +25,10 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface @@ -37,12 +36,12 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -69,14 +68,12 @@ import com.wire.android.ui.common.rememberTopBarElevationState import com.wire.android.ui.common.textfield.WirePasswordTextField import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState -import com.wire.android.ui.common.textfield.clearAutofillTree import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar import com.wire.android.ui.destinations.CreateAccountCodeScreenDestination import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography import com.wire.kalium.logic.configuration.server.ServerConfig -import kotlinx.coroutines.launch @CreatePersonalAccountNavGraph @CreateTeamAccountNavGraph @@ -86,7 +83,6 @@ fun CreateAccountDetailsScreen( navigator: Navigator, createAccountDetailsViewModel: CreateAccountDetailsViewModel = hiltViewModel() ) { - clearAutofillTree() with(createAccountDetailsViewModel) { fun navigateToCodeScreen() = navigator.navigate( NavigationCommand( @@ -143,7 +139,6 @@ private fun DetailsContent( serverConfig: ServerConfig.Links ) { val scrollState = rememberScrollState() - val coroutineScope = rememberCoroutineScope() Scaffold( topBar = { WireCenterAlignedTopAppBar( @@ -166,58 +161,82 @@ private fun DetailsContent( .padding(internalPadding) .fillMaxHeight() ) { - val listState = rememberLazyListState() val keyboardOptions = KeyboardOptions(KeyboardCapitalization.Words, true, KeyboardType.Text, ImeAction.Next) val keyboardController = LocalSoftwareKeyboardController.current val focusRequesterFirstName = remember { FocusRequester() } val focusRequesterLastName = remember { FocusRequester() } val focusRequesterTeamName = remember { FocusRequester() } - LazyColumn( - state = listState, + Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, - modifier = Modifier.weight(1f) + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) ) { - item { - Text( - text = stringResource(R.string.create_personal_account_details_text), - style = MaterialTheme.wireTypography.body01, - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = MaterialTheme.wireDimensions.spacing16x, - vertical = MaterialTheme.wireDimensions.spacing24x - ) - ) - } - item { - WireTextField( - value = state.firstName, - onValueChange = onFirstNameChange, - placeholderText = stringResource(R.string.create_account_details_first_name_placeholder), - labelText = stringResource(R.string.create_account_details_first_name_label), - labelMandatoryIcon = true, - state = WireTextFieldState.Default, - keyboardOptions = keyboardOptions, - modifier = Modifier - .padding( - start = MaterialTheme.wireDimensions.spacing16x, - end = MaterialTheme.wireDimensions.spacing16x, - bottom = MaterialTheme.wireDimensions.spacing16x - ) - .focusRequester(focusRequesterFirstName) - .testTag("firstName"), - ) + val requestFocus = remember<(Offset) -> Unit> { + { _ -> + keyboardController?.show() + focusRequesterTeamName.requestFocus() + } } - item { + Text( + text = stringResource(R.string.create_personal_account_details_text), + style = MaterialTheme.wireTypography.body01, + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = MaterialTheme.wireDimensions.spacing16x, + vertical = MaterialTheme.wireDimensions.spacing24x + ) + ) + + WireTextField( + value = state.firstName, + onValueChange = onFirstNameChange, + placeholderText = stringResource(R.string.create_account_details_first_name_placeholder), + labelText = stringResource(R.string.create_account_details_first_name_label), + labelMandatoryIcon = true, + state = WireTextFieldState.Default, + keyboardOptions = keyboardOptions, + modifier = Modifier + .padding( + start = MaterialTheme.wireDimensions.spacing16x, + end = MaterialTheme.wireDimensions.spacing16x, + bottom = MaterialTheme.wireDimensions.spacing16x + ) + .focusRequester(focusRequesterFirstName) + .testTag("firstName"), + ) + + WireTextField( + value = state.lastName, + onValueChange = onLastNameChange, + placeholderText = stringResource(R.string.create_account_details_last_name_placeholder), + labelText = stringResource(R.string.create_account_details_last_name_label), + labelMandatoryIcon = true, + state = WireTextFieldState.Default, + keyboardOptions = keyboardOptions, + modifier = Modifier + .padding( + start = MaterialTheme.wireDimensions.spacing16x, + end = MaterialTheme.wireDimensions.spacing16x, + bottom = MaterialTheme.wireDimensions.spacing16x + ) + .focusRequester(focusRequesterLastName) + .testTag("lastName"), + shouldDetectTaps = true, + onTap = requestFocus + ) + + if (state.type == CreateAccountFlowType.CreateTeam) { WireTextField( - value = state.lastName, - onValueChange = onLastNameChange, - placeholderText = stringResource(R.string.create_account_details_last_name_placeholder), - labelText = stringResource(R.string.create_account_details_last_name_label), + value = state.teamName, + onValueChange = onTeamNameChange, + placeholderText = stringResource(R.string.create_account_details_team_name_placeholder), + labelText = stringResource(R.string.create_account_details_team_name_label), labelMandatoryIcon = true, state = WireTextFieldState.Default, keyboardOptions = keyboardOptions, @@ -227,93 +246,56 @@ private fun DetailsContent( end = MaterialTheme.wireDimensions.spacing16x, bottom = MaterialTheme.wireDimensions.spacing16x ) - .focusRequester(focusRequesterLastName) - .testTag("lastName"), + .testTag("teamName") + .focusRequester(focusRequesterTeamName), shouldDetectTaps = true, - onTap = { - coroutineScope.launch { - keyboardController?.show() - focusRequesterLastName.requestFocus() - listState.animateScrollToItem(1) - } - } + onTap = requestFocus ) } - item { - if (state.type == CreateAccountFlowType.CreateTeam) { - WireTextField( - value = state.teamName, - onValueChange = onTeamNameChange, - placeholderText = stringResource(R.string.create_account_details_team_name_placeholder), - labelText = stringResource(R.string.create_account_details_team_name_label), - labelMandatoryIcon = true, - state = WireTextFieldState.Default, - keyboardOptions = keyboardOptions, - modifier = Modifier - .padding( - start = MaterialTheme.wireDimensions.spacing16x, - end = MaterialTheme.wireDimensions.spacing16x, - bottom = MaterialTheme.wireDimensions.spacing16x - ) - .testTag("teamName") - .focusRequester(focusRequesterTeamName), - shouldDetectTaps = true, - onTap = { - coroutineScope.launch { - keyboardController?.show() - focusRequesterTeamName.requestFocus() - listState.animateScrollToItem(2) - } - } - ) - } - } - - item { - WirePasswordTextField( - value = state.password, - onValueChange = onPasswordChange, - labelMandatoryIcon = true, - descriptionText = stringResource(R.string.create_account_details_password_description), - imeAction = ImeAction.Next, - autofillTypes = listOf(), - modifier = Modifier - .padding(horizontal = MaterialTheme.wireDimensions.spacing16x) - .testTag("password"), - state = if (state.error is CreateAccountDetailsViewState.DetailsError.TextFieldError.InvalidPasswordError) { - WireTextFieldState.Error() - } else { - WireTextFieldState.Default - } - ) - } + WirePasswordTextField( + value = state.password, + onValueChange = onPasswordChange, + labelMandatoryIcon = true, + descriptionText = stringResource(R.string.create_account_details_password_description), + imeAction = ImeAction.Next, + modifier = Modifier + .padding(horizontal = MaterialTheme.wireDimensions.spacing16x) + .testTag("password"), + state = if (state.error is CreateAccountDetailsViewState.DetailsError.TextFieldError.InvalidPasswordError) { + WireTextFieldState.Error() + } else { + WireTextFieldState.Default + }, + autofill = false, + onTap = requestFocus + ) - item { - WirePasswordTextField( - value = state.confirmPassword, - onValueChange = onConfirmPasswordChange, - labelText = stringResource(R.string.create_account_details_confirm_password_label), - labelMandatoryIcon = true, - imeAction = ImeAction.Done, - keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), - autofillTypes = listOf(), - modifier = Modifier - .padding( - horizontal = MaterialTheme.wireDimensions.spacing16x, - vertical = MaterialTheme.wireDimensions.spacing16x - ) - .testTag("confirmPassword"), - state = if (state.error is CreateAccountDetailsViewState.DetailsError.TextFieldError) when (state.error) { - CreateAccountDetailsViewState.DetailsError.TextFieldError.PasswordsNotMatchingError -> - WireTextFieldState.Error(stringResource(id = R.string.create_account_details_password_not_matching_error)) + WirePasswordTextField( + value = state.confirmPassword, + onValueChange = onConfirmPasswordChange, + labelText = stringResource(R.string.create_account_details_confirm_password_label), + labelMandatoryIcon = true, + imeAction = ImeAction.Done, + keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), + modifier = Modifier + .padding( + horizontal = MaterialTheme.wireDimensions.spacing16x, + vertical = MaterialTheme.wireDimensions.spacing16x + ) + .testTag("confirmPassword"), + state = if (state.error is CreateAccountDetailsViewState.DetailsError.TextFieldError) when (state.error) { + CreateAccountDetailsViewState.DetailsError.TextFieldError.PasswordsNotMatchingError -> + WireTextFieldState.Error(stringResource(id = R.string.create_account_details_password_not_matching_error)) - CreateAccountDetailsViewState.DetailsError.TextFieldError.InvalidPasswordError -> - WireTextFieldState.Error(stringResource(id = R.string.create_account_details_password_error)) - } else WireTextFieldState.Default - ) - } + CreateAccountDetailsViewState.DetailsError.TextFieldError.InvalidPasswordError -> + WireTextFieldState.Error(stringResource(id = R.string.create_account_details_password_error)) + } else WireTextFieldState.Default, + autofill = false, + onTap = requestFocus + ) } + LaunchedEffect(Unit) { focusRequesterFirstName.requestFocus() keyboardController?.show() 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 9d1747750c6..66a226503d1 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 @@ -41,7 +41,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.autofill.AutofillType import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext @@ -77,9 +76,8 @@ import com.wire.android.ui.common.button.WireButtonState 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.textfield.AutoFillTextField +import com.wire.android.ui.common.textfield.WireTextField import com.wire.android.ui.common.textfield.WireTextFieldState -import com.wire.android.ui.common.textfield.clearAutofillTree import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar import com.wire.android.ui.destinations.CreateAccountDetailsScreenDestination import com.wire.android.ui.destinations.LoginScreenDestination @@ -139,7 +137,6 @@ private fun EmailContent( tosUrl: String, serverConfig: ServerConfig.Links ) { - clearAutofillTree() val focusRequester = remember { FocusRequester() } Scaffold(topBar = { @@ -174,8 +171,7 @@ private fun EmailContent( ) .testTag("createTeamText") ) - AutoFillTextField( - autofillTypes = listOf(AutofillType.EmailAddress), + WireTextField( value = state.email, onValueChange = onEmailChange, placeholderText = stringResource(R.string.create_account_email_placeholder), diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/register/RegisterDeviceScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/register/RegisterDeviceScreen.kt index 3405aee3d6f..0e620c51b7d 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/register/RegisterDeviceScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/register/RegisterDeviceScreen.kt @@ -197,7 +197,8 @@ private fun PasswordTextField(state: RegisterDeviceState, onPasswordChange: (Tex keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), modifier = Modifier .padding(horizontal = MaterialTheme.wireDimensions.spacing16x) - .testTag("password field") + .testTag("password field"), + autofill = true ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/remove/RemoveDeviceDialog.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/remove/RemoveDeviceDialog.kt index f3f726e2137..23e28368388 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/remove/RemoveDeviceDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/remove/RemoveDeviceDialog.kt @@ -84,7 +84,8 @@ fun RemoveDeviceDialog( modifier = Modifier .focusRequester(focusRequester) .padding(bottom = MaterialTheme.wireDimensions.spacing8x) - .testTag("remove device password field") + .testTag("remove device password field"), + autofill = true ) LaunchedEffect(Unit) { // executed only once when showing the dialog focusRequester.requestFocus() 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 4decddc22d1..56ba1bd9349 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 @@ -234,7 +234,8 @@ private fun PasswordInput(modifier: Modifier, password: TextFieldValue, onPasswo onValueChange = onPasswordChange, imeAction = ImeAction.Done, keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), - modifier = modifier.testTag("passwordField") + modifier = modifier.testTag("passwordField"), + autofill = true ) } 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 ea2e35b7490..8b49292885c 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 @@ -152,7 +152,7 @@ private fun ProxyPasswordInput(modifier: Modifier, proxyPassword: TextFieldValue labelText = stringResource(R.string.label_proxy_password), keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), modifier = modifier.testTag("passwordField"), - autofillTypes = listOf() + autofill = false ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/WireDialog.kt b/app/src/main/kotlin/com/wire/android/ui/common/WireDialog.kt index 18632ba033e..fec85a7d299 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/WireDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/WireDialog.kt @@ -289,7 +289,9 @@ fun PreviewWireDialog() { ) { WirePasswordTextField( value = password, - onValueChange = { password = it }) + onValueChange = { password = it }, + autofill = false + ) } } } @@ -337,7 +339,9 @@ fun PreviewWireDialogWith2OptionButtons() { ) { WirePasswordTextField( value = password, - onValueChange = { password = it }) + onValueChange = { password = it }, + autofill = true + ) } } } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/textfield/AutoFillTextField.kt b/app/src/main/kotlin/com/wire/android/ui/common/textfield/AutoFillTextField.kt index 1ee7ae7575d..7ef00e2fe08 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/textfield/AutoFillTextField.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/textfield/AutoFillTextField.kt @@ -29,9 +29,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.autofill.Autofill import androidx.compose.ui.autofill.AutofillNode import androidx.compose.ui.autofill.AutofillType import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned @@ -70,7 +72,8 @@ internal fun AutoFillTextField( inputMinHeight: Dp = MaterialTheme.wireDimensions.textFieldMinHeight, shape: Shape = RoundedCornerShape(MaterialTheme.wireDimensions.textFieldCornerSize), colors: WireTextFieldColors = wireTextFieldColors(), - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onTap: (Offset) -> Unit = { }, ) { val autofillNode = AutofillNode( autofillTypes = autofillTypes, @@ -103,19 +106,27 @@ internal fun AutoFillTextField( shape = shape, colors = colors, modifier = modifier - .onGloballyPositioned { autofillNode.boundingBox = it.boundsInWindow() } - .onFocusChanged { focusState -> - autofill?.run { - if (focusState.isFocused) { - requestAutofillForNode(autofillNode) - } else { - cancelAutofillForNode(autofillNode) - } - } - } + .fillBounds(autofillNode) + .defaultOnFocusAutoFill(autofill, autofillNode), + onTap = onTap ) } +@OptIn(ExperimentalComposeUiApi::class) +fun Modifier.fillBounds(autofillNode: AutofillNode) = this.then( + Modifier.onGloballyPositioned { autofillNode.boundingBox = it.boundsInWindow() } +) + +@OptIn(ExperimentalComposeUiApi::class) +fun Modifier.defaultOnFocusAutoFill(autofill: Autofill?, autofillNode: AutofillNode): Modifier = + then(Modifier.onFocusChanged { focusState -> + if (focusState.isFocused) { + autofill?.requestAutofillForNode(autofillNode) + } else { + autofill?.cancelAutofillForNode(autofillNode) + } + }) + @OptIn(ExperimentalComposeUiApi::class) @Composable fun clearAutofillTree() { diff --git a/app/src/main/kotlin/com/wire/android/ui/common/textfield/WirePasswordTextField.kt b/app/src/main/kotlin/com/wire/android/ui/common/textfield/WirePasswordTextField.kt index 7a93f0456e6..82c3f48044c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/textfield/WirePasswordTextField.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/textfield/WirePasswordTextField.kt @@ -40,6 +40,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.autofill.AutofillType +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -76,57 +77,99 @@ fun WirePasswordTextField( shape: Shape = RoundedCornerShape(16.dp), colors: WireTextFieldColors = wireTextFieldColors(), modifier: Modifier = Modifier, - autofillTypes: List = listOf(AutofillType.Password) + autofill: Boolean, + onTap: (Offset) -> Unit = { }, ) { var passwordVisibility by remember { mutableStateOf(false) } - AutoFillTextField( - autofillTypes = autofillTypes, - value = value, - onValueChange = onValueChange, - readOnly = readOnly, - singleLine = true, - maxLines = 1, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, autoCorrect = false, imeAction = imeAction), - keyboardActions = keyboardActions, - placeholderText = placeholderText, - labelText = labelText, - labelMandatoryIcon = labelMandatoryIcon, - descriptionText = descriptionText, - state = state, - interactionSource = interactionSource, - textStyle = textStyle, - placeholderTextStyle = placeHolderTextStyle, - inputMinHeight = inputMinHeight, - shape = shape, - colors = colors, - modifier = modifier, - visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), - trailingIcon = { - val image = if (passwordVisibility) Icons.Filled.Visibility else Icons.Filled.VisibilityOff - IconButton(onClick = { passwordVisibility = !passwordVisibility }) { - Icon( - imageVector = image, - contentDescription = stringResource( - if (!passwordVisibility) R.string.content_description_reveal_password - else R.string.content_description_hide_password - ), - modifier = Modifier - .size(20.dp) - .testTag("hidePassword") - ) - } - }, - ) -} + val keyBoardOption = remember { + KeyboardOptions(keyboardType = KeyboardType.Password, autoCorrect = false, imeAction = imeAction) + } + + val visualTransformation = remember(passwordVisibility) { + if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation() + } + + val icon = remember(passwordVisibility) { + if (passwordVisibility) Icons.Filled.Visibility else Icons.Filled.VisibilityOff + } + + val iconButton = @Composable { + IconButton(onClick = { passwordVisibility = !passwordVisibility }) { + Icon( + imageVector = icon, + contentDescription = stringResource( + if (!passwordVisibility) R.string.content_description_reveal_password + else R.string.content_description_hide_password + ), + modifier = Modifier + .size(20.dp) + .testTag("hidePassword") + ) + } + } + + if (autofill) { + AutoFillTextField( + value = value, + onValueChange = onValueChange, + readOnly = readOnly, + singleLine = true, + maxLines = 1, + keyboardOptions = keyBoardOption, + keyboardActions = keyboardActions, + placeholderText = placeholderText, + labelText = labelText, + labelMandatoryIcon = labelMandatoryIcon, + descriptionText = descriptionText, + state = state, + interactionSource = interactionSource, + textStyle = textStyle, + placeholderTextStyle = placeHolderTextStyle, + inputMinHeight = inputMinHeight, + shape = shape, + colors = colors, + modifier = modifier, + visualTransformation = visualTransformation, + trailingIcon = iconButton, + autofillTypes = listOf(AutofillType.Password), + onTap = onTap + ) + } else { + WireTextField( + value = value, + onValueChange = onValueChange, + readOnly = readOnly, + singleLine = true, + maxLines = 1, + keyboardOptions = keyBoardOption, + keyboardActions = keyboardActions, + placeholderText = placeholderText, + labelText = labelText, + labelMandatoryIcon = labelMandatoryIcon, + descriptionText = descriptionText, + state = state, + interactionSource = interactionSource, + textStyle = textStyle, + placeholderTextStyle = placeHolderTextStyle, + inputMinHeight = inputMinHeight, + shape = shape, + colors = colors, + modifier = modifier, + visualTransformation = visualTransformation, + trailingIcon = iconButton, + onTap = onTap + ) + } +} -@OptIn(ExperimentalComposeUiApi::class) @Preview(name = "Default WirePasswordTextField") @Composable fun PreviewWirePasswordTextField() { WirePasswordTextField( value = TextFieldValue(""), onValueChange = {}, - modifier = Modifier.padding(16.dp) + modifier = Modifier.padding(16.dp), + autofill = false ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/createPasswordProtectedGuestLink/CreatePasswordProtectedGuestLinkScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/createPasswordProtectedGuestLink/CreatePasswordProtectedGuestLinkScreen.kt index a38db4c3400..095611041a2 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/createPasswordProtectedGuestLink/CreatePasswordProtectedGuestLinkScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/createPasswordProtectedGuestLink/CreatePasswordProtectedGuestLinkScreen.kt @@ -35,7 +35,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext @@ -60,7 +59,6 @@ import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography -@OptIn(ExperimentalComposeUiApi::class) @RootNavGraph @Destination( navArgsDelegate = CreatePasswordGuestLinkNavArgs::class @@ -161,7 +159,7 @@ fun CreatePasswordProtectedGuestLinkScreen( id = R.string.conversation_options_create_password_protected_guest_link_button_placeholder_text ), onValueChange = viewModel::onPasswordUpdated, - autofillTypes = emptyList() + autofill = false ) Spacer(modifier = Modifier.height(dimensions().spacing8x)) } @@ -185,7 +183,7 @@ fun CreatePasswordProtectedGuestLinkScreen( ), value = viewModel.state.passwordConfirm, onValueChange = viewModel::onPasswordConfirmUpdated, - autofillTypes = emptyList() + autofill = false ) Spacer(modifier = Modifier.height(dimensions().spacing24x)) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/create/CreateBackupDialogs.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/create/CreateBackupDialogs.kt index 04ac81e9cbc..655666c7558 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/create/CreateBackupDialogs.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/create/CreateBackupDialogs.kt @@ -33,7 +33,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue @@ -52,7 +51,6 @@ import com.wire.android.util.permission.rememberCreateFileFlow import java.util.Locale import kotlin.math.roundToInt -@OptIn(ExperimentalComposeUiApi::class) @Composable fun SetBackupPasswordDialog( isBackupPasswordValid: Boolean, @@ -85,7 +83,8 @@ fun SetBackupPasswordDialog( onValueChange = { backupPassword = it onBackupPasswordChanged(it) - } + }, + autofill = false ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/restore/RestoreBackupDialogs.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/restore/RestoreBackupDialogs.kt index 13a0754d7bb..ffd633ca6e9 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/restore/RestoreBackupDialogs.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/dialog/restore/RestoreBackupDialogs.kt @@ -102,7 +102,8 @@ fun EnterRestorePasswordDialog( ) { WirePasswordTextField( value = restorePassword, - onValueChange = { restorePassword = it } + onValueChange = { restorePassword = it }, + autofill = false ) } } else { diff --git a/app/src/main/kotlin/com/wire/android/ui/joinConversation/JoinConversationViaDeepLinkDialog.kt b/app/src/main/kotlin/com/wire/android/ui/joinConversation/JoinConversationViaDeepLinkDialog.kt index 80432fd3c21..6c682cfce9f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/joinConversation/JoinConversationViaDeepLinkDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/joinConversation/JoinConversationViaDeepLinkDialog.kt @@ -143,11 +143,11 @@ fun JoinConversationViaDeepLinkDialog( }, imeAction = ImeAction.Done, keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), - autofillTypes = emptyList(), modifier = Modifier .focusRequester(focusRequester) .padding(bottom = MaterialTheme.wireDimensions.spacing8x) - .testTag("remove device password field") + .testTag("remove device password field"), + autofill = false ) LaunchedEffect(Unit) { // executed only once when showing the dialog focusRequester.requestFocus()