mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(settings): replace interval inputs with dropdowns (#3352)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
e5a28d6942
commit
c6be5be72f
29 changed files with 2343 additions and 2028 deletions
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
|
|
@ -32,7 +33,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.protobuf.ProtocolMessageEnum
|
||||
|
|
@ -84,49 +84,45 @@ fun <T> DropDownPreference(
|
|||
emptyList()
|
||||
}
|
||||
}
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = {
|
||||
if (enabled) {
|
||||
expanded = !expanded
|
||||
}
|
||||
},
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth().menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, enabled),
|
||||
readOnly = true,
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
prefix = { Text(title) },
|
||||
suffix = { Text(items.firstOrNull { it.first == selectedItem }?.second ?: "") },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||
colors =
|
||||
ExposedDropdownMenuDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
),
|
||||
enabled = enabled,
|
||||
supportingText =
|
||||
if (summary != null) {
|
||||
{ Text(text = summary, modifier = Modifier.padding(bottom = 8.dp)) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
items
|
||||
.filterNot { it.first in deprecatedItems }
|
||||
.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(selectionOption.second) },
|
||||
onClick = {
|
||||
onItemSelected(selectionOption.first)
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
Column(modifier = modifier.fillMaxWidth().padding(8.dp)) {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = {
|
||||
if (enabled) {
|
||||
expanded = !expanded
|
||||
}
|
||||
},
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = title) },
|
||||
modifier =
|
||||
Modifier.fillMaxWidth().menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, enabled),
|
||||
readOnly = true,
|
||||
value = items.firstOrNull { it.first == selectedItem }?.second ?: "",
|
||||
onValueChange = {},
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||
enabled = enabled,
|
||||
supportingText =
|
||||
if (summary != null) {
|
||||
{ Text(text = summary, modifier = Modifier.padding(bottom = 8.dp)) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
items
|
||||
.filterNot { it.first in deprecatedItems }
|
||||
.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(selectionOption.second) },
|
||||
onClick = {
|
||||
onItemSelected(selectionOption.first)
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,8 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Info
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -40,12 +38,10 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusState
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.strings.R
|
||||
|
|
@ -211,15 +207,11 @@ fun EditTextPreference(
|
|||
) {
|
||||
var isFocused by remember { mutableStateOf(false) }
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Column(modifier = modifier.fillMaxWidth().padding(8.dp)) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth().onFocusEvent { onFocusChanged(it) },
|
||||
value = value,
|
||||
singleLine = true,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth().onFocusEvent {
|
||||
isFocused = it.isFocused
|
||||
onFocusChanged(it)
|
||||
},
|
||||
enabled = enabled,
|
||||
isError = isError,
|
||||
onValueChange = {
|
||||
|
|
@ -231,7 +223,7 @@ fun EditTextPreference(
|
|||
onValueChanged(it)
|
||||
}
|
||||
},
|
||||
prefix = { Text(title) },
|
||||
label = { Text(title) },
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
visualTransformation = visualTransformation,
|
||||
|
|
@ -249,14 +241,6 @@ fun EditTextPreference(
|
|||
} else {
|
||||
null
|
||||
},
|
||||
colors =
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
),
|
||||
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.End),
|
||||
)
|
||||
if (summary != null) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun <T> SliderPreference(
|
||||
title: String,
|
||||
enabled: Boolean,
|
||||
items: List<Pair<T, String>>,
|
||||
selectedValue: T,
|
||||
onValueChange: (T) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
summary: String? = null,
|
||||
) {
|
||||
if (items.isEmpty()) return
|
||||
|
||||
val selectedIndex = items.indexOfFirst { it.first == selectedValue }.toFloat()
|
||||
val valueRange = 0f..(items.size - 1).toFloat()
|
||||
val steps = (items.size - 2).coerceAtLeast(0)
|
||||
|
||||
ListItem(
|
||||
modifier = modifier,
|
||||
headlineContent = {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.End,
|
||||
text = items.firstOrNull { it.first == selectedValue }?.second ?: items.first().second,
|
||||
)
|
||||
},
|
||||
overlineContent = { Text(text = title) },
|
||||
supportingContent = {
|
||||
Column {
|
||||
summary?.let { Text(text = it, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) }
|
||||
Slider(
|
||||
value = selectedIndex.coerceIn(valueRange),
|
||||
onValueChange = {
|
||||
val index = it.roundToInt()
|
||||
if (index in items.indices) {
|
||||
onValueChange(items[index].first)
|
||||
}
|
||||
},
|
||||
valueRange = valueRange,
|
||||
steps = steps,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SliderPreferencePreview() {
|
||||
val items = listOf(1L to "One", 2L to "Two", 3L to "Three", 4L to "Four", 5L to "Five")
|
||||
AppTheme {
|
||||
SliderPreference(
|
||||
title = "Slider",
|
||||
summary = "Select a value",
|
||||
enabled = true,
|
||||
items = items,
|
||||
selectedValue = 3L,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SliderPreferenceDisabledPreview() {
|
||||
val items = listOf(1L to "One", 2L to "Two", 3L to "Three", 4L to "Four", 5L to "Five")
|
||||
AppTheme {
|
||||
SliderPreference(
|
||||
title = "Slider",
|
||||
summary = "Select a value",
|
||||
enabled = false,
|
||||
items = items,
|
||||
selectedValue = 3L,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -28,8 +29,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -53,56 +54,46 @@ fun AmbientLightingConfigScreen(navController: NavController, viewModel: RadioCo
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.ambient_lighting_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.led_state),
|
||||
checked = formState.value.ledState,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ledState = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
TitledCard(title = stringResource(R.string.ambient_lighting_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.led_state),
|
||||
checked = formState.value.ledState,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ledState = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.current),
|
||||
value = formState.value.current,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { current = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.red),
|
||||
value = formState.value.red,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { red = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.green),
|
||||
value = formState.value.green,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { green = it } },
|
||||
)
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.current),
|
||||
value = formState.value.current,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { current = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.red),
|
||||
value = formState.value.red,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { red = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.green),
|
||||
value = formState.value.green,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { green = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.blue),
|
||||
value = formState.value.blue,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { blue = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.blue),
|
||||
value = formState.value.blue,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { blue = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -29,8 +30,8 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig.AudioConfig
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -55,80 +56,63 @@ fun AudioConfigScreen(navController: NavController, viewModel: RadioConfigViewMo
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.audio_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.codec_2_enabled),
|
||||
checked = formState.value.codec2Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { codec2Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ptt_pin),
|
||||
value = formState.value.pttPin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { pttPin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.codec2_sample_rate),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
AudioConfig.Audio_Baud.entries
|
||||
.filter { it != AudioConfig.Audio_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.bitrate,
|
||||
onItemSelected = { formState.value = formState.value.copy { bitrate = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_word_select),
|
||||
value = formState.value.i2SWs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SWs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_data_in),
|
||||
value = formState.value.i2SSd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SSd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_data_out),
|
||||
value = formState.value.i2SDin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SDin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_clock),
|
||||
value = formState.value.i2SSck,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SSck = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.audio_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.codec_2_enabled),
|
||||
checked = formState.value.codec2Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { codec2Enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ptt_pin),
|
||||
value = formState.value.pttPin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { pttPin = it } },
|
||||
)
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.codec2_sample_rate),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
AudioConfig.Audio_Baud.entries
|
||||
.filter { it != AudioConfig.Audio_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.bitrate,
|
||||
onItemSelected = { formState.value = formState.value.copy { bitrate = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_word_select),
|
||||
value = formState.value.i2SWs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SWs = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_data_in),
|
||||
value = formState.value.i2SSd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SSd = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_data_out),
|
||||
value = formState.value.i2SDin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SDin = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.i2s_clock),
|
||||
value = formState.value.i2SSck,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { i2SSck = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -29,8 +30,8 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ConfigProtos.Config.BluetoothConfig
|
||||
import org.meshtastic.proto.config
|
||||
|
|
@ -55,44 +56,39 @@ fun BluetoothConfigScreen(navController: NavController, viewModel: RadioConfigVi
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.bluetooth_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.bluetooth_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.pairing_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
BluetoothConfig.PairingMode.entries
|
||||
.filter { it != BluetoothConfig.PairingMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.mode,
|
||||
onItemSelected = { formState.value = formState.value.copy { mode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.fixed_pin),
|
||||
value = formState.value.fixedPin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
if (it.toString().length == 6) { // ensure 6 digits
|
||||
formState.value = formState.value.copy { fixedPin = it }
|
||||
}
|
||||
},
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.bluetooth_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.bluetooth_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.pairing_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
BluetoothConfig.PairingMode.entries
|
||||
.filter { it != BluetoothConfig.PairingMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.mode,
|
||||
onItemSelected = { formState.value = formState.value.copy { mode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.fixed_pin),
|
||||
value = formState.value.fixedPin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
if (it.toString().length == 6) { // ensure 6 digits
|
||||
formState.value = formState.value.copy { fixedPin = it }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -35,8 +36,8 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -68,146 +69,117 @@ fun CannedMessageConfigScreen(navController: NavController, viewModel: RadioConf
|
|||
}
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.canned_message_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.canned_message_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.rotary_encoder_1_enabled),
|
||||
checked = formState.value.rotary1Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { rotary1Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_a_port),
|
||||
value = formState.value.inputbrokerPinA,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinA = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_b_port),
|
||||
value = formState.value.inputbrokerPinB,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinB = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_press_port),
|
||||
value = formState.value.inputbrokerPinPress,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinPress = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_press),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventPress,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventPress = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_cw),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventCw,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventCw = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_ccw),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventCcw,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventCcw = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.up_down_select_input_enabled),
|
||||
checked = formState.value.updown1Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { updown1Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.allow_input_source),
|
||||
value = formState.value.allowInputSource,
|
||||
maxSize = 63, // allow_input_source max_size:16
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { allowInputSource = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.send_bell),
|
||||
checked = formState.value.sendBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.messages),
|
||||
value = messagesInput,
|
||||
maxSize = 200, // messages max_size:201
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { messagesInput = it },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.canned_message_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.canned_message_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.rotary_encoder_1_enabled),
|
||||
checked = formState.value.rotary1Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { rotary1Enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_a_port),
|
||||
value = formState.value.inputbrokerPinA,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinA = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_b_port),
|
||||
value = formState.value.inputbrokerPinB,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinB = it } },
|
||||
)
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_for_rotary_encoder_press_port),
|
||||
value = formState.value.inputbrokerPinPress,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinPress = it } },
|
||||
)
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_press),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventPress,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventPress = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_cw),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventCw,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventCw = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_ccw),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.inputbrokerEventCcw,
|
||||
onItemSelected = { formState.value = formState.value.copy { inputbrokerEventCcw = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.up_down_select_input_enabled),
|
||||
checked = formState.value.updown1Enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { updown1Enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.allow_input_source),
|
||||
value = formState.value.allowInputSource,
|
||||
maxSize = 63, // allow_input_source max_size:16
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { allowInputSource = it } },
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.send_bell),
|
||||
checked = formState.value.sendBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.messages),
|
||||
value = messagesInput,
|
||||
maxSize = 200, // messages max_size:201
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { messagesInput = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
|
|
@ -32,9 +34,12 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.gpioPins
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -58,94 +63,85 @@ fun DetectionSensorConfigScreen(navController: NavController, viewModel: RadioCo
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.detection_sensor_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.detection_sensor_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
TitledCard(title = stringResource(R.string.detection_sensor_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.detection_sensor_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val minimumBroadcastIntervals = remember {
|
||||
IntervalConfiguration.DETECTION_SENSOR_MINIMUM.allowedIntervals
|
||||
}
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.minimum_broadcast_seconds),
|
||||
selectedItem = formState.value.minimumBroadcastSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = minimumBroadcastIntervals.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { minimumBroadcastSecs = it.toInt() } },
|
||||
)
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.minimum_broadcast_seconds),
|
||||
value = formState.value.minimumBroadcastSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { minimumBroadcastSecs = it } },
|
||||
)
|
||||
val stateBroadcastIntervals = remember { IntervalConfiguration.DETECTION_SENSOR_STATE.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.state_broadcast_seconds),
|
||||
selectedItem = formState.value.stateBroadcastSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = stateBroadcastIntervals.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { stateBroadcastSecs = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.send_bell_with_alert_message),
|
||||
checked = formState.value.sendBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.friendly_name),
|
||||
value = formState.value.name,
|
||||
maxSize = 19, // name max_size:20
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { name = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val pins = remember { gpioPins }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gpio_pin_to_monitor),
|
||||
items = pins,
|
||||
selectedItem = formState.value.monitorPin,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { monitorPin = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.detection_trigger_type),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
ModuleConfig.DetectionSensorConfig.TriggerType.entries
|
||||
.filter { it != ModuleConfig.DetectionSensorConfig.TriggerType.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.detectionTriggerType,
|
||||
onItemSelected = { formState.value = formState.value.copy { detectionTriggerType = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_input_pullup_mode),
|
||||
checked = formState.value.usePullup,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { usePullup = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.state_broadcast_seconds),
|
||||
value = formState.value.stateBroadcastSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { stateBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.send_bell_with_alert_message),
|
||||
checked = formState.value.sendBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.friendly_name),
|
||||
value = formState.value.name,
|
||||
maxSize = 19, // name max_size:20
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { name = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gpio_pin_to_monitor),
|
||||
value = formState.value.monitorPin,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { monitorPin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.detection_trigger_type),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
ModuleConfig.DetectionSensorConfig.TriggerType.entries
|
||||
.filter { it != ModuleConfig.DetectionSensorConfig.TriggerType.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.detectionTriggerType,
|
||||
onItemSelected = { formState.value = formState.value.copy { detectionTriggerType = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_input_pullup_mode),
|
||||
checked = formState.value.usePullup,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { usePullup = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -31,6 +32,7 @@ import androidx.compose.material3.TextButton
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -50,9 +52,11 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DeviceConfig
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -117,102 +121,105 @@ fun DeviceConfigScreen(navController: NavController, viewModel: RadioConfigViewM
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.options)) }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.role),
|
||||
enabled = state.connected,
|
||||
selectedItem = formState.value.role,
|
||||
onItemSelected = { selectedRole = it },
|
||||
summary = stringResource(id = formState.value.role.description),
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.rebroadcast_mode),
|
||||
enabled = state.connected,
|
||||
selectedItem = formState.value.rebroadcastMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { rebroadcastMode = it } },
|
||||
summary = stringResource(id = formState.value.rebroadcastMode.description),
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.nodeinfo_broadcast_interval),
|
||||
value = formState.value.nodeInfoBroadcastSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { nodeInfoBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.hardware)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.double_tap_as_button_press),
|
||||
summary = stringResource(id = R.string.config_device_doubleTapAsButtonPress_summary),
|
||||
checked = formState.value.doubleTapAsButtonPress,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { doubleTapAsButtonPress = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.triple_click_adhoc_ping),
|
||||
summary = stringResource(id = R.string.config_device_tripleClickAsAdHocPing_summary),
|
||||
checked = !formState.value.disableTripleClick,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { disableTripleClick = !it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.led_heartbeat),
|
||||
summary = stringResource(id = R.string.config_device_ledHeartbeatEnabled_summary),
|
||||
checked = !formState.value.ledHeartbeatDisabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ledHeartbeatDisabled = !it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
TitledCard(title = stringResource(R.string.options)) {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.role),
|
||||
enabled = state.connected,
|
||||
selectedItem = formState.value.role,
|
||||
onItemSelected = { selectedRole = it },
|
||||
summary = stringResource(id = formState.value.role.description),
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
item { PreferenceCategory(text = stringResource(R.string.debug)) }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.rebroadcast_mode),
|
||||
enabled = state.connected,
|
||||
selectedItem = formState.value.rebroadcastMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { rebroadcastMode = it } },
|
||||
summary = stringResource(id = formState.value.rebroadcastMode.description),
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
val nodeInfoBroadcastIntervals = remember { IntervalConfiguration.NODE_INFO_BROADCAST.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.nodeinfo_broadcast_interval),
|
||||
selectedItem = formState.value.nodeInfoBroadcastSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = nodeInfoBroadcastIntervals.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { nodeInfoBroadcastSecs = it.toInt() } },
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.time_zone),
|
||||
value = formState.value.tzdef,
|
||||
summary = stringResource(id = R.string.config_device_tzdef_summary),
|
||||
maxSize = 64, // tzdef max_size:65
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { tzdef = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.hardware)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.double_tap_as_button_press),
|
||||
summary = stringResource(id = R.string.config_device_doubleTapAsButtonPress_summary),
|
||||
checked = formState.value.doubleTapAsButtonPress,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { doubleTapAsButtonPress = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.triple_click_adhoc_ping),
|
||||
summary = stringResource(id = R.string.config_device_tripleClickAsAdHocPing_summary),
|
||||
checked = !formState.value.disableTripleClick,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { disableTripleClick = !it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.led_heartbeat),
|
||||
summary = stringResource(id = R.string.config_device_ledHeartbeatEnabled_summary),
|
||||
checked = !formState.value.ledHeartbeatDisabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ledHeartbeatDisabled = !it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.debug)) {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.time_zone),
|
||||
value = formState.value.tzdef,
|
||||
summary = stringResource(id = R.string.config_device_tzdef_summary),
|
||||
maxSize = 64, // tzdef max_size:65
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { tzdef = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item { PreferenceCategory(text = stringResource(R.string.gpio)) }
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.button_gpio),
|
||||
value = formState.value.buttonGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { buttonGpio = it } },
|
||||
)
|
||||
}
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.buzzer_gpio),
|
||||
value = formState.value.buzzerGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { buzzerGpio = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.gpio)) {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.button_gpio),
|
||||
value = formState.value.buttonGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { buttonGpio = it } },
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.buzzer_gpio),
|
||||
value = formState.value.buzzerGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { buzzerGpio = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,21 +17,22 @@
|
|||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -41,7 +42,6 @@ fun DisplayConfigScreen(navController: NavController, viewModel: RadioConfigView
|
|||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
val displayConfig = state.radioConfig.display
|
||||
val formState = rememberConfigState(initialValue = displayConfig)
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
RadioConfigScreenList(
|
||||
title = stringResource(id = R.string.display),
|
||||
|
|
@ -55,136 +55,129 @@ fun DisplayConfigScreen(navController: NavController, viewModel: RadioConfigView
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.display_config)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.always_point_north),
|
||||
summary = stringResource(id = R.string.config_display_compass_north_top_summary),
|
||||
checked = formState.value.compassNorthTop,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { compassNorthTop = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.display_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.always_point_north),
|
||||
summary = stringResource(id = R.string.config_display_compass_north_top_summary),
|
||||
checked = formState.value.compassNorthTop,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { compassNorthTop = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_12h_format),
|
||||
summary = stringResource(R.string.display_time_in_12h_format),
|
||||
enabled = state.connected,
|
||||
checked = formState.value.use12HClock,
|
||||
onCheckedChange = { formState.value = formState.value.copy { use12HClock = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.bold_heading),
|
||||
summary = stringResource(id = R.string.config_display_heading_bold_summary),
|
||||
checked = formState.value.headingBold,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { headingBold = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.display_units),
|
||||
summary = stringResource(id = R.string.config_display_units_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.DisplayUnits.entries
|
||||
.filter { it != DisplayConfig.DisplayUnits.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.units,
|
||||
onItemSelected = { formState.value = formState.value.copy { units = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_12h_format),
|
||||
summary = stringResource(R.string.display_time_in_12h_format),
|
||||
enabled = state.connected,
|
||||
checked = formState.value.use12HClock,
|
||||
onCheckedChange = { formState.value = formState.value.copy { use12HClock = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.advanced)) {
|
||||
val screenOnIntervals = remember { IntervalConfiguration.DISPLAY_SCREEN_ON.allowedIntervals }
|
||||
val carouselIntervals = remember { IntervalConfiguration.DISPLAY_CAROUSEL.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.screen_on_for),
|
||||
summary = stringResource(id = R.string.config_display_screen_on_secs_summary),
|
||||
enabled = state.connected,
|
||||
items = screenOnIntervals.map { it to it.toDisplayString() },
|
||||
selectedItem =
|
||||
screenOnIntervals.find { it.value == formState.value.screenOnSecs.toLong() }
|
||||
?: screenOnIntervals.first(),
|
||||
onItemSelected = { formState.value = formState.value.copy { screenOnSecs = it.value.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.carousel_interval),
|
||||
summary = stringResource(id = R.string.config_display_auto_screen_carousel_secs_summary),
|
||||
enabled = state.connected,
|
||||
items = carouselIntervals.map { it to it.toDisplayString() },
|
||||
selectedItem =
|
||||
carouselIntervals.find { it.value == formState.value.autoScreenCarouselSecs.toLong() }
|
||||
?: carouselIntervals.first(),
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { autoScreenCarouselSecs = it.value.toInt() }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.wake_on_tap_or_motion),
|
||||
summary = stringResource(id = R.string.config_display_wake_on_tap_or_motion_summary),
|
||||
checked = formState.value.wakeOnTapOrMotion,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { wakeOnTapOrMotion = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.flip_screen),
|
||||
summary = stringResource(id = R.string.config_display_flip_screen_summary),
|
||||
checked = formState.value.flipScreen,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { flipScreen = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.display_mode),
|
||||
summary = stringResource(id = R.string.config_display_displaymode_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.DisplayMode.entries
|
||||
.filter { it != DisplayConfig.DisplayMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.displaymode,
|
||||
onItemSelected = { formState.value = formState.value.copy { displaymode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.oled_type),
|
||||
summary = stringResource(id = R.string.config_display_oled_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.OledType.entries
|
||||
.filter { it != DisplayConfig.OledType.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.oled,
|
||||
onItemSelected = { formState.value = formState.value.copy { oled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.compass_orientation),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.CompassOrientation.entries
|
||||
.filter { it != DisplayConfig.CompassOrientation.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.compassOrientation,
|
||||
onItemSelected = { formState.value = formState.value.copy { compassOrientation = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.bold_heading),
|
||||
summary = stringResource(id = R.string.config_display_heading_bold_summary),
|
||||
checked = formState.value.headingBold,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { headingBold = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.display_units),
|
||||
summary = stringResource(id = R.string.config_display_units_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.DisplayUnits.entries
|
||||
.filter { it != DisplayConfig.DisplayUnits.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.units,
|
||||
onItemSelected = { formState.value = formState.value.copy { units = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item { PreferenceCategory(text = stringResource(R.string.advanced)) }
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.screen_on_for),
|
||||
summary = stringResource(id = R.string.config_display_screen_on_secs_summary),
|
||||
value = formState.value.screenOnSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { screenOnSecs = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.carousel_interval),
|
||||
summary = stringResource(id = R.string.config_display_auto_screen_carousel_secs_summary),
|
||||
value = formState.value.autoScreenCarouselSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { autoScreenCarouselSecs = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.wake_on_tap_or_motion),
|
||||
summary = stringResource(id = R.string.config_display_wake_on_tap_or_motion_summary),
|
||||
checked = formState.value.wakeOnTapOrMotion,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { wakeOnTapOrMotion = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.flip_screen),
|
||||
summary = stringResource(id = R.string.config_display_flip_screen_summary),
|
||||
checked = formState.value.flipScreen,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { flipScreen = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.display_mode),
|
||||
summary = stringResource(id = R.string.config_display_displaymode_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.DisplayMode.entries
|
||||
.filter { it != DisplayConfig.DisplayMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.displaymode,
|
||||
onItemSelected = { formState.value = formState.value.copy { displaymode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.oled_type),
|
||||
summary = stringResource(id = R.string.config_display_oled_summary),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.OledType.entries
|
||||
.filter { it != DisplayConfig.OledType.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.oled,
|
||||
onItemSelected = { formState.value = formState.value.copy { oled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.compass_orientation),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
DisplayConfig.CompassOrientation.entries
|
||||
.filter { it != DisplayConfig.CompassOrientation.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.compassOrientation,
|
||||
onItemSelected = { formState.value = formState.value.copy { compassOrientation = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
|
|
@ -33,11 +35,14 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TextDividerPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.gpioPins
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
||||
|
|
@ -67,183 +72,159 @@ fun ExternalNotificationConfigScreen(navController: NavController, viewModel: Ra
|
|||
}
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.external_notification_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.external_notification_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TextDividerPreference(stringResource(R.string.notifications_on_message_receipt), enabled = state.connected)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_message_led),
|
||||
checked = formState.value.alertMessage,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessage = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_message_buzzer),
|
||||
checked = formState.value.alertMessageBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessageBuzzer = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_message_vibra),
|
||||
checked = formState.value.alertMessageVibra,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessageVibra = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TextDividerPreference(
|
||||
stringResource(R.string.notifications_on_alert_bell_receipt),
|
||||
enabled = state.connected,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_led),
|
||||
checked = formState.value.alertBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_buzzer),
|
||||
checked = formState.value.alertBellBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBellBuzzer = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_vibra),
|
||||
checked = formState.value.alertBellVibra,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBellVibra = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.output_led_gpio),
|
||||
value = formState.value.output,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { output = it } },
|
||||
)
|
||||
}
|
||||
|
||||
if (formState.value.output != 0) {
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.external_notification_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.output_led_active_high),
|
||||
checked = formState.value.active,
|
||||
title = stringResource(R.string.external_notification_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { active = it } },
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.output_buzzer_gpio),
|
||||
value = formState.value.outputBuzzer,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { outputBuzzer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
if (formState.value.outputBuzzer != 0) {
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.notifications_on_message_receipt)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_pwm_buzzer),
|
||||
checked = formState.value.usePwm,
|
||||
title = stringResource(R.string.alert_message_led),
|
||||
checked = formState.value.alertMessage,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { usePwm = it } },
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessage = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_message_buzzer),
|
||||
checked = formState.value.alertMessageBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessageBuzzer = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_message_vibra),
|
||||
checked = formState.value.alertMessageVibra,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertMessageVibra = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.output_vibra_gpio),
|
||||
value = formState.value.outputVibra,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { outputVibra = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.notifications_on_alert_bell_receipt)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_led),
|
||||
checked = formState.value.alertBell,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBell = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_buzzer),
|
||||
checked = formState.value.alertBellBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBellBuzzer = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.alert_bell_vibra),
|
||||
checked = formState.value.alertBellVibra,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { alertBellVibra = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.output_duration_milliseconds),
|
||||
value = formState.value.outputMs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { outputMs = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.advanced)) {
|
||||
val gpio = remember { gpioPins }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.output_led_gpio),
|
||||
items = gpio,
|
||||
selectedItem = formState.value.output,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { output = it } },
|
||||
)
|
||||
if (formState.value.output != 0) {
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.output_led_active_high),
|
||||
checked = formState.value.active,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { active = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.output_buzzer_gpio),
|
||||
items = gpio,
|
||||
selectedItem = formState.value.outputBuzzer,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { outputBuzzer = it } },
|
||||
)
|
||||
if (formState.value.outputBuzzer != 0) {
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_pwm_buzzer),
|
||||
checked = formState.value.usePwm,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { usePwm = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.output_vibra_gpio),
|
||||
items = gpio,
|
||||
selectedItem = formState.value.outputVibra,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { outputVibra = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val outputItems = remember { IntervalConfiguration.OUTPUT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.output_duration_milliseconds),
|
||||
items = outputItems.map { it.value to it.toDisplayString() },
|
||||
selectedItem = formState.value.outputMs,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { outputMs = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val nagItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.nag_timeout_seconds),
|
||||
items = nagItems.map { it.value to it.toDisplayString() },
|
||||
selectedItem = formState.value.nagTimeout,
|
||||
enabled = state.connected,
|
||||
onItemSelected = { formState.value = formState.value.copy { nagTimeout = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ringtone),
|
||||
value = ringtoneInput,
|
||||
maxSize = 230, // ringtone max_size:231
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ringtoneInput = it },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_i2s_as_buzzer),
|
||||
checked = formState.value.useI2SAsBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { useI2SAsBuzzer = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.nag_timeout_seconds),
|
||||
value = formState.value.nagTimeout,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { nagTimeout = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ringtone),
|
||||
value = ringtoneInput,
|
||||
maxSize = 230, // ringtone max_size:231
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ringtoneInput = it },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_i2s_as_buzzer),
|
||||
checked = formState.value.useI2SAsBuzzer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { useI2SAsBuzzer = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,19 +17,16 @@
|
|||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.model.Channel
|
||||
|
|
@ -39,11 +36,11 @@ import org.meshtastic.core.model.numChannels
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceDivider
|
||||
import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.hopLimits
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
|
||||
|
|
@ -79,19 +76,15 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
selectedItem = formState.value.region,
|
||||
onItemSelected = { formState.value = formState.value.copy { region = it } },
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_modem_preset),
|
||||
checked = formState.value.usePreset,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { usePreset = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
if (formState.value.usePreset) {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.modem_preset),
|
||||
|
|
@ -109,9 +102,7 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { bandwidth = it } },
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.spread_factor),
|
||||
value = formState.value.spreadFactor,
|
||||
|
|
@ -119,9 +110,7 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { spreadFactor = it } },
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.coding_rate),
|
||||
value = formState.value.codingRate,
|
||||
|
|
@ -133,8 +122,6 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
}
|
||||
}
|
||||
|
||||
item { Spacer(modifier = Modifier.height(16.dp)) }
|
||||
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.advanced)) {
|
||||
SwitchPreference(
|
||||
|
|
@ -142,42 +129,35 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
checked = formState.value.ignoreMqtt,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ignoreMqtt = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.ok_to_mqtt),
|
||||
checked = formState.value.configOkToMqtt,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { configOkToMqtt = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.tx_enabled),
|
||||
checked = formState.value.txEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { txEnabled = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
EditTextPreference(
|
||||
HorizontalDivider()
|
||||
val hopLimitItems = remember { hopLimits }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.hop_limit),
|
||||
summary = stringResource(id = R.string.config_lora_hop_limit_summary),
|
||||
value = formState.value.hopLimit,
|
||||
items = hopLimitItems,
|
||||
selectedItem = formState.value.hopLimit,
|
||||
onItemSelected = { formState.value = formState.value.copy { hopLimit = it } },
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { hopLimit = it } },
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
var isFocusedSlot by remember { mutableStateOf(false) }
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.frequency_slot),
|
||||
|
|
@ -197,19 +177,15 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
}
|
||||
},
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.sx126x_rx_boosted_gain),
|
||||
checked = formState.value.sx126XRxBoostedGain,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { sx126XRxBoostedGain = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
var isFocusedOverride by remember { mutableStateOf(false) }
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.override_frequency_mhz),
|
||||
|
|
@ -224,9 +200,7 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
onFocusChanged = { isFocusedOverride = it.isFocused },
|
||||
onValueChanged = { formState.value = formState.value.copy { overrideFrequency = it } },
|
||||
)
|
||||
|
||||
PreferenceDivider()
|
||||
|
||||
HorizontalDivider()
|
||||
SignedIntegerEditTextPreference(
|
||||
title = stringResource(R.string.tx_power_dbm),
|
||||
value = formState.value.txPower,
|
||||
|
|
@ -234,14 +208,14 @@ fun LoRaConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { txPower = it } },
|
||||
)
|
||||
|
||||
if (viewModel.hasPaFan) {
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.pa_fan_disabled),
|
||||
checked = formState.value.paFanDisabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { paFanDisabled = it } },
|
||||
containerColor = Color.Transparent,
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -34,8 +35,8 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditPasswordPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -77,141 +78,125 @@ fun MQTTConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.mqtt_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.mqtt_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.address),
|
||||
value = formState.value.address,
|
||||
maxSize = 63, // address max_size:64
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { address = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.mqtt_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.mqtt_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.address),
|
||||
value = formState.value.address,
|
||||
maxSize = 63, // address max_size:64
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { address = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.username),
|
||||
value = formState.value.username,
|
||||
maxSize = 63, // username max_size:64
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { username = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditPasswordPreference(
|
||||
title = stringResource(R.string.password),
|
||||
value = formState.value.password,
|
||||
maxSize = 63, // password max_size:64
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { password = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.encryption_enabled),
|
||||
checked = formState.value.encryptionEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { encryptionEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.json_output_enabled),
|
||||
checked = formState.value.jsonEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { jsonEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val defaultAddress = stringResource(R.string.default_mqtt_address)
|
||||
val isDefault = formState.value.address.isEmpty() || formState.value.address.contains(defaultAddress)
|
||||
val enforceTls = isDefault && formState.value.proxyToClientEnabled
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.tls_enabled),
|
||||
checked = formState.value.tlsEnabled || enforceTls,
|
||||
enabled = state.connected && !enforceTls,
|
||||
onCheckedChange = { formState.value = formState.value.copy { tlsEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.root_topic),
|
||||
value = formState.value.root,
|
||||
maxSize = 31, // root max_size:32
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { root = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.proxy_to_client_enabled),
|
||||
checked = formState.value.proxyToClientEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { proxyToClientEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.username),
|
||||
value = formState.value.username,
|
||||
maxSize = 63, // username max_size:64
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { username = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.map_reporting)) {
|
||||
MapReportingPreference(
|
||||
mapReportingEnabled = formState.value.mapReportingEnabled,
|
||||
onMapReportingEnabledChanged = {
|
||||
formState.value = formState.value.copy { mapReportingEnabled = it }
|
||||
},
|
||||
shouldReportLocation = formState.value.mapReportSettings.shouldReportLocation,
|
||||
onShouldReportLocationChanged = {
|
||||
viewModel.setShouldReportLocation(destNum, it)
|
||||
val settings = formState.value.mapReportSettings.copy { this.shouldReportLocation = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
positionPrecision = formState.value.mapReportSettings.positionPrecision,
|
||||
onPositionPrecisionChanged = {
|
||||
val settings = formState.value.mapReportSettings.copy { positionPrecision = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
publishIntervalSecs = formState.value.mapReportSettings.publishIntervalSecs,
|
||||
onPublishIntervalSecsChanged = {
|
||||
val settings = formState.value.mapReportSettings.copy { publishIntervalSecs = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
enabled = state.connected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditPasswordPreference(
|
||||
title = stringResource(R.string.password),
|
||||
value = formState.value.password,
|
||||
maxSize = 63, // password max_size:64
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { password = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.encryption_enabled),
|
||||
checked = formState.value.encryptionEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { encryptionEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.json_output_enabled),
|
||||
checked = formState.value.jsonEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { jsonEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
val defaultAddress = stringResource(R.string.default_mqtt_address)
|
||||
val isDefault = formState.value.address.isEmpty() || formState.value.address.contains(defaultAddress)
|
||||
val enforceTls = isDefault && formState.value.proxyToClientEnabled
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.tls_enabled),
|
||||
checked = formState.value.tlsEnabled || enforceTls,
|
||||
enabled = state.connected && !enforceTls,
|
||||
onCheckedChange = { formState.value = formState.value.copy { tlsEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.root_topic),
|
||||
value = formState.value.root,
|
||||
maxSize = 31, // root max_size:32
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { root = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.proxy_to_client_enabled),
|
||||
checked = formState.value.proxyToClientEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { proxyToClientEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
// mqtt map reporting opt in
|
||||
item { PreferenceCategory(text = stringResource(R.string.map_reporting)) }
|
||||
|
||||
item {
|
||||
MapReportingPreference(
|
||||
mapReportingEnabled = formState.value.mapReportingEnabled,
|
||||
onMapReportingEnabledChanged = { formState.value = formState.value.copy { mapReportingEnabled = it } },
|
||||
shouldReportLocation = formState.value.mapReportSettings.shouldReportLocation,
|
||||
onShouldReportLocationChanged = {
|
||||
viewModel.setShouldReportLocation(destNum, it)
|
||||
val settings = formState.value.mapReportSettings.copy { this.shouldReportLocation = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
positionPrecision = formState.value.mapReportSettings.positionPrecision,
|
||||
onPositionPrecisionChanged = {
|
||||
val settings = formState.value.mapReportSettings.copy { positionPrecision = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
publishIntervalSecs = formState.value.mapReportSettings.publishIntervalSecs,
|
||||
onPublishIntervalSecsChanged = {
|
||||
val settings = formState.value.mapReportSettings.copy { publishIntervalSecs = it }
|
||||
formState.value = formState.value.copy { mapReportSettings = settings }
|
||||
},
|
||||
enabled = state.connected,
|
||||
focusManager = focusManager,
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package org.meshtastic.feature.settings.radio.component
|
|||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
|
|
@ -31,11 +30,10 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -43,9 +41,11 @@ import androidx.compose.ui.unit.dp
|
|||
import org.meshtastic.core.model.util.DistanceUnit
|
||||
import org.meshtastic.core.model.util.toDistanceString
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.precisionBitsToMeters
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val POSITION_PRECISION_MIN = 12
|
||||
|
|
@ -63,7 +63,6 @@ fun MapReportingPreference(
|
|||
publishIntervalSecs: Int = 3600,
|
||||
onPublishIntervalSecsChanged: (Int) -> Unit = {},
|
||||
enabled: Boolean,
|
||||
focusManager: FocusManager,
|
||||
) {
|
||||
Column {
|
||||
var showMapReportingWarning by rememberSaveable { mutableStateOf(mapReportingEnabled) }
|
||||
|
|
@ -121,14 +120,14 @@ fun MapReportingPreference(
|
|||
overflow = TextOverflow.Companion.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
EditTextPreference(
|
||||
val publishItems = remember { IntervalConfiguration.BROADCAST_MEDIUM.allowedIntervals }
|
||||
DropDownPreference(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
title = stringResource(R.string.map_reporting_interval_seconds),
|
||||
value = publishIntervalSecs,
|
||||
isError = publishIntervalSecs < 3600,
|
||||
items = publishItems.map { it.value to it.toDisplayString() },
|
||||
selectedItem = publishIntervalSecs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = onPublishIntervalSecsChanged,
|
||||
onItemSelected = { onPublishIntervalSecsChanged(it.toInt()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +138,6 @@ fun MapReportingPreference(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MapReportingPreview() {
|
||||
val focusManager = LocalFocusManager.current
|
||||
MapReportingPreference(
|
||||
mapReportingEnabled = true,
|
||||
onMapReportingEnabledChanged = {},
|
||||
|
|
@ -148,6 +146,5 @@ fun MapReportingPreview() {
|
|||
positionPrecision = 5,
|
||||
onPositionPrecisionChanged = {},
|
||||
enabled = true,
|
||||
focusManager = focusManager,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -28,8 +29,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -53,37 +54,33 @@ fun NeighborInfoConfigScreen(navController: NavController, viewModel: RadioConfi
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.neighbor_info_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.neighbor_info_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.update_interval_seconds),
|
||||
value = formState.value.updateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { updateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.transmit_over_lora),
|
||||
summary = stringResource(id = R.string.config_device_transmitOverLora_summary),
|
||||
checked = formState.value.transmitOverLora,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { transmitOverLora = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
TitledCard(title = stringResource(R.string.neighbor_info_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.neighbor_info_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.update_interval_seconds),
|
||||
value = formState.value.updateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { updateInterval = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.transmit_over_lora),
|
||||
summary = stringResource(id = R.string.config_device_transmitOverLora_summary),
|
||||
checked = formState.value.transmitOverLora,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { transmitOverLora = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -47,9 +48,9 @@ import org.meshtastic.core.ui.component.DropDownPreference
|
|||
import org.meshtastic.core.ui.component.EditIPv4Preference
|
||||
import org.meshtastic.core.ui.component.EditPasswordPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SimpleAlertDialog
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ConfigProtos.Config.NetworkConfig
|
||||
import org.meshtastic.proto.config
|
||||
|
|
@ -111,181 +112,163 @@ fun NetworkConfigScreen(navController: NavController, viewModel: RadioConfigView
|
|||
},
|
||||
) {
|
||||
if (state.metadata?.hasWifi == true) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.wifi_config)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.wifi_enabled),
|
||||
summary = stringResource(id = R.string.config_network_wifi_enabled_summary),
|
||||
checked = formState.value.wifiEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { wifiEnabled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ssid),
|
||||
value = formState.value.wifiSsid,
|
||||
maxSize = 32, // wifi_ssid max_size:33
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiSsid = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditPasswordPreference(
|
||||
title = stringResource(R.string.password),
|
||||
value = formState.value.wifiPsk,
|
||||
maxSize = 64, // wifi_psk max_size:65
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiPsk = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Button(
|
||||
onClick = { zxingScan() },
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
|
||||
enabled = state.connected,
|
||||
) {
|
||||
Text(text = stringResource(R.string.wifi_qr_code_scan))
|
||||
TitledCard(title = stringResource(R.string.wifi_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.wifi_enabled),
|
||||
summary = stringResource(id = R.string.config_network_wifi_enabled_summary),
|
||||
checked = formState.value.wifiEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { wifiEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ssid),
|
||||
value = formState.value.wifiSsid,
|
||||
maxSize = 32, // wifi_ssid max_size:33
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiSsid = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditPasswordPreference(
|
||||
title = stringResource(R.string.password),
|
||||
value = formState.value.wifiPsk,
|
||||
maxSize = 64, // wifi_psk max_size:65
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiPsk = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
Button(
|
||||
onClick = { zxingScan() },
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
|
||||
enabled = state.connected,
|
||||
) {
|
||||
Text(text = stringResource(R.string.wifi_qr_code_scan))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.metadata?.hasEthernet == true) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.ethernet_config)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.ethernet_enabled),
|
||||
summary = stringResource(id = R.string.config_network_eth_enabled_summary),
|
||||
checked = formState.value.ethEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ethEnabled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
TitledCard(title = stringResource(R.string.ethernet_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.ethernet_enabled),
|
||||
summary = stringResource(id = R.string.config_network_eth_enabled_summary),
|
||||
checked = formState.value.ethEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { ethEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.metadata?.hasEthernet == true || state.metadata?.hasWifi == true) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.udp_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.udp_enabled),
|
||||
summary = stringResource(id = R.string.config_network_udp_enabled_summary),
|
||||
checked = formState.value.enabledProtocols == 1,
|
||||
TitledCard(title = stringResource(R.string.udp_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.udp_enabled),
|
||||
summary = stringResource(id = R.string.config_network_udp_enabled_summary),
|
||||
checked = formState.value.enabledProtocols == 1,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = {
|
||||
formState.value =
|
||||
formState.value.copy { if (it) enabledProtocols = 1 else enabledProtocols = 0 }
|
||||
},
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.advanced)) {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ntp_server),
|
||||
value = formState.value.ntpServer,
|
||||
maxSize = 32, // ntp_server max_size:33
|
||||
enabled = state.connected,
|
||||
onCheckedChange = {
|
||||
formState.value =
|
||||
formState.value.copy { if (it) enabledProtocols = 1 else enabledProtocols = 0 }
|
||||
isError = formState.value.ntpServer.isEmpty(),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { ntpServer = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.rsyslog_server),
|
||||
value = formState.value.rsyslogServer,
|
||||
maxSize = 32, // rsyslog_server max_size:33
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { rsyslogServer = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.ipv4_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
NetworkConfig.AddressMode.entries
|
||||
.filter { it != NetworkConfig.AddressMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.addressMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { addressMode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.ip),
|
||||
value = formState.value.ipv4Config.ip,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { ip = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.gateway),
|
||||
value = formState.value.ipv4Config.gateway,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { gateway = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.subnet),
|
||||
value = formState.value.ipv4Config.subnet,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { subnet = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditIPv4Preference(
|
||||
title = "DNS",
|
||||
value = formState.value.ipv4Config.dns,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { dns = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
|
||||
item { PreferenceCategory(text = stringResource(R.string.advanced)) }
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.ntp_server),
|
||||
value = formState.value.ntpServer,
|
||||
maxSize = 32, // ntp_server max_size:33
|
||||
enabled = state.connected,
|
||||
isError = formState.value.ntpServer.isEmpty(),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { ntpServer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.rsyslog_server),
|
||||
value = formState.value.rsyslogServer,
|
||||
maxSize = 32, // rsyslog_server max_size:33
|
||||
enabled = state.connected,
|
||||
isError = false,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { rsyslogServer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.ipv4_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
NetworkConfig.AddressMode.entries
|
||||
.filter { it != NetworkConfig.AddressMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.addressMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { addressMode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
||||
item {
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.ip),
|
||||
value = formState.value.ipv4Config.ip,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { ip = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.gateway),
|
||||
value = formState.value.ipv4Config.gateway,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { gateway = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditIPv4Preference(
|
||||
title = stringResource(R.string.subnet),
|
||||
value = formState.value.ipv4Config.subnet,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { subnet = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditIPv4Preference(
|
||||
title = "DNS",
|
||||
value = formState.value.ipv4Config.dns,
|
||||
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
val ipv4 = formState.value.ipv4Config.copy { dns = it }
|
||||
formState.value = formState.value.copy { ipv4Config = ipv4 }
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,24 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
||||
|
|
@ -54,46 +58,43 @@ fun PaxcounterConfigScreen(navController: NavController, viewModel: RadioConfigV
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.paxcounter_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.paxcounter_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.update_interval_seconds),
|
||||
value = formState.value.paxcounterUpdateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { paxcounterUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SignedIntegerEditTextPreference(
|
||||
title = stringResource(R.string.wifi_rssi_threshold_defaults_to_80),
|
||||
value = formState.value.wifiThreshold,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiThreshold = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SignedIntegerEditTextPreference(
|
||||
title = stringResource(R.string.ble_rssi_threshold_defaults_to_80),
|
||||
value = formState.value.bleThreshold,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { bleThreshold = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.paxcounter_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.paxcounter_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val items = remember { IntervalConfiguration.PAX_COUNTER.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.update_interval_seconds),
|
||||
selectedItem = formState.value.paxcounterUpdateInterval.toLong(),
|
||||
enabled = state.connected,
|
||||
items = items.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { paxcounterUpdateInterval = it.toInt() }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
SignedIntegerEditTextPreference(
|
||||
title = stringResource(R.string.wifi_rssi_threshold_defaults_to_80),
|
||||
value = formState.value.wifiThreshold,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { wifiThreshold = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SignedIntegerEditTextPreference(
|
||||
title = stringResource(R.string.ble_rssi_threshold_defaults_to_80),
|
||||
value = formState.value.bleThreshold,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { bleThreshold = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import android.annotation.SuppressLint
|
|||
import android.location.Location
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
|
|
@ -47,10 +48,13 @@ import org.meshtastic.core.strings.R
|
|||
import org.meshtastic.core.ui.component.BitwisePreference
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ConfigProtos
|
||||
import org.meshtastic.feature.settings.util.FixedUpdateIntervals
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.gpioPins
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.ConfigProtos.Config.PositionConfig
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -70,7 +74,23 @@ fun PositionConfigScreen(navController: NavController, viewModel: RadioConfigVie
|
|||
time = 1, // ignore time for fixed_position
|
||||
)
|
||||
val positionConfig = state.radioConfig.position
|
||||
val formState = rememberConfigState(initialValue = positionConfig)
|
||||
val sanitizedPositionConfig =
|
||||
remember(positionConfig) {
|
||||
val positionItems = IntervalConfiguration.POSITION.allowedIntervals
|
||||
val smartBroadcastItems = IntervalConfiguration.SMART_BROADCAST_MINIMUM.allowedIntervals
|
||||
positionConfig.copy {
|
||||
if (FixedUpdateIntervals.fromValue(positionBroadcastSecs.toLong()) == null) {
|
||||
positionBroadcastSecs = positionItems.first().value.toInt()
|
||||
}
|
||||
if (FixedUpdateIntervals.fromValue(broadcastSmartMinimumIntervalSecs.toLong()) == null) {
|
||||
broadcastSmartMinimumIntervalSecs = smartBroadcastItems.first().value.toInt()
|
||||
}
|
||||
if (FixedUpdateIntervals.fromValue(gpsUpdateInterval.toLong()) == null) {
|
||||
gpsUpdateInterval = positionItems.first().value.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
val formState = rememberConfigState(initialValue = sanitizedPositionConfig)
|
||||
var locationInput by rememberSaveable { mutableStateOf(currentPosition) }
|
||||
|
||||
val locationPermissionState =
|
||||
|
|
@ -122,182 +142,182 @@ fun PositionConfigScreen(navController: NavController, viewModel: RadioConfigVie
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.position_packet)) }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.broadcast_interval),
|
||||
summary = stringResource(id = R.string.config_position_broadcast_secs_summary),
|
||||
value = formState.value.positionBroadcastSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { positionBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.smart_position),
|
||||
checked = formState.value.positionBroadcastSmartEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { positionBroadcastSmartEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
if (formState.value.positionBroadcastSmartEnabled) {
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.minimum_interval),
|
||||
summary =
|
||||
stringResource(id = R.string.config_position_broadcast_smart_minimum_interval_secs_summary),
|
||||
value = formState.value.broadcastSmartMinimumIntervalSecs,
|
||||
TitledCard(title = stringResource(R.string.position_packet)) {
|
||||
val items = remember { IntervalConfiguration.BROADCAST_MEDIUM.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.broadcast_interval),
|
||||
summary = stringResource(id = R.string.config_position_broadcast_secs_summary),
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
formState.value = formState.value.copy { broadcastSmartMinimumIntervalSecs = it }
|
||||
items = items.map { it to it.toDisplayString() },
|
||||
selectedItem =
|
||||
FixedUpdateIntervals.fromValue(formState.value.positionBroadcastSecs.toLong()) ?: items.first(),
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { positionBroadcastSecs = it.value.toInt() }
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.minimum_distance),
|
||||
summary = stringResource(id = R.string.config_position_broadcast_smart_minimum_distance_summary),
|
||||
value = formState.value.broadcastSmartMinimumDistance,
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.smart_position),
|
||||
checked = formState.value.positionBroadcastSmartEnabled,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { broadcastSmartMinimumDistance = it } },
|
||||
onCheckedChange = { formState.value = formState.value.copy { positionBroadcastSmartEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.device_gps)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.fixed_position),
|
||||
checked = formState.value.fixedPosition,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { fixedPosition = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
if (formState.value.fixedPosition) {
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.latitude),
|
||||
value = locationInput.latitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value >= -90 && value <= 90.0) {
|
||||
locationInput = locationInput.copy(latitude = value)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.longitude),
|
||||
value = locationInput.longitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value >= -180 && value <= 180.0) {
|
||||
locationInput = locationInput.copy(longitude = value)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.altitude),
|
||||
value = locationInput.altitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value -> locationInput = locationInput.copy(altitude = value) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextButton(
|
||||
enabled = state.connected,
|
||||
onClick = { coroutineScope.launch { locationPermissionState.launchPermissionRequest() } },
|
||||
) {
|
||||
Text(text = stringResource(R.string.position_config_set_fixed_from_phone))
|
||||
if (formState.value.positionBroadcastSmartEnabled) {
|
||||
HorizontalDivider()
|
||||
val smartItems = remember { IntervalConfiguration.SMART_BROADCAST_MINIMUM.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.minimum_interval),
|
||||
summary =
|
||||
stringResource(id = R.string.config_position_broadcast_smart_minimum_interval_secs_summary),
|
||||
enabled = state.connected,
|
||||
items = smartItems.map { it to it.toDisplayString() },
|
||||
selectedItem =
|
||||
FixedUpdateIntervals.fromValue(formState.value.broadcastSmartMinimumIntervalSecs.toLong())
|
||||
?: smartItems.first(),
|
||||
onItemSelected = {
|
||||
formState.value =
|
||||
formState.value.copy { broadcastSmartMinimumIntervalSecs = it.value.toInt() }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.minimum_distance),
|
||||
summary =
|
||||
stringResource(id = R.string.config_position_broadcast_smart_minimum_distance_summary),
|
||||
value = formState.value.broadcastSmartMinimumDistance,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
formState.value = formState.value.copy { broadcastSmartMinimumDistance = it }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gps_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
ConfigProtos.Config.PositionConfig.GpsMode.entries
|
||||
.filter { it != ConfigProtos.Config.PositionConfig.GpsMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.gpsMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { gpsMode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.update_interval),
|
||||
summary = stringResource(id = R.string.config_position_gps_update_interval_summary),
|
||||
value = formState.value.gpsUpdateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { gpsUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.position_flags)) }
|
||||
item {
|
||||
BitwisePreference(
|
||||
title = stringResource(R.string.position_flags),
|
||||
summary = stringResource(id = R.string.config_position_flags_summary),
|
||||
value = formState.value.positionFlags,
|
||||
enabled = state.connected,
|
||||
items =
|
||||
ConfigProtos.Config.PositionConfig.PositionFlags.entries
|
||||
.filter {
|
||||
it != PositionConfig.PositionFlags.UNSET && it != PositionConfig.PositionFlags.UNRECOGNIZED
|
||||
TitledCard(title = stringResource(R.string.device_gps)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.fixed_position),
|
||||
checked = formState.value.fixedPosition,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { fixedPosition = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
if (formState.value.fixedPosition) {
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.latitude),
|
||||
value = locationInput.latitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value >= -90 && value <= 90.0) {
|
||||
locationInput = locationInput.copy(latitude = value)
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.longitude),
|
||||
value = locationInput.longitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value ->
|
||||
if (value >= -180 && value <= 180.0) {
|
||||
locationInput = locationInput.copy(longitude = value)
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.altitude),
|
||||
value = locationInput.altitude,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { value -> locationInput = locationInput.copy(altitude = value) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
TextButton(
|
||||
enabled = state.connected,
|
||||
onClick = { coroutineScope.launch { locationPermissionState.launchPermissionRequest() } },
|
||||
) {
|
||||
Text(text = stringResource(R.string.position_config_set_fixed_from_phone))
|
||||
}
|
||||
.map { it.number to it.name },
|
||||
onItemSelected = { formState.value = formState.value.copy { positionFlags = it } },
|
||||
)
|
||||
} else {
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gps_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
PositionConfig.GpsMode.entries
|
||||
.filter { it != PositionConfig.GpsMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.gpsMode,
|
||||
onItemSelected = { formState.value = formState.value.copy { gpsMode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val items = remember { IntervalConfiguration.GPS_UPDATE.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.update_interval),
|
||||
summary = stringResource(id = R.string.config_position_gps_update_interval_summary),
|
||||
enabled = state.connected,
|
||||
items = items.map { it to it.toDisplayString() },
|
||||
selectedItem =
|
||||
FixedUpdateIntervals.fromValue(formState.value.gpsUpdateInterval.toLong()) ?: items.first(),
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { gpsUpdateInterval = it.value.toInt() }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item { PreferenceCategory(text = stringResource(R.string.advanced_device_gps)) }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gps_receive_gpio),
|
||||
value = formState.value.rxGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { rxGpio = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.position_flags)) {
|
||||
BitwisePreference(
|
||||
title = stringResource(R.string.position_flags),
|
||||
summary = stringResource(id = R.string.config_position_flags_summary),
|
||||
value = formState.value.positionFlags,
|
||||
enabled = state.connected,
|
||||
items =
|
||||
PositionConfig.PositionFlags.entries
|
||||
.filter {
|
||||
it != PositionConfig.PositionFlags.UNSET &&
|
||||
it != PositionConfig.PositionFlags.UNRECOGNIZED
|
||||
}
|
||||
.map { it.number to it.name },
|
||||
onItemSelected = { formState.value = formState.value.copy { positionFlags = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gps_transmit_gpio),
|
||||
value = formState.value.txGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { txGpio = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.gps_en_gpio),
|
||||
value = formState.value.gpsEnGpio,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { gpsEnGpio = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.advanced_device_gps)) {
|
||||
val pins = remember { gpioPins }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gps_receive_gpio),
|
||||
enabled = state.connected,
|
||||
items = pins,
|
||||
selectedItem = formState.value.rxGpio,
|
||||
onItemSelected = { formState.value = formState.value.copy { rxGpio = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gps_transmit_gpio),
|
||||
enabled = state.connected,
|
||||
items = pins,
|
||||
selectedItem = formState.value.txGpio,
|
||||
onItemSelected = { formState.value = formState.value.copy { txGpio = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.gps_en_gpio),
|
||||
enabled = state.connected,
|
||||
items = pins,
|
||||
selectedItem = formState.value.gpsEnGpio,
|
||||
onItemSelected = { formState.value = formState.value.copy { gpsEnGpio = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,19 +18,24 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
|
||||
|
|
@ -53,107 +58,83 @@ fun PowerConfigScreen(navController: NavController, viewModel: RadioConfigViewMo
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.power_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.enable_power_saving_mode),
|
||||
summary = stringResource(id = R.string.config_power_is_power_saving_summary),
|
||||
checked = formState.value.isPowerSaving,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isPowerSaving = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.shutdown_on_power_loss),
|
||||
checked = formState.value.onBatteryShutdownAfterSecs > 0,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = {
|
||||
formState.value = formState.value.copy { onBatteryShutdownAfterSecs = if (it) 3600 else 0 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (formState.value.onBatteryShutdownAfterSecs > 0) {
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.power_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.enable_power_saving_mode),
|
||||
summary = stringResource(id = R.string.config_power_is_power_saving_summary),
|
||||
checked = formState.value.isPowerSaving,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isPowerSaving = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val items = remember { IntervalConfiguration.ALL.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.shutdown_on_power_loss),
|
||||
selectedItem = formState.value.onBatteryShutdownAfterSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = items.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { onBatteryShutdownAfterSecs = it.toInt() }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.adc_multiplier_override),
|
||||
checked = formState.value.adcMultiplierOverride > 0f,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = {
|
||||
formState.value = formState.value.copy { adcMultiplierOverride = if (it) 1.0f else 0.0f }
|
||||
},
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
if (formState.value.adcMultiplierOverride > 0f) {
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.adc_multiplier_override_ratio),
|
||||
value = formState.value.adcMultiplierOverride,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { adcMultiplierOverride = it } },
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
val waitBluetoothItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.wait_for_bluetooth_duration_seconds),
|
||||
selectedItem = formState.value.waitBluetoothSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = waitBluetoothItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { waitBluetoothSecs = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val sdsSecsItems = remember { IntervalConfiguration.ALL.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.super_deep_sleep_duration_seconds),
|
||||
selectedItem = formState.value.sdsSecs.toLong(),
|
||||
onItemSelected = { formState.value = formState.value.copy { sdsSecs = it.toInt() } },
|
||||
enabled = state.connected,
|
||||
items = sdsSecsItems.map { it.value to it.toDisplayString() },
|
||||
)
|
||||
HorizontalDivider()
|
||||
val minWakeItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.minimum_wake_time_seconds),
|
||||
selectedItem = formState.value.minWakeSecs.toLong(),
|
||||
enabled = state.connected,
|
||||
items = minWakeItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { minWakeSecs = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.shutdown_on_battery_delay_seconds),
|
||||
value = formState.value.onBatteryShutdownAfterSecs,
|
||||
title = stringResource(R.string.battery_ina_2xx_i2c_address),
|
||||
value = formState.value.deviceBatteryInaAddress,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { onBatteryShutdownAfterSecs = it } },
|
||||
onValueChanged = { formState.value = formState.value.copy { deviceBatteryInaAddress = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.adc_multiplier_override),
|
||||
checked = formState.value.adcMultiplierOverride > 0f,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = {
|
||||
formState.value = formState.value.copy { adcMultiplierOverride = if (it) 1.0f else 0.0f }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (formState.value.adcMultiplierOverride > 0f) {
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.adc_multiplier_override_ratio),
|
||||
value = formState.value.adcMultiplierOverride,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { adcMultiplierOverride = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.wait_for_bluetooth_duration_seconds),
|
||||
value = formState.value.waitBluetoothSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { waitBluetoothSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.super_deep_sleep_duration_seconds),
|
||||
value = formState.value.sdsSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { sdsSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.minimum_wake_time_seconds),
|
||||
value = formState.value.minWakeSecs,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { minWakeSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.battery_ina_2xx_i2c_address),
|
||||
value = formState.value.deviceBatteryInaAddress,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { deviceBatteryInaAddress = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,20 +17,22 @@
|
|||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
||||
|
|
@ -39,7 +41,6 @@ fun RangeTestConfigScreen(navController: NavController, viewModel: RadioConfigVi
|
|||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
val rangeTestConfig = state.moduleConfig.rangeTest
|
||||
val formState = rememberConfigState(initialValue = rangeTestConfig)
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
RadioConfigScreenList(
|
||||
title = stringResource(id = R.string.range_test),
|
||||
|
|
@ -53,36 +54,33 @@ fun RangeTestConfigScreen(navController: NavController, viewModel: RadioConfigVi
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.range_test_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.range_test_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.range_test_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.range_test_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val rangeItems = remember { IntervalConfiguration.RANGE_TEST_SENDER.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.sender_message_interval_seconds),
|
||||
selectedItem = formState.value.sender.toLong(),
|
||||
enabled = state.connected,
|
||||
items = rangeItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { sender = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.save_csv_in_storage_esp32_only),
|
||||
checked = formState.value.save,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { save = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.sender_message_interval_seconds),
|
||||
value = formState.value.sender,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { sender = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.save_csv_in_storage_esp32_only),
|
||||
checked = formState.value.save,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { save = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -28,8 +29,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditListPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -53,43 +54,39 @@ fun RemoteHardwareConfigScreen(navController: NavController, viewModel: RadioCon
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.remote_hardware_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.remote_hardware_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.allow_undefined_pin_access),
|
||||
checked = formState.value.allowUndefinedPinAccess,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { allowUndefinedPinAccess = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditListPreference(
|
||||
title = stringResource(R.string.available_pins),
|
||||
list = formState.value.availablePinsList,
|
||||
maxCount = 4, // available_pins max_count:4
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = { list ->
|
||||
formState.value =
|
||||
formState.value.copy {
|
||||
availablePins.clear()
|
||||
availablePins.addAll(list)
|
||||
}
|
||||
},
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.remote_hardware_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.remote_hardware_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.allow_undefined_pin_access),
|
||||
checked = formState.value.allowUndefinedPinAccess,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { allowUndefinedPinAccess = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditListPreference(
|
||||
title = stringResource(R.string.available_pins),
|
||||
list = formState.value.availablePinsList,
|
||||
maxCount = 4, // available_pins max_count:4
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = { list ->
|
||||
formState.value =
|
||||
formState.value.copy {
|
||||
availablePins.clear()
|
||||
availablePins.addAll(list)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -50,8 +51,8 @@ import org.meshtastic.core.strings.R
|
|||
import org.meshtastic.core.ui.component.CopyIconButton
|
||||
import org.meshtastic.core.ui.component.EditBase64Preference
|
||||
import org.meshtastic.core.ui.component.EditListPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ConfigProtos.Config.SecurityConfig
|
||||
import org.meshtastic.proto.config
|
||||
|
|
@ -134,121 +135,114 @@ fun SecurityConfigScreen(navController: NavController, viewModel: RadioConfigVie
|
|||
viewModel.setConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.direct_message_key)) }
|
||||
|
||||
item {
|
||||
EditBase64Preference(
|
||||
title = stringResource(R.string.public_key),
|
||||
summary = stringResource(id = R.string.config_security_public_key),
|
||||
value = publicKey,
|
||||
enabled = state.connected,
|
||||
readOnly = true,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChange = {
|
||||
if (it.size() == 32) {
|
||||
formState.value = formState.value.copy { this.publicKey = it }
|
||||
}
|
||||
},
|
||||
trailingIcon = { CopyIconButton(valueToCopy = formState.value.publicKey.encodeToString()) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditBase64Preference(
|
||||
title = stringResource(R.string.private_key),
|
||||
summary = stringResource(id = R.string.config_security_private_key),
|
||||
value = formState.value.privateKey,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChange = {
|
||||
if (it.size() == 32) {
|
||||
formState.value = formState.value.copy { privateKey = it }
|
||||
}
|
||||
},
|
||||
trailingIcon = { CopyIconButton(valueToCopy = formState.value.privateKey.encodeToString()) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
NodeActionButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
title = stringResource(R.string.regenerate_private_key),
|
||||
enabled = state.connected,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = { showKeyGenerationDialog = true },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
NodeActionButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
title = stringResource(R.string.export_keys),
|
||||
enabled = state.connected,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = { showEditSecurityConfigDialog = true },
|
||||
)
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.admin_keys)) }
|
||||
item {
|
||||
EditListPreference(
|
||||
title = stringResource(R.string.admin_key),
|
||||
summary = stringResource(id = R.string.config_security_admin_key),
|
||||
list = formState.value.adminKeyList,
|
||||
maxCount = 3,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = {
|
||||
formState.value =
|
||||
formState.value.copy {
|
||||
adminKey.clear()
|
||||
adminKey.addAll(it)
|
||||
TitledCard(title = stringResource(R.string.direct_message_key)) {
|
||||
EditBase64Preference(
|
||||
title = stringResource(R.string.public_key),
|
||||
summary = stringResource(id = R.string.config_security_public_key),
|
||||
value = publicKey,
|
||||
enabled = state.connected,
|
||||
readOnly = true,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChange = {
|
||||
if (it.size() == 32) {
|
||||
formState.value = formState.value.copy { this.publicKey = it }
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
trailingIcon = { CopyIconButton(valueToCopy = formState.value.publicKey.encodeToString()) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditBase64Preference(
|
||||
title = stringResource(R.string.private_key),
|
||||
summary = stringResource(id = R.string.config_security_private_key),
|
||||
value = formState.value.privateKey,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChange = {
|
||||
if (it.size() == 32) {
|
||||
formState.value = formState.value.copy { privateKey = it }
|
||||
}
|
||||
},
|
||||
trailingIcon = { CopyIconButton(valueToCopy = formState.value.privateKey.encodeToString()) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
NodeActionButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
title = stringResource(R.string.regenerate_private_key),
|
||||
enabled = state.connected,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = { showKeyGenerationDialog = true },
|
||||
)
|
||||
HorizontalDivider()
|
||||
NodeActionButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
title = stringResource(R.string.export_keys),
|
||||
enabled = state.connected,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = { showEditSecurityConfigDialog = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.logs)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.serial_console),
|
||||
summary = stringResource(id = R.string.config_security_serial_enabled),
|
||||
checked = formState.value.serialEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { serialEnabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.admin_keys)) {
|
||||
EditListPreference(
|
||||
title = stringResource(R.string.admin_key),
|
||||
summary = stringResource(id = R.string.config_security_admin_key),
|
||||
list = formState.value.adminKeyList,
|
||||
maxCount = 3,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = {
|
||||
formState.value =
|
||||
formState.value.copy {
|
||||
adminKey.clear()
|
||||
adminKey.addAll(it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.debug_log_api_enabled),
|
||||
summary = stringResource(id = R.string.config_security_debug_log_api_enabled),
|
||||
checked = formState.value.debugLogApiEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { debugLogApiEnabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.logs)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.serial_console),
|
||||
summary = stringResource(id = R.string.config_security_serial_enabled),
|
||||
checked = formState.value.serialEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { serialEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.debug_log_api_enabled),
|
||||
summary = stringResource(id = R.string.config_security_debug_log_api_enabled),
|
||||
checked = formState.value.debugLogApiEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { debugLogApiEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item { PreferenceCategory(text = stringResource(R.string.administration)) }
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.managed_mode),
|
||||
summary = stringResource(id = R.string.config_security_is_managed),
|
||||
checked = formState.value.isManaged,
|
||||
enabled = state.connected && formState.value.adminKeyCount > 0,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isManaged = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.administration)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.managed_mode),
|
||||
summary = stringResource(id = R.string.config_security_is_managed),
|
||||
checked = formState.value.isManaged,
|
||||
enabled = state.connected && formState.value.adminKeyCount > 0,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isManaged = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.legacy_admin_channel),
|
||||
checked = formState.value.adminChannelEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { adminChannelEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.legacy_admin_channel),
|
||||
checked = formState.value.adminChannelEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { adminChannelEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -29,8 +30,8 @@ import androidx.navigation.NavController
|
|||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig.SerialConfig
|
||||
import org.meshtastic.proto.copy
|
||||
|
|
@ -55,94 +56,78 @@ fun SerialConfigScreen(navController: NavController, viewModel: RadioConfigViewM
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.serial_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.serial_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.serial_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.serial_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.echo_enabled),
|
||||
checked = formState.value.echo,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { echo = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = "RX",
|
||||
value = formState.value.rxd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { rxd = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = "TX",
|
||||
value = formState.value.txd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { txd = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_baud_rate),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
SerialConfig.Serial_Baud.entries
|
||||
.filter { it != SerialConfig.Serial_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.baud,
|
||||
onItemSelected = { formState.value = formState.value.copy { baud = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.timeout),
|
||||
value = formState.value.timeout,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { timeout = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
SerialConfig.Serial_Mode.entries
|
||||
.filter { it != SerialConfig.Serial_Mode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.mode,
|
||||
onItemSelected = { formState.value = formState.value.copy { mode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.override_console_serial_port),
|
||||
checked = formState.value.overrideConsoleSerialPort,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { overrideConsoleSerialPort = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.echo_enabled),
|
||||
checked = formState.value.echo,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { echo = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "RX",
|
||||
value = formState.value.rxd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { rxd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = "TX",
|
||||
value = formState.value.txd,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { txd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_baud_rate),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
SerialConfig.Serial_Baud.entries
|
||||
.filter { it != SerialConfig.Serial_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.baud,
|
||||
onItemSelected = { formState.value = formState.value.copy { baud = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.timeout),
|
||||
value = formState.value.timeout,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { timeout = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_mode),
|
||||
enabled = state.connected,
|
||||
items =
|
||||
SerialConfig.Serial_Mode.entries
|
||||
.filter { it != SerialConfig.Serial_Mode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = formState.value.mode,
|
||||
onItemSelected = { formState.value = formState.value.copy { mode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.override_console_serial_port),
|
||||
checked = formState.value.overrideConsoleSerialPort,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { overrideConsoleSerialPort = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -28,8 +29,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
|
@ -53,66 +54,56 @@ fun StoreForwardConfigScreen(navController: NavController, viewModel: RadioConfi
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.store_forward_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.store_forward_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.store_forward_config)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.store_forward_enabled),
|
||||
checked = formState.value.enabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.heartbeat),
|
||||
checked = formState.value.heartbeat,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { heartbeat = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.number_of_records),
|
||||
value = formState.value.records,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { records = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.history_return_max),
|
||||
value = formState.value.historyReturnMax,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { historyReturnMax = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.history_return_window),
|
||||
value = formState.value.historyReturnWindow,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { historyReturnWindow = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.server),
|
||||
checked = formState.value.isServer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isServer = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.heartbeat),
|
||||
checked = formState.value.heartbeat,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { heartbeat = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.number_of_records),
|
||||
value = formState.value.records,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { records = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.history_return_max),
|
||||
value = formState.value.historyReturnMax,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { historyReturnMax = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.history_return_window),
|
||||
value = formState.value.historyReturnWindow,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { historyReturnWindow = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.server),
|
||||
checked = formState.value.isServer,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isServer = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,29 +17,35 @@
|
|||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import org.meshtastic.core.model.DeviceVersion
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.IntervalConfiguration
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
|
||||
private const val MIN_FW_FOR_TELEMETRY_TOGGLE = "2.7.12"
|
||||
|
||||
@Composable
|
||||
fun TelemetryConfigScreen(navController: NavController, viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
val telemetryConfig = state.moduleConfig.telemetry
|
||||
val formState = rememberConfigState(initialValue = telemetryConfig)
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val firmwareVersion = state.metadata?.firmwareVersion ?: "1"
|
||||
|
||||
RadioConfigScreenList(
|
||||
title = stringResource(id = R.string.telemetry),
|
||||
|
|
@ -53,116 +59,105 @@ fun TelemetryConfigScreen(navController: NavController, viewModel: RadioConfigVi
|
|||
viewModel.setModuleConfig(config)
|
||||
},
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.telemetry_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.device_telemetry_enabled),
|
||||
summary = stringResource(R.string.device_telemetry_enabled_summary),
|
||||
checked = formState.value.deviceTelemetryEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { deviceTelemetryEnabled = it } },
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.telemetry_config)) {
|
||||
if (DeviceVersion(firmwareVersion) >= DeviceVersion(MIN_FW_FOR_TELEMETRY_TOGGLE)) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.device_telemetry_enabled),
|
||||
summary = stringResource(R.string.device_telemetry_enabled_summary),
|
||||
checked = formState.value.deviceTelemetryEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { deviceTelemetryEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
val items = remember { IntervalConfiguration.BROADCAST_SHORT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.device_metrics_update_interval_seconds),
|
||||
selectedItem = formState.value.deviceUpdateInterval.toLong(),
|
||||
enabled = state.connected,
|
||||
items = items.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { deviceUpdateInterval = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_module_enabled),
|
||||
checked = formState.value.environmentMeasurementEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentMeasurementEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val envItems = remember { IntervalConfiguration.BROADCAST_SHORT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.environment_metrics_update_interval_seconds),
|
||||
selectedItem = formState.value.environmentUpdateInterval.toLong(),
|
||||
enabled = state.connected,
|
||||
items = envItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = {
|
||||
formState.value = formState.value.copy { environmentUpdateInterval = it.toInt() }
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_on_screen_enabled),
|
||||
checked = formState.value.environmentScreenEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentScreenEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_use_fahrenheit),
|
||||
checked = formState.value.environmentDisplayFahrenheit,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentDisplayFahrenheit = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.air_quality_metrics_module_enabled),
|
||||
checked = formState.value.airQualityEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { airQualityEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val airItems = remember { IntervalConfiguration.BROADCAST_SHORT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.air_quality_metrics_update_interval_seconds),
|
||||
selectedItem = formState.value.airQualityInterval.toLong(),
|
||||
enabled = state.connected,
|
||||
items = airItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { airQualityInterval = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.power_metrics_module_enabled),
|
||||
checked = formState.value.powerMeasurementEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { powerMeasurementEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
val powerItems = remember { IntervalConfiguration.BROADCAST_SHORT.allowedIntervals }
|
||||
DropDownPreference(
|
||||
title = stringResource(R.string.power_metrics_update_interval_seconds),
|
||||
selectedItem = formState.value.powerUpdateInterval.toLong(),
|
||||
enabled = state.connected,
|
||||
items = powerItems.map { it.value to it.toDisplayString() },
|
||||
onItemSelected = { formState.value = formState.value.copy { powerUpdateInterval = it.toInt() } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.power_metrics_on_screen_enabled),
|
||||
checked = formState.value.powerScreenEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { powerScreenEnabled = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.device_metrics_update_interval_seconds),
|
||||
value = formState.value.deviceUpdateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { deviceUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.environment_metrics_update_interval_seconds),
|
||||
value = formState.value.environmentUpdateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { environmentUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_module_enabled),
|
||||
checked = formState.value.environmentMeasurementEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentMeasurementEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_on_screen_enabled),
|
||||
checked = formState.value.environmentScreenEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentScreenEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.environment_metrics_use_fahrenheit),
|
||||
checked = formState.value.environmentDisplayFahrenheit,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { environmentDisplayFahrenheit = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.air_quality_metrics_module_enabled),
|
||||
checked = formState.value.airQualityEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { airQualityEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.air_quality_metrics_update_interval_seconds),
|
||||
value = formState.value.airQualityInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { airQualityInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.power_metrics_module_enabled),
|
||||
checked = formState.value.powerMeasurementEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { powerMeasurementEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.power_metrics_update_interval_seconds),
|
||||
value = formState.value.powerUpdateInterval,
|
||||
enabled = state.connected,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { powerUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.power_metrics_on_screen_enabled),
|
||||
checked = formState.value.powerScreenEnabled,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { powerScreenEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -33,9 +34,9 @@ import org.meshtastic.core.database.model.isUnmessageableRole
|
|||
import org.meshtastic.core.model.DeviceVersion
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.component.RegularPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.proto.copy
|
||||
|
||||
|
|
@ -60,73 +61,60 @@ fun UserConfigScreen(navController: NavController, viewModel: RadioConfigViewMod
|
|||
onDismissPacketResponse = viewModel::clearPacketResponse,
|
||||
onSave = viewModel::setOwner,
|
||||
) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.user_config)) }
|
||||
|
||||
item {
|
||||
RegularPreference(title = stringResource(R.string.node_id), subtitle = formState.value.id, onClick = {})
|
||||
TitledCard(title = stringResource(R.string.user_config)) {
|
||||
RegularPreference(title = stringResource(R.string.node_id), subtitle = formState.value.id, onClick = {})
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.long_name),
|
||||
value = formState.value.longName,
|
||||
maxSize = 39, // long_name max_size:40
|
||||
enabled = state.connected,
|
||||
isError = !validLongName,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { longName = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.short_name),
|
||||
value = formState.value.shortName,
|
||||
maxSize = 4, // short_name max_size:5
|
||||
enabled = state.connected,
|
||||
isError = !validShortName,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { shortName = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
RegularPreference(
|
||||
title = stringResource(R.string.hardware_model),
|
||||
subtitle = formState.value.hwModel.name,
|
||||
onClick = {},
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.unmessageable),
|
||||
summary = stringResource(R.string.unmonitored_or_infrastructure),
|
||||
checked =
|
||||
formState.value.isUnmessagable ||
|
||||
(firmwareVersion < DeviceVersion("2.6.9") && formState.value.role.isUnmessageableRole()),
|
||||
enabled = formState.value.hasIsUnmessagable() || firmwareVersion >= DeviceVersion("2.6.9"),
|
||||
onCheckedChange = { formState.value = formState.value.copy { isUnmessagable = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
HorizontalDivider()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.licensed_amateur_radio),
|
||||
summary = stringResource(R.string.licensed_amateur_radio_text),
|
||||
checked = formState.value.isLicensed,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isLicensed = it } },
|
||||
containerColor = CardDefaults.cardColors().containerColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.long_name),
|
||||
value = formState.value.longName,
|
||||
maxSize = 39, // long_name max_size:40
|
||||
enabled = state.connected,
|
||||
isError = !validLongName,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { longName = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.short_name),
|
||||
value = formState.value.shortName,
|
||||
maxSize = 4, // short_name max_size:5
|
||||
enabled = state.connected,
|
||||
isError = !validShortName,
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { formState.value = formState.value.copy { shortName = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
RegularPreference(
|
||||
title = stringResource(R.string.hardware_model),
|
||||
subtitle = formState.value.hwModel.name,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.unmessageable),
|
||||
summary = stringResource(R.string.unmonitored_or_infrastructure),
|
||||
checked =
|
||||
formState.value.isUnmessagable ||
|
||||
(firmwareVersion < DeviceVersion("2.6.9") && formState.value.role.isUnmessageableRole()),
|
||||
enabled = formState.value.hasIsUnmessagable() || firmwareVersion >= DeviceVersion("2.6.9"),
|
||||
onCheckedChange = { formState.value = formState.value.copy { isUnmessagable = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.licensed_amateur_radio),
|
||||
summary = stringResource(R.string.licensed_amateur_radio_text),
|
||||
checked = formState.value.isLicensed,
|
||||
enabled = state.connected,
|
||||
onCheckedChange = { formState.value = formState.value.copy { isLicensed = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package org.meshtastic.feature.settings.util
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Defines a set of fixed time intervals in seconds, commonly used for configuration settings.
|
||||
*
|
||||
* @param value The interval duration in seconds.
|
||||
*/
|
||||
enum class FixedUpdateIntervals(val value: Long) {
|
||||
UNSET(0L),
|
||||
ONE_SECOND(1L),
|
||||
TWO_SECONDS(2L),
|
||||
THREE_SECONDS(3L),
|
||||
FOUR_SECONDS(4L),
|
||||
FIVE_SECONDS(5L),
|
||||
TEN_SECONDS(10L),
|
||||
FIFTEEN_SECONDS(15L),
|
||||
TWENTY_SECONDS(20L),
|
||||
THIRTY_SECONDS(30L),
|
||||
FORTY_FIVE_SECONDS(45L),
|
||||
ONE_MINUTE(TimeUnit.MINUTES.toSeconds(1)),
|
||||
TWO_MINUTES(TimeUnit.MINUTES.toSeconds(2)),
|
||||
FIVE_MINUTES(TimeUnit.MINUTES.toSeconds(5)),
|
||||
TEN_MINUTES(TimeUnit.MINUTES.toSeconds(10)),
|
||||
FIFTEEN_MINUTES(TimeUnit.MINUTES.toSeconds(15)),
|
||||
THIRTY_MINUTES(TimeUnit.MINUTES.toSeconds(30)),
|
||||
ONE_HOUR(TimeUnit.HOURS.toSeconds(1)),
|
||||
TWO_HOURS(TimeUnit.HOURS.toSeconds(2)),
|
||||
THREE_HOURS(TimeUnit.HOURS.toSeconds(3)),
|
||||
FOUR_HOURS(TimeUnit.HOURS.toSeconds(4)),
|
||||
FIVE_HOURS(TimeUnit.HOURS.toSeconds(5)),
|
||||
SIX_HOURS(TimeUnit.HOURS.toSeconds(6)),
|
||||
TWELVE_HOURS(TimeUnit.HOURS.toSeconds(12)),
|
||||
EIGHTEEN_HOURS(TimeUnit.HOURS.toSeconds(18)),
|
||||
TWENTY_FOUR_HOURS(TimeUnit.HOURS.toSeconds(24)),
|
||||
THIRTY_SIX_HOURS(TimeUnit.HOURS.toSeconds(36)),
|
||||
FORTY_EIGHT_HOURS(TimeUnit.HOURS.toSeconds(48)),
|
||||
SEVENTY_TWO_HOURS(TimeUnit.HOURS.toSeconds(72)),
|
||||
ALWAYS_ON(Int.MAX_VALUE.toLong()),
|
||||
;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Finds a [FixedUpdateIntervals] that matches the given value.
|
||||
*
|
||||
* @return The corresponding [FixedUpdateIntervals] or null if no match is found.
|
||||
*/
|
||||
fun fromValue(value: Long): FixedUpdateIntervals? = entries.find { it.value == value }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a specific configuration context that determines a subset of allowed update intervals. This is used to
|
||||
* filter the available [FixedUpdateIntervals] for a particular setting.
|
||||
*/
|
||||
enum class IntervalConfiguration {
|
||||
ALL,
|
||||
BROADCAST_SHORT,
|
||||
BROADCAST_MEDIUM,
|
||||
BROADCAST_LONG,
|
||||
NODE_INFO_BROADCAST,
|
||||
DETECTION_SENSOR_MINIMUM,
|
||||
DETECTION_SENSOR_STATE,
|
||||
NAG_TIMEOUT,
|
||||
OUTPUT,
|
||||
PAX_COUNTER,
|
||||
POSITION,
|
||||
POSITION_BROADCAST,
|
||||
GPS_UPDATE,
|
||||
RANGE_TEST_SENDER,
|
||||
SMART_BROADCAST_MINIMUM,
|
||||
DISPLAY_SCREEN_ON,
|
||||
DISPLAY_CAROUSEL,
|
||||
;
|
||||
|
||||
/** A list of [FixedUpdateIntervals] that are permissible for this configuration. */
|
||||
val allowedIntervals: List<FixedUpdateIntervals> by lazy {
|
||||
when (this) {
|
||||
ALL -> FixedUpdateIntervals.entries
|
||||
BROADCAST_SHORT ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
BROADCAST_MEDIUM ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
BROADCAST_LONG ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
NODE_INFO_BROADCAST ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
OUTPUT ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.ONE_SECOND,
|
||||
FixedUpdateIntervals.TWO_SECONDS,
|
||||
FixedUpdateIntervals.THREE_SECONDS,
|
||||
FixedUpdateIntervals.FOUR_SECONDS,
|
||||
FixedUpdateIntervals.FIVE_SECONDS,
|
||||
FixedUpdateIntervals.TEN_SECONDS,
|
||||
)
|
||||
DETECTION_SENSOR_MINIMUM ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.TWO_MINUTES,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
DETECTION_SENSOR_STATE ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
NAG_TIMEOUT ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.ONE_SECOND,
|
||||
FixedUpdateIntervals.FIVE_SECONDS,
|
||||
FixedUpdateIntervals.TEN_SECONDS,
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
)
|
||||
PAX_COUNTER ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
POSITION ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.ONE_SECOND,
|
||||
FixedUpdateIntervals.TWO_SECONDS,
|
||||
FixedUpdateIntervals.FIVE_SECONDS,
|
||||
FixedUpdateIntervals.TEN_SECONDS,
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.TWENTY_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.FORTY_FIVE_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.TWO_MINUTES,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
)
|
||||
POSITION_BROADCAST ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.TWO_HOURS,
|
||||
FixedUpdateIntervals.THREE_HOURS,
|
||||
FixedUpdateIntervals.FOUR_HOURS,
|
||||
FixedUpdateIntervals.FIVE_HOURS,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.EIGHTEEN_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
FixedUpdateIntervals.THIRTY_SIX_HOURS,
|
||||
FixedUpdateIntervals.FORTY_EIGHT_HOURS,
|
||||
FixedUpdateIntervals.SEVENTY_TWO_HOURS,
|
||||
)
|
||||
GPS_UPDATE ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.TWO_MINUTES,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.SIX_HOURS,
|
||||
FixedUpdateIntervals.TWELVE_HOURS,
|
||||
FixedUpdateIntervals.TWENTY_FOUR_HOURS,
|
||||
)
|
||||
RANGE_TEST_SENDER ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.FORTY_FIVE_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
)
|
||||
SMART_BROADCAST_MINIMUM ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.FORTY_FIVE_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
)
|
||||
|
||||
DISPLAY_SCREEN_ON ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
FixedUpdateIntervals.THIRTY_MINUTES,
|
||||
FixedUpdateIntervals.ONE_HOUR,
|
||||
FixedUpdateIntervals.ALWAYS_ON,
|
||||
)
|
||||
|
||||
DISPLAY_CAROUSEL ->
|
||||
listOf(
|
||||
FixedUpdateIntervals.UNSET,
|
||||
FixedUpdateIntervals.FIFTEEN_SECONDS,
|
||||
FixedUpdateIntervals.THIRTY_SECONDS,
|
||||
FixedUpdateIntervals.ONE_MINUTE,
|
||||
FixedUpdateIntervals.FIVE_MINUTES,
|
||||
FixedUpdateIntervals.TEN_MINUTES,
|
||||
FixedUpdateIntervals.FIFTEEN_MINUTES,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an update interval, which can be either a predefined fixed value or a custom manual value in seconds. This
|
||||
* is a type-safe representation for settings that involve time durations.
|
||||
*/
|
||||
sealed class UpdateInterval {
|
||||
/** The duration of the interval in seconds. */
|
||||
abstract val value: Long
|
||||
|
||||
/** A unique, stable identifier for this interval, suitable for use in Compose keys. */
|
||||
val id: String
|
||||
get() =
|
||||
when (this) {
|
||||
is Fixed -> "fixed_$value"
|
||||
is Manual -> "manual_$value"
|
||||
}
|
||||
|
||||
/** A predefined, fixed interval. */
|
||||
data class Fixed(val interval: FixedUpdateIntervals) : UpdateInterval() {
|
||||
override val value: Long = interval.value
|
||||
}
|
||||
|
||||
/** A user-defined interval, specified in seconds. */
|
||||
data class Manual(override val value: Long) : UpdateInterval()
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an [UpdateInterval] from a raw Long value in seconds. If the value matches a predefined
|
||||
* [FixedUpdateIntervals], a [Fixed] instance is returned. Otherwise, a [Manual] instance is returned.
|
||||
*
|
||||
* @param value The interval duration in seconds.
|
||||
*/
|
||||
fun fromValue(value: Long): UpdateInterval =
|
||||
FixedUpdateIntervals.fromValue(value)?.let { Fixed(it) } ?: Manual(value)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.util
|
||||
|
||||
fun FixedUpdateIntervals.toDisplayString(): String = if (this == FixedUpdateIntervals.UNSET) {
|
||||
"Never"
|
||||
} else {
|
||||
name.split('_').joinToString(" ") { word -> word.lowercase().replaceFirstChar { it.uppercase() } }
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.util
|
||||
|
||||
val gpioPins = (0..48).map { it to "Pin $it" }
|
||||
val hopLimits = (0..7).map { it to it.toString() }
|
||||
Loading…
Add table
Add a link
Reference in a new issue