config clean up and updates (#522)

This commit is contained in:
Andre K
2022-11-15 12:19:59 -03:00
committed by GitHub
parent e328b5b5d9
commit 9442c1d510
11 changed files with 436 additions and 346 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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