From 79bf8d15365924a062d5ba082cf28892f5020068 Mon Sep 17 00:00:00 2001 From: andrekir Date: Thu, 27 Jul 2023 06:47:34 -0300 Subject: [PATCH] feat: add password TextField --- .../mesh/ui/components/EditIPv4Preference.kt | 66 +++++++++++++++++ .../ui/components/EditPasswordPreference.kt | 72 +++++++++++++++++++ .../mesh/ui/components/EditTextPreference.kt | 53 ++------------ .../components/config/MQTTConfigItemList.kt | 7 +- .../config/NetworkConfigItemList.kt | 11 +-- .../res/drawable/ic_twotone_visibility_24.xml | 14 ++++ .../drawable/ic_twotone_visibility_off_24.xml | 14 ++++ 7 files changed, 175 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/components/EditIPv4Preference.kt create mode 100644 app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt create mode 100644 app/src/main/res/drawable/ic_twotone_visibility_24.xml create mode 100644 app/src/main/res/drawable/ic_twotone_visibility_off_24.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditIPv4Preference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditIPv4Preference.kt new file mode 100644 index 000000000..239a397f8 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditIPv4Preference.kt @@ -0,0 +1,66 @@ +package com.geeksville.mesh.ui.components + +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview + +@Composable +fun EditIPv4Preference( + title: String, + value: Int, + enabled: Boolean, + keyboardActions: KeyboardActions, + onValueChanged: (Int) -> Unit, + modifier: Modifier = Modifier, +) { + val pattern = """\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b""".toRegex() + + fun convertIntToIpAddress(int: Int): String { + return "${int and 0xff}.${int shr 8 and 0xff}.${int shr 16 and 0xff}.${int shr 24 and 0xff}" + } + + fun convertIpAddressToInt(ipAddress: String): Int? = ipAddress.split(".") + .map { it.toIntOrNull() }.reversed() // little-endian byte order + .fold(0) { total, next -> + if (next == null) return null else total shl 8 or next + } + + var valueState by remember(value) { mutableStateOf(convertIntToIpAddress(value)) } + + EditTextPreference( + title = title, + value = valueState, + enabled = enabled, + isError = convertIntToIpAddress(value) != valueState, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number, imeAction = ImeAction.Done + ), + keyboardActions = keyboardActions, + onValueChanged = { + valueState = it + if (pattern.matches(it)) convertIpAddressToInt(it)?.let { int -> onValueChanged(int) } + }, + onFocusChanged = {}, + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +private fun EditIPv4PreferencePreview() { + EditIPv4Preference( + title = "IP Address", + value = 16820416, + enabled = true, + keyboardActions = KeyboardActions {}, + onValueChanged = {} + ) +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt new file mode 100644 index 000000000..b3af71cfb --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt @@ -0,0 +1,72 @@ +package com.geeksville.mesh.ui.components + +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +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.input.VisualTransformation +import androidx.compose.ui.tooling.preview.Preview +import com.geeksville.mesh.R + +@Composable +fun EditPasswordPreference( + title: String, + value: String, + maxSize: Int, + enabled: Boolean, + keyboardActions: KeyboardActions, + onValueChanged: (String) -> Unit, + modifier: Modifier = Modifier, +) { + var isPasswordVisible by remember { mutableStateOf(false) } + + EditTextPreference( + title = title, + value = value, + maxSize = maxSize, + enabled = enabled, + isError = false, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Password, imeAction = ImeAction.Done + ), + keyboardActions = keyboardActions, + onValueChanged = { + onValueChanged(it) + }, + onFocusChanged = {}, + visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) { + Icon( + painter = if (isPasswordVisible) painterResource(R.drawable.ic_twotone_visibility_off_24) + else painterResource(R.drawable.ic_twotone_visibility_24), + contentDescription = if (isPasswordVisible) "Hide password" else "Show password", + ) + } + }, + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +private fun EditPasswordPreferencePreview() { + EditPasswordPreference( + title = "Password", + value = "top secret", + maxSize = 63, + enabled = true, + keyboardActions = KeyboardActions {}, + onValueChanged = {} + ) +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt index 1620f83bf..20bb61b8c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.focus.FocusState import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -127,47 +128,6 @@ fun EditTextPreference( ) } -@Composable -fun EditIPv4Preference( - title: String, - value: Int, - enabled: Boolean, - keyboardActions: KeyboardActions, - onValueChanged: (Int) -> Unit, - modifier: Modifier = Modifier, -) { - val pattern = """\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b""".toRegex() - - fun convertIntToIpAddress(int: Int): String { - return "${int and 0xff}.${int shr 8 and 0xff}.${int shr 16 and 0xff}.${int shr 24 and 0xff}" - } - - fun convertIpAddressToInt(ipAddress: String): Int? = ipAddress.split(".") - .map { it.toIntOrNull() }.reversed() // little-endian byte order - .fold(0) { total, next -> - if (next == null) return null else total shl 8 or next - } - - var valueState by remember(value) { mutableStateOf(convertIntToIpAddress(value)) } - - EditTextPreference( - title = title, - value = valueState, - enabled = enabled, - isError = convertIntToIpAddress(value) != valueState, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, imeAction = ImeAction.Done - ), - keyboardActions = keyboardActions, - onValueChanged = { - valueState = it - if (pattern.matches(it)) convertIpAddressToInt(it)?.let { int -> onValueChanged(int) } - }, - onFocusChanged = {}, - modifier = modifier - ) -} - @Composable fun EditTextPreference( title: String, @@ -181,6 +141,7 @@ fun EditTextPreference( maxSize: Int = 0, // max_size - 1 (in bytes) onFocusChanged: (FocusState) -> Unit = {}, trailingIcon: (@Composable () -> Unit)? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, ) { var isFocused by remember { mutableStateOf(false) } @@ -202,13 +163,14 @@ fun EditTextPreference( label = { Text(title) }, keyboardOptions = keyboardOptions, keyboardActions = keyboardActions, + visualTransformation = visualTransformation, trailingIcon = { if (trailingIcon != null) { trailingIcon() } else { if (isError) Icon(Icons.TwoTone.Info, "Error", tint = MaterialTheme.colors.error) } - } + }, ) if (maxSize > 0 && isFocused) { @@ -247,12 +209,5 @@ private fun EditTextPreferencePreview() { keyboardActions = KeyboardActions {}, onValueChanged = {} ) - EditIPv4Preference( - title = "IP Address", - value = 16820416, - enabled = true, - keyboardActions = KeyboardActions {}, - onValueChanged = {} - ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/config/MQTTConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/components/config/MQTTConfigItemList.kt index 8837a059b..d06f0952e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/config/MQTTConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/config/MQTTConfigItemList.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.MQTTConfig import com.geeksville.mesh.copy +import com.geeksville.mesh.ui.components.EditPasswordPreference import com.geeksville.mesh.ui.components.EditTextPreference import com.geeksville.mesh.ui.components.PreferenceCategory import com.geeksville.mesh.ui.components.PreferenceFooter @@ -72,14 +73,10 @@ fun MQTTConfigItemList( } item { - EditTextPreference(title = "Password", + EditPasswordPreference(title = "Password", value = mqttInput.password, maxSize = 63, // password max_size:64 enabled = enabled, - isError = false, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Text, imeAction = ImeAction.Done - ), keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), onValueChanged = { mqttInput = mqttInput.copy { password = it } }) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/config/NetworkConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/components/config/NetworkConfigItemList.kt index 3912b4782..054c76792 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/config/NetworkConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/config/NetworkConfigItemList.kt @@ -20,6 +20,7 @@ import com.geeksville.mesh.ConfigProtos.Config.NetworkConfig import com.geeksville.mesh.copy import com.geeksville.mesh.ui.components.DropDownPreference import com.geeksville.mesh.ui.components.EditIPv4Preference +import com.geeksville.mesh.ui.components.EditPasswordPreference import com.geeksville.mesh.ui.components.EditTextPreference import com.geeksville.mesh.ui.components.PreferenceCategory import com.geeksville.mesh.ui.components.PreferenceFooter @@ -63,18 +64,12 @@ fun NetworkConfigItemList( } item { - EditTextPreference(title = "PSK", + EditPasswordPreference(title = "PSK", value = networkInput.wifiPsk, maxSize = 63, // wifi_psk max_size:64 enabled = enabled, - isError = false, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Password, imeAction = ImeAction.Done - ), keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), - onValueChanged = { - networkInput = networkInput.copy { wifiPsk = it } - }) + onValueChanged = { networkInput = networkInput.copy { wifiPsk = it } }) } item { diff --git a/app/src/main/res/drawable/ic_twotone_visibility_24.xml b/app/src/main/res/drawable/ic_twotone_visibility_24.xml new file mode 100644 index 000000000..507c5fdb2 --- /dev/null +++ b/app/src/main/res/drawable/ic_twotone_visibility_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_twotone_visibility_off_24.xml b/app/src/main/res/drawable/ic_twotone_visibility_off_24.xml new file mode 100644 index 000000000..a19e1d4b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_twotone_visibility_off_24.xml @@ -0,0 +1,14 @@ + + + +