add ModuleConfig settings (#526)

This commit is contained in:
Andre K 2022-11-22 22:01:37 -03:00 committed by GitHub
parent 36cb78a332
commit 689e7e7eca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 938 additions and 74 deletions

View file

@ -7,33 +7,31 @@ import android.view.ViewGroup
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.activityViewModels
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.databinding.AdvancedSettingsBinding
import com.geeksville.mesh.databinding.ComposeViewBinding
import com.geeksville.mesh.model.UIViewModel
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
private var _binding: AdvancedSettingsBinding? = null
class DeviceSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
private var _binding: ComposeViewBinding? = null
private val binding get() = _binding!!
private val model: UIViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = AdvancedSettingsBinding.inflate(inflater, container, false)
_binding = ComposeViewBinding.inflate(inflater, container, false)
.apply {
deviceConfig.apply {
composeView.apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
MdcTheme {
PreferenceScreen(model)
DeviceSettingsItemList(model)
}
}
}

View file

@ -32,7 +32,7 @@ import com.geeksville.mesh.ui.components.RegularPreference
import com.geeksville.mesh.ui.components.SwitchPreference
@Composable
fun PreferenceItemList(viewModel: UIViewModel) {
fun DeviceSettingsItemList(viewModel: UIViewModel) {
val focusManager = LocalFocusManager.current
val hasWifi = viewModel.hasWifi()

View file

@ -0,0 +1,41 @@
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.ViewCompositionStrategy
import androidx.fragment.app.activityViewModels
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.databinding.ComposeViewBinding
import com.geeksville.mesh.model.UIViewModel
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class ModuleSettingsFragment : ScreenFragment("Module Settings"), Logging {
private var _binding: ComposeViewBinding? = null
private val binding get() = _binding!!
private val model: UIViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = ComposeViewBinding.inflate(inflater, container, false)
.apply {
composeView.apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
MdcTheme {
ModuleSettingsItemList(model)
}
}
}
}
return binding.root
}
}

View file

@ -0,0 +1,613 @@
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.collectAsState
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 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) {
val focusManager = LocalFocusManager.current
val connectionState by viewModel.connectionState.observeAsState()
val connected = connectionState == MeshService.ConnectionState.CONNECTED
val moduleConfig by viewModel.moduleConfig.collectAsState()
// 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) }
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,
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value.toByteArray().size <= 31) // address max_size:32
mqttInput = mqttInput.copy { address = value }
})
}
item {
EditTextPreference(title = "Username",
value = mqttInput.username,
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value.toByteArray().size <= 31) // username max_size:32
mqttInput = mqttInput.copy { username = value }
})
}
item {
EditTextPreference(title = "Password",
value = mqttInput.password,
enabled = connected,
isError = false,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { value ->
if (value.toByteArray().size <= 31) // password max_size:32
mqttInput = mqttInput.copy { password = value }
})
}
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 {
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 baud rate",
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 {
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 = "Rotary encoder 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 {
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 = "ADC where Microphone is connected",
value = audioInput.micChan,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { micChan = it } })
}
item {
EditTextPreference(title = "DAC where Speaker is connected",
value = audioInput.ampPin,
enabled = connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { audioInput = audioInput.copy { ampPin = it } })
}
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 {
PreferenceFooter(
enabled = audioInput != moduleConfig.audio,
onCancelClicked = {
focusManager.clearFocus()
audioInput = moduleConfig.audio
},
onSaveClicked = {
focusManager.clearFocus()
viewModel.updateAudioConfig { audioInput }
})
}
}
}

View file

@ -1,21 +0,0 @@
package com.geeksville.mesh.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.model.UIViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.viewModelFactory
import com.geeksville.mesh.ui.theme.AppTheme
@Composable
fun PreferenceScreen(viewModel: UIViewModel = viewModel()) {
PreferenceItemList(viewModel)
}
//@Preview(showBackground = true)
//@Composable
//fun PreferencePreview() {
// AppTheme {
// PreferenceScreen(viewModel(factory = viewModelFactory { }))
// }
//}

View file

@ -310,6 +310,14 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
} else updateNodeInfo()
}
model.moduleConfig.asLiveData().observe(viewLifecycleOwner) {
if (!model.isConnected()) {
val moduleCount = it.allFields.size
if (moduleCount > 0)
binding.scanStatusText.text = "Module config ($moduleCount / 8)"
} else updateNodeInfo()
}
model.channels.asLiveData().observe(viewLifecycleOwner) {
if (!model.isConnected()) it.protobuf.let { ch ->
if (!ch.hasLoraConfig() && ch.settingsCount > 0)