mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-02 05:04:37 -04:00
refactor: split configs into individual components (#623)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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 = { },
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user