mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-31 12:11:15 -04:00
config clean up and updates (#522)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.geeksville.mesh.model
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.MeshUser
|
||||
@@ -62,5 +63,14 @@ class NodeDB(private val ui: UIViewModel) {
|
||||
}
|
||||
|
||||
/// Could be null if we haven't received our node DB yet
|
||||
val ourNodeInfo get() = nodes.value?.get(myId.value)
|
||||
val ourNodeInfo = MediatorLiveData<NodeInfo?>()
|
||||
|
||||
init {
|
||||
ourNodeInfo.addSource(_nodes) { updatedValue ->
|
||||
ourNodeInfo.value = updatedValue[_myId.value]
|
||||
}
|
||||
ourNodeInfo.addSource(_myId) { updatedValue ->
|
||||
ourNodeInfo.value = _nodes.value?.get(updatedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ import kotlin.math.roundToInt
|
||||
/// 3 or more characters, use the first three characters. If not, just take the first 3 characters of the
|
||||
/// original name.
|
||||
fun getInitials(nameIn: String): String {
|
||||
val nchars = 3
|
||||
val nchars = 4
|
||||
val minchars = 2
|
||||
val name = nameIn.trim()
|
||||
val words = name.split(Regex("\\s+")).filter { it.isNotEmpty() }
|
||||
@@ -263,7 +263,7 @@ class UIViewModel @Inject constructor(
|
||||
try {
|
||||
// Pull down our real node ID - This must be done AFTER reading the nodedb because we need the DB to find our nodeinof object
|
||||
nodeDB.setMyId(service.myId)
|
||||
val ownerName = nodeDB.ourNodeInfo?.user?.longName
|
||||
val ownerName = nodeDB.ourNodeInfo.value?.user?.longName
|
||||
_ownerName.value = ownerName
|
||||
} catch (ex: Exception) {
|
||||
warn("Ignoring failure to get myId, service is probably just uninited... ${ex.message}")
|
||||
@@ -364,13 +364,10 @@ class UIViewModel @Inject constructor(
|
||||
// clean up all this nasty owner state management FIXME
|
||||
fun setOwner(longName: String? = null, shortName: String? = null, isLicensed: Boolean? = null) {
|
||||
|
||||
if (longName != null) {
|
||||
_ownerName.value = longName
|
||||
|
||||
// note: we allow an empty userstring to be written to prefs
|
||||
preferences.edit {
|
||||
putString("owner", longName)
|
||||
}
|
||||
longName?.trim()?.let { ownerName ->
|
||||
// note: we allow an empty user string to be written to prefs
|
||||
_ownerName.value = ownerName
|
||||
preferences.edit { putString("owner", ownerName) }
|
||||
}
|
||||
|
||||
// Note: we are careful to not set a new unique ID
|
||||
@@ -379,7 +376,7 @@ class UIViewModel @Inject constructor(
|
||||
meshService?.setOwner(
|
||||
null,
|
||||
_ownerName.value,
|
||||
shortName ?: getInitials(_ownerName.value!!),
|
||||
shortName?.trim() ?: getInitials(_ownerName.value!!),
|
||||
isLicensed ?: false
|
||||
) // Note: we use ?. here because we might be running in the emulator
|
||||
} catch (ex: RemoteException) {
|
||||
|
||||
@@ -459,7 +459,7 @@ class MapFragment : ScreenFragment("Map"), Logging, View.OnClickListener {
|
||||
marker = MarkerWithLabel(map, label)
|
||||
marker.title = "${it.longName} ${node.batteryStr}"
|
||||
marker.snippet = model.gpsString(p)
|
||||
model.nodeDB.ourNodeInfo?.let { our ->
|
||||
model.nodeDB.ourNodeInfo.value?.let { our ->
|
||||
our.distanceStr(node)?.let { dist ->
|
||||
marker.subDescription = "bearing: ${our.bearing(node)}° distance: $dist"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.model.getInitials
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.ui.components.BitwisePreference
|
||||
import com.geeksville.mesh.ui.components.DropDownPreference
|
||||
import com.geeksville.mesh.ui.components.EditTextPreference
|
||||
import com.geeksville.mesh.ui.components.PreferenceCategory
|
||||
@@ -29,29 +31,26 @@ import com.geeksville.mesh.ui.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.components.RegularPreference
|
||||
import com.geeksville.mesh.ui.components.SwitchPreference
|
||||
|
||||
private fun Int.uintToString(): String = this.toUInt().toString()
|
||||
private fun String.stringToIntOrNull(): Int? = this.toUIntOrNull()?.toInt()
|
||||
|
||||
@Composable
|
||||
fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val hasWifi = viewModel.hasWifi()
|
||||
val connectionState = viewModel.connectionState.observeAsState()
|
||||
val connected = connectionState.value == MeshService.ConnectionState.CONNECTED
|
||||
val connectionState by viewModel.connectionState.observeAsState()
|
||||
val connected = connectionState == MeshService.ConnectionState.CONNECTED
|
||||
|
||||
val localConfig by viewModel.localConfig.collectAsState()
|
||||
val user = viewModel.nodeDB.ourNodeInfo?.user
|
||||
val ourNodeInfo by viewModel.nodeDB.ourNodeInfo.observeAsState()
|
||||
var userInput by remember(ourNodeInfo?.user) { mutableStateOf(ourNodeInfo?.user) }
|
||||
|
||||
// Temporary [ConfigProtos.Config] state holders
|
||||
var userInput by remember { mutableStateOf(user) }
|
||||
var deviceInput by remember { mutableStateOf(localConfig.device) }
|
||||
var positionInput by remember { mutableStateOf(localConfig.position) }
|
||||
var powerInput by remember { mutableStateOf(localConfig.power) }
|
||||
var networkInput by remember { mutableStateOf(localConfig.network) }
|
||||
var displayInput by remember { mutableStateOf(localConfig.display) }
|
||||
var loraInput by remember { mutableStateOf(localConfig.lora) }
|
||||
var bluetoothInput by remember { mutableStateOf(localConfig.bluetooth) }
|
||||
var deviceInput by remember(localConfig.device) { mutableStateOf(localConfig.device) }
|
||||
var positionInput by remember(localConfig.position) { mutableStateOf(localConfig.position) }
|
||||
var powerInput by remember(localConfig.power) { mutableStateOf(localConfig.power) }
|
||||
var networkInput by remember(localConfig.network) { mutableStateOf(localConfig.network) }
|
||||
var displayInput by remember(localConfig.display) { mutableStateOf(localConfig.display) }
|
||||
var loraInput by remember(localConfig.lora) { mutableStateOf(localConfig.lora) }
|
||||
var bluetoothInput by remember(localConfig.bluetooth) { mutableStateOf(localConfig.bluetooth) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
@@ -59,8 +58,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "User Config") }
|
||||
|
||||
item {
|
||||
RegularPreference(
|
||||
title = "Node ID",
|
||||
RegularPreference(title = "Node ID",
|
||||
subtitle = userInput?.id ?: stringResource(id = R.string.unknown),
|
||||
onClick = {})
|
||||
}
|
||||
@@ -70,15 +68,16 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
EditTextPreference(title = "Long name",
|
||||
value = userInput?.longName ?: stringResource(id = R.string.unknown_username),
|
||||
enabled = connected && userInput?.longName != null,
|
||||
isError = userInput?.longName.isNullOrEmpty(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value.toByteArray().size <= 39) // long_name max_size:40
|
||||
userInput?.let { userInput = it.copy(longName = value) }
|
||||
if (getInitials(value).toByteArray().size <= 4) // short_name max_size:5
|
||||
userInput?.let { userInput = it.copy(shortName = getInitials(value)) }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,12 +85,11 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
EditTextPreference(title = "Short name",
|
||||
value = userInput?.shortName ?: stringResource(id = R.string.unknown),
|
||||
enabled = connected && userInput?.shortName != null,
|
||||
isError = userInput?.shortName.isNullOrEmpty(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value.toByteArray().size <= 4) // short_name max_size:5
|
||||
userInput?.let { userInput = it.copy(shortName = value) }
|
||||
@@ -99,8 +97,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
}
|
||||
|
||||
item {
|
||||
RegularPreference(
|
||||
title = "Hardware model",
|
||||
RegularPreference(title = "Hardware model",
|
||||
subtitle = userInput?.hwModel?.name ?: stringResource(id = R.string.unknown),
|
||||
onClick = {})
|
||||
}
|
||||
@@ -118,10 +115,10 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
|
||||
item {
|
||||
PreferenceFooter(
|
||||
enabled = userInput != user,
|
||||
enabled = userInput != ourNodeInfo?.user,
|
||||
onCancelClicked = {
|
||||
focusManager.clearFocus()
|
||||
userInput = user
|
||||
userInput = ourNodeInfo?.user
|
||||
}, onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
userInput?.let { viewModel.setOwner(it.longName, it.shortName, it.isLicensed) }
|
||||
@@ -174,14 +171,11 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
|
||||
item {
|
||||
EditTextPreference(title = "Position broadcast interval",
|
||||
value = positionInput.positionBroadcastSecs.uintToString(),
|
||||
value = positionInput.positionBroadcastSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { positionInput = positionInput.copy { positionBroadcastSecs = it } }
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
positionInput = positionInput.copy { positionBroadcastSecs = it }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -213,42 +207,31 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
|
||||
item {
|
||||
EditTextPreference(title = "GPS update interval",
|
||||
value = positionInput.gpsUpdateInterval.uintToString(),
|
||||
value = positionInput.gpsUpdateInterval,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { positionInput = positionInput.copy { gpsUpdateInterval = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { positionInput = positionInput.copy { gpsUpdateInterval = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(title = "Fix attempt duration",
|
||||
value = positionInput.gpsAttemptTime.uintToString(),
|
||||
value = positionInput.gpsAttemptTime,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { positionInput = positionInput.copy { gpsAttemptTime = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { positionInput = positionInput.copy { gpsAttemptTime = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(title = "Position flags",
|
||||
value = positionInput.positionFlags.uintToString(),
|
||||
BitwisePreference(title = "Position flags",
|
||||
value = positionInput.positionFlags,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { positionInput = positionInput.copy { positionFlags = it } }
|
||||
})
|
||||
items = ConfigProtos.Config.PositionConfig.PositionFlags.values()
|
||||
.filter { it != ConfigProtos.Config.PositionConfig.PositionFlags.UNSET && it != ConfigProtos.Config.PositionConfig.PositionFlags.UNRECOGNIZED }
|
||||
.map { it.number to it.name },
|
||||
onItemSelected = { positionInput = positionInput.copy { positionFlags = it } }
|
||||
)
|
||||
}
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
PreferenceFooter(
|
||||
@@ -274,101 +257,61 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Shutdown on battery delay",
|
||||
value = powerInput.onBatteryShutdownAfterSecs.uintToString(),
|
||||
EditTextPreference(title = "Shutdown on battery delay",
|
||||
value = powerInput.onBatteryShutdownAfterSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { onBatteryShutdownAfterSecs = it } }
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
powerInput = powerInput.copy { onBatteryShutdownAfterSecs = it }
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "ADC multiplier override ratio",
|
||||
value = powerInput.adcMultiplierOverride.toString(),
|
||||
EditTextPreference(title = "ADC multiplier override ratio",
|
||||
value = powerInput.adcMultiplierOverride,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toFloatOrNull()
|
||||
?.let { powerInput = powerInput.copy { adcMultiplierOverride = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { adcMultiplierOverride = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Wait for Bluetooth duration",
|
||||
value = powerInput.waitBluetoothSecs.uintToString(),
|
||||
EditTextPreference(title = "Wait for Bluetooth duration",
|
||||
value = powerInput.waitBluetoothSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { waitBluetoothSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { waitBluetoothSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Mesh SDS timeout",
|
||||
value = powerInput.meshSdsTimeoutSecs.uintToString(),
|
||||
EditTextPreference(title = "Mesh SDS timeout",
|
||||
value = powerInput.meshSdsTimeoutSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { meshSdsTimeoutSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { meshSdsTimeoutSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Super deep sleep duration",
|
||||
value = powerInput.sdsSecs.uintToString(),
|
||||
EditTextPreference(title = "Super deep sleep duration",
|
||||
value = powerInput.sdsSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { sdsSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { sdsSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Light sleep duration",
|
||||
value = powerInput.lsSecs.uintToString(),
|
||||
EditTextPreference(title = "Light sleep duration",
|
||||
value = powerInput.lsSecs,
|
||||
enabled = connected && hasWifi, // we consider hasWifi = ESP32
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { lsSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { lsSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Minimum wake time",
|
||||
value = powerInput.minWakeSecs.uintToString(),
|
||||
EditTextPreference(title = "Minimum wake time",
|
||||
value = powerInput.minWakeSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { powerInput = powerInput.copy { minWakeSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { powerInput = powerInput.copy { minWakeSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -387,8 +330,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "Network Config") }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "WiFi enabled",
|
||||
SwitchPreference(title = "WiFi enabled",
|
||||
checked = networkInput.wifiEnabled,
|
||||
enabled = connected && hasWifi,
|
||||
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } })
|
||||
@@ -396,16 +338,14 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "SSID",
|
||||
value = networkInput.wifiSsid.toString(),
|
||||
EditTextPreference(title = "SSID",
|
||||
value = networkInput.wifiSsid,
|
||||
enabled = connected && hasWifi,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value.toByteArray().size <= 32) // wifi_ssid max_size:33
|
||||
networkInput = networkInput.copy { wifiSsid = value }
|
||||
@@ -413,16 +353,14 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "PSK",
|
||||
value = networkInput.wifiPsk .toString(),
|
||||
EditTextPreference(title = "PSK",
|
||||
value = networkInput.wifiPsk,
|
||||
enabled = connected && hasWifi,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Password, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Password, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value.toByteArray().size <= 63) // wifi_psk max_size:64
|
||||
networkInput = networkInput.copy { wifiPsk = value }
|
||||
@@ -430,16 +368,14 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "NTP server",
|
||||
value = networkInput.ntpServer.toString(),
|
||||
EditTextPreference(title = "NTP server",
|
||||
value = networkInput.ntpServer,
|
||||
enabled = connected && hasWifi,
|
||||
isError = networkInput.ntpServer.isEmpty(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value.toByteArray().size <= 32) // ntp_server max_size:33
|
||||
networkInput = networkInput.copy { ntpServer = value }
|
||||
@@ -447,8 +383,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "Ethernet enabled",
|
||||
SwitchPreference(title = "Ethernet enabled",
|
||||
checked = networkInput.ethEnabled,
|
||||
enabled = connected,
|
||||
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } })
|
||||
@@ -469,66 +404,46 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "IPv4 Config") }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "IP",
|
||||
value = networkInput.ipv4Config.ip.toString(),
|
||||
EditTextPreference(title = "IP",
|
||||
value = networkInput.ipv4Config.ip,
|
||||
enabled = connected && networkInput.ethMode == ConfigProtos.Config.NetworkConfig.EthMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toIntOrNull()?.let {
|
||||
val ipv4 = networkInput.ipv4Config.copy { ip = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { ip = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Gateway",
|
||||
value = networkInput.ipv4Config.gateway.toString(),
|
||||
EditTextPreference(title = "Gateway",
|
||||
value = networkInput.ipv4Config.gateway,
|
||||
enabled = connected && networkInput.ethMode == ConfigProtos.Config.NetworkConfig.EthMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toIntOrNull()?.let {
|
||||
val ipv4 = networkInput.ipv4Config.copy { gateway = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { gateway = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Subnet",
|
||||
value = networkInput.ipv4Config.subnet.toString(),
|
||||
EditTextPreference(title = "Subnet",
|
||||
value = networkInput.ipv4Config.subnet,
|
||||
enabled = connected && networkInput.ethMode == ConfigProtos.Config.NetworkConfig.EthMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toIntOrNull()?.let {
|
||||
val ipv4 = networkInput.ipv4Config.copy { subnet = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { subnet = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "DNS",
|
||||
value = networkInput.ipv4Config.dns.toString(),
|
||||
EditTextPreference(title = "DNS",
|
||||
value = networkInput.ipv4Config.dns,
|
||||
enabled = connected && networkInput.ethMode == ConfigProtos.Config.NetworkConfig.EthMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toIntOrNull()?.let {
|
||||
val ipv4 = networkInput.ipv4Config.copy { dns = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { dns = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -548,17 +463,11 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "Display Config") }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Screen timeout",
|
||||
value = displayInput.screenOnSecs.uintToString(),
|
||||
EditTextPreference(title = "Screen timeout",
|
||||
value = displayInput.screenOnSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { displayInput = displayInput.copy { screenOnSecs = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { displayInput = displayInput.copy { screenOnSecs = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -573,22 +482,17 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Auto screen carousel",
|
||||
value = displayInput.autoScreenCarouselSecs.uintToString(),
|
||||
EditTextPreference(title = "Auto screen carousel",
|
||||
value = displayInput.autoScreenCarouselSecs,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { displayInput = displayInput.copy { autoScreenCarouselSecs = it } }
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
displayInput = displayInput.copy { autoScreenCarouselSecs = it }
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "Compass north top",
|
||||
SwitchPreference(title = "Compass north top",
|
||||
checked = displayInput.compassNorthTop,
|
||||
enabled = connected,
|
||||
onCheckedChange = { displayInput = displayInput.copy { compassNorthTop = it } })
|
||||
@@ -596,8 +500,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "Flip screen",
|
||||
SwitchPreference(title = "Flip screen",
|
||||
checked = displayInput.flipScreen,
|
||||
enabled = connected,
|
||||
onCheckedChange = { displayInput = displayInput.copy { flipScreen = it } })
|
||||
@@ -642,8 +545,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "LoRa Config") }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "Use modem preset",
|
||||
SwitchPreference(title = "Use modem preset",
|
||||
checked = loraInput.usePreset,
|
||||
enabled = connected,
|
||||
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } })
|
||||
@@ -662,59 +564,35 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Bandwidth",
|
||||
value = loraInput.bandwidth.uintToString(),
|
||||
EditTextPreference(title = "Bandwidth",
|
||||
value = loraInput.bandwidth,
|
||||
enabled = connected && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { bandwidth = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { bandwidth = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Spread factor",
|
||||
value = loraInput.spreadFactor.uintToString(),
|
||||
EditTextPreference(title = "Spread factor",
|
||||
value = loraInput.spreadFactor,
|
||||
enabled = connected && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { spreadFactor = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { spreadFactor = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Coding rate",
|
||||
value = loraInput.codingRate.uintToString(),
|
||||
EditTextPreference(title = "Coding rate",
|
||||
value = loraInput.codingRate,
|
||||
enabled = connected && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { codingRate = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { codingRate = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Frequency offset",
|
||||
value = loraInput.frequencyOffset.toString(),
|
||||
EditTextPreference(title = "Frequency offset",
|
||||
value = loraInput.frequencyOffset,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.toFloatOrNull()
|
||||
?.let { loraInput = loraInput.copy { frequencyOffset = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { frequencyOffset = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -729,22 +607,15 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Hop limit",
|
||||
value = loraInput.hopLimit.uintToString(),
|
||||
EditTextPreference(title = "Hop limit",
|
||||
value = loraInput.hopLimit,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { hopLimit = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { hopLimit = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "TX enabled",
|
||||
SwitchPreference(title = "TX enabled",
|
||||
checked = loraInput.txEnabled,
|
||||
enabled = connected,
|
||||
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } })
|
||||
@@ -752,31 +623,19 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "TX power",
|
||||
value = loraInput.txPower.uintToString(),
|
||||
EditTextPreference(title = "TX power",
|
||||
value = loraInput.txPower,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { txPower = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { txPower = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Channel number",
|
||||
value = loraInput.channelNum.uintToString(),
|
||||
EditTextPreference(title = "Channel number",
|
||||
value = loraInput.channelNum,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { loraInput = loraInput.copy { channelNum = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { channelNum = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -795,8 +654,7 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { PreferenceCategory(text = "Bluetooth Config") }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = "Bluetooth enabled",
|
||||
SwitchPreference(title = "Bluetooth enabled",
|
||||
checked = bluetoothInput.enabled,
|
||||
enabled = connected,
|
||||
onCheckedChange = { bluetoothInput = bluetoothInput.copy { enabled = it } })
|
||||
@@ -815,17 +673,11 @@ fun PreferenceItemList(viewModel: UIViewModel) {
|
||||
item { Divider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "Fixed PIN",
|
||||
value = bluetoothInput.fixedPin.uintToString(),
|
||||
EditTextPreference(title = "Fixed PIN",
|
||||
value = bluetoothInput.fixedPin,
|
||||
enabled = connected,
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
focusManager.clearFocus()
|
||||
}),
|
||||
onValueChanged = { value ->
|
||||
value.stringToIntOrNull()
|
||||
?.let { bluetoothInput = bluetoothInput.copy { fixedPin = it } }
|
||||
})
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { bluetoothInput = bluetoothInput.copy { fixedPin = it } })
|
||||
}
|
||||
|
||||
item {
|
||||
|
||||
@@ -334,6 +334,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||
|
||||
// show the spinner when [spinner] is true
|
||||
scanModel.spinner.observe(viewLifecycleOwner) { show ->
|
||||
binding.changeRadioButton.isEnabled = !show
|
||||
binding.scanProgressBar.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||
holder.coordsView.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
val ourNodeInfo = model.nodeDB.ourNodeInfo
|
||||
val ourNodeInfo = model.nodeDB.ourNodeInfo.value
|
||||
val distance = ourNodeInfo?.distanceStr(n)
|
||||
if (distance != null) {
|
||||
holder.distanceView.text = distance
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.geeksville.mesh.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.twotone.KeyboardArrowUp
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
|
||||
@Composable
|
||||
fun BitwisePreference(
|
||||
title: String,
|
||||
value: Int,
|
||||
enabled: Boolean,
|
||||
items: List<Pair<Int, String>>,
|
||||
onItemSelected: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var dropDownExpanded by remember { mutableStateOf(value = false) }
|
||||
|
||||
RegularPreference(
|
||||
title = title,
|
||||
subtitle = value.toString(),
|
||||
onClick = { dropDownExpanded = !dropDownExpanded },
|
||||
enabled = enabled,
|
||||
trailingIcon = if (dropDownExpanded) Icons.TwoTone.KeyboardArrowDown
|
||||
else Icons.TwoTone.KeyboardArrowUp,
|
||||
)
|
||||
|
||||
Box {
|
||||
DropdownMenu(
|
||||
expanded = dropDownExpanded,
|
||||
onDismissRequest = { dropDownExpanded = !dropDownExpanded },
|
||||
) {
|
||||
items.forEach { item ->
|
||||
DropdownMenuItem(
|
||||
onClick = { onItemSelected(value xor item.first) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
content = {
|
||||
Text(
|
||||
text = item.second,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Checkbox(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.End),
|
||||
checked = value and item.first != 0,
|
||||
onCheckedChange = { onItemSelected(value xor item.first) },
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Button(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
enabled = enabled,
|
||||
onClick = { onItemSelected(0) },
|
||||
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.clear_last_messages),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
Button(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
enabled = enabled,
|
||||
onClick = { dropDownExpanded = false },
|
||||
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Green)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.close),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = Color.DarkGray,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun BitwisePreferencePreview() {
|
||||
BitwisePreference(
|
||||
title = "Settings",
|
||||
value = 3,
|
||||
enabled = true,
|
||||
items = listOf(1 to "TEST1", 2 to "TEST2"),
|
||||
onItemSelected = {}
|
||||
)
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.twotone.KeyboardArrowUp
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -33,15 +36,10 @@ fun <T> DropDownPreference(
|
||||
onClick = {
|
||||
dropDownExpanded = true
|
||||
},
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = if (dropDownExpanded)
|
||||
MaterialTheme.colors.primary.copy(alpha = 0.2f)
|
||||
else
|
||||
Color.Unspecified
|
||||
),
|
||||
enabled = enabled,
|
||||
)
|
||||
trailingIcon = if (dropDownExpanded) Icons.TwoTone.KeyboardArrowDown
|
||||
else Icons.TwoTone.KeyboardArrowUp,
|
||||
)
|
||||
|
||||
Box {
|
||||
DropdownMenu(
|
||||
|
||||
@@ -3,32 +3,111 @@ package com.geeksville.mesh.ui.components
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Info
|
||||
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 // Default keyboardOptions: KeyboardType.Number, ImeAction.Send
|
||||
@Composable
|
||||
fun EditTextPreference(
|
||||
title: String,
|
||||
value: String,
|
||||
value: Int,
|
||||
enabled: Boolean,
|
||||
keyboardActions: KeyboardActions,
|
||||
onValueChanged: (String) -> Unit,
|
||||
onValueChanged: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var valueState by remember(value) { mutableStateOf(value.toUInt().toString()) }
|
||||
|
||||
EditTextPreference(
|
||||
title = title,
|
||||
value = value,
|
||||
value = valueState,
|
||||
enabled = enabled,
|
||||
isError = value.toUInt().toString() != valueState,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Number, imeAction = ImeAction.Send
|
||||
keyboardType = KeyboardType.Number, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = keyboardActions,
|
||||
onValueChanged = onValueChanged,
|
||||
onValueChanged = {
|
||||
if (it.isEmpty()) valueState = it
|
||||
else it.toUIntOrNull()?.toInt()?.let { int ->
|
||||
valueState = it
|
||||
onValueChanged(int)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditTextPreference(
|
||||
title: String,
|
||||
value: Float,
|
||||
enabled: Boolean,
|
||||
keyboardActions: KeyboardActions,
|
||||
onValueChanged: (Float) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var valueState by remember(value) { mutableStateOf(value.toString()) }
|
||||
|
||||
EditTextPreference(
|
||||
title = title,
|
||||
value = valueState,
|
||||
enabled = enabled,
|
||||
isError = value.toString() != valueState,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Number, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = keyboardActions,
|
||||
onValueChanged = {
|
||||
if (it.isEmpty()) valueState = it
|
||||
else it.toFloatOrNull()?.let { float ->
|
||||
valueState = it
|
||||
onValueChanged(float)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditTextPreference(
|
||||
title: String,
|
||||
value: Double,
|
||||
enabled: Boolean,
|
||||
keyboardActions: KeyboardActions,
|
||||
onValueChanged: (Double) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var valueState by remember(value) { mutableStateOf(value.toString()) }
|
||||
|
||||
EditTextPreference(
|
||||
title = title,
|
||||
value = valueState,
|
||||
enabled = enabled,
|
||||
isError = value.toString() != valueState,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Number, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = keyboardActions,
|
||||
onValueChanged = {
|
||||
if (it.isEmpty()) valueState = it
|
||||
else it.toDoubleOrNull()?.let { double ->
|
||||
valueState = it
|
||||
onValueChanged(double)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -38,6 +117,7 @@ fun EditTextPreference(
|
||||
title: String,
|
||||
value: String,
|
||||
enabled: Boolean,
|
||||
isError: Boolean,
|
||||
keyboardOptions: KeyboardOptions,
|
||||
keyboardActions: KeyboardActions,
|
||||
onValueChanged: (String) -> Unit,
|
||||
@@ -48,10 +128,14 @@ fun EditTextPreference(
|
||||
singleLine = true,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
enabled = enabled,
|
||||
isError = isError,
|
||||
onValueChange = onValueChanged,
|
||||
label = { Text(title) },
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
trailingIcon = {
|
||||
if (isError) Icon(Icons.TwoTone.Info, "Error", tint = MaterialTheme.colors.error)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -60,7 +144,7 @@ fun EditTextPreference(
|
||||
private fun EditTextPreferencePreview() {
|
||||
EditTextPreference(
|
||||
title = "Advanced Settings",
|
||||
value = "${UInt.MAX_VALUE}",
|
||||
value = UInt.MAX_VALUE.toInt(),
|
||||
enabled = true,
|
||||
keyboardActions = KeyboardActions {},
|
||||
onValueChanged = {}
|
||||
|
||||
@@ -2,14 +2,19 @@ package com.geeksville.mesh.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -21,13 +26,15 @@ fun RegularPreference(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
trailingIcon: ImageVector? = null,
|
||||
) {
|
||||
RegularPreference(
|
||||
title = title,
|
||||
subtitle = AnnotatedString(text = subtitle),
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
enabled = enabled,
|
||||
trailingIcon = trailingIcon,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,8 +45,9 @@ fun RegularPreference(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
trailingIcon: ImageVector? = null,
|
||||
) {
|
||||
Column(
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
@@ -47,17 +55,28 @@ fun RegularPreference(
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(all = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified,
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else MaterialTheme.colors.onSurface.copy(
|
||||
alpha = ContentAlpha.medium
|
||||
),
|
||||
)
|
||||
}
|
||||
if (trailingIcon != null) Icon(
|
||||
trailingIcon, "trailingIcon",
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.End)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,4 +162,5 @@
|
||||
<string name="tile_download_estimate">Tile download estimate:</string>
|
||||
<string name="start_download">Start Download</string>
|
||||
<string name="request_position">Request position</string>
|
||||
<string name="close">Close</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user