feat: add password TextField

This commit is contained in:
andrekir 2023-07-27 06:47:34 -03:00
parent f222fe4d5e
commit 79bf8d1536
7 changed files with 175 additions and 62 deletions

View file

@ -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 = {}
)
}

View file

@ -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 = {}
)
}

View file

@ -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 = {}
)
}
}

View file

@ -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 } })
}

View file

@ -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 {

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="@android:color/white"
android:pathData="M12,6c-3.79,0 -7.17,2.13 -8.82,5.5C4.83,14.87 8.21,17 12,17s7.17,-2.13 8.82,-5.5C19.17,8.13 15.79,6 12,6zM12,16c-2.48,0 -4.5,-2.02 -4.5,-4.5S9.52,7 12,7s4.5,2.02 4.5,4.5S14.48,16 12,16z"
android:strokeAlpha="0.3" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,4C7,4 2.73,7.11 1,11.5 2.73,15.89 7,19 12,19s9.27,-3.11 11,-7.5C21.27,7.11 17,4 12,4zM12,17c-3.79,0 -7.17,-2.13 -8.82,-5.5C4.83,8.13 8.21,6 12,6s7.17,2.13 8.82,5.5C19.17,14.87 15.79,17 12,17zM12,7c-2.48,0 -4.5,2.02 -4.5,4.5S9.52,16 12,16s4.5,-2.02 4.5,-4.5S14.48,7 12,7zM12,14c-1.38,0 -2.5,-1.12 -2.5,-2.5S10.62,9 12,9s2.5,1.12 2.5,2.5S13.38,14 12,14z" />
</vector>

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="@android:color/white"
android:pathData="M12,14c0.04,0 0.08,-0.01 0.12,-0.01l-2.61,-2.61c0,0.04 -0.01,0.08 -0.01,0.12 0,1.38 1.12,2.5 2.5,2.5zM13.01,9.21l1.28,1.28c-0.26,-0.57 -0.71,-1.03 -1.28,-1.28zM20.82,11.5C19.17,8.13 15.79,6 12,6c-0.68,0 -1.34,0.09 -1.99,0.22l0.92,0.92c0.35,-0.09 0.7,-0.14 1.07,-0.14 2.48,0 4.5,2.02 4.5,4.5 0,0.37 -0.06,0.72 -0.14,1.07l2.05,2.05c0.98,-0.86 1.81,-1.91 2.41,-3.12zM12,17c0.95,0 1.87,-0.13 2.75,-0.39l-0.98,-0.98c-0.54,0.24 -1.14,0.37 -1.77,0.37 -2.48,0 -4.5,-2.02 -4.5,-4.5 0,-0.63 0.13,-1.23 0.36,-1.77L6.11,7.97c-1.22,0.91 -2.23,2.1 -2.93,3.52C4.83,14.86 8.21,17 12,17z"
android:strokeAlpha="0.3" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,6c3.79,0 7.17,2.13 8.82,5.5 -0.59,1.22 -1.42,2.27 -2.41,3.12l1.41,1.41c1.39,-1.23 2.49,-2.77 3.18,-4.53C21.27,7.11 17,4 12,4c-1.27,0 -2.49,0.2 -3.64,0.57l1.65,1.65C10.66,6.09 11.32,6 12,6zM14.28,10.49l2.07,2.07c0.08,-0.34 0.14,-0.7 0.14,-1.07C16.5,9.01 14.48,7 12,7c-0.37,0 -0.72,0.06 -1.07,0.14L13,9.21c0.58,0.25 1.03,0.71 1.28,1.28zM2.01,3.87l2.68,2.68C3.06,7.83 1.77,9.53 1,11.5 2.73,15.89 7,19 12,19c1.52,0 2.98,-0.29 4.32,-0.82l3.42,3.42 1.41,-1.41L3.42,2.45 2.01,3.87zM9.51,11.37l2.61,2.61c-0.04,0.01 -0.08,0.02 -0.12,0.02 -1.38,0 -2.5,-1.12 -2.5,-2.5 0,-0.05 0.01,-0.08 0.01,-0.13zM6.11,7.97l1.75,1.75c-0.23,0.55 -0.36,1.15 -0.36,1.78 0,2.48 2.02,4.5 4.5,4.5 0.63,0 1.23,-0.13 1.77,-0.36l0.98,0.98c-0.88,0.24 -1.8,0.38 -2.75,0.38 -3.79,0 -7.17,-2.13 -8.82,-5.5 0.7,-1.43 1.72,-2.61 2.93,-3.53z" />
</vector>