mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Replaced the types selection dialog with modes selection
This commit is contained in:
1 parent
2d3adfc6a6
commit
f9806d7f7f
24 files changed
+393
-192
No files matched your search
@@ -62,16 +62,9 @@ import com.rtbishop.look4sat.core.presentation.SharedDialog
|
||||
import com.rtbishop.look4sat.core.presentation.ElevationHighColor
|
||||
import com.rtbishop.look4sat.core.presentation.ElevationLowColor
|
||||
import com.rtbishop.look4sat.core.presentation.elevationColor
|
||||
import com.rtbishop.look4sat.core.domain.source.Sources
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private val allModes = listOf(
|
||||
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||
"CERTO", "CW", "DQPSK", "DSTAR", "DUV", "FFSK", "FM", "FMN", "FSK", "FSK AX.100 Mode 5",
|
||||
"FSK AX.100 Mode 6", "FSK AX.25 G3RUH", "GFSK", "GFSK Rktr", "GMSK", "HRPT", "LoRa",
|
||||
"LRPT", "LSB", "MFSK", "MSK", "MSK AX.100 Mode 5", "MSK AX.100 Mode 6", "OFDM", "OQPSK",
|
||||
"PSK", "PSK31", "PSK63", "QPSK", "QPSK31", "QPSK63", "SSTV", "USB", "WSJT"
|
||||
)
|
||||
|
||||
private val hourSteps = listOf(1, 2, 4, 8, 12, 24, 48, 72, 96, 120, 144, 168, 192, 216, 240)
|
||||
private const val dayMinutes = 24 * 60
|
||||
private const val minuteStep = 15
|
||||
@@ -381,7 +374,7 @@ internal fun RadiosDialog(modes: List<String>, cancel: () -> Unit, accept: (List
|
||||
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
itemsIndexed(allModes) { index, item ->
|
||||
itemsIndexed(Sources.satelliteModes) { index, item ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
|
||||
+27
-22
@@ -60,7 +60,7 @@ class PassesViewModel(
|
||||
aosEndMinute = settingsRepo.passesSettings.value.aosEndMinute,
|
||||
invertAosTimeWindow = settingsRepo.passesSettings.value.invertAosTimeWindow,
|
||||
showDeepSpace = settingsRepo.passesSettings.value.showDeepSpace,
|
||||
modes = settingsRepo.passesSettings.value.selectedModes,
|
||||
modes = settingsRepo.selectedSatModes.value,
|
||||
shouldSeeWhatsNew = settingsRepo.otherSettings.value.shouldSeeWhatsNew
|
||||
)
|
||||
)
|
||||
@@ -86,6 +86,11 @@ class PassesViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
settingsRepo.selectedSatModes.collectLatest { modes ->
|
||||
_uiState.update { it.copy(modes = modes) }
|
||||
}
|
||||
}
|
||||
// Main tick loop: reacts to new passes, then ticks every second
|
||||
viewModelScope.launch {
|
||||
satelliteRepo.passes.collectLatest { allPasses ->
|
||||
@@ -127,21 +132,9 @@ class PassesViewModel(
|
||||
aosStartMinute = action.aosStartMinute,
|
||||
aosEndMinute = action.aosEndMinute,
|
||||
invertAosTimeWindow = action.invertAosTimeWindow,
|
||||
showDeepSpace = action.showDeepSpace,
|
||||
modes = _uiState.value.modes
|
||||
)
|
||||
is PassesAction.FilterRadios ->
|
||||
applyFilter(
|
||||
hoursAhead = _uiState.value.hours,
|
||||
minElevation = _uiState.value.elevation,
|
||||
lowElevation = _uiState.value.lowElevation,
|
||||
highElevation = _uiState.value.highElevation,
|
||||
aosStartMinute = _uiState.value.aosStartMinute,
|
||||
aosEndMinute = _uiState.value.aosEndMinute,
|
||||
invertAosTimeWindow = _uiState.value.invertAosTimeWindow,
|
||||
showDeepSpace = _uiState.value.showDeepSpace,
|
||||
modes = action.modes
|
||||
showDeepSpace = action.showDeepSpace
|
||||
)
|
||||
is PassesAction.FilterRadios -> setModesFilter(action.modes)
|
||||
PassesAction.RefreshPasses -> refreshPasses()
|
||||
PassesAction.TogglePassesDialog ->
|
||||
_uiState.update { it.copy(isPassesDialogShown = !it.isPassesDialogShown) }
|
||||
@@ -235,8 +228,7 @@ class PassesViewModel(
|
||||
aosStartMinute: Int,
|
||||
aosEndMinute: Int,
|
||||
invertAosTimeWindow: Boolean,
|
||||
showDeepSpace: Boolean,
|
||||
modes: List<String>
|
||||
showDeepSpace: Boolean
|
||||
) = viewModelScope.launch {
|
||||
settingsRepo.setPassesSettings(
|
||||
PassesSettings(
|
||||
@@ -245,8 +237,7 @@ class PassesViewModel(
|
||||
minElevation,
|
||||
aosStartMinute,
|
||||
aosEndMinute,
|
||||
invertAosTimeWindow,
|
||||
modes
|
||||
invertAosTimeWindow
|
||||
)
|
||||
)
|
||||
settingsRepo.updateOtherSettings { it.copy(lowElevation = lowElevation, highElevation = highElevation) }
|
||||
@@ -259,10 +250,10 @@ class PassesViewModel(
|
||||
aosStartMinute = aosStartMinute,
|
||||
aosEndMinute = aosEndMinute,
|
||||
invertAosTimeWindow = invertAosTimeWindow,
|
||||
showDeepSpace = showDeepSpace,
|
||||
modes = modes
|
||||
showDeepSpace = showDeepSpace
|
||||
)
|
||||
}
|
||||
val modes = settingsRepo.selectedSatModes.value
|
||||
satelliteRepo.calculatePasses(
|
||||
time = System.currentTimeMillis(),
|
||||
hoursAhead = hoursAhead,
|
||||
@@ -274,6 +265,20 @@ class PassesViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
private fun setModesFilter(modes: List<String>) = viewModelScope.launch {
|
||||
settingsRepo.setSelectedSatModes(modes)
|
||||
_uiState.update { it.copy(modes = modes) }
|
||||
satelliteRepo.calculatePasses(
|
||||
time = System.currentTimeMillis(),
|
||||
hoursAhead = _uiState.value.hours,
|
||||
minElevation = _uiState.value.elevation,
|
||||
aosStartMinute = _uiState.value.aosStartMinute,
|
||||
aosEndMinute = _uiState.value.aosEndMinute,
|
||||
invertAosTimeWindow = _uiState.value.invertAosTimeWindow,
|
||||
modes = modes
|
||||
)
|
||||
}
|
||||
|
||||
private fun refreshPasses() = viewModelScope.launch {
|
||||
val settings = settingsRepo.passesSettings.value
|
||||
satelliteRepo.calculatePasses(
|
||||
@@ -283,7 +288,7 @@ class PassesViewModel(
|
||||
aosStartMinute = settings.aosStartMinute,
|
||||
aosEndMinute = settings.aosEndMinute,
|
||||
invertAosTimeWindow = settings.invertAosTimeWindow,
|
||||
modes = settings.selectedModes
|
||||
modes = settingsRepo.selectedSatModes.value
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -45,18 +45,18 @@ import com.rtbishop.look4sat.core.presentation.SharedDialog
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun MultiTypesDialogPreview() {
|
||||
val types = listOf("Amateur", "Geostationary", "Military", "Weather")
|
||||
MainTheme { MultiTypesDialog(types, emptyList(), {}) {} }
|
||||
private fun MultiModesDialogPreview() {
|
||||
val modes = listOf("FM", "APT", "SSTV", "BPSK")
|
||||
MainTheme { MultiModesDialog(modes, emptyList(), {}) {} }
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MultiTypesDialog(
|
||||
allTypes: List<String>, types: List<String>, cancel: () -> Unit, accept: (List<String>) -> Unit
|
||||
internal fun MultiModesDialog(
|
||||
allModes: List<String>, modes: List<String>, cancel: () -> Unit, accept: (List<String>) -> Unit
|
||||
) {
|
||||
val selected = remember { mutableStateOf(types.toSet()) }
|
||||
val toggle = { type: String ->
|
||||
selected.value = if (type in selected.value) selected.value - type else selected.value + type
|
||||
val selected = remember { mutableStateOf(modes.toSet()) }
|
||||
val toggle = { mode: String ->
|
||||
selected.value = if (mode in selected.value) selected.value - mode else selected.value + mode
|
||||
}
|
||||
val onAccept = { accept(selected.value.toList()) }
|
||||
SharedDialog(title = stringResource(R.string.sat_type_title), onCancel = cancel, onAccept = onAccept) {
|
||||
@@ -68,7 +68,7 @@ internal fun MultiTypesDialog(
|
||||
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
itemsIndexed(allTypes) { index, item ->
|
||||
itemsIndexed(allModes) { index, item ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
|
||||
+62
-17
@@ -87,11 +87,11 @@ private fun SatellitesScreen(
|
||||
navigateUp: () -> Unit
|
||||
) {
|
||||
if (uiState.isDialogShown) {
|
||||
MultiTypesDialog(
|
||||
allTypes = uiState.typesList,
|
||||
types = uiState.currentTypes,
|
||||
cancel = { onAction(SatellitesAction.ToggleTypesDialog) },
|
||||
accept = { onAction(SatellitesAction.SelectTypes(it)) }
|
||||
MultiModesDialog(
|
||||
allModes = uiState.modesList,
|
||||
modes = uiState.currentModes,
|
||||
cancel = { onAction(SatellitesAction.ToggleModesDialog) },
|
||||
accept = { onAction(SatellitesAction.SelectModes(it)) }
|
||||
)
|
||||
}
|
||||
if (uiState.shouldSeeWarning) {
|
||||
@@ -123,9 +123,9 @@ private fun SatellitesScreen(
|
||||
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (isVerticalLayout()) {
|
||||
TopBar {
|
||||
TypeCard(
|
||||
types = uiState.currentTypes,
|
||||
onClick = { onAction(SatellitesAction.ToggleTypesDialog) },
|
||||
ModeCard(
|
||||
modes = uiState.currentModes,
|
||||
onClick = { onAction(SatellitesAction.ToggleModesDialog) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
PrimaryIconCard(
|
||||
@@ -157,9 +157,9 @@ private fun SatellitesScreen(
|
||||
resId = R.drawable.ic_done,
|
||||
modifier = Modifier.semantics { contentDescription = primCardCd }
|
||||
)
|
||||
TypeCard(
|
||||
types = uiState.currentTypes,
|
||||
onClick = { onAction(SatellitesAction.ToggleTypesDialog) },
|
||||
ModeCard(
|
||||
modes = uiState.currentModes,
|
||||
onClick = { onAction(SatellitesAction.ToggleModesDialog) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
SearchBar(
|
||||
@@ -241,8 +241,8 @@ private fun SearchBar(onQueryChange: (String) -> Unit, modifier: Modifier = Modi
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
|
||||
private fun ModeCard(modes: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val modesText = if (modes.isEmpty()) "All" else modes.joinToString(", ")
|
||||
ElevatedCard(modifier = modifier) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
@@ -252,12 +252,12 @@ private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifie
|
||||
.clickable { onClick() }) {
|
||||
Spacer(Modifier)
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_tags),
|
||||
painter = painterResource(id = R.drawable.ic_radios),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.sat_type_hint, typesText),
|
||||
text = stringResource(R.string.sat_type_hint, modesText),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier
|
||||
@@ -323,9 +323,54 @@ private fun SatellitesPreview() {
|
||||
|
||||
@Composable
|
||||
private fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
||||
val selectedItems = items.filter { it.isSelected }
|
||||
val availableItems = items.filterNot { it.isSelected }
|
||||
|
||||
LazyVerticalGrid(columns = GridCells.Adaptive(320.dp)) {
|
||||
items(items = items, key = { item -> item.catnum }) { entry ->
|
||||
Satellite(entry, onSelected, Modifier.animateItem())
|
||||
stickyHeader(key = "sat_summary_header") {
|
||||
SummaryHeader(
|
||||
selectedText = stringResource(R.string.sat_group_selected_count, selectedItems.size),
|
||||
availableText = stringResource(R.string.sat_group_available_count, availableItems.size)
|
||||
)
|
||||
}
|
||||
|
||||
if (selectedItems.isNotEmpty()) {
|
||||
items(items = selectedItems, key = { item -> item.catnum }) { entry ->
|
||||
Satellite(entry, onSelected, Modifier.animateItem())
|
||||
}
|
||||
}
|
||||
|
||||
if (availableItems.isNotEmpty()) {
|
||||
items(items = availableItems, key = { item -> item.catnum }) { entry ->
|
||||
Satellite(entry, onSelected, Modifier.animateItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SummaryHeader(selectedText: String, availableText: String) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||
.padding(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = selectedText,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = availableText,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -24,8 +24,8 @@ data class SatellitesState(
|
||||
val isLoading: Boolean = true,
|
||||
val shouldSeeWarning: Boolean = false,
|
||||
val itemsList: List<SatItem> = emptyList(),
|
||||
val currentTypes: List<String> = emptyList(),
|
||||
val typesList: List<String> = emptyList()
|
||||
val currentModes: List<String> = emptyList(),
|
||||
val modesList: List<String> = emptyList()
|
||||
)
|
||||
|
||||
sealed interface SatellitesAction {
|
||||
@@ -34,7 +34,7 @@ sealed interface SatellitesAction {
|
||||
data class SearchFor(val query: String) : SatellitesAction
|
||||
data object SelectAll : SatellitesAction
|
||||
data class SelectSingle(val id: Int, val isTicked: Boolean) : SatellitesAction
|
||||
data class SelectTypes(val types: List<String>) : SatellitesAction
|
||||
data object ToggleTypesDialog : SatellitesAction
|
||||
data class SelectModes(val modes: List<String>) : SatellitesAction
|
||||
data object ToggleModesDialog : SatellitesAction
|
||||
data object UnselectAll : SatellitesAction
|
||||
}
|
||||
+10
-10
@@ -35,13 +35,13 @@ class SatellitesViewModel(
|
||||
private val settingsRepo: ISettingsRepo
|
||||
) : ViewModel() {
|
||||
|
||||
private val defaultTypes = selectionRepo.getCurrentTypes()
|
||||
private val defaultModes = selectionRepo.getCurrentModes()
|
||||
private val _uiState = MutableStateFlow(
|
||||
SatellitesState(
|
||||
isLoading = true,
|
||||
shouldSeeWarning = settingsRepo.otherSettings.value.shouldSeeWarning,
|
||||
currentTypes = defaultTypes,
|
||||
typesList = selectionRepo.getTypesList()
|
||||
currentModes = defaultModes,
|
||||
modesList = selectionRepo.getModesList()
|
||||
)
|
||||
)
|
||||
val uiState: StateFlow<SatellitesState> = _uiState
|
||||
@@ -49,7 +49,7 @@ class SatellitesViewModel(
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
selectionRepo.setQuery(String())
|
||||
selectionRepo.setTypes(defaultTypes)
|
||||
selectionRepo.setModes(defaultModes)
|
||||
selectionRepo.getEntriesFlow().collectLatest { items ->
|
||||
_uiState.update { it.copy(isLoading = false, itemsList = items) }
|
||||
}
|
||||
@@ -68,8 +68,8 @@ class SatellitesViewModel(
|
||||
is SatellitesAction.SearchFor -> searchFor(action.query)
|
||||
SatellitesAction.SelectAll -> selectAll(true)
|
||||
is SatellitesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
|
||||
is SatellitesAction.SelectTypes -> selectTypes(action.types)
|
||||
SatellitesAction.ToggleTypesDialog -> toggleTypesDialog()
|
||||
is SatellitesAction.SelectModes -> selectModes(action.modes)
|
||||
SatellitesAction.ToggleModesDialog -> toggleModesDialog()
|
||||
SatellitesAction.UnselectAll -> selectAll(false)
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,12 @@ class SatellitesViewModel(
|
||||
selectionRepo.setSelection(listOf(id), isTicked.not())
|
||||
}
|
||||
|
||||
private fun selectTypes(types: List<String>) = viewModelScope.launch {
|
||||
selectionRepo.setTypes(types)
|
||||
_uiState.update { it.copy(currentTypes = types, isDialogShown = false) }
|
||||
private fun selectModes(modes: List<String>) = viewModelScope.launch {
|
||||
selectionRepo.setModes(modes)
|
||||
_uiState.update { it.copy(currentModes = modes, isDialogShown = false) }
|
||||
}
|
||||
|
||||
private fun toggleTypesDialog() {
|
||||
private fun toggleModesDialog() {
|
||||
_uiState.update { it.copy(isDialogShown = !it.isDialogShown) }
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user