From 5a522397216d6776cc7bfdb114fcd5c9d322d3c1 Mon Sep 17 00:00:00 2001 From: Robert-0410 <62630290+Robert-0410@users.noreply.github.com> Date: Tue, 20 May 2025 19:22:30 -0700 Subject: [PATCH] feat: Channel Configuration ui (#1873) --- .../components/ChannelSettingsItemList.kt | 167 +++++++++++++----- app/src/main/res/values/strings.xml | 8 + 2 files changed, 134 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt index d2f72e221..d54bd4ebd 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt @@ -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, - modemPresetName: String = stringResource(R.string.default_), + loraConfig: LoRaConfig, maxChannels: Int = 8, enabled: Boolean, onNegativeClicked: () -> Unit = { }, onPositiveClicked: (List) -> 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 = { }, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 193b68389..e47ecce8e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -607,6 +607,14 @@ Heading Sats Alt + Freq + Slot + Primary + Periodic position and telemetry broadcast + Secondary + No periodic telemetry broadcast + Manual position request required + Press and drag to reorder Set Region Unmute Dynamic