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