mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: add password TextField
This commit is contained in:
parent
f222fe4d5e
commit
79bf8d1536
7 changed files with 175 additions and 62 deletions
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 } })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
14
app/src/main/res/drawable/ic_twotone_visibility_24.xml
Normal file
14
app/src/main/res/drawable/ic_twotone_visibility_24.xml
Normal 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>
|
||||
14
app/src/main/res/drawable/ic_twotone_visibility_off_24.xml
Normal file
14
app/src/main/res/drawable/ic_twotone_visibility_off_24.xml
Normal 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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue