Switched to MVI architecture for PassesScreen

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:45 +00:00
1 parent b9ade5f243
commit a088a7a0a2
20 files changed
+203 -236

No files matched your search

@@ -25,6 +25,7 @@ import com.rtbishop.look4sat.presentation.entries.EntriesViewModel
import com.rtbishop.look4sat.presentation.info.InfoScreen
import com.rtbishop.look4sat.presentation.map.MapScreen
import com.rtbishop.look4sat.presentation.passes.PassesScreen
import com.rtbishop.look4sat.presentation.passes.PassesViewModel
import com.rtbishop.look4sat.presentation.radar.RadarScreen
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
@@ -73,10 +74,11 @@ private fun MainNavGraph(navController: NavHostController) {
EntriesScreen(viewModel.uiState.value, navToPasses)
}
composable(Screen.Passes.route) {
val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory)
val navToRadar = { catNum: Int, aosTime: Long ->
navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}")
}
PassesScreen(navToRadar)
PassesScreen(viewModel.uiState.value, navToRadar)
}
composable(radarRoute, radarArgs) { RadarScreen() }
composable(Screen.Map.route) { MapScreen() }
@@ -38,7 +38,7 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
dismiss()
}
Dialog(onDismissRequest = { dismiss() }) {
ElevatedCard(modifier = Modifier.fillMaxHeight(0.75f)) {
ElevatedCard(modifier = Modifier.fillMaxHeight(0.80f)) {
LazyVerticalGrid(
columns = GridCells.Adaptive(160.dp),
modifier = Modifier.background(MaterialTheme.colorScheme.background),
@@ -47,7 +47,8 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
) {
itemsIndexed(items) { index, item ->
Surface {
Row(verticalAlignment = Alignment.CenterVertically,
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.clickable { clickAction(item) }) {
@@ -64,7 +65,7 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
RadioButton(selected = item == selected, onClick = { clickAction(item) })
RadioButton(selected = item == selected, onClick = null, modifier = Modifier.padding(8.dp))
}
}
}
@@ -46,18 +46,17 @@ import com.rtbishop.look4sat.presentation.components.CardLoadingIndicator
@Composable
fun EntriesScreen(uiState: EntriesState, navToPasses: () -> Unit) {
val showDialog = rememberSaveable { mutableStateOf(false) }
val toggleDialog = { showDialog.value = showDialog.value.not() }
if (showDialog.value) {
val toggleDialog = { uiState.takeAction(EntriesAction.ToggleTypesDialog) }
if (uiState.isDialogShown) {
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
uiState.takeAction(EntriesAction.SelectType(it))
}
}
val unselectAll = { uiState.takeAction(EntriesAction.UnselectAll) }
val selectAll = { uiState.takeAction(EntriesAction.SelectAll) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TopBar(setQuery = { newQuery: String -> uiState.takeAction(EntriesAction.SearchFor(newQuery)) },
TopBar(
setQuery = { newQuery: String -> uiState.takeAction(EntriesAction.SearchFor(newQuery)) },
saveSelection = {
uiState.takeAction(EntriesAction.SaveSelection)
navToPasses()
@@ -3,6 +3,7 @@ package com.rtbishop.look4sat.presentation.entries
import com.rtbishop.look4sat.domain.model.SatItem
data class EntriesState(
val isDialogShown: Boolean,
val isLoading: Boolean,
val itemsList: List<SatItem>,
val currentType: String,
@@ -16,5 +17,6 @@ sealed class EntriesAction {
data object SelectAll : EntriesAction()
data class SelectSingle(val id: Int, val isTicked: Boolean) : EntriesAction()
data class SelectType(val type: String) : EntriesAction()
data object ToggleTypesDialog : EntriesAction()
data object UnselectAll : EntriesAction()
}
@@ -17,6 +17,7 @@
*/
package com.rtbishop.look4sat.presentation.entries
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -30,20 +31,23 @@ import kotlinx.coroutines.launch
class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
private val defaultUiState = EntriesState(
isLoading = true,
itemsList = emptyList(),
currentType = selectionRepo.getCurrentType(),
typesList = selectionRepo.getTypesList(),
takeAction = ::handleAction
private val _uiState = mutableStateOf(
EntriesState(
isDialogShown = false,
isLoading = true,
itemsList = emptyList(),
currentType = selectionRepo.getCurrentType(),
typesList = selectionRepo.getTypesList(),
takeAction = ::handleAction
)
)
val uiState = mutableStateOf(defaultUiState)
val uiState: State<EntriesState> = _uiState
init {
viewModelScope.launch {
delay(1000)
selectionRepo.getEntriesFlow().collect { items ->
uiState.value = uiState.value.copy(isLoading = false, itemsList = items)
_uiState.value = _uiState.value.copy(isLoading = false, itemsList = items)
}
}
}
@@ -55,6 +59,7 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
EntriesAction.SelectAll -> selectAll(true)
is EntriesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
is EntriesAction.SelectType -> selectType(action.type)
EntriesAction.ToggleTypesDialog -> toggleTypesDialog()
EntriesAction.UnselectAll -> selectAll(false)
}
}
@@ -73,7 +78,12 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
private fun selectType(type: String) = viewModelScope.launch {
selectionRepo.setType(type)
uiState.value = uiState.value.copy(currentType = type)
_uiState.value = _uiState.value.copy(currentType = type)
}
private fun toggleTypesDialog() {
val currentDialogState = _uiState.value.isDialogShown
_uiState.value = _uiState.value.copy(isDialogShown = currentDialogState.not())
}
companion object {
@@ -39,7 +39,8 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -54,27 +55,43 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.components.CardButton
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"
)
@Preview(showBackground = true)
@Composable
private fun FilterDialogPreview() {
MainTheme { FilterDialog(24, 16.0, {}) { _, _ -> } }
MainTheme { FilterDialog(24, 16.0, emptyList(), {}) { _, _, _ -> } }
}
@Composable
fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int, Double) -> Unit) {
val hoursValue = rememberSaveable { mutableIntStateOf(hours) }
val elevationValue = rememberSaveable { mutableIntStateOf(elevation.toInt()) }
fun FilterDialog(
hours: Int,
elev: Double,
modes: List<String>,
dismiss: () -> Unit,
save: (Int, Double, List<String>) -> Unit
) {
val maxWidthModifier = Modifier.fillMaxWidth(1f)
val hoursValue = remember { mutableIntStateOf(hours) }
val elevationValue = remember { mutableIntStateOf(elev.toInt()) }
val selected = remember { mutableStateListOf<String>().apply { addAll(modes) } }
val select = { mode: String -> if (selected.contains(mode)) selected.remove(mode) else selected.add(mode) }
Dialog(onDismissRequest = { dismiss() }) {
ElevatedCard {
ElevatedCard(modifier = Modifier.fillMaxHeight(0.80f)) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = maxWidthModifier.padding(12.dp)
) {
Text(text = "Filter passes", fontSize = 20.sp, color = MaterialTheme.colorScheme.primary)
Text(text = "Filter passes", fontSize = 18.sp, color = MaterialTheme.colorScheme.primary)
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
Text(text = "Show passes within", fontSize = 18.sp, modifier = Modifier.weight(1f))
Text(text = "Show passes within", fontSize = 16.sp, modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.drawable.ic_time),
contentDescription = null,
@@ -85,7 +102,7 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
)
Text(
text = "${hoursValue.intValue}h",
fontSize = 20.sp,
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary
)
@@ -97,7 +114,7 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
)
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
Text(text = "Show passes above", fontSize = 18.sp, modifier = Modifier.weight(1f))
Text(text = "Show passes above", fontSize = 16.sp, modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.drawable.ic_elevation),
contentDescription = null,
@@ -108,7 +125,7 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
)
Text(
text = "${elevationValue.intValue}°",
fontSize = 20.sp,
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary
)
@@ -119,11 +136,47 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
valueRange = 0f..90f,
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
)
LazyVerticalGrid(
columns = GridCells.Adaptive(160.dp),
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.weight(1f),
horizontalArrangement = Arrangement.spacedBy(1.dp),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
itemsIndexed(allModes) { index, item ->
Surface {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.clickable { select(item) }) {
Text(
text = "$index).",
modifier = Modifier.padding(start = 8.dp, end = 6.dp),
fontWeight = FontWeight.Normal,
color = MaterialTheme.colorScheme.secondary
)
Text(
text = item,
modifier = Modifier.weight(1f),
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Checkbox(
checked = selected.contains(item),
onCheckedChange = null,
modifier = Modifier.padding(8.dp)
)
}
}
}
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
CardButton(
onClick = {
save(hoursValue.intValue, elevationValue.intValue.toDouble())
save(hoursValue.intValue, elevationValue.intValue.toDouble(), selected)
dismiss()
}, text = stringResource(id = R.string.btn_accept)
)
@@ -132,54 +185,3 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
}
}
}
@Preview(showBackground = true)
@Composable
private fun ModesDialogPreview() {
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"
)
MainTheme { ModesDialog(allModes, listOf("AFSK", "AFSK S-Net"), {}) {} }
}
@Composable
fun ModesDialog(items: List<String>, selected: List<String>, dismiss: () -> Unit, select: (String) -> Unit) {
Dialog(onDismissRequest = { dismiss() }) {
ElevatedCard(modifier = Modifier.fillMaxHeight(0.75f)) {
LazyVerticalGrid(
columns = GridCells.Adaptive(160.dp),
modifier = Modifier.background(MaterialTheme.colorScheme.background),
horizontalArrangement = Arrangement.spacedBy(1.dp),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
itemsIndexed(items) { index, item ->
Surface {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.clickable { select(item) }) {
Text(
text = "$index).",
modifier = Modifier.padding(start = 12.dp, end = 6.dp),
fontWeight = FontWeight.Normal,
color = MaterialTheme.colorScheme.secondary
)
Text(
text = item,
modifier = Modifier.weight(1f),
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Checkbox(checked = selected.contains(item), onCheckedChange = { select(item) })
}
}
}
}
}
}
}
@@ -23,9 +23,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -36,9 +33,7 @@ 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.lifecycle.viewmodel.compose.viewModel
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.NearEarthSatellite
import com.rtbishop.look4sat.domain.predict.OrbitalData
import com.rtbishop.look4sat.domain.predict.SatPass
@@ -53,27 +48,16 @@ import java.util.*
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
@Composable
fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory)
val state = viewModel.passes.collectAsState(initial = null)
val timerText = viewModel.timerText.collectAsState(initial = null)
val isRefreshing = state.value is DataState.Loading
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
onRefresh = { viewModel.calculatePasses() })
val value = state.value
var passes = emptyList<SatPass>()
if (value is DataState.Success) passes = value.data
val showDialog = rememberSaveable { mutableStateOf(false) }
val toggleDialog = { showDialog.value = showDialog.value.not() }
if (showDialog.value) {
val (hoursAhead, minElevation) = viewModel.getFilterSettings()
FilterDialog(hoursAhead, minElevation, { toggleDialog() }) { newHours, newElevation ->
viewModel.calculatePasses(System.currentTimeMillis(), newHours, newElevation)
fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
val refreshState = rememberPullRefreshState(refreshing = uiState.isRefreshing, onRefresh = {
uiState.takeAction(PassesAction.RefreshPasses)
})
val toggleDialog = { uiState.takeAction(PassesAction.ToggleFilterDialog) }
if (uiState.isDialogShown) {
FilterDialog(uiState.hours, uiState.elevation, uiState.modes, toggleDialog) { hours, elevation, modes ->
uiState.takeAction(PassesAction.ApplyFilter(hours, elevation, modes))
}
}
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
ElevatedCard(modifier = Modifier.height(52.dp)) {
Row(
@@ -88,11 +72,11 @@ fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
)
}
Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)) {
Text(text = timerText.value?.first ?: "Null")
Text(text = timerText.value?.second ?: "Null")
Text(text = "Next - Id:${uiState.nextId}", maxLines = 1, overflow = TextOverflow.Ellipsis)
Text(text = uiState.nextName, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
Text(
text = timerText.value?.third ?: "00:00:00",
text = uiState.nextTime,
fontSize = 36.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary,
@@ -103,7 +87,7 @@ fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
)
}
}
PassesCard(refreshState, isRefreshing, passes, navToRadar)
PassesCard(refreshState, uiState.isRefreshing, uiState.itemsList, navToRadar)
}
}
@@ -0,0 +1,23 @@
package com.rtbishop.look4sat.presentation.passes
import com.rtbishop.look4sat.domain.predict.SatPass
data class PassesState(
val isDialogShown: Boolean,
val isRefreshing: Boolean,
val isUtc: Boolean,
val nextId: Int,
val nextName: String,
val nextTime: String,
val hours: Int,
val elevation: Double,
val modes: List<String>,
val itemsList: List<SatPass>,
val takeAction: (PassesAction) -> Unit
)
sealed class PassesAction {
data class ApplyFilter(val hoursAhead: Int, val minElevation: Double, val modes: List<String>) : PassesAction()
data object RefreshPasses : PassesAction()
data object ToggleFilterDialog : PassesAction()
}
@@ -17,13 +17,14 @@
*/
package com.rtbishop.look4sat.presentation.passes
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.PassesSettings
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
@@ -32,8 +33,6 @@ import com.rtbishop.look4sat.domain.utility.toTimerString
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
@@ -41,46 +40,35 @@ class PassesViewModel(
private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() {
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
private val timerData = Triple("Next - Id:Null", "Name: Null", "00:00:00")
private val _timerText = MutableStateFlow(timerData)
private var passesProcessing: Job? = null
val passes: StateFlow<DataState<List<SatPass>>> = _passes
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
fun getFilterSettings() = settingsRepo.passesSettings.value
// fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
private val _uiState = mutableStateOf(
PassesState(
isDialogShown = false,
isRefreshing = true,
isUtc = settingsRepo.otherSettings.value.stateOfUtc,
nextId = 0,
nextName = "...",
nextTime = "00:00:00",
hours = settingsRepo.passesSettings.value.hoursAhead,
elevation = settingsRepo.passesSettings.value.minElevation,
modes = settingsRepo.passesSettings.value.selectedModes,
itemsList = emptyList(),
takeAction = ::handleAction
)
)
private var processing: Job? = null
val uiState: State<PassesState> = _uiState
init {
viewModelScope.launch { satelliteRepo.initRepository() }
viewModelScope.launch {
satelliteRepo.passes.collect { passes ->
passesProcessing?.cancelAndJoin()
passesProcessing = viewModelScope.launch {
processing?.cancelAndJoin()
processing = viewModelScope.launch {
while (isActive) {
val timeNow = System.currentTimeMillis()
val newPasses = satelliteRepo.processPasses(passes, timeNow)
if (newPasses.isNotEmpty()) {
try {
val nextPass = newPasses.first { it.aosTime.minus(timeNow) > 0 }
val catNum = nextPass.catNum
val name = nextPass.name
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
val timerString = millisBeforeStart.toTimerString()
_timerText.emit(Triple("Next - Id:$catNum", name, timerString))
} catch (exception: NoSuchElementException) {
val lastPass = newPasses.last()
val catNum = lastPass.catNum
val name = lastPass.name
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
val timerString = millisBeforeEnd.toTimerString()
_timerText.emit(Triple("Next - Id:$catNum", name, timerString))
}
} else _timerText.emit(timerData)
_passes.emit(DataState.Success(newPasses))
setPassInfo(newPasses, timeNow)
_uiState.value = _uiState.value.copy(isRefreshing = false, itemsList = newPasses)
delay(1000)
}
}
@@ -88,18 +76,45 @@ class PassesViewModel(
}
}
fun calculatePasses() = viewModelScope.launch {
_passes.emit(DataState.Loading)
passesProcessing?.cancelAndJoin()
private fun handleAction(action: PassesAction) {
when (action) {
is PassesAction.ApplyFilter -> applyFilter(action.hoursAhead, action.minElevation, action.modes)
PassesAction.RefreshPasses -> refreshPasses()
PassesAction.ToggleFilterDialog -> toggleFilterDialog()
}
}
private fun applyFilter(hoursAhead: Int, minElevation: Double, modes: List<String>) = viewModelScope.launch {
_uiState.value = _uiState.value.copy(isRefreshing = true)
processing?.cancelAndJoin()
settingsRepo.setPassesSettings(PassesSettings(hoursAhead, minElevation, modes))
_uiState.value = _uiState.value.copy(hours = hoursAhead, elevation = minElevation, modes = modes)
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
}
private fun refreshPasses() = viewModelScope.launch {
_uiState.value = _uiState.value.copy(isRefreshing = true)
processing?.cancelAndJoin()
val (hoursAhead, minElevation) = settingsRepo.passesSettings.value
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
}
fun calculatePasses(timeRef: Long, hoursAhead: Int, minElev: Double) = viewModelScope.launch {
_passes.emit(DataState.Loading)
passesProcessing?.cancelAndJoin()
settingsRepo.updatePassesSettings(PassesSettings(hoursAhead, minElev))
satelliteRepo.calculatePasses(timeRef, hoursAhead, minElev)
private fun setPassInfo(passes: List<SatPass>, timeNow: Long) {
if (passes.isEmpty()) return
try {
val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
val time = nextPass.aosTime.minus(timeNow).toTimerString()
_uiState.value = _uiState.value.copy(nextId = nextPass.catNum, nextName = nextPass.name, nextTime = time)
} catch (exception: NoSuchElementException) {
val lastPass = passes.last()
val time = lastPass.losTime.minus(timeNow).toTimerString()
_uiState.value = _uiState.value.copy(nextId = lastPass.catNum, nextName = lastPass.name, nextTime = time)
}
}
private fun toggleFilterDialog() {
val currentDialogState = _uiState.value.isDialogShown
_uiState.value = _uiState.value.copy(isDialogShown = currentDialogState.not())
}
companion object {
@@ -148,7 +148,7 @@ class RadarViewModel(
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
viewModelScope.launch {
delay(125)
val list = satelliteRepo.processRadios(satellite, stationPos, radios, time)
val list = satelliteRepo.getRadios(satellite, stationPos, radios, time)
transmitters.value = list
}
}