mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Switched to MVI architecture for PassesScreen
This commit is contained in:
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.info.InfoScreen
|
||||||
import com.rtbishop.look4sat.presentation.map.MapScreen
|
import com.rtbishop.look4sat.presentation.map.MapScreen
|
||||||
import com.rtbishop.look4sat.presentation.passes.PassesScreen
|
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.radar.RadarScreen
|
||||||
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
||||||
|
|
||||||
@@ -73,10 +74,11 @@ private fun MainNavGraph(navController: NavHostController) {
|
|||||||
EntriesScreen(viewModel.uiState.value, navToPasses)
|
EntriesScreen(viewModel.uiState.value, navToPasses)
|
||||||
}
|
}
|
||||||
composable(Screen.Passes.route) {
|
composable(Screen.Passes.route) {
|
||||||
|
val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory)
|
||||||
val navToRadar = { catNum: Int, aosTime: Long ->
|
val navToRadar = { catNum: Int, aosTime: Long ->
|
||||||
navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}")
|
navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}")
|
||||||
}
|
}
|
||||||
PassesScreen(navToRadar)
|
PassesScreen(viewModel.uiState.value, navToRadar)
|
||||||
}
|
}
|
||||||
composable(radarRoute, radarArgs) { RadarScreen() }
|
composable(radarRoute, radarArgs) { RadarScreen() }
|
||||||
composable(Screen.Map.route) { MapScreen() }
|
composable(Screen.Map.route) { MapScreen() }
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
Dialog(onDismissRequest = { dismiss() }) {
|
Dialog(onDismissRequest = { dismiss() }) {
|
||||||
ElevatedCard(modifier = Modifier.fillMaxHeight(0.75f)) {
|
ElevatedCard(modifier = Modifier.fillMaxHeight(0.80f)) {
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
columns = GridCells.Adaptive(160.dp),
|
columns = GridCells.Adaptive(160.dp),
|
||||||
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
||||||
@@ -47,7 +47,8 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
|
|||||||
) {
|
) {
|
||||||
itemsIndexed(items) { index, item ->
|
itemsIndexed(items) { index, item ->
|
||||||
Surface {
|
Surface {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.clickable { clickAction(item) }) {
|
.clickable { clickAction(item) }) {
|
||||||
@@ -64,7 +65,7 @@ fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, sele
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
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
|
@Composable
|
||||||
fun EntriesScreen(uiState: EntriesState, navToPasses: () -> Unit) {
|
fun EntriesScreen(uiState: EntriesState, navToPasses: () -> Unit) {
|
||||||
val showDialog = rememberSaveable { mutableStateOf(false) }
|
val toggleDialog = { uiState.takeAction(EntriesAction.ToggleTypesDialog) }
|
||||||
val toggleDialog = { showDialog.value = showDialog.value.not() }
|
if (uiState.isDialogShown) {
|
||||||
if (showDialog.value) {
|
|
||||||
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
|
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
|
||||||
uiState.takeAction(EntriesAction.SelectType(it))
|
uiState.takeAction(EntriesAction.SelectType(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val unselectAll = { uiState.takeAction(EntriesAction.UnselectAll) }
|
val unselectAll = { uiState.takeAction(EntriesAction.UnselectAll) }
|
||||||
val selectAll = { uiState.takeAction(EntriesAction.SelectAll) }
|
val selectAll = { uiState.takeAction(EntriesAction.SelectAll) }
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
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 = {
|
saveSelection = {
|
||||||
uiState.takeAction(EntriesAction.SaveSelection)
|
uiState.takeAction(EntriesAction.SaveSelection)
|
||||||
navToPasses()
|
navToPasses()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.rtbishop.look4sat.presentation.entries
|
|||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
import com.rtbishop.look4sat.domain.model.SatItem
|
||||||
|
|
||||||
data class EntriesState(
|
data class EntriesState(
|
||||||
|
val isDialogShown: Boolean,
|
||||||
val isLoading: Boolean,
|
val isLoading: Boolean,
|
||||||
val itemsList: List<SatItem>,
|
val itemsList: List<SatItem>,
|
||||||
val currentType: String,
|
val currentType: String,
|
||||||
@@ -16,5 +17,6 @@ sealed class EntriesAction {
|
|||||||
data object SelectAll : EntriesAction()
|
data object SelectAll : EntriesAction()
|
||||||
data class SelectSingle(val id: Int, val isTicked: Boolean) : EntriesAction()
|
data class SelectSingle(val id: Int, val isTicked: Boolean) : EntriesAction()
|
||||||
data class SelectType(val type: String) : EntriesAction()
|
data class SelectType(val type: String) : EntriesAction()
|
||||||
|
data object ToggleTypesDialog : EntriesAction()
|
||||||
data object UnselectAll : EntriesAction()
|
data object UnselectAll : EntriesAction()
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.entries
|
package com.rtbishop.look4sat.presentation.entries
|
||||||
|
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -30,20 +31,23 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
|
class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
|
||||||
|
|
||||||
private val defaultUiState = EntriesState(
|
private val _uiState = mutableStateOf(
|
||||||
isLoading = true,
|
EntriesState(
|
||||||
itemsList = emptyList(),
|
isDialogShown = false,
|
||||||
currentType = selectionRepo.getCurrentType(),
|
isLoading = true,
|
||||||
typesList = selectionRepo.getTypesList(),
|
itemsList = emptyList(),
|
||||||
takeAction = ::handleAction
|
currentType = selectionRepo.getCurrentType(),
|
||||||
|
typesList = selectionRepo.getTypesList(),
|
||||||
|
takeAction = ::handleAction
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val uiState = mutableStateOf(defaultUiState)
|
val uiState: State<EntriesState> = _uiState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
selectionRepo.getEntriesFlow().collect { items ->
|
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)
|
EntriesAction.SelectAll -> selectAll(true)
|
||||||
is EntriesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
|
is EntriesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
|
||||||
is EntriesAction.SelectType -> selectType(action.type)
|
is EntriesAction.SelectType -> selectType(action.type)
|
||||||
|
EntriesAction.ToggleTypesDialog -> toggleTypesDialog()
|
||||||
EntriesAction.UnselectAll -> selectAll(false)
|
EntriesAction.UnselectAll -> selectAll(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +78,12 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
|
|||||||
|
|
||||||
private fun selectType(type: String) = viewModelScope.launch {
|
private fun selectType(type: String) = viewModelScope.launch {
|
||||||
selectionRepo.setType(type)
|
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 {
|
companion object {
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
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.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.components.CardButton
|
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)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun FilterDialogPreview() {
|
private fun FilterDialogPreview() {
|
||||||
MainTheme { FilterDialog(24, 16.0, {}) { _, _ -> } }
|
MainTheme { FilterDialog(24, 16.0, emptyList(), {}) { _, _, _ -> } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int, Double) -> Unit) {
|
fun FilterDialog(
|
||||||
val hoursValue = rememberSaveable { mutableIntStateOf(hours) }
|
hours: Int,
|
||||||
val elevationValue = rememberSaveable { mutableIntStateOf(elevation.toInt()) }
|
elev: Double,
|
||||||
|
modes: List<String>,
|
||||||
|
dismiss: () -> Unit,
|
||||||
|
save: (Int, Double, List<String>) -> Unit
|
||||||
|
) {
|
||||||
val maxWidthModifier = Modifier.fillMaxWidth(1f)
|
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() }) {
|
Dialog(onDismissRequest = { dismiss() }) {
|
||||||
ElevatedCard {
|
ElevatedCard(modifier = Modifier.fillMaxHeight(0.80f)) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
modifier = maxWidthModifier.padding(12.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) {
|
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(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_time),
|
painter = painterResource(id = R.drawable.ic_time),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -85,7 +102,7 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "${hoursValue.intValue}h",
|
text = "${hoursValue.intValue}h",
|
||||||
fontSize = 20.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
color = MaterialTheme.colorScheme.primary
|
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)
|
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.Bottom) {
|
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(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_elevation),
|
painter = painterResource(id = R.drawable.ic_elevation),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -108,7 +125,7 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "${elevationValue.intValue}°",
|
text = "${elevationValue.intValue}°",
|
||||||
fontSize = 20.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
@@ -119,11 +136,47 @@ fun FilterDialog(hours: Int, elevation: Double, dismiss: () -> Unit, save: (Int,
|
|||||||
valueRange = 0f..90f,
|
valueRange = 0f..90f,
|
||||||
colors = SliderDefaults.colors(inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant)
|
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()) {
|
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||||
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
CardButton(onClick = { dismiss() }, text = stringResource(id = R.string.btn_cancel))
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
save(hoursValue.intValue, elevationValue.intValue.toDouble())
|
save(hoursValue.intValue, elevationValue.intValue.toDouble(), selected)
|
||||||
dismiss()
|
dismiss()
|
||||||
}, text = stringResource(id = R.string.btn_accept)
|
}, 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.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
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.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.rtbishop.look4sat.R
|
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.NearEarthSatellite
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
@@ -53,27 +48,16 @@ import java.util.*
|
|||||||
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
|
private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
|
fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
|
||||||
val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory)
|
val refreshState = rememberPullRefreshState(refreshing = uiState.isRefreshing, onRefresh = {
|
||||||
val state = viewModel.passes.collectAsState(initial = null)
|
uiState.takeAction(PassesAction.RefreshPasses)
|
||||||
val timerText = viewModel.timerText.collectAsState(initial = null)
|
})
|
||||||
val isRefreshing = state.value is DataState.Loading
|
val toggleDialog = { uiState.takeAction(PassesAction.ToggleFilterDialog) }
|
||||||
val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
|
if (uiState.isDialogShown) {
|
||||||
onRefresh = { viewModel.calculatePasses() })
|
FilterDialog(uiState.hours, uiState.elevation, uiState.modes, toggleDialog) { hours, elevation, modes ->
|
||||||
|
uiState.takeAction(PassesAction.ApplyFilter(hours, elevation, modes))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
ElevatedCard(modifier = Modifier.height(52.dp)) {
|
ElevatedCard(modifier = Modifier.height(52.dp)) {
|
||||||
Row(
|
Row(
|
||||||
@@ -88,11 +72,11 @@ fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)) {
|
Column(verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.weight(1f)) {
|
||||||
Text(text = timerText.value?.first ?: "Null")
|
Text(text = "Next - Id:${uiState.nextId}", maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
Text(text = timerText.value?.second ?: "Null")
|
Text(text = uiState.nextName, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = timerText.value?.third ?: "00:00:00",
|
text = uiState.nextTime,
|
||||||
fontSize = 36.sp,
|
fontSize = 36.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
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
|
package com.rtbishop.look4sat.presentation.passes
|
||||||
|
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import com.rtbishop.look4sat.MainApplication
|
import com.rtbishop.look4sat.MainApplication
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
|
||||||
import com.rtbishop.look4sat.domain.model.PassesSettings
|
import com.rtbishop.look4sat.domain.model.PassesSettings
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
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.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -41,46 +40,35 @@ class PassesViewModel(
|
|||||||
private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo
|
private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
|
private val _uiState = mutableStateOf(
|
||||||
private val timerData = Triple("Next - Id:Null", "Name: Null", "00:00:00")
|
PassesState(
|
||||||
private val _timerText = MutableStateFlow(timerData)
|
isDialogShown = false,
|
||||||
private var passesProcessing: Job? = null
|
isRefreshing = true,
|
||||||
val passes: StateFlow<DataState<List<SatPass>>> = _passes
|
isUtc = settingsRepo.otherSettings.value.stateOfUtc,
|
||||||
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
|
nextId = 0,
|
||||||
|
nextName = "...",
|
||||||
fun getFilterSettings() = settingsRepo.passesSettings.value
|
nextTime = "00:00:00",
|
||||||
|
hours = settingsRepo.passesSettings.value.hoursAhead,
|
||||||
// fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
|
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 {
|
init {
|
||||||
viewModelScope.launch { satelliteRepo.initRepository() }
|
viewModelScope.launch { satelliteRepo.initRepository() }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
satelliteRepo.passes.collect { passes ->
|
satelliteRepo.passes.collect { passes ->
|
||||||
passesProcessing?.cancelAndJoin()
|
processing?.cancelAndJoin()
|
||||||
passesProcessing = viewModelScope.launch {
|
processing = viewModelScope.launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val newPasses = satelliteRepo.processPasses(passes, timeNow)
|
val newPasses = satelliteRepo.processPasses(passes, timeNow)
|
||||||
|
setPassInfo(newPasses, timeNow)
|
||||||
if (newPasses.isNotEmpty()) {
|
_uiState.value = _uiState.value.copy(isRefreshing = false, itemsList = newPasses)
|
||||||
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))
|
|
||||||
delay(1000)
|
delay(1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,18 +76,45 @@ class PassesViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculatePasses() = viewModelScope.launch {
|
private fun handleAction(action: PassesAction) {
|
||||||
_passes.emit(DataState.Loading)
|
when (action) {
|
||||||
passesProcessing?.cancelAndJoin()
|
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
|
val (hoursAhead, minElevation) = settingsRepo.passesSettings.value
|
||||||
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
|
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculatePasses(timeRef: Long, hoursAhead: Int, minElev: Double) = viewModelScope.launch {
|
private fun setPassInfo(passes: List<SatPass>, timeNow: Long) {
|
||||||
_passes.emit(DataState.Loading)
|
if (passes.isEmpty()) return
|
||||||
passesProcessing?.cancelAndJoin()
|
try {
|
||||||
settingsRepo.updatePassesSettings(PassesSettings(hoursAhead, minElev))
|
val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
|
||||||
satelliteRepo.calculatePasses(timeRef, hoursAhead, minElev)
|
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 {
|
companion object {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ class RadarViewModel(
|
|||||||
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
|
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
delay(125)
|
delay(125)
|
||||||
val list = satelliteRepo.processRadios(satellite, stationPos, radios, time)
|
val list = satelliteRepo.getRadios(satellite, stationPos, radios, time)
|
||||||
transmitters.value = list
|
transmitters.value = list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,9 +59,7 @@ class SatelliteRepo(
|
|||||||
return withContext(dispatcher) { sat.getPosition(pos, time) }
|
return withContext(dispatcher) { sat.getPosition(pos, time) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTrack(
|
override suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos> {
|
||||||
sat: Satellite, pos: GeoPos, start: Long, end: Long
|
|
||||||
): List<SatPos> {
|
|
||||||
return withContext(dispatcher) {
|
return withContext(dispatcher) {
|
||||||
val positions = mutableListOf<SatPos>()
|
val positions = mutableListOf<SatPos>()
|
||||||
var currentTime = start
|
var currentTime = start
|
||||||
@@ -73,9 +71,7 @@ class SatelliteRepo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun processRadios(
|
override suspend fun getRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio> {
|
||||||
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
|
|
||||||
): List<SatRadio> {
|
|
||||||
return withContext(dispatcher) {
|
return withContext(dispatcher) {
|
||||||
val satPos = sat.getPosition(pos, time)
|
val satPos = sat.getPosition(pos, time)
|
||||||
val copiedList = radios.map { it.copy() }
|
val copiedList = radios.map { it.copy() }
|
||||||
|
|||||||
@@ -82,26 +82,19 @@ class SettingsRepo(private val manager: LocationManager, private val preferences
|
|||||||
private val _passesSettings = MutableStateFlow(getPassesSettings())
|
private val _passesSettings = MutableStateFlow(getPassesSettings())
|
||||||
override val passesSettings: StateFlow<PassesSettings> = _passesSettings
|
override val passesSettings: StateFlow<PassesSettings> = _passesSettings
|
||||||
|
|
||||||
override fun getSelectedModes(): List<String> {
|
override fun setPassesSettings(settings: PassesSettings) = preferences.edit {
|
||||||
val selectedModesString = preferences.getString(keySelectedModes, null)
|
putInt(keyFilterHoursAhead, settings.hoursAhead)
|
||||||
return selectedModesString?.split(separatorComma)?.sorted() ?: emptyList()
|
putLong(keyFilterMinElevation, settings.minElevation.toRawBits())
|
||||||
}
|
putString(keySelectedModes, settings.selectedModes.joinToString(separatorComma))
|
||||||
|
|
||||||
override fun setSelectedModes(modes: List<String>) {
|
|
||||||
val selectedModes = modes.joinToString(separatorComma)
|
|
||||||
preferences.edit { putString(keySelectedModes, selectedModes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updatePassesSettings(settings: PassesSettings) = preferences.edit {
|
|
||||||
putInt(keyFilterHoursAhead, settings.filterHoursAhead)
|
|
||||||
putLong(keyFilterMinElevation, settings.filterMinElevation.toRawBits())
|
|
||||||
_passesSettings.value = settings
|
_passesSettings.value = settings
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPassesSettings(): PassesSettings {
|
private fun getPassesSettings(): PassesSettings {
|
||||||
val hoursAhead = preferences.getInt(keyFilterHoursAhead, 24)
|
val hoursAhead = preferences.getInt(keyFilterHoursAhead, 24)
|
||||||
val minElevation = Double.fromBits(preferences.getLong(keyFilterMinElevation, 16.0.toRawBits()))
|
val minElevation = Double.fromBits(preferences.getLong(keyFilterMinElevation, 16.0.toRawBits()))
|
||||||
return PassesSettings(hoursAhead, minElevation)
|
val selectedModesString = preferences.getString(keySelectedModes, null)
|
||||||
|
val selectedModes = selectedModesString?.split(separatorComma)?.sorted() ?: emptyList()
|
||||||
|
return PassesSettings(hoursAhead, minElevation, selectedModes)
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* 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 com.rtbishop.look4sat.domain.model
|
|
||||||
|
|
||||||
sealed class DataState<out T> {
|
|
||||||
data class Success<out T>(val data: T) : DataState<T>()
|
|
||||||
data class Exception(val message: String?) : DataState<Nothing>()
|
|
||||||
data object Loading : DataState<Nothing>()
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package com.rtbishop.look4sat.domain.model
|
|||||||
|
|
||||||
data class DatabaseState(val numberOfRadios: Int, val numberOfSatellites: Int, val updateTimestamp: Long)
|
data class DatabaseState(val numberOfRadios: Int, val numberOfSatellites: Int, val updateTimestamp: Long)
|
||||||
|
|
||||||
data class PassesSettings(val filterHoursAhead: Int, val filterMinElevation: Double)
|
data class PassesSettings(val hoursAhead: Int, val minElevation: Double, val selectedModes: List<String>)
|
||||||
|
|
||||||
data class OtherSettings(
|
data class OtherSettings(
|
||||||
val stateOfAutoUpdate: Boolean,
|
val stateOfAutoUpdate: Boolean,
|
||||||
|
|||||||
@@ -18,10 +18,7 @@
|
|||||||
package com.rtbishop.look4sat.domain.repository
|
package com.rtbishop.look4sat.domain.repository
|
||||||
|
|
||||||
interface IDatabaseRepo {
|
interface IDatabaseRepo {
|
||||||
|
|
||||||
suspend fun updateFromFile(uri: String)
|
suspend fun updateFromFile(uri: String)
|
||||||
|
|
||||||
suspend fun updateFromRemote()
|
suspend fun updateFromRemote()
|
||||||
|
|
||||||
suspend fun clearAllData()
|
suspend fun clearAllData()
|
||||||
}
|
}
|
||||||
@@ -8,24 +8,13 @@ import com.rtbishop.look4sat.domain.predict.Satellite
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface ISatelliteRepo {
|
interface ISatelliteRepo {
|
||||||
|
|
||||||
val passes: StateFlow<List<SatPass>>
|
val passes: StateFlow<List<SatPass>>
|
||||||
|
|
||||||
val satellites: StateFlow<List<Satellite>>
|
val satellites: StateFlow<List<Satellite>>
|
||||||
|
|
||||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
suspend fun initRepository()
|
suspend fun initRepository()
|
||||||
|
|
||||||
suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos
|
suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos
|
||||||
|
|
||||||
suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos>
|
suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos>
|
||||||
|
suspend fun getRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
|
||||||
suspend fun processRadios(
|
|
||||||
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
|
|
||||||
): List<SatRadio>
|
|
||||||
|
|
||||||
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
|
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
|
||||||
|
|
||||||
suspend fun calculatePasses(time: Long, hoursAhead: Int = 8, minElevation: Double = 16.0)
|
suspend fun calculatePasses(time: Long, hoursAhead: Int = 8, minElevation: Double = 16.0)
|
||||||
}
|
}
|
||||||
@@ -4,20 +4,12 @@ import com.rtbishop.look4sat.domain.model.SatItem
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface ISelectionRepo {
|
interface ISelectionRepo {
|
||||||
|
|
||||||
fun getCurrentType(): String
|
fun getCurrentType(): String
|
||||||
|
|
||||||
fun getTypesList(): List<String>
|
fun getTypesList(): List<String>
|
||||||
|
|
||||||
suspend fun getEntriesFlow(): Flow<List<SatItem>>
|
suspend fun getEntriesFlow(): Flow<List<SatItem>>
|
||||||
|
|
||||||
suspend fun setType(type: String)
|
suspend fun setType(type: String)
|
||||||
|
|
||||||
suspend fun setQuery(query: String)
|
suspend fun setQuery(query: String)
|
||||||
|
|
||||||
suspend fun setSelection(selectAll: Boolean)
|
suspend fun setSelection(selectAll: Boolean)
|
||||||
|
|
||||||
suspend fun setSelection(ids: List<Int>, isTicked: Boolean)
|
suspend fun setSelection(ids: List<Int>, isTicked: Boolean)
|
||||||
|
|
||||||
suspend fun saveSelection()
|
suspend fun saveSelection()
|
||||||
}
|
}
|
||||||
@@ -63,9 +63,7 @@ interface ISettingsRepo {
|
|||||||
|
|
||||||
//region # Passes filter settings
|
//region # Passes filter settings
|
||||||
val passesSettings: StateFlow<PassesSettings>
|
val passesSettings: StateFlow<PassesSettings>
|
||||||
fun getSelectedModes(): List<String>
|
fun setPassesSettings(settings: PassesSettings)
|
||||||
fun setSelectedModes(modes: List<String>)
|
|
||||||
fun updatePassesSettings(settings: PassesSettings)
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region # Station position settings
|
//region # Station position settings
|
||||||
|
|||||||
@@ -23,22 +23,13 @@ import com.rtbishop.look4sat.domain.model.SatRadio
|
|||||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||||
|
|
||||||
interface ILocalSource {
|
interface ILocalSource {
|
||||||
|
|
||||||
suspend fun getEntriesTotal(): Int
|
suspend fun getEntriesTotal(): Int
|
||||||
|
|
||||||
suspend fun getEntriesList(): List<SatItem>
|
suspend fun getEntriesList(): List<SatItem>
|
||||||
|
|
||||||
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||||
|
|
||||||
suspend fun insertEntries(entries: List<SatEntry>)
|
suspend fun insertEntries(entries: List<SatEntry>)
|
||||||
|
|
||||||
suspend fun deleteEntries()
|
suspend fun deleteEntries()
|
||||||
|
|
||||||
suspend fun getRadiosTotal(): Int
|
suspend fun getRadiosTotal(): Int
|
||||||
|
|
||||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
suspend fun insertRadios(radios: List<SatRadio>)
|
suspend fun insertRadios(radios: List<SatRadio>)
|
||||||
|
|
||||||
suspend fun deleteRadios()
|
suspend fun deleteRadios()
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,7 @@ package com.rtbishop.look4sat.domain.source
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface ISensorSource {
|
interface ISensorSource {
|
||||||
|
|
||||||
val orientation: StateFlow<Pair<Float, Float>>
|
val orientation: StateFlow<Pair<Float, Float>>
|
||||||
|
|
||||||
fun enableSensor()
|
fun enableSensor()
|
||||||
|
|
||||||
fun disableSensor()
|
fun disableSensor()
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user