feat: Channel Configuration ui (#1873)

This commit is contained in:
Robert-0410
2025-05-20 19:22:30 -07:00
committed by GitHub
parent 71446f61d6
commit 5a52239721
2 changed files with 134 additions and 41 deletions

View File

@@ -23,12 +23,16 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
@@ -47,6 +51,7 @@ import androidx.compose.material3.Text
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.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@@ -59,9 +64,11 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ChannelProtos.ChannelSettings
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
import com.geeksville.mesh.R
import com.geeksville.mesh.channelSettings
import com.geeksville.mesh.model.Channel
@@ -165,7 +172,7 @@ fun ChannelConfigScreen(
ChannelSettingsItemList(
settingsList = state.channelList,
modemPresetName = Channel(loraConfig = state.radioConfig.lora).name,
loraConfig = state.radioConfig.lora,
enabled = state.connected,
maxChannels = viewModel.maxChannels,
onPositiveClicked = { channelListInput ->
@@ -174,16 +181,21 @@ fun ChannelConfigScreen(
)
}
@Suppress("LongMethod")
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
fun ChannelSettingsItemList(
settingsList: List<ChannelSettings>,
modemPresetName: String = stringResource(R.string.default_),
loraConfig: LoRaConfig,
maxChannels: Int = 8,
enabled: Boolean,
onNegativeClicked: () -> Unit = { },
onPositiveClicked: (List<ChannelSettings>) -> Unit,
) {
val primarySettings = settingsList.getOrNull(0) ?: return
val primaryChannel by remember(loraConfig) {
mutableStateOf(Channel(primarySettings, loraConfig))
}
val focusManager = LocalFocusManager.current
val settingsListInput = rememberSaveable(
saver = listSaver(save = { it.toList() }, restore = { it.toMutableStateList() })
@@ -207,7 +219,7 @@ fun ChannelSettingsItemList(
channelSettings = with(settingsListInput) {
if (size > index) get(index) else channelSettings { }
},
modemPresetName = modemPresetName,
modemPresetName = primaryChannel.name,
onAddClick = {
if (settingsListInput.size > index) {
settingsListInput[index] = it
@@ -225,45 +237,94 @@ fun ChannelSettingsItemList(
.fillMaxSize()
.clickable(onClick = { }, enabled = false)
) {
LazyColumn(
modifier = Modifier.dragContainer(
dragDropState = dragDropState,
haptics = LocalHapticFeedback.current,
),
state = listState,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
item { PreferenceCategory(text = stringResource(R.string.channels)) }
Column {
dragDropItemsIndexed(
items = settingsListInput,
dragDropState = dragDropState,
) { index, channel, isDragging ->
ChannelCard(
index = index,
title = channel.name.ifEmpty { modemPresetName },
enabled = enabled,
onEditClick = { showEditChannelDialog = index },
onDeleteClick = { settingsListInput.removeAt(index) }
)
}
item {
PreferenceFooter(
enabled = enabled && isEditing,
negativeText = R.string.cancel,
onNegativeClicked = {
focusManager.clearFocus()
settingsListInput.clear()
settingsListInput.addAll(settingsList)
onNegativeClicked()
},
positiveText = R.string.send,
onPositiveClicked = {
focusManager.clearFocus()
onPositiveClicked(settingsListInput)
ChannelsConfigHeader(
frequency = if (loraConfig.overrideFrequency != 0f) {
loraConfig.overrideFrequency
} else {
primaryChannel.radioFreq
},
slot = if (loraConfig.channelNum != 0) {
loraConfig.channelNum
} else {
primaryChannel.channelNum
}
)
Text(
text = stringResource(R.string.press_and_drag),
fontSize = 11.sp,
modifier = Modifier.padding(start = 16.dp)
)
LazyColumn(
modifier = Modifier.dragContainer(
dragDropState = dragDropState,
haptics = LocalHapticFeedback.current,
),
state = listState,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
item {
Text(
text = stringResource(R.string.primary),
color = MaterialTheme.colorScheme.primary
)
}
dragDropItemsIndexed(
items = settingsListInput,
dragDropState = dragDropState,
) { index, channel, isDragging ->
ChannelCard(
index = index,
title = channel.name.ifEmpty { primaryChannel.name },
enabled = enabled,
onEditClick = { showEditChannelDialog = index },
onDeleteClick = { settingsListInput.removeAt(index) }
)
if (index == 0 && !isDragging) {
Text(
text = stringResource(R.string.primary_channel_feature),
color = MaterialTheme.colorScheme.primary,
fontSize = 10.sp,
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.secondary),
color = MaterialTheme.colorScheme.onBackground
)
}
)
}
item {
Column {
Text(
text = stringResource(R.string.secondary_no_telemetry),
color = MaterialTheme.colorScheme.onBackground,
fontSize = 10.sp,
)
Text(
text = stringResource(R.string.manuel_position_request),
color = MaterialTheme.colorScheme.onBackground,
fontSize = 10.sp,
)
}
}
item {
PreferenceFooter(
enabled = enabled && isEditing,
negativeText = R.string.cancel,
onNegativeClicked = {
focusManager.clearFocus()
settingsListInput.clear()
settingsListInput.addAll(settingsList)
onNegativeClicked()
},
positiveText = R.string.send,
onPositiveClicked = {
focusManager.clearFocus()
onPositiveClicked(settingsListInput)
}
)
}
}
}
@@ -296,6 +357,29 @@ fun ChannelSettingsItemList(
}
}
@Composable
private fun ChannelsConfigHeader(
frequency: Float,
slot: Int
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
PreferenceCategory(text = stringResource(R.string.channels))
Column {
Text(
text = "${stringResource(R.string.freq)}: ${frequency}MHz",
fontSize = 11.sp,
)
Text(
text = "${stringResource(R.string.slot)}: $slot",
fontSize = 11.sp,
)
}
}
}
@Preview(showBackground = true)
@Composable
private fun ChannelSettingsPreview() {
@@ -309,6 +393,7 @@ private fun ChannelSettingsPreview() {
name = stringResource(R.string.channel_name)
},
),
loraConfig = Channel.default.loraConfig,
enabled = true,
onPositiveClicked = { },
)

View File

@@ -607,6 +607,14 @@
<string name="heading">Heading</string>
<string name="sats">Sats</string>
<string name="alt">Alt</string>
<string name="freq">Freq</string>
<string name="slot">Slot</string>
<string name="primary">Primary</string>
<string name="primary_channel_feature">Periodic position and telemetry broadcast</string>
<string name="secondary">Secondary</string>
<string name="secondary_no_telemetry">No periodic telemetry broadcast</string>
<string name="manuel_position_request">Manual position request required</string>
<string name="press_and_drag">Press and drag to reorder</string>
<string name="set_region">Set Region</string>
<string name="unmute">Unmute</string>
<string name="dynamic">Dynamic</string>