refactor: split configs into individual components (#623)

This commit is contained in:
Andre K
2023-04-17 17:13:26 -03:00
committed by GitHub
parent 6fe5f2733a
commit a5fa47292e
26 changed files with 2793 additions and 1756 deletions

View File

@@ -160,8 +160,14 @@ dependencies {
// Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
// implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
// Navigation
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-compose:$nav_version"
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
// Compose
def composeBom = platform('androidx.compose:compose-bom:2023.03.00')
implementation composeBom

View File

@@ -768,20 +768,13 @@ class MainActivity : AppCompatActivity(), Logging {
handler.removeCallbacksAndMessages(null)
return true
}
R.id.device_settings -> {
R.id.radio_config -> {
supportFragmentManager.beginTransaction()
.add(R.id.mainActivityLayout, DeviceSettingsFragment())
.addToBackStack(null)
.commit()
return true
}
R.id.module_settings -> {
supportFragmentManager.beginTransaction()
.add(R.id.mainActivityLayout, ModuleSettingsFragment())
.addToBackStack(null)
.commit()
return true
}
R.id.save_messages_csv -> {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)

View File

@@ -4,13 +4,61 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.KeyboardArrowRight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.config.AudioConfigItemList
import com.geeksville.mesh.ui.components.config.BluetoothConfigItemList
import com.geeksville.mesh.ui.components.config.CannedMessageConfigItemList
import com.geeksville.mesh.ui.components.config.DeviceConfigItemList
import com.geeksville.mesh.ui.components.config.DisplayConfigItemList
import com.geeksville.mesh.ui.components.config.ExternalNotificationConfigItemList
import com.geeksville.mesh.ui.components.config.LoRaConfigItemList
import com.geeksville.mesh.ui.components.config.MQTTConfigItemList
import com.geeksville.mesh.ui.components.config.NetworkConfigItemList
import com.geeksville.mesh.ui.components.config.PositionConfigItemList
import com.geeksville.mesh.ui.components.config.PowerConfigItemList
import com.geeksville.mesh.ui.components.config.RangeTestConfigItemList
import com.geeksville.mesh.ui.components.config.RemoteHardwareConfigItemList
import com.geeksville.mesh.ui.components.config.SerialConfigItemList
import com.geeksville.mesh.ui.components.config.StoreForwardConfigItemList
import com.geeksville.mesh.ui.components.config.TelemetryConfigItemList
import com.geeksville.mesh.ui.components.config.UserConfigItemList
import com.google.accompanist.themeadapter.appcompat.AppCompatTheme
import dagger.hilt.android.AndroidEntryPoint
@@ -29,9 +77,296 @@ class DeviceSettingsFragment : ScreenFragment("Device Settings"), Logging {
setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground))
setContent {
AppCompatTheme {
DeviceSettingsItemList(model)
RadioConfigNavHost(model)
}
}
}
}
}
enum class ConfigDest(val title: String, val route: String) {
USER("User", "user"),
DEVICE("Device", "device"),
POSITION("Position", "position"),
POWER("Power", "power"),
NETWORK("Network", "network"),
DISPLAY("Display", "display"),
LORA("LoRa", "lora"),
BLUETOOTH("Bluetooth", "bluetooth")
}
enum class ModuleDest(val title: String, val route: String) {
MQTT("MQTT", "mqtt"),
SERIAL("Serial", "serial"),
EXT_NOTIFICATION("External Notification", "ext_notification"),
STORE_FORWARD("Store & Forward", "store_forward"),
RANGE_TEST("Range Test", "range_test"),
TELEMETRY("Telemetry", "telemetry"),
CANNED_MESSAGE("Canned Message", "canned_message"),
AUDIO("Audio", "audio"),
REMOTE_HARDWARE("Remote Hardware", "remote_hardware")
}
@Composable
fun RadioConfigNavHost(viewModel: UIViewModel = viewModel()) {
val navController = rememberNavController()
val focusManager = LocalFocusManager.current
val connectionState by viewModel.connectionState.observeAsState()
val connected = connectionState == MeshService.ConnectionState.CONNECTED
val ourNodeInfo by viewModel.ourNodeInfo.collectAsStateWithLifecycle()
val localConfig by viewModel.localConfig.collectAsStateWithLifecycle()
val moduleConfig by viewModel.moduleConfig.collectAsStateWithLifecycle()
NavHost(navController = navController, startDestination = "home") {
composable("home") { RadioSettingsScreen(navController) }
composable("user") {
UserConfigItemList(
userConfig = ourNodeInfo?.user!!,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { userInput ->
focusManager.clearFocus()
viewModel.setOwner(userInput)
}
)
}
composable("device") {
DeviceConfigItemList(
deviceConfig = localConfig.device,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { deviceInput ->
focusManager.clearFocus()
viewModel.updateDeviceConfig { deviceInput }
}
)
}
composable("position") {
PositionConfigItemList(
positionInfo = ourNodeInfo?.position,
positionConfig = localConfig.position,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { positionPair ->
focusManager.clearFocus()
val (locationInput, positionInput) = positionPair
if (locationInput != ourNodeInfo?.position && positionInput.fixedPosition)
locationInput?.let { viewModel.requestPosition(0, it) }
if (positionInput != localConfig.position) viewModel.updatePositionConfig { positionInput }
}
)
}
composable("power") {
PowerConfigItemList(
powerConfig = localConfig.power,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { powerInput ->
focusManager.clearFocus()
viewModel.updatePowerConfig { powerInput }
}
)
}
composable("network") {
NetworkConfigItemList(
networkConfig = localConfig.network,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { networkInput ->
focusManager.clearFocus()
viewModel.updateNetworkConfig { networkInput }
}
)
}
composable("display") {
DisplayConfigItemList(
displayConfig = localConfig.display,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { displayInput ->
focusManager.clearFocus()
viewModel.updateDisplayConfig { displayInput }
}
)
}
composable("lora") {
LoRaConfigItemList(
loraConfig = localConfig.lora,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { loraInput ->
focusManager.clearFocus()
viewModel.updateLoraConfig { loraInput }
}
)
}
composable("bluetooth") {
BluetoothConfigItemList(
bluetoothConfig = localConfig.bluetooth,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { bluetoothInput ->
focusManager.clearFocus()
viewModel.updateBluetoothConfig { bluetoothInput }
}
)
}
composable("mqtt") {
MQTTConfigItemList(
mqttConfig = moduleConfig.mqtt,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { mqttInput ->
focusManager.clearFocus()
viewModel.updateMQTTConfig { mqttInput }
}
)
}
composable("serial") {
SerialConfigItemList(
serialConfig = moduleConfig.serial,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { serialInput ->
focusManager.clearFocus()
viewModel.updateSerialConfig { serialInput }
}
)
}
composable("ext_notification") {
ExternalNotificationConfigItemList(
externalNotificationConfig = moduleConfig.externalNotification,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { externalNotificationInput ->
focusManager.clearFocus()
viewModel.updateExternalNotificationConfig { externalNotificationInput }
}
)
}
composable("store_forward") {
StoreForwardConfigItemList(
storeForwardConfig = moduleConfig.storeForward,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { storeForwardInput ->
focusManager.clearFocus()
viewModel.updateStoreForwardConfig { storeForwardInput }
}
)
}
composable("range_test") {
RangeTestConfigItemList(
rangeTestConfig = moduleConfig.rangeTest,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { rangeTestInput ->
focusManager.clearFocus()
viewModel.updateRangeTestConfig { rangeTestInput }
}
)
}
composable("telemetry") {
TelemetryConfigItemList(
telemetryConfig = moduleConfig.telemetry,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { telemetryInput ->
focusManager.clearFocus()
viewModel.updateTelemetryConfig { telemetryInput }
}
)
}
composable("canned_message") {
CannedMessageConfigItemList(
cannedMessageConfig = moduleConfig.cannedMessage,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { cannedMessageInput ->
focusManager.clearFocus()
viewModel.updateCannedMessageConfig { cannedMessageInput }
}
)
}
composable("audio") {
AudioConfigItemList(
audioConfig = moduleConfig.audio,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { audioInput ->
focusManager.clearFocus()
viewModel.updateAudioConfig { audioInput }
}
)
}
composable("remote_hardware") {
RemoteHardwareConfigItemList(
remoteHardwareConfig = moduleConfig.remoteHardware,
enabled = connected,
focusManager = focusManager,
onSaveClicked = { remoteHardwareInput ->
focusManager.clearFocus()
viewModel.updateRemoteHardwareConfig { remoteHardwareInput }
}
)
}
}
}
@Composable
fun NavCard(title: String, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 2.dp, horizontal = 16.dp)
.clickable { onClick() },
elevation = 4.dp
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.body1,
modifier = Modifier.weight(1f)
)
Icon(
Icons.TwoTone.KeyboardArrowRight, "trailingIcon",
modifier = Modifier.wrapContentSize(),
)
}
}
}
@Composable
fun RadioSettingsScreen(navController: NavHostController) {
LazyColumn {
item {
PreferenceCategory(
stringResource(id = R.string.device_settings), Modifier.padding(horizontal = 16.dp)
)
}
items(ConfigDest.values()) { configs ->
NavCard(configs.title) { navController.navigate(configs.route) }
}
item {
PreferenceCategory(
stringResource(id = R.string.module_settings), Modifier.padding(horizontal = 16.dp)
)
}
items(ModuleDest.values()) { modules ->
NavCard(modules.title) { navController.navigate(modules.route) }
}
}
}
@Preview(showBackground = true)
@Composable
fun RadioSettingsScreenPreview(){
RadioSettingsScreen(NavHostController(LocalContext.current))
}

View File

@@ -1,910 +0,0 @@
package com.geeksville.mesh.ui
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.geeksville.mesh.ConfigProtos
import com.geeksville.mesh.ConfigProtos.Config.NetworkConfig
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.EditIPv4Preference
import com.geeksville.mesh.ui.components.EditListPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.RegularPreference
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun DeviceSettingsItemList(viewModel: UIViewModel = viewModel()) {
val focusManager = LocalFocusManager.current
val hasWifi = viewModel.hasWifi()
val connectionState by viewModel.connectionState.observeAsState()
val connected = connectionState == MeshService.ConnectionState.CONNECTED
val localConfig by viewModel.localConfig.collectAsStateWithLifecycle()
val ourNodeInfo by viewModel.ourNodeInfo.collectAsStateWithLifecycle()
var userInput by remember(ourNodeInfo?.user) { mutableStateOf(ourNodeInfo?.user) }
var positionInfo by remember(ourNodeInfo?.position) { mutableStateOf(ourNodeInfo?.position) }
// Temporary [ConfigProtos.Config] state holders
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()
) {
item { PreferenceCategory(text = "User Config") }
item {
RegularPreference(title = "Node ID",
subtitle = userInput?.id ?: stringResource(id = R.string.unknown),
onClick = {})
}
item { Divider() }
item {
EditTextPreference(title = "Long name",
value = userInput?.longName ?: stringResource(id = R.string.unknown_username),
maxSize = 39, // long_name max_size:40
enabled = connected && userInput?.longName != null,
isError = userInput?.longName.isNullOrEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
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)) }
})
}
item {
EditTextPreference(title = "Short name",
value = userInput?.shortName ?: stringResource(id = R.string.unknown),
maxSize = 4, // short_name max_size:5
enabled = connected && userInput?.shortName != null,
isError = userInput?.shortName.isNullOrEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
userInput?.let { userInput = it.copy(shortName = value) }
})
}
item {
RegularPreference(title = "Hardware model",
subtitle = userInput?.hwModel?.name ?: stringResource(id = R.string.unknown),
onClick = {})
}
item { Divider() }
item {
SwitchPreference(title = "Licensed amateur radio",
checked = userInput?.isLicensed ?: false,
enabled = connected && userInput?.isLicensed != null,
onCheckedChange = { value ->
userInput?.let { userInput = it.copy(isLicensed = value) }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = userInput != ourNodeInfo?.user,
onCancelClicked = {
focusManager.clearFocus()
userInput = ourNodeInfo?.user
}, onSaveClicked = {
focusManager.clearFocus()
userInput?.let { viewModel.setOwner(it) }
})
}
item { PreferenceCategory(text = "Device Config") }
item {
DropDownPreference(title = "Role",
enabled = connected,
items = ConfigProtos.Config.DeviceConfig.Role.values()
.filter { it != ConfigProtos.Config.DeviceConfig.Role.UNRECOGNIZED }
.map { it to it.name },
selectedItem = deviceInput.role,
onItemSelected = { deviceInput = deviceInput.copy { role = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Serial output enabled",
checked = deviceInput.serialEnabled,
enabled = connected,
onCheckedChange = { deviceInput = deviceInput.copy { serialEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Debug log enabled",
checked = deviceInput.debugLogEnabled,
enabled = connected,
onCheckedChange = { deviceInput = deviceInput.copy { debugLogEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Redefine PIN_BUTTON",
value = deviceInput.buttonGpio,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { buttonGpio = it }
})
}
item {
EditTextPreference(title = "Redefine PIN_BUZZER",
value = deviceInput.buzzerGpio,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { buzzerGpio = it }
})
}
item {
DropDownPreference(title = "Rebroadcast mode",
enabled = connected,
items = ConfigProtos.Config.DeviceConfig.RebroadcastMode.values()
.filter { it != ConfigProtos.Config.DeviceConfig.RebroadcastMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = deviceInput.rebroadcastMode,
onItemSelected = { deviceInput = deviceInput.copy { rebroadcastMode = it } })
}
item { Divider() }
item {
EditTextPreference(title = "NodeInfo broadcast interval",
value = deviceInput.nodeInfoBroadcastSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { nodeInfoBroadcastSecs = it }
})
}
item {
SwitchPreference(title = "Double tap as button press",
checked = deviceInput.doubleTapAsButtonPress,
enabled = connected,
onCheckedChange = {
deviceInput = deviceInput.copy { doubleTapAsButtonPress = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = deviceInput != localConfig.device,
onCancelClicked = {
focusManager.clearFocus()
deviceInput = localConfig.device
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateDeviceConfig { deviceInput }
})
}
item { PreferenceCategory(text = "Position Config") }
item {
EditTextPreference(title = "Position broadcast interval",
value = positionInput.positionBroadcastSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { positionBroadcastSecs = it }
})
}
item {
SwitchPreference(title = "Smart position enabled",
checked = positionInput.positionBroadcastSmartEnabled,
enabled = connected,
onCheckedChange = {
positionInput = positionInput.copy { positionBroadcastSmartEnabled = it }
})
}
item { Divider() }
if (positionInput.positionBroadcastSmartEnabled) {
item {
EditTextPreference(title = "Smart broadcast minimum distance",
value = positionInput.broadcastSmartMinimumDistance,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { broadcastSmartMinimumDistance = it }
})
}
item {
EditTextPreference(title = "Smart broadcast minimum interval",
value = positionInput.broadcastSmartMinimumIntervalSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { broadcastSmartMinimumIntervalSecs = it }
})
}
}
item {
SwitchPreference(title = "Use fixed position",
checked = positionInput.fixedPosition,
enabled = connected,
onCheckedChange = { positionInput = positionInput.copy { fixedPosition = it } })
}
item { Divider() }
if (positionInput.fixedPosition) {
item {
EditTextPreference(title = "Latitude",
value = positionInfo?.latitude ?: 0.0,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value >= -90 && value <= 90.0)
positionInfo?.let { positionInfo = it.copy(latitude = value) }
})
}
item {
EditTextPreference(title = "Longitude",
value = positionInfo?.longitude ?: 0.0,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value >= -180 && value <= 180.0)
positionInfo?.let { positionInfo = it.copy(longitude = value) }
})
}
item {
EditTextPreference(title = "Altitude",
value = positionInfo?.altitude ?: 0,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
positionInfo?.let { positionInfo = it.copy(altitude = value) }
})
}
}
item {
SwitchPreference(title = "GPS enabled",
checked = positionInput.gpsEnabled,
enabled = connected,
onCheckedChange = { positionInput = positionInput.copy { gpsEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "GPS update interval",
value = positionInput.gpsUpdateInterval,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { gpsUpdateInterval = it } })
}
item {
EditTextPreference(title = "Fix attempt duration",
value = positionInput.gpsAttemptTime,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { gpsAttemptTime = it } })
}
item {
BitwisePreference(title = "Position flags",
value = positionInput.positionFlags,
enabled = connected,
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 {
EditTextPreference(title = "Redefine GPS_RX_PIN",
value = positionInput.rxGpio,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { rxGpio = it } })
}
item {
EditTextPreference(title = "Redefine GPS_TX_PIN",
value = positionInput.txGpio,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { txGpio = it } })
}
item {
PreferenceFooter(
enabled = positionInput != localConfig.position || positionInfo != ourNodeInfo?.position,
onCancelClicked = {
focusManager.clearFocus()
positionInput = localConfig.position
positionInfo = ourNodeInfo?.position
},
onSaveClicked = {
focusManager.clearFocus()
if (positionInfo != ourNodeInfo?.position && positionInput.fixedPosition)
positionInfo?.let { viewModel.requestPosition(0, it) }
if (positionInput != localConfig.position) viewModel.updatePositionConfig { positionInput }
})
}
item { PreferenceCategory(text = "Power Config") }
item {
SwitchPreference(title = "Enable power saving mode",
checked = powerInput.isPowerSaving,
enabled = connected && hasWifi, // We consider hasWifi = ESP32
onCheckedChange = { powerInput = powerInput.copy { isPowerSaving = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Shutdown on battery delay",
value = powerInput.onBatteryShutdownAfterSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
powerInput = powerInput.copy { onBatteryShutdownAfterSecs = it }
})
}
item {
EditTextPreference(title = "ADC multiplier override ratio",
value = powerInput.adcMultiplierOverride,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { adcMultiplierOverride = it } })
}
item {
EditTextPreference(title = "Wait for Bluetooth duration",
value = powerInput.waitBluetoothSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { waitBluetoothSecs = it } })
}
item {
EditTextPreference(title = "Mesh SDS timeout",
value = powerInput.meshSdsTimeoutSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { meshSdsTimeoutSecs = it } })
}
item {
EditTextPreference(title = "Super deep sleep duration",
value = powerInput.sdsSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { sdsSecs = it } })
}
item {
EditTextPreference(title = "Light sleep duration",
value = powerInput.lsSecs,
enabled = connected && hasWifi, // we consider hasWifi = ESP32
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { lsSecs = it } })
}
item {
EditTextPreference(title = "Minimum wake time",
value = powerInput.minWakeSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { minWakeSecs = it } })
}
item {
PreferenceFooter(
enabled = powerInput != localConfig.power,
onCancelClicked = {
focusManager.clearFocus()
powerInput = localConfig.power
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updatePowerConfig { powerInput }
})
}
item { PreferenceCategory(text = "Network Config") }
item {
SwitchPreference(title = "WiFi enabled",
checked = networkInput.wifiEnabled,
enabled = connected && hasWifi,
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "SSID",
value = networkInput.wifiSsid,
maxSize = 32, // wifi_ssid max_size:33
enabled = connected && hasWifi,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { wifiSsid = it }
})
}
item {
EditTextPreference(title = "PSK",
value = networkInput.wifiPsk,
maxSize = 63, // wifi_psk max_size:64
enabled = connected && hasWifi,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Password, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { wifiPsk = it }
})
}
item {
EditTextPreference(title = "NTP server",
value = networkInput.ntpServer,
maxSize = 32, // ntp_server max_size:33
enabled = connected && hasWifi,
isError = networkInput.ntpServer.isEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { ntpServer = it }
})
}
item {
EditTextPreference(title = "rsyslog server",
value = networkInput.rsyslogServer,
maxSize = 32, // rsyslog_server max_size:33
enabled = connected && hasWifi,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { rsyslogServer = it }
})
}
item {
SwitchPreference(title = "Ethernet enabled",
checked = networkInput.ethEnabled,
enabled = connected,
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "IPv4 mode",
enabled = connected,
items = NetworkConfig.AddressMode.values()
.filter { it != NetworkConfig.AddressMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = networkInput.addressMode,
onItemSelected = { networkInput = networkInput.copy { addressMode = it } })
}
item { Divider() }
item { PreferenceCategory(text = "IPv4 Config") }
item {
EditIPv4Preference(title = "IP",
value = networkInput.ipv4Config.ip,
enabled = connected && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { ip = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "Gateway",
value = networkInput.ipv4Config.gateway,
enabled = connected && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { gateway = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "Subnet",
value = networkInput.ipv4Config.subnet,
enabled = connected && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { subnet = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "DNS",
value = networkInput.ipv4Config.dns,
enabled = connected && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { dns = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
PreferenceFooter(
enabled = networkInput != localConfig.network,
onCancelClicked = {
focusManager.clearFocus()
networkInput = localConfig.network
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateNetworkConfig { networkInput }
})
}
item { PreferenceCategory(text = "Display Config") }
item {
EditTextPreference(title = "Screen timeout",
value = displayInput.screenOnSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { displayInput = displayInput.copy { screenOnSecs = it } })
}
item {
DropDownPreference(title = "GPS coordinates format",
enabled = connected,
items = ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.values()
.filter { it != ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.gpsFormat,
onItemSelected = { displayInput = displayInput.copy { gpsFormat = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Auto screen carousel",
value = displayInput.autoScreenCarouselSecs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
displayInput = displayInput.copy { autoScreenCarouselSecs = it }
})
}
item {
SwitchPreference(title = "Compass north top",
checked = displayInput.compassNorthTop,
enabled = connected,
onCheckedChange = { displayInput = displayInput.copy { compassNorthTop = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Flip screen",
checked = displayInput.flipScreen,
enabled = connected,
onCheckedChange = { displayInput = displayInput.copy { flipScreen = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Display units",
enabled = connected,
items = ConfigProtos.Config.DisplayConfig.DisplayUnits.values()
.filter { it != ConfigProtos.Config.DisplayConfig.DisplayUnits.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.units,
onItemSelected = { displayInput = displayInput.copy { units = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Override OLED auto-detect",
enabled = connected,
items = ConfigProtos.Config.DisplayConfig.OledType.values()
.filter { it != ConfigProtos.Config.DisplayConfig.OledType.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.oled,
onItemSelected = { displayInput = displayInput.copy { oled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Display mode",
enabled = connected,
items = ConfigProtos.Config.DisplayConfig.DisplayMode.values()
.filter { it != ConfigProtos.Config.DisplayConfig.DisplayMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.displaymode,
onItemSelected = { displayInput = displayInput.copy { displaymode = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Heading bold",
checked = displayInput.headingBold,
enabled = connected,
onCheckedChange = { displayInput = displayInput.copy { headingBold = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Wake screen on tap or motion",
checked = displayInput.wakeOnTapOrMotion,
enabled = connected,
onCheckedChange = { displayInput = displayInput.copy { wakeOnTapOrMotion = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = displayInput != localConfig.display,
onCancelClicked = {
focusManager.clearFocus()
displayInput = localConfig.display
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateDisplayConfig { displayInput }
})
}
item { PreferenceCategory(text = "LoRa Config") }
item {
SwitchPreference(title = "Use modem preset",
checked = loraInput.usePreset,
enabled = connected,
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } })
}
item { Divider() }
if (loraInput.usePreset) {
item {
DropDownPreference(title = "Modem preset",
enabled = connected && loraInput.usePreset,
items = ConfigProtos.Config.LoRaConfig.ModemPreset.values()
.filter { it != ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED }
.map { it to it.name },
selectedItem = loraInput.modemPreset,
onItemSelected = { loraInput = loraInput.copy { modemPreset = it } })
}
item { Divider() }
} else {
item {
EditTextPreference(title = "Bandwidth",
value = loraInput.bandwidth,
enabled = connected && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { bandwidth = it } })
}
item {
EditTextPreference(title = "Spread factor",
value = loraInput.spreadFactor,
enabled = connected && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { spreadFactor = it } })
}
item {
EditTextPreference(title = "Coding rate",
value = loraInput.codingRate,
enabled = connected && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { codingRate = it } })
}
}
item {
EditTextPreference(title = "Frequency offset (MHz)",
value = loraInput.frequencyOffset,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { frequencyOffset = it } })
}
item {
DropDownPreference(title = "Region (frequency plan)",
enabled = connected,
items = ConfigProtos.Config.LoRaConfig.RegionCode.values()
.filter { it != ConfigProtos.Config.LoRaConfig.RegionCode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = loraInput.region,
onItemSelected = { loraInput = loraInput.copy { region = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Hop limit",
value = loraInput.hopLimit,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { hopLimit = it } })
}
item {
SwitchPreference(title = "TX enabled",
checked = loraInput.txEnabled,
enabled = connected,
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "TX power",
value = loraInput.txPower,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { txPower = it } })
}
item {
EditTextPreference(title = "Channel number",
value = loraInput.channelNum,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { channelNum = it } })
}
item {
SwitchPreference(title = "Override Duty Cycle",
checked = loraInput.overrideDutyCycle,
enabled = connected,
onCheckedChange = { loraInput = loraInput.copy { overrideDutyCycle = it } })
}
item { Divider() }
item {
EditListPreference(title = "Ignore incoming",
list = loraInput.ignoreIncomingList,
maxCount = 3, // ignore_incoming max_count:3
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValuesChanged = { list ->
loraInput = loraInput.copy {
ignoreIncoming.clear()
ignoreIncoming.addAll(list.filter { it != 0 })
}
})
}
item {
SwitchPreference(title = "SX126X RX boosted gain",
checked = loraInput.sx126XRxBoostedGain,
enabled = connected,
onCheckedChange = { loraInput = loraInput.copy { sx126XRxBoostedGain = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Override frequency (MHz)",
value = loraInput.overrideFrequency,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { overrideFrequency = it } })
}
item {
PreferenceFooter(
enabled = loraInput != localConfig.lora,
onCancelClicked = {
focusManager.clearFocus()
loraInput = localConfig.lora
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateLoraConfig { loraInput }
})
}
item { PreferenceCategory(text = "Bluetooth Config") }
item {
SwitchPreference(title = "Bluetooth enabled",
checked = bluetoothInput.enabled,
enabled = connected,
onCheckedChange = { bluetoothInput = bluetoothInput.copy { enabled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Pairing mode",
enabled = connected,
items = ConfigProtos.Config.BluetoothConfig.PairingMode.values()
.filter { it != ConfigProtos.Config.BluetoothConfig.PairingMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = bluetoothInput.mode,
onItemSelected = { bluetoothInput = bluetoothInput.copy { mode = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Fixed PIN",
value = bluetoothInput.fixedPin,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
if (it.toString().length == 6) // ensure 6 digits
bluetoothInput = bluetoothInput.copy { fixedPin = it }
})
}
item {
PreferenceFooter(
enabled = bluetoothInput != localConfig.bluetooth,
onCancelClicked = {
focusManager.clearFocus()
bluetoothInput = localConfig.bluetooth
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateBluetoothConfig { bluetoothInput }
})
}
}
}

View File

@@ -1,37 +0,0 @@
package com.geeksville.mesh.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.UIViewModel
import com.google.accompanist.themeadapter.appcompat.AppCompatTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class ModuleSettingsFragment : ScreenFragment("Module Settings"), Logging {
private val model: UIViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground))
setContent {
AppCompatTheme {
ModuleSettingsItemList(model)
}
}
}
}
}

View File

@@ -1,790 +0,0 @@
package com.geeksville.mesh.ui
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun ModuleSettingsItemList(viewModel: UIViewModel = viewModel()) {
val focusManager = LocalFocusManager.current
val connectionState by viewModel.connectionState.observeAsState()
val connected = connectionState == MeshService.ConnectionState.CONNECTED
val moduleConfig by viewModel.moduleConfig.collectAsStateWithLifecycle()
// Temporary [ModuleConfigProtos.ModuleConfig] state holders
var mqttInput by remember(moduleConfig.mqtt) { mutableStateOf(moduleConfig.mqtt) }
var serialInput by remember(moduleConfig.serial) { mutableStateOf(moduleConfig.serial) }
var externalNotificationInput by remember(moduleConfig.externalNotification) { mutableStateOf(moduleConfig.externalNotification) }
var storeForwardInput by remember(moduleConfig.storeForward) { mutableStateOf(moduleConfig.storeForward) }
var rangeTestInput by remember(moduleConfig.rangeTest) { mutableStateOf(moduleConfig.rangeTest) }
var telemetryInput by remember(moduleConfig.telemetry) { mutableStateOf(moduleConfig.telemetry) }
var cannedMessageInput by remember(moduleConfig.cannedMessage) { mutableStateOf(moduleConfig.cannedMessage) }
var audioInput by remember(moduleConfig.audio) { mutableStateOf(moduleConfig.audio) }
var remoteHardwareInput by remember(moduleConfig.remoteHardware) { mutableStateOf(moduleConfig.remoteHardware) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "MQTT Config") }
item {
SwitchPreference(title = "MQTT enabled",
checked = mqttInput.enabled,
enabled = connected,
onCheckedChange = { mqttInput = mqttInput.copy { enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Address",
value = mqttInput.address,
maxSize = 63, // address max_size:64
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { mqttInput = mqttInput.copy { address = it } })
}
item {
EditTextPreference(title = "Username",
value = mqttInput.username,
maxSize = 63, // username max_size:64
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { mqttInput = mqttInput.copy { username = it } })
}
item {
EditTextPreference(title = "Password",
value = mqttInput.password,
maxSize = 63, // password max_size:64
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { mqttInput = mqttInput.copy { password = it } })
}
item {
SwitchPreference(title = "Encryption enabled",
checked = mqttInput.encryptionEnabled,
enabled = connected,
onCheckedChange = { mqttInput = mqttInput.copy { encryptionEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "JSON output enabled",
checked = mqttInput.jsonEnabled,
enabled = connected,
onCheckedChange = { mqttInput = mqttInput.copy { jsonEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "TLS enabled",
checked = mqttInput.tlsEnabled,
enabled = connected,
onCheckedChange = { mqttInput = mqttInput.copy { tlsEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Root topic",
value = mqttInput.root,
maxSize = 15, // root max_size:16
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { mqttInput = mqttInput.copy { root = it } })
}
item {
PreferenceFooter(
enabled = mqttInput != moduleConfig.mqtt,
onCancelClicked = {
focusManager.clearFocus()
mqttInput = moduleConfig.mqtt
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateMQTTConfig { mqttInput }
})
}
item { PreferenceCategory(text = "Serial Config") }
item {
SwitchPreference(title = "Serial enabled",
checked = serialInput.enabled,
enabled = connected,
onCheckedChange = { serialInput = serialInput.copy { enabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Echo enabled",
checked = serialInput.echo,
enabled = connected,
onCheckedChange = { serialInput = serialInput.copy { echo = it } })
}
item { Divider() }
item {
EditTextPreference(title = "RX",
value = serialInput.rxd,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { rxd = it } })
}
item {
EditTextPreference(title = "TX",
value = serialInput.txd,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { txd = it } })
}
item {
DropDownPreference(title = "Serial baud rate",
enabled = connected,
items = ModuleConfig.SerialConfig.Serial_Baud.values()
.filter { it != ModuleConfig.SerialConfig.Serial_Baud.UNRECOGNIZED }
.map { it to it.name },
selectedItem = serialInput.baud,
onItemSelected = { serialInput = serialInput.copy { baud = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Timeout",
value = serialInput.timeout,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { timeout = it } })
}
item {
DropDownPreference(title = "Serial mode",
enabled = connected,
items = ModuleConfig.SerialConfig.Serial_Mode.values()
.filter { it != ModuleConfig.SerialConfig.Serial_Mode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = serialInput.mode,
onItemSelected = { serialInput = serialInput.copy { mode = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = serialInput != moduleConfig.serial,
onCancelClicked = {
focusManager.clearFocus()
serialInput = moduleConfig.serial
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateSerialConfig { serialInput }
})
}
item { PreferenceCategory(text = "External Notification Config") }
item {
SwitchPreference(title = "External notification enabled",
checked = externalNotificationInput.enabled,
enabled = connected,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { enabled = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Output milliseconds",
value = externalNotificationInput.outputMs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputMs = it }
})
}
item {
EditTextPreference(title = "Output",
value = externalNotificationInput.output,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { output = it }
})
}
item {
SwitchPreference(title = "Active",
checked = externalNotificationInput.active,
enabled = connected,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { active = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert message",
checked = externalNotificationInput.alertMessage,
enabled = connected,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { alertMessage = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert bell",
checked = externalNotificationInput.alertBell,
enabled = connected,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { alertBell = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Use PWM buzzer",
checked = externalNotificationInput.usePwm,
enabled = connected,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { usePwm = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Output vibra",
value = externalNotificationInput.outputVibra,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputVibra = it }
})
}
item {
EditTextPreference(title = "Output buzzer",
value = externalNotificationInput.outputBuzzer,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputBuzzer = it }
})
}
item {
SwitchPreference(title = "Alert message vibra",
checked = externalNotificationInput.alertMessageVibra,
enabled = connected,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertMessageVibra = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert message buzzer",
checked = externalNotificationInput.alertMessageBuzzer,
enabled = connected,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertMessageBuzzer = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert bell vibra",
checked = externalNotificationInput.alertBellVibra,
enabled = connected,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertBellVibra = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert bell buzzer",
checked = externalNotificationInput.alertBellBuzzer,
enabled = connected,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertBellBuzzer = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Nag timeout",
value = externalNotificationInput.nagTimeout,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { nagTimeout = it }
})
}
item {
PreferenceFooter(
enabled = externalNotificationInput != moduleConfig.externalNotification,
onCancelClicked = {
focusManager.clearFocus()
externalNotificationInput = moduleConfig.externalNotification
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateExternalNotificationConfig { externalNotificationInput }
})
}
item { PreferenceCategory(text = "Store & Forward Config") }
item {
SwitchPreference(title = "Store & Forward enabled",
checked = storeForwardInput.enabled,
enabled = connected,
onCheckedChange = { storeForwardInput = storeForwardInput.copy { enabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Heartbeat",
checked = storeForwardInput.heartbeat,
enabled = connected,
onCheckedChange = { storeForwardInput = storeForwardInput.copy { heartbeat = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Number of records",
value = storeForwardInput.records,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { storeForwardInput = storeForwardInput.copy { records = it } })
}
item {
EditTextPreference(title = "History return max",
value = storeForwardInput.historyReturnMax,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
storeForwardInput = storeForwardInput.copy { historyReturnMax = it }
})
}
item {
EditTextPreference(title = "History return window",
value = storeForwardInput.historyReturnWindow,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
storeForwardInput = storeForwardInput.copy { historyReturnWindow = it }
})
}
item {
PreferenceFooter(
enabled = storeForwardInput != moduleConfig.storeForward,
onCancelClicked = {
focusManager.clearFocus()
storeForwardInput = moduleConfig.storeForward
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateStoreForwardConfig { storeForwardInput }
})
}
item { PreferenceCategory(text = "Range Test Config") }
item {
SwitchPreference(title = "Range test enabled",
checked = rangeTestInput.enabled,
enabled = connected,
onCheckedChange = { rangeTestInput = rangeTestInput.copy { enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Sender message interval",
value = rangeTestInput.sender,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { rangeTestInput = rangeTestInput.copy { sender = it } })
}
item {
SwitchPreference(title = "Save .CSV in storage (ESP32 only)",
checked = rangeTestInput.save,
enabled = connected,
onCheckedChange = { rangeTestInput = rangeTestInput.copy { save = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = rangeTestInput != moduleConfig.rangeTest,
onCancelClicked = {
focusManager.clearFocus()
rangeTestInput = moduleConfig.rangeTest
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateRangeTestConfig { rangeTestInput }
})
}
item { PreferenceCategory(text = "Telemetry Config") }
item {
EditTextPreference(title = "Device metrics update interval",
value = telemetryInput.deviceUpdateInterval,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
telemetryInput = telemetryInput.copy { deviceUpdateInterval = it }
})
}
item {
EditTextPreference(title = "Environment metrics update interval",
value = telemetryInput.environmentUpdateInterval,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
telemetryInput = telemetryInput.copy { environmentUpdateInterval = it }
})
}
item {
SwitchPreference(title = "Environment metrics module enabled",
checked = telemetryInput.environmentMeasurementEnabled,
enabled = connected,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentMeasurementEnabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Environment metrics on-screen enabled",
checked = telemetryInput.environmentScreenEnabled,
enabled = connected,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentScreenEnabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Environment metrics use Fahrenheit",
checked = telemetryInput.environmentDisplayFahrenheit,
enabled = connected,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentDisplayFahrenheit = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = telemetryInput != moduleConfig.telemetry,
onCancelClicked = {
focusManager.clearFocus()
telemetryInput = moduleConfig.telemetry
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateTelemetryConfig { telemetryInput }
})
}
item { PreferenceCategory(text = "Canned Message Config") }
item {
SwitchPreference(title = "Canned message enabled",
checked = cannedMessageInput.enabled,
enabled = connected,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { enabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Rotary encoder #1 enabled",
checked = cannedMessageInput.rotary1Enabled,
enabled = connected,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { rotary1Enabled = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "GPIO pin for rotary encoder A port",
value = cannedMessageInput.inputbrokerPinA,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinA = it }
})
}
item {
EditTextPreference(title = "GPIO pin for rotary encoder B port",
value = cannedMessageInput.inputbrokerPinB,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinB = it }
})
}
item {
EditTextPreference(title = "GPIO pin for rotary encoder Press port",
value = cannedMessageInput.inputbrokerPinPress,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinPress = it }
})
}
item {
DropDownPreference(title = "Generate input event on Press",
enabled = connected,
items = ModuleConfig.CannedMessageConfig.InputEventChar.values()
.filter { it != ModuleConfig.CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventPress,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventPress = it }
})
}
item { Divider() }
item {
DropDownPreference(title = "Generate input event on CW",
enabled = connected,
items = ModuleConfig.CannedMessageConfig.InputEventChar.values()
.filter { it != ModuleConfig.CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventCw,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCw = it }
})
}
item { Divider() }
item {
DropDownPreference(title = "Generate input event on CCW",
enabled = connected,
items = ModuleConfig.CannedMessageConfig.InputEventChar.values()
.filter { it != ModuleConfig.CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventCcw,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCcw = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Up/Down/Select input enabled",
checked = cannedMessageInput.updown1Enabled,
enabled = connected,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { updown1Enabled = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Allow input source",
value = cannedMessageInput.allowInputSource,
maxSize = 63, // allow_input_source max_size:16
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { allowInputSource = it }
})
}
item {
SwitchPreference(title = "Send bell",
checked = cannedMessageInput.sendBell,
enabled = connected,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { sendBell = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = cannedMessageInput != moduleConfig.cannedMessage,
onCancelClicked = {
focusManager.clearFocus()
cannedMessageInput = moduleConfig.cannedMessage
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateCannedMessageConfig { cannedMessageInput }
})
}
item { PreferenceCategory(text = "Audio Config") }
item {
SwitchPreference(title = "CODEC 2 enabled",
checked = audioInput.codec2Enabled,
enabled = connected,
onCheckedChange = { audioInput = audioInput.copy { codec2Enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "PTT pin",
value = audioInput.pttPin,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { pttPin = it } })
}
item {
DropDownPreference(title = "CODEC2 sample rate",
enabled = connected,
items = ModuleConfig.AudioConfig.Audio_Baud.values()
.filter { it != ModuleConfig.AudioConfig.Audio_Baud.UNRECOGNIZED }
.map { it to it.name },
selectedItem = audioInput.bitrate,
onItemSelected = { audioInput = audioInput.copy { bitrate = it } })
}
item { Divider() }
item {
EditTextPreference(title = "I2S word select",
value = audioInput.i2SWs,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SWs = it } })
}
item {
EditTextPreference(title = "I2S data in",
value = audioInput.i2SSd,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SSd = it } })
}
item {
EditTextPreference(title = "I2S data out",
value = audioInput.i2SDin,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SDin = it } })
}
item {
EditTextPreference(title = "I2S clock",
value = audioInput.i2SSck,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SSck = it } })
}
item {
PreferenceFooter(
enabled = audioInput != moduleConfig.audio,
onCancelClicked = {
focusManager.clearFocus()
audioInput = moduleConfig.audio
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateAudioConfig { audioInput }
})
}
item { PreferenceCategory(text = "Remote Hardware Config") }
item {
SwitchPreference(title = "Remote Hardware enabled",
checked = remoteHardwareInput.enabled,
enabled = connected,
onCheckedChange = {
remoteHardwareInput = remoteHardwareInput.copy { enabled = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = remoteHardwareInput != moduleConfig.remoteHardware,
onCancelClicked = {
focusManager.clearFocus()
remoteHardwareInput = moduleConfig.remoteHardware
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateRemoteHardwareConfig { remoteHardwareInput }
})
}
}
}

View File

@@ -41,35 +41,41 @@ fun PreferenceFooter(
Row(
modifier = modifier
.fillMaxWidth()
.size(48.dp),
.height(64.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Button(
modifier = modifier
.fillMaxWidth()
.height(48.dp)
.weight(1f),
enabled = enabled,
onClick = onNegativeClicked,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Red.copy(alpha = 0.6f),
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) {
Text(
text = stringResource(id = negativeText),
style = MaterialTheme.typography.body1,
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified,
)
}
Button(
modifier = modifier
.fillMaxWidth()
.height(48.dp)
.weight(1f),
enabled = enabled,
onClick = onPositiveClicked,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Green)
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Green.copy(alpha = 0.6f),
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) {
Text(
text = stringResource(id = positiveText),
style = MaterialTheme.typography.body1,
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.DarkGray,
)
}
}

View File

@@ -0,0 +1,73 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.isSystemInDarkTheme
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.Card
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
@Composable
fun TextDividerPreference(
title: String,
modifier: Modifier = Modifier,
enabled: Boolean = true,
trailingIcon: ImageVector? = null,
) {
TextDividerPreference(
title = AnnotatedString(text = title),
enabled = enabled,
modifier = modifier,
trailingIcon = trailingIcon,
)
}
@Composable
fun TextDividerPreference(
title: AnnotatedString,
modifier: Modifier = Modifier,
enabled: Boolean = true,
trailingIcon: ImageVector? = null,
) {
Card(
modifier = modifier.fillMaxWidth(),
backgroundColor = if (isSystemInDarkTheme()) Color.DarkGray else Color.LightGray,
) {
Row(
modifier = modifier
.fillMaxWidth()
.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,
)
if (trailingIcon != null) Icon(
trailingIcon, "trailingIcon",
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
)
}
}
}
@Preview(showBackground = true)
@Composable
private fun TextDividerPreferencePreview() {
TextDividerPreference(title = "Advanced settings")
}

View File

@@ -0,0 +1,119 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.AudioConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun AudioConfigItemList(
audioConfig: AudioConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (AudioConfig) -> Unit,
) {
var audioInput by remember(audioConfig) { mutableStateOf(audioConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Audio Config") }
item {
SwitchPreference(title = "CODEC 2 enabled",
checked = audioInput.codec2Enabled,
enabled = enabled,
onCheckedChange = { audioInput = audioInput.copy { codec2Enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "PTT pin",
value = audioInput.pttPin,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { pttPin = it } })
}
item {
DropDownPreference(title = "CODEC2 sample rate",
enabled = enabled,
items = AudioConfig.Audio_Baud.values()
.filter { it != AudioConfig.Audio_Baud.UNRECOGNIZED }
.map { it to it.name },
selectedItem = audioInput.bitrate,
onItemSelected = { audioInput = audioInput.copy { bitrate = it } })
}
item { Divider() }
item {
EditTextPreference(title = "I2S word select",
value = audioInput.i2SWs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SWs = it } })
}
item {
EditTextPreference(title = "I2S data in",
value = audioInput.i2SSd,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SSd = it } })
}
item {
EditTextPreference(title = "I2S data out",
value = audioInput.i2SDin,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SDin = it } })
}
item {
EditTextPreference(title = "I2S clock",
value = audioInput.i2SSck,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { i2SSck = it } })
}
item {
PreferenceFooter(
enabled = audioInput != audioConfig,
onCancelClicked = {
focusManager.clearFocus()
audioInput = audioConfig
},
onSaveClicked = { onSaveClicked(audioInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun AudioConfigPreview(){
AudioConfigItemList(
audioConfig = AudioConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,90 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos.Config.BluetoothConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun BluetoothConfigItemList(
bluetoothConfig: BluetoothConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (BluetoothConfig) -> Unit,
) {
var bluetoothInput by remember(bluetoothConfig) { mutableStateOf(bluetoothConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Bluetooth Config") }
item {
SwitchPreference(title = "Bluetooth enabled",
checked = bluetoothInput.enabled,
enabled = enabled,
onCheckedChange = { bluetoothInput = bluetoothInput.copy { this.enabled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Pairing mode",
enabled = enabled,
items = BluetoothConfig.PairingMode.values()
.filter { it != BluetoothConfig.PairingMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = bluetoothInput.mode,
onItemSelected = { bluetoothInput = bluetoothInput.copy { mode = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Fixed PIN",
value = bluetoothInput.fixedPin,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
if (it.toString().length == 6) // ensure 6 digits
bluetoothInput = bluetoothInput.copy { fixedPin = it }
})
}
item {
PreferenceFooter(
enabled = bluetoothInput != bluetoothConfig,
onCancelClicked = {
focusManager.clearFocus()
bluetoothInput = bluetoothConfig
},
onSaveClicked = { onSaveClicked(bluetoothInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun BluetoothConfigPreview(){
BluetoothConfigItemList(
bluetoothConfig = BluetoothConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,187 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun CannedMessageConfigItemList(
cannedMessageConfig: CannedMessageConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (CannedMessageConfig) -> Unit,
) {
var cannedMessageInput by remember(cannedMessageConfig) { mutableStateOf(cannedMessageConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Canned Message Config") }
item {
SwitchPreference(title = "Canned message enabled",
checked = cannedMessageInput.enabled,
enabled = enabled,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { this.enabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Rotary encoder #1 enabled",
checked = cannedMessageInput.rotary1Enabled,
enabled = enabled,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { rotary1Enabled = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "GPIO pin for rotary encoder A port",
value = cannedMessageInput.inputbrokerPinA,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinA = it }
})
}
item {
EditTextPreference(title = "GPIO pin for rotary encoder B port",
value = cannedMessageInput.inputbrokerPinB,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinB = it }
})
}
item {
EditTextPreference(title = "GPIO pin for rotary encoder Press port",
value = cannedMessageInput.inputbrokerPinPress,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinPress = it }
})
}
item {
DropDownPreference(title = "Generate input event on Press",
enabled = enabled,
items = CannedMessageConfig.InputEventChar.values()
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventPress,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventPress = it }
})
}
item { Divider() }
item {
DropDownPreference(title = "Generate input event on CW",
enabled = enabled,
items = CannedMessageConfig.InputEventChar.values()
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventCw,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCw = it }
})
}
item { Divider() }
item {
DropDownPreference(title = "Generate input event on CCW",
enabled = enabled,
items = CannedMessageConfig.InputEventChar.values()
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
.map { it to it.name },
selectedItem = cannedMessageInput.inputbrokerEventCcw,
onItemSelected = {
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCcw = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Up/Down/Select input enabled",
checked = cannedMessageInput.updown1Enabled,
enabled = enabled,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { updown1Enabled = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Allow input source",
value = cannedMessageInput.allowInputSource,
maxSize = 63, // allow_input_source max_size:16
enabled = enabled,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
cannedMessageInput = cannedMessageInput.copy { allowInputSource = it }
})
}
item {
SwitchPreference(title = "Send bell",
checked = cannedMessageInput.sendBell,
enabled = enabled,
onCheckedChange = {
cannedMessageInput = cannedMessageInput.copy { sendBell = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = cannedMessageInput != cannedMessageConfig,
onCancelClicked = {
focusManager.clearFocus()
cannedMessageInput = cannedMessageConfig
},
onSaveClicked = { onSaveClicked(cannedMessageInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun CannedMessageConfigPreview(){
CannedMessageConfigItemList(
cannedMessageConfig = CannedMessageConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,138 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun DeviceConfigItemList(
deviceConfig: DeviceConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (DeviceConfig) -> Unit,
) {
var deviceInput by remember(deviceConfig) { mutableStateOf(deviceConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Device Config") }
item {
DropDownPreference(title = "Role",
enabled = enabled,
items = DeviceConfig.Role.values()
.filter { it != DeviceConfig.Role.UNRECOGNIZED }
.map { it to it.name },
selectedItem = deviceInput.role,
onItemSelected = { deviceInput = deviceInput.copy { role = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Serial output enabled",
checked = deviceInput.serialEnabled,
enabled = enabled,
onCheckedChange = { deviceInput = deviceInput.copy { serialEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Debug log enabled",
checked = deviceInput.debugLogEnabled,
enabled = enabled,
onCheckedChange = { deviceInput = deviceInput.copy { debugLogEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Redefine PIN_BUTTON",
value = deviceInput.buttonGpio,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { buttonGpio = it }
})
}
item {
EditTextPreference(title = "Redefine PIN_BUZZER",
value = deviceInput.buzzerGpio,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { buzzerGpio = it }
})
}
item {
DropDownPreference(title = "Rebroadcast mode",
enabled = enabled,
items = DeviceConfig.RebroadcastMode.values()
.filter { it != DeviceConfig.RebroadcastMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = deviceInput.rebroadcastMode,
onItemSelected = { deviceInput = deviceInput.copy { rebroadcastMode = it } })
}
item { Divider() }
item {
EditTextPreference(title = "NodeInfo broadcast interval (seconds)",
value = deviceInput.nodeInfoBroadcastSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
deviceInput = deviceInput.copy { nodeInfoBroadcastSecs = it }
})
}
item {
SwitchPreference(title = "Double tap as button press",
checked = deviceInput.doubleTapAsButtonPress,
enabled = enabled,
onCheckedChange = {
deviceInput = deviceInput.copy { doubleTapAsButtonPress = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = deviceInput != deviceConfig,
onCancelClicked = {
focusManager.clearFocus()
deviceInput = deviceConfig
},
onSaveClicked = { onSaveClicked(deviceInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun DeviceConfigPreview(){
DeviceConfigItemList(
deviceConfig = DeviceConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,155 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun DisplayConfigItemList(
displayConfig: DisplayConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (DisplayConfig) -> Unit,
) {
var displayInput by remember(displayConfig) { mutableStateOf(displayConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Display Config") }
item {
EditTextPreference(title = "Screen timeout (seconds)",
value = displayInput.screenOnSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { displayInput = displayInput.copy { screenOnSecs = it } })
}
item {
DropDownPreference(title = "GPS coordinates format",
enabled = enabled,
items = DisplayConfig.GpsCoordinateFormat.values()
.filter { it != DisplayConfig.GpsCoordinateFormat.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.gpsFormat,
onItemSelected = { displayInput = displayInput.copy { gpsFormat = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Auto screen carousel (seconds)",
value = displayInput.autoScreenCarouselSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
displayInput = displayInput.copy { autoScreenCarouselSecs = it }
})
}
item {
SwitchPreference(title = "Compass north top",
checked = displayInput.compassNorthTop,
enabled = enabled,
onCheckedChange = { displayInput = displayInput.copy { compassNorthTop = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Flip screen",
checked = displayInput.flipScreen,
enabled = enabled,
onCheckedChange = { displayInput = displayInput.copy { flipScreen = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Display units",
enabled = enabled,
items = DisplayConfig.DisplayUnits.values()
.filter { it != DisplayConfig.DisplayUnits.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.units,
onItemSelected = { displayInput = displayInput.copy { units = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Override OLED auto-detect",
enabled = enabled,
items = DisplayConfig.OledType.values()
.filter { it != DisplayConfig.OledType.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.oled,
onItemSelected = { displayInput = displayInput.copy { oled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "Display mode",
enabled = enabled,
items = DisplayConfig.DisplayMode.values()
.filter { it != DisplayConfig.DisplayMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = displayInput.displaymode,
onItemSelected = { displayInput = displayInput.copy { displaymode = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Heading bold",
checked = displayInput.headingBold,
enabled = enabled,
onCheckedChange = { displayInput = displayInput.copy { headingBold = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Wake screen on tap or motion",
checked = displayInput.wakeOnTapOrMotion,
enabled = enabled,
onCheckedChange = { displayInput = displayInput.copy { wakeOnTapOrMotion = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = displayInput != displayConfig,
onCancelClicked = {
focusManager.clearFocus()
displayInput = displayConfig
},
onSaveClicked = { onSaveClicked(displayInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun DisplayConfigPreview(){
DisplayConfigItemList(
displayConfig = DisplayConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,208 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.ExternalNotificationConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.components.TextDividerPreference
@Composable
fun ExternalNotificationConfigItemList(
externalNotificationConfig: ExternalNotificationConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (ExternalNotificationConfig) -> Unit,
) {
var externalNotificationInput by remember(externalNotificationConfig) {
mutableStateOf(externalNotificationConfig)
}
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "External Notification Config") }
item {
SwitchPreference(title = "External notification enabled",
checked = externalNotificationInput.enabled,
enabled = enabled,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { this.enabled = it }
})
}
item { TextDividerPreference("Notifications on message receipt", enabled = enabled) }
item {
SwitchPreference(title = "Alert message LED",
checked = externalNotificationInput.alertMessage,
enabled = enabled,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { alertMessage = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert message buzzer",
checked = externalNotificationInput.alertMessageBuzzer,
enabled = enabled,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertMessageBuzzer = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert message vibra",
checked = externalNotificationInput.alertMessageVibra,
enabled = enabled,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertMessageVibra = it }
})
}
item { TextDividerPreference("Notifications on alert/bell receipt", enabled = enabled) }
item {
SwitchPreference(title = "Alert bell LED",
checked = externalNotificationInput.alertBell,
enabled = enabled,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { alertBell = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert bell buzzer",
checked = externalNotificationInput.alertBellBuzzer,
enabled = enabled,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertBellBuzzer = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Alert bell vibra",
checked = externalNotificationInput.alertBellVibra,
enabled = enabled,
onCheckedChange = {
externalNotificationInput =
externalNotificationInput.copy { alertBellVibra = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Output LED (GPIO)",
value = externalNotificationInput.output,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { output = it }
})
}
if (externalNotificationInput.output != 0) item {
SwitchPreference(title = "Output LED active high",
checked = externalNotificationInput.active,
enabled = enabled,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { active = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Output buzzer (GPIO)",
value = externalNotificationInput.outputBuzzer,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputBuzzer = it }
})
}
if (externalNotificationInput.outputBuzzer != 0) item {
SwitchPreference(title = "Use PWM buzzer",
checked = externalNotificationInput.usePwm,
enabled = enabled,
onCheckedChange = {
externalNotificationInput = externalNotificationInput.copy { usePwm = it }
})
}
item { Divider() }
item {
EditTextPreference(title = "Output vibra (GPIO)",
value = externalNotificationInput.outputVibra,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputVibra = it }
})
}
item {
EditTextPreference(title = "Output duration (milliseconds)",
value = externalNotificationInput.outputMs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { outputMs = it }
})
}
item {
EditTextPreference(title = "Nag timeout (seconds)",
value = externalNotificationInput.nagTimeout,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
externalNotificationInput = externalNotificationInput.copy { nagTimeout = it }
})
}
item {
PreferenceFooter(
enabled = externalNotificationInput != externalNotificationConfig,
onCancelClicked = {
focusManager.clearFocus()
externalNotificationInput = externalNotificationConfig
},
onSaveClicked = { onSaveClicked(externalNotificationInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun ExternalNotificationConfigPreview(){
ExternalNotificationConfigItemList(
externalNotificationConfig = ExternalNotificationConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,195 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditListPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun LoRaConfigItemList(
loraConfig: LoRaConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (LoRaConfig) -> Unit,
) {
var loraInput by remember(loraConfig) { mutableStateOf(loraConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "LoRa Config") }
item {
SwitchPreference(title = "Use modem preset",
checked = loraInput.usePreset,
enabled = enabled,
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } })
}
item { Divider() }
if (loraInput.usePreset) {
item {
DropDownPreference(title = "Modem preset",
enabled = enabled && loraInput.usePreset,
items = LoRaConfig.ModemPreset.values()
.filter { it != LoRaConfig.ModemPreset.UNRECOGNIZED }
.map { it to it.name },
selectedItem = loraInput.modemPreset,
onItemSelected = { loraInput = loraInput.copy { modemPreset = it } })
}
item { Divider() }
} else {
item {
EditTextPreference(title = "Bandwidth",
value = loraInput.bandwidth,
enabled = enabled && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { bandwidth = it } })
}
item {
EditTextPreference(title = "Spread factor",
value = loraInput.spreadFactor,
enabled = enabled && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { spreadFactor = it } })
}
item {
EditTextPreference(title = "Coding rate",
value = loraInput.codingRate,
enabled = enabled && !loraInput.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { codingRate = it } })
}
}
item {
EditTextPreference(title = "Frequency offset (MHz)",
value = loraInput.frequencyOffset,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { frequencyOffset = it } })
}
item {
DropDownPreference(title = "Region (frequency plan)",
enabled = enabled,
items = LoRaConfig.RegionCode.values()
.filter { it != LoRaConfig.RegionCode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = loraInput.region,
onItemSelected = { loraInput = loraInput.copy { region = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Hop limit",
value = loraInput.hopLimit,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { hopLimit = it } })
}
item {
SwitchPreference(title = "TX enabled",
checked = loraInput.txEnabled,
enabled = enabled,
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "TX power",
value = loraInput.txPower,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { txPower = it } })
}
item {
EditTextPreference(title = "Channel number",
value = loraInput.channelNum,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { channelNum = it } })
}
item {
SwitchPreference(title = "Override Duty Cycle",
checked = loraInput.overrideDutyCycle,
enabled = enabled,
onCheckedChange = { loraInput = loraInput.copy { overrideDutyCycle = it } })
}
item { Divider() }
item {
EditListPreference(title = "Ignore incoming",
list = loraInput.ignoreIncomingList,
maxCount = 3, // ignore_incoming max_count:3
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValuesChanged = { list ->
loraInput = loraInput.copy {
ignoreIncoming.clear()
ignoreIncoming.addAll(list.filter { it != 0 })
}
})
}
item {
SwitchPreference(title = "SX126X RX boosted gain",
checked = loraInput.sx126XRxBoostedGain,
enabled = enabled,
onCheckedChange = { loraInput = loraInput.copy { sx126XRxBoostedGain = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Override frequency (MHz)",
value = loraInput.overrideFrequency,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { loraInput = loraInput.copy { overrideFrequency = it } })
}
item {
PreferenceFooter(
enabled = loraInput != loraConfig,
onCancelClicked = {
focusManager.clearFocus()
loraInput = loraConfig
},
onSaveClicked = { onSaveClicked(loraInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun LoRaConfigPreview(){
LoRaConfigItemList(
loraConfig = LoRaConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,146 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
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.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun MQTTConfigItemList(
mqttConfig: MQTTConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (MQTTConfig) -> Unit,
) {
var mqttInput by remember(mqttConfig) { mutableStateOf(mqttConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "MQTT Config") }
item {
SwitchPreference(title = "MQTT enabled",
checked = mqttInput.enabled,
enabled = enabled,
onCheckedChange = { mqttInput = mqttInput.copy { this.enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Address",
value = mqttInput.address,
maxSize = 63, // address 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 { address = it } })
}
item {
EditTextPreference(title = "Username",
value = mqttInput.username,
maxSize = 63, // username 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 { username = it } })
}
item {
EditTextPreference(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 } })
}
item {
SwitchPreference(title = "Encryption enabled",
checked = mqttInput.encryptionEnabled,
enabled = enabled,
onCheckedChange = { mqttInput = mqttInput.copy { encryptionEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "JSON output enabled",
checked = mqttInput.jsonEnabled,
enabled = enabled,
onCheckedChange = { mqttInput = mqttInput.copy { jsonEnabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "TLS enabled",
checked = mqttInput.tlsEnabled,
enabled = enabled,
onCheckedChange = { mqttInput = mqttInput.copy { tlsEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Root topic",
value = mqttInput.root,
maxSize = 15, // root max_size:16
enabled = enabled,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { mqttInput = mqttInput.copy { root = it } })
}
item {
PreferenceFooter(
enabled = mqttInput != mqttConfig,
onCancelClicked = {
focusManager.clearFocus()
mqttInput = mqttConfig
},
onSaveClicked = { onSaveClicked(mqttInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun MQTTConfigPreview(){
MQTTConfigItemList(
mqttConfig = MQTTConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,195 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
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.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun NetworkConfigItemList(
networkConfig: NetworkConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (NetworkConfig) -> Unit,
) {
var networkInput by remember(networkConfig) { mutableStateOf(networkConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Network Config") }
item {
SwitchPreference(title = "WiFi enabled",
checked = networkInput.wifiEnabled,
enabled = enabled,
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "SSID",
value = networkInput.wifiSsid,
maxSize = 32, // wifi_ssid max_size:33
enabled = enabled,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { wifiSsid = it }
})
}
item {
EditTextPreference(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 }
})
}
item {
EditTextPreference(title = "NTP server",
value = networkInput.ntpServer,
maxSize = 32, // ntp_server max_size:33
enabled = enabled,
isError = networkInput.ntpServer.isEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { ntpServer = it }
})
}
item {
EditTextPreference(title = "rsyslog server",
value = networkInput.rsyslogServer,
maxSize = 32, // rsyslog_server max_size:33
enabled = enabled,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
networkInput = networkInput.copy { rsyslogServer = it }
})
}
item {
SwitchPreference(title = "Ethernet enabled",
checked = networkInput.ethEnabled,
enabled = enabled,
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } })
}
item { Divider() }
item {
DropDownPreference(title = "IPv4 mode",
enabled = enabled,
items = NetworkConfig.AddressMode.values()
.filter { it != NetworkConfig.AddressMode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = networkInput.addressMode,
onItemSelected = { networkInput = networkInput.copy { addressMode = it } })
}
item { Divider() }
item {
EditIPv4Preference(title = "IP",
value = networkInput.ipv4Config.ip,
enabled = enabled && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { ip = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "Gateway",
value = networkInput.ipv4Config.gateway,
enabled = enabled && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { gateway = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "Subnet",
value = networkInput.ipv4Config.subnet,
enabled = enabled && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { subnet = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
EditIPv4Preference(title = "DNS",
value = networkInput.ipv4Config.dns,
enabled = enabled && networkInput.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
val ipv4 = networkInput.ipv4Config.copy { dns = it }
networkInput = networkInput.copy { ipv4Config = ipv4 }
})
}
item {
PreferenceFooter(
enabled = networkInput != networkConfig,
onCancelClicked = {
focusManager.clearFocus()
networkInput = networkConfig
},
onSaveClicked = { onSaveClicked(networkInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun NetworkConfigPreview(){
NetworkConfigItemList(
networkConfig = NetworkConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,200 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos
import com.geeksville.mesh.ConfigProtos.Config.PositionConfig
import com.geeksville.mesh.Position
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.BitwisePreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun PositionConfigItemList(
positionInfo: Position?,
positionConfig: PositionConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (Pair<Position?, PositionConfig>) -> Unit,
) {
var locationInput by remember(positionInfo) { mutableStateOf(positionInfo) }
var positionInput by remember(positionConfig) { mutableStateOf(positionConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Position Config") }
item {
EditTextPreference(title = "Position broadcast interval (seconds)",
value = positionInput.positionBroadcastSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { positionBroadcastSecs = it }
})
}
item {
SwitchPreference(title = "Smart position enabled",
checked = positionInput.positionBroadcastSmartEnabled,
enabled = enabled,
onCheckedChange = {
positionInput = positionInput.copy { positionBroadcastSmartEnabled = it }
})
}
item { Divider() }
if (positionInput.positionBroadcastSmartEnabled) {
item {
EditTextPreference(title = "Smart broadcast minimum distance (meters)",
value = positionInput.broadcastSmartMinimumDistance,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { broadcastSmartMinimumDistance = it }
})
}
item {
EditTextPreference(title = "Smart broadcast minimum interval (seconds)",
value = positionInput.broadcastSmartMinimumIntervalSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
positionInput = positionInput.copy { broadcastSmartMinimumIntervalSecs = it }
})
}
}
item {
SwitchPreference(title = "Use fixed position",
checked = positionInput.fixedPosition,
enabled = enabled,
onCheckedChange = { positionInput = positionInput.copy { fixedPosition = it } })
}
item { Divider() }
if (positionInput.fixedPosition) {
item {
EditTextPreference(title = "Latitude",
value = locationInput?.latitude ?: 0.0,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value >= -90 && value <= 90.0)
locationInput?.let { locationInput = it.copy(latitude = value) }
})
}
item {
EditTextPreference(title = "Longitude",
value = locationInput?.longitude ?: 0.0,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value >= -180 && value <= 180.0)
locationInput?.let { locationInput = it.copy(longitude = value) }
})
}
item {
EditTextPreference(title = "Altitude (meters)",
value = locationInput?.altitude ?: 0,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
locationInput?.let { locationInput = it.copy(altitude = value) }
})
}
}
item {
SwitchPreference(title = "GPS enabled",
checked = positionInput.gpsEnabled,
enabled = enabled,
onCheckedChange = { positionInput = positionInput.copy { gpsEnabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "GPS update interval (seconds)",
value = positionInput.gpsUpdateInterval,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { gpsUpdateInterval = it } })
}
item {
EditTextPreference(title = "Fix attempt duration (seconds)",
value = positionInput.gpsAttemptTime,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { gpsAttemptTime = it } })
}
item {
BitwisePreference(title = "Position flags",
value = positionInput.positionFlags,
enabled = enabled,
items = ConfigProtos.Config.PositionConfig.PositionFlags.values()
.filter { it != PositionConfig.PositionFlags.UNSET && it != PositionConfig.PositionFlags.UNRECOGNIZED }
.map { it.number to it.name },
onItemSelected = { positionInput = positionInput.copy { positionFlags = it } }
)
}
item { Divider() }
item {
EditTextPreference(title = "Redefine GPS_RX_PIN",
value = positionInput.rxGpio,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { rxGpio = it } })
}
item {
EditTextPreference(title = "Redefine GPS_TX_PIN",
value = positionInput.txGpio,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { positionInput = positionInput.copy { txGpio = it } })
}
item {
PreferenceFooter(
enabled = positionInput != positionConfig || locationInput != positionInfo,
onCancelClicked = {
focusManager.clearFocus()
locationInput = positionInfo
positionInput = positionConfig
},
onSaveClicked = { onSaveClicked(Pair(locationInput, positionInput)) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun PositionConfigPreview(){
PositionConfigItemList(
positionInfo = null,
positionConfig = PositionConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,125 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ConfigProtos.Config.PowerConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun PowerConfigItemList(
powerConfig: PowerConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (PowerConfig) -> Unit,
) {
var powerInput by remember(powerConfig) { mutableStateOf(powerConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Power Config") }
item {
SwitchPreference(title = "Enable power saving mode",
checked = powerInput.isPowerSaving,
enabled = enabled,
onCheckedChange = { powerInput = powerInput.copy { isPowerSaving = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Shutdown on battery delay (seconds)",
value = powerInput.onBatteryShutdownAfterSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
powerInput = powerInput.copy { onBatteryShutdownAfterSecs = it }
})
}
item {
EditTextPreference(title = "ADC multiplier override ratio",
value = powerInput.adcMultiplierOverride,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { adcMultiplierOverride = it } })
}
item {
EditTextPreference(title = "Wait for Bluetooth duration (seconds)",
value = powerInput.waitBluetoothSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { waitBluetoothSecs = it } })
}
item {
EditTextPreference(title = "Mesh SDS timeout (seconds)",
value = powerInput.meshSdsTimeoutSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { meshSdsTimeoutSecs = it } })
}
item {
EditTextPreference(title = "Super deep sleep duration (seconds)",
value = powerInput.sdsSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { sdsSecs = it } })
}
item {
EditTextPreference(title = "Light sleep duration (seconds)",
value = powerInput.lsSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { lsSecs = it } })
}
item {
EditTextPreference(title = "Minimum wake time (seconds)",
value = powerInput.minWakeSecs,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { powerInput = powerInput.copy { minWakeSecs = it } })
}
item {
PreferenceFooter(
enabled = powerInput != powerConfig,
onCancelClicked = {
focusManager.clearFocus()
powerInput = powerConfig
},
onSaveClicked = { onSaveClicked(powerInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun PowerConfigPreview(){
PowerConfigItemList(
powerConfig = PowerConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,83 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RangeTestConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun RangeTestConfigItemList(
rangeTestConfig: RangeTestConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (RangeTestConfig) -> Unit,
) {
var rangeTestInput by remember(rangeTestConfig) { mutableStateOf(rangeTestConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Range Test Config") }
item {
SwitchPreference(title = "Range test enabled",
checked = rangeTestInput.enabled,
enabled = enabled,
onCheckedChange = { rangeTestInput = rangeTestInput.copy { this.enabled = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Sender message interval (seconds)",
value = rangeTestInput.sender,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { rangeTestInput = rangeTestInput.copy { sender = it } })
}
item {
SwitchPreference(title = "Save .CSV in storage (ESP32 only)",
checked = rangeTestInput.save,
enabled = enabled,
onCheckedChange = { rangeTestInput = rangeTestInput.copy { save = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = rangeTestInput != rangeTestConfig,
onCancelClicked = {
focusManager.clearFocus()
rangeTestInput = rangeTestConfig
},
onSaveClicked = { onSaveClicked(rangeTestInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun RangeTestConfig(){
RangeTestConfigItemList(
rangeTestConfig = RangeTestConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,67 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RemoteHardwareConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun RemoteHardwareConfigItemList(
remoteHardwareConfig: RemoteHardwareConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (RemoteHardwareConfig) -> Unit,
) {
var remoteHardwareInput by remember(remoteHardwareConfig) { mutableStateOf(remoteHardwareConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Remote Hardware Config") }
item {
SwitchPreference(title = "Remote Hardware enabled",
checked = remoteHardwareInput.enabled,
enabled = enabled,
onCheckedChange = {
remoteHardwareInput = remoteHardwareInput.copy { this.enabled = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = remoteHardwareInput != remoteHardwareConfig,
onCancelClicked = {
focusManager.clearFocus()
remoteHardwareInput = remoteHardwareConfig
},
onSaveClicked = { onSaveClicked(remoteHardwareInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun RemoteHardwareConfigPreview(){
RemoteHardwareConfigItemList(
remoteHardwareConfig = RemoteHardwareConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,122 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.SerialConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun SerialConfigItemList(
serialConfig: SerialConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (SerialConfig) -> Unit,
) {
var serialInput by remember(serialConfig) { mutableStateOf(serialConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Serial Config") }
item {
SwitchPreference(title = "Serial enabled",
checked = serialInput.enabled,
enabled = enabled,
onCheckedChange = { serialInput = serialInput.copy { this.enabled = it } })
}
item { Divider() }
item {
SwitchPreference(title = "Echo enabled",
checked = serialInput.echo,
enabled = enabled,
onCheckedChange = { serialInput = serialInput.copy { echo = it } })
}
item { Divider() }
item {
EditTextPreference(title = "RX",
value = serialInput.rxd,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { rxd = it } })
}
item {
EditTextPreference(title = "TX",
value = serialInput.txd,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { txd = it } })
}
item {
DropDownPreference(title = "Serial baud rate",
enabled = enabled,
items = SerialConfig.Serial_Baud.values()
.filter { it != SerialConfig.Serial_Baud.UNRECOGNIZED }
.map { it to it.name },
selectedItem = serialInput.baud,
onItemSelected = { serialInput = serialInput.copy { baud = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Timeout",
value = serialInput.timeout,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { serialInput = serialInput.copy { timeout = it } })
}
item {
DropDownPreference(title = "Serial mode",
enabled = enabled,
items = SerialConfig.Serial_Mode.values()
.filter { it != SerialConfig.Serial_Mode.UNRECOGNIZED }
.map { it to it.name },
selectedItem = serialInput.mode,
onItemSelected = { serialInput = serialInput.copy { mode = it } })
}
item { Divider() }
item {
PreferenceFooter(
enabled = serialInput != serialConfig,
onCancelClicked = {
focusManager.clearFocus()
serialInput = serialConfig
},
onSaveClicked = { onSaveClicked(serialInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun SerialConfigPreview(){
SerialConfigItemList(
serialConfig = SerialConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,105 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.StoreForwardConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun StoreForwardConfigItemList(
storeForwardConfig: StoreForwardConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (StoreForwardConfig) -> Unit,
) {
var storeForwardInput by remember(storeForwardConfig) { mutableStateOf(storeForwardConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Store & Forward Config") }
item {
SwitchPreference(title = "Store & Forward enabled",
checked = storeForwardInput.enabled,
enabled = enabled,
onCheckedChange = {
storeForwardInput = storeForwardInput.copy { this.enabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Heartbeat",
checked = storeForwardInput.heartbeat,
enabled = enabled,
onCheckedChange = { storeForwardInput = storeForwardInput.copy { heartbeat = it } })
}
item { Divider() }
item {
EditTextPreference(title = "Number of records",
value = storeForwardInput.records,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { storeForwardInput = storeForwardInput.copy { records = it } })
}
item {
EditTextPreference(title = "History return max",
value = storeForwardInput.historyReturnMax,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
storeForwardInput = storeForwardInput.copy { historyReturnMax = it }
})
}
item {
EditTextPreference(title = "History return window",
value = storeForwardInput.historyReturnWindow,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
storeForwardInput = storeForwardInput.copy { historyReturnWindow = it }
})
}
item {
PreferenceFooter(
enabled = storeForwardInput != storeForwardConfig,
onCancelClicked = {
focusManager.clearFocus()
storeForwardInput = storeForwardConfig
},
onSaveClicked = { onSaveClicked(storeForwardInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun StoreForwardConfigPreview(){
StoreForwardConfigItemList(
storeForwardConfig = StoreForwardConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,109 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.TelemetryConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun TelemetryConfigItemList(
telemetryConfig: TelemetryConfig,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (TelemetryConfig) -> Unit,
) {
var telemetryInput by remember(telemetryConfig) { mutableStateOf(telemetryConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "Telemetry Config") }
item {
EditTextPreference(title = "Device metrics update interval (seconds)",
value = telemetryInput.deviceUpdateInterval,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
telemetryInput = telemetryInput.copy { deviceUpdateInterval = it }
})
}
item {
EditTextPreference(title = "Environment metrics update interval (seconds)",
value = telemetryInput.environmentUpdateInterval,
enabled = enabled,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
telemetryInput = telemetryInput.copy { environmentUpdateInterval = it }
})
}
item {
SwitchPreference(title = "Environment metrics module enabled",
checked = telemetryInput.environmentMeasurementEnabled,
enabled = enabled,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentMeasurementEnabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Environment metrics on-screen enabled",
checked = telemetryInput.environmentScreenEnabled,
enabled = enabled,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentScreenEnabled = it }
})
}
item { Divider() }
item {
SwitchPreference(title = "Environment metrics use Fahrenheit",
checked = telemetryInput.environmentDisplayFahrenheit,
enabled = enabled,
onCheckedChange = {
telemetryInput = telemetryInput.copy { environmentDisplayFahrenheit = it }
})
}
item { Divider() }
item {
PreferenceFooter(
enabled = telemetryInput != telemetryConfig,
onCancelClicked = {
focusManager.clearFocus()
telemetryInput = telemetryConfig
},
onSaveClicked = { onSaveClicked(telemetryInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun TelemetryConfigPreview(){
TelemetryConfigItemList(
telemetryConfig = TelemetryConfig.getDefaultInstance(),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -0,0 +1,121 @@
package com.geeksville.mesh.ui.components.config
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
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.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.MeshUser
import com.geeksville.mesh.model.getInitials
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.RegularPreference
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun UserConfigItemList(
userConfig: MeshUser,
enabled: Boolean,
focusManager: FocusManager,
onSaveClicked: (MeshUser) -> Unit,
) {
var userInput by remember(userConfig) { mutableStateOf(userConfig) }
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item { PreferenceCategory(text = "User Config") }
item {
RegularPreference(title = "Node ID",
subtitle = userInput.id,
onClick = {})
}
item { Divider() }
item {
EditTextPreference(title = "Long name",
value = userInput.longName,
maxSize = 39, // long_name max_size:40
enabled = enabled,
isError = userInput.longName.isEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = {
userInput = userInput.copy(longName = it)
if (getInitials(it).toByteArray().size <= 4) // short_name max_size:5
userInput = userInput.copy(shortName = getInitials(it))
})
}
item {
EditTextPreference(title = "Short name",
value = userInput.shortName,
maxSize = 4, // short_name max_size:5
enabled = enabled,
isError = userInput.shortName.isEmpty(),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { userInput = userInput.copy(shortName = it) })
}
item {
RegularPreference(title = "Hardware model",
subtitle = userInput.hwModel.name,
onClick = {})
}
item { Divider() }
item {
SwitchPreference(title = "Licensed amateur radio",
checked = userInput.isLicensed,
enabled = enabled,
onCheckedChange = { userInput = userInput.copy(isLicensed = it) })
}
item { Divider() }
item {
PreferenceFooter(
enabled = userInput != userConfig,
onCancelClicked = {
focusManager.clearFocus()
userInput = userConfig
}, onSaveClicked = { onSaveClicked(userInput) }
)
}
}
}
@Preview(showBackground = true)
@Composable
fun UserConfigPreview(){
UserConfigItemList(
userConfig = MeshUser(
id = "!a280d9c8",
longName = "Meshtastic d9c8",
shortName = "d9c8",
hwModel = MeshProtos.HardwareModel.RAK4631,
isLicensed = false
),
enabled = true,
focusManager = LocalFocusManager.current,
onSaveClicked = { },
)
}

View File

@@ -17,13 +17,9 @@
android:checked="false"
android:title="@string/protocol_stress_test" />
<item
android:id="@+id/device_settings"
android:id="@+id/radio_config"
app:showAsAction="withText"
android:title="@string/device_settings" />
<item
android:id="@+id/module_settings"
app:showAsAction="withText"
android:title="@string/module_settings" />
<item
android:id="@+id/save_messages_csv"
app:showAsAction="withText"