mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Implemented AOS window and elevation highlight filters
This commit is contained in:
1 parent
e86bc2c700
commit
6d2b0ced49
20 files changed
+777
-252
No files matched your search
+222
-18
@@ -21,8 +21,11 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
@@ -31,6 +34,7 @@ import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RangeSlider
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
@@ -55,7 +59,10 @@ import com.rtbishop.look4sat.core.presentation.LocalSpacing
|
||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.core.presentation.R
|
||||
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 kotlin.math.roundToInt
|
||||
|
||||
private val allModes = listOf(
|
||||
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||
@@ -66,33 +73,81 @@ private val allModes = listOf(
|
||||
)
|
||||
|
||||
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
|
||||
private const val endOfDayMinute = dayMinutes - 1
|
||||
private const val quarterHourSlots = dayMinutes / minuteStep
|
||||
|
||||
data class PassFilterParams(
|
||||
val hours: Int,
|
||||
val elevation: Double,
|
||||
val lowElevation: Double,
|
||||
val highElevation: Double,
|
||||
val aosStartMinute: Int,
|
||||
val aosEndMinute: Int,
|
||||
val invertAosTimeWindow: Boolean,
|
||||
val showDeepSpace: Boolean
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PassesDialogPreview() {
|
||||
MainTheme { PassesDialog(24, 16.0, true, {}) { _, _, _ -> } }
|
||||
MainTheme {
|
||||
PassesFilterDialog(
|
||||
hours = 24,
|
||||
elevation = 16.0,
|
||||
lowElevation = 16.0,
|
||||
highElevation = 65.0,
|
||||
aosStartMinute = 0,
|
||||
aosEndMinute = 23 * 60 + 59,
|
||||
invertAosTimeWindow = false,
|
||||
showDeepSpace = true,
|
||||
cancel = {},
|
||||
accept = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun PassesDialog(
|
||||
internal fun PassesFilterDialog(
|
||||
hours: Int,
|
||||
elevation: Double,
|
||||
lowElevation: Double,
|
||||
highElevation: Double,
|
||||
aosStartMinute: Int,
|
||||
aosEndMinute: Int,
|
||||
invertAosTimeWindow: Boolean,
|
||||
showDeepSpace: Boolean,
|
||||
cancel: () -> Unit,
|
||||
accept: (Int, Double, Boolean) -> Unit
|
||||
accept: (PassFilterParams) -> Unit
|
||||
) {
|
||||
val hoursIndex = remember { mutableIntStateOf(hourSteps.indexOfFirst { it >= hours }.coerceAtLeast(0)) }
|
||||
val elevationValueNew = remember { mutableDoubleStateOf(elevation) }
|
||||
val highlightBounds = 0f..90f
|
||||
var highlightRange by remember(lowElevation, highElevation) {
|
||||
mutableStateOf(lowElevation.toFloat()..highElevation.toFloat())
|
||||
}
|
||||
var aosRangeValue by remember {
|
||||
mutableStateOf(minuteToSlot(aosStartMinute).toFloat()..minuteToSlot(aosEndMinute).toFloat())
|
||||
}
|
||||
var invertedAosRange by remember { mutableStateOf(invertAosTimeWindow) }
|
||||
var deepSpaceEnabled by remember { mutableStateOf(showDeepSpace) }
|
||||
val onAccept = {
|
||||
accept(hourSteps[hoursIndex.intValue], elevationValueNew.doubleValue, deepSpaceEnabled).also { cancel() }
|
||||
accept(
|
||||
PassFilterParams(
|
||||
hours = hourSteps[hoursIndex.intValue],
|
||||
elevation = elevationValueNew.doubleValue,
|
||||
lowElevation = highlightRange.start.roundToInt().toDouble(),
|
||||
highElevation = highlightRange.endInclusive.roundToInt().toDouble(),
|
||||
aosStartMinute = slotToMinute(aosRangeValue.start),
|
||||
aosEndMinute = slotToMinute(aosRangeValue.endInclusive),
|
||||
invertAosTimeWindow = invertedAosRange,
|
||||
showDeepSpace = deepSpaceEnabled
|
||||
)
|
||||
)
|
||||
cancel()
|
||||
}
|
||||
SharedDialog(title = stringResource(R.string.pass_filter_title), onCancel = cancel, onAccept = onAccept) {
|
||||
ToggleRow(
|
||||
title = stringResource(R.string.pass_filter_deep_space),
|
||||
checked = deepSpaceEnabled,
|
||||
onCheckedChange = { deepSpaceEnabled = it }
|
||||
)
|
||||
SliderRow(
|
||||
title = stringResource(R.string.pass_filter_elev),
|
||||
value = elevationValueNew.doubleValue,
|
||||
@@ -104,24 +159,74 @@ internal fun PassesDialog(
|
||||
SliderRow(
|
||||
title = stringResource(R.string.pass_filter_hours),
|
||||
value = hoursIndex.intValue.toDouble(),
|
||||
displayValue = "${hourSteps[hoursIndex.intValue]}h",
|
||||
displayValue = formatHoursLabel(hourSteps[hoursIndex.intValue]),
|
||||
valueResId = R.drawable.ic_clock,
|
||||
valueRange = 0f..(hourSteps.size - 1).toFloat(),
|
||||
steps = hourSteps.size - 2
|
||||
) { hoursIndex.intValue = it.toInt().coerceIn(0, hourSteps.size - 1) }
|
||||
ToggleRow(
|
||||
title = stringResource(R.string.pass_filter_deep_space),
|
||||
checked = deepSpaceEnabled,
|
||||
onCheckedChange = { deepSpaceEnabled = it }
|
||||
)
|
||||
ElevationColorsRangeSliderRow(
|
||||
title = stringResource(R.string.prefs_highlight_title),
|
||||
range = highlightRange,
|
||||
valueRange = highlightBounds
|
||||
) { highlightRange = it }
|
||||
TimeRangeSliderRow(
|
||||
title = stringResource(R.string.pass_filter_aos_time),
|
||||
range = aosRangeValue,
|
||||
displayValue = formatTimeRange(aosRangeValue, invertedAosRange),
|
||||
valueResId = R.drawable.ic_clock,
|
||||
valueRange = 0f..quarterHourSlots.toFloat(),
|
||||
) { aosRangeValue = it }
|
||||
ToggleRow(
|
||||
title = stringResource(R.string.pass_filter_invert_time),
|
||||
checked = invertedAosRange,
|
||||
onCheckedChange = { invertedAosRange = it }
|
||||
)
|
||||
Spacer(modifier = Modifier.height(0.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private fun slotToMinute(value: Float): Int {
|
||||
val slot = value.roundToInt().coerceIn(0, quarterHourSlots)
|
||||
return if (slot == quarterHourSlots) endOfDayMinute else slot * minuteStep
|
||||
}
|
||||
|
||||
private fun minuteToSlot(minute: Int): Int {
|
||||
if (minute >= endOfDayMinute) return quarterHourSlots
|
||||
return ((minute + minuteStep / 2) / minuteStep).coerceIn(0, quarterHourSlots)
|
||||
}
|
||||
|
||||
private fun formatMinuteOfDay(minute: Int): String {
|
||||
val hourPart = minute / 60
|
||||
val minutePart = minute % 60
|
||||
return "%02d:%02d".format(hourPart, minutePart)
|
||||
}
|
||||
|
||||
private fun formatHoursLabel(hours: Int): String {
|
||||
if (hours < 24) return "${hours}h"
|
||||
val days = hours / 24
|
||||
val remainder = hours % 24
|
||||
return if (remainder == 0) "${days}d" else "${days}d ${remainder}h"
|
||||
}
|
||||
|
||||
private fun formatTimeRange(
|
||||
range: ClosedFloatingPointRange<Float>,
|
||||
inverted: Boolean
|
||||
): String {
|
||||
val start = formatMinuteOfDay(slotToMinute(range.start))
|
||||
val end = formatMinuteOfDay(slotToMinute(range.endInclusive))
|
||||
return if (inverted) "$end - $start" else "$start - $end"
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SliderRow(
|
||||
private fun SliderSection(
|
||||
title: String,
|
||||
value: Double,
|
||||
displayValue: String,
|
||||
valueResId: Int,
|
||||
valueRange: ClosedFloatingPointRange<Float>,
|
||||
steps: Int = 0,
|
||||
accentColor: Color = MaterialTheme.colorScheme.primary,
|
||||
onChange: (Float) -> Unit
|
||||
trailingContent: @Composable () -> Unit,
|
||||
sliderContent: @Composable () -> Unit
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -138,6 +243,26 @@ private fun SliderRow(
|
||||
modifier = Modifier.weight(1f),
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
trailingContent()
|
||||
}
|
||||
sliderContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SliderRow(
|
||||
title: String,
|
||||
value: Double,
|
||||
displayValue: String,
|
||||
valueResId: Int,
|
||||
valueRange: ClosedFloatingPointRange<Float>,
|
||||
steps: Int = 0,
|
||||
accentColor: Color = MaterialTheme.colorScheme.primary,
|
||||
onChange: (Float) -> Unit
|
||||
) {
|
||||
SliderSection(
|
||||
title = title,
|
||||
trailingContent = {
|
||||
Icon(
|
||||
painter = painterResource(id = valueResId),
|
||||
contentDescription = null,
|
||||
@@ -151,10 +276,89 @@ private fun SliderRow(
|
||||
color = accentColor
|
||||
)
|
||||
}
|
||||
) {
|
||||
Slider(value = value.toFloat(), onValueChange = onChange, valueRange = valueRange, steps = steps)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TimeRangeSliderRow(
|
||||
title: String,
|
||||
range: ClosedFloatingPointRange<Float>,
|
||||
displayValue: String,
|
||||
valueResId: Int,
|
||||
valueRange: ClosedFloatingPointRange<Float>,
|
||||
onChange: (ClosedFloatingPointRange<Float>) -> Unit
|
||||
) {
|
||||
SliderSection(
|
||||
title = title,
|
||||
trailingContent = {
|
||||
Icon(
|
||||
painter = painterResource(id = valueResId),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Text(
|
||||
text = displayValue,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
) {
|
||||
RangeSlider(
|
||||
value = range,
|
||||
onValueChange = { onChange(it.start..it.endInclusive) },
|
||||
valueRange = valueRange,
|
||||
steps = 0,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ElevationColorsRangeSliderRow(
|
||||
title: String,
|
||||
range: ClosedFloatingPointRange<Float>,
|
||||
valueRange: ClosedFloatingPointRange<Float>,
|
||||
onChange: (ClosedFloatingPointRange<Float>) -> Unit
|
||||
) {
|
||||
val low = range.start.roundToInt()
|
||||
val high = range.endInclusive.roundToInt()
|
||||
SliderSection(
|
||||
title = title,
|
||||
trailingContent = {
|
||||
Text(
|
||||
text = "$low°",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = ElevationLowColor
|
||||
)
|
||||
Text(
|
||||
text = "..",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = "$high°",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = ElevationHighColor
|
||||
)
|
||||
}
|
||||
) {
|
||||
RangeSlider(
|
||||
value = range,
|
||||
onValueChange = { onChange(it.start..it.endInclusive) },
|
||||
valueRange = valueRange,
|
||||
steps = 0,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadiosDialogPreview() {
|
||||
|
||||
@@ -67,11 +67,11 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||
import com.rtbishop.look4sat.core.presentation.EmptyListCard
|
||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||
import com.rtbishop.look4sat.core.presentation.InfoDialog
|
||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||
import com.rtbishop.look4sat.core.presentation.R
|
||||
import com.rtbishop.look4sat.core.presentation.ScreenColumn
|
||||
import com.rtbishop.look4sat.core.presentation.SharedDialog
|
||||
import com.rtbishop.look4sat.core.presentation.SwipeableItem
|
||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||
@@ -99,14 +99,31 @@ private fun PassesScreen(
|
||||
navigateToRadar: (Int, Long) -> Unit
|
||||
) {
|
||||
if (uiState.isPassesDialogShown) {
|
||||
PassesDialog(
|
||||
PassesFilterDialog(
|
||||
hours = uiState.hours,
|
||||
elevation = uiState.elevation,
|
||||
lowElevation = uiState.lowElevation,
|
||||
highElevation = uiState.highElevation,
|
||||
aosStartMinute = uiState.aosStartMinute,
|
||||
aosEndMinute = uiState.aosEndMinute,
|
||||
invertAosTimeWindow = uiState.invertAosTimeWindow,
|
||||
showDeepSpace = uiState.showDeepSpace,
|
||||
cancel = { onAction(PassesAction.TogglePassesDialog) }
|
||||
) { hours, elevation, showDeepSpace ->
|
||||
onAction(PassesAction.FilterPasses(hours, elevation, showDeepSpace))
|
||||
}
|
||||
cancel = { onAction(PassesAction.TogglePassesDialog) },
|
||||
accept = { params ->
|
||||
onAction(
|
||||
PassesAction.FilterPasses(
|
||||
hoursAhead = params.hours,
|
||||
minElevation = params.elevation,
|
||||
lowElevation = params.lowElevation,
|
||||
highElevation = params.highElevation,
|
||||
aosStartMinute = params.aosStartMinute,
|
||||
aosEndMinute = params.aosEndMinute,
|
||||
invertAosTimeWindow = params.invertAosTimeWindow,
|
||||
showDeepSpace = params.showDeepSpace
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (uiState.isRadiosDialogShown) {
|
||||
RadiosDialog(
|
||||
@@ -117,11 +134,19 @@ private fun PassesScreen(
|
||||
}
|
||||
}
|
||||
if (uiState.shouldSeeWhatsNew) {
|
||||
InfoDialog(
|
||||
val dismiss = { onAction(PassesAction.DismissWhatsNew) }
|
||||
SharedDialog(
|
||||
title = stringResource(R.string.pass_whatsnew_title),
|
||||
text = stringResource(R.string.pass_whatsnew_message)
|
||||
) {
|
||||
onAction(PassesAction.DismissWhatsNew)
|
||||
onDismissRequest = dismiss,
|
||||
onAccept = dismiss,
|
||||
titleFontSize = 18
|
||||
) { padding ->
|
||||
Text(
|
||||
text = stringResource(R.string.pass_whatsnew_message),
|
||||
fontSize = 16.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(horizontal = padding)
|
||||
)
|
||||
}
|
||||
}
|
||||
ScreenColumn(
|
||||
|
||||
@@ -29,6 +29,11 @@ data class PassesState(
|
||||
val isNextTimeAos: Boolean = true,
|
||||
val hours: Int = 24,
|
||||
val elevation: Double = 16.0,
|
||||
val lowElevation: Double = 16.0,
|
||||
val highElevation: Double = 65.0,
|
||||
val aosStartMinute: Int = 0,
|
||||
val aosEndMinute: Int = 23 * 60 + 59,
|
||||
val invertAosTimeWindow: Boolean = false,
|
||||
val showDeepSpace: Boolean = true,
|
||||
val modes: List<String> = emptyList(),
|
||||
val itemsList: List<OrbitalPass> = emptyList(),
|
||||
@@ -40,7 +45,16 @@ data class PassesState(
|
||||
|
||||
sealed interface PassesAction {
|
||||
data object DismissWhatsNew : PassesAction
|
||||
data class FilterPasses(val hoursAhead: Int, val minElevation: Double, val showDeepSpace: Boolean) : PassesAction
|
||||
data class FilterPasses(
|
||||
val hoursAhead: Int,
|
||||
val minElevation: Double,
|
||||
val lowElevation: Double,
|
||||
val highElevation: Double,
|
||||
val aosStartMinute: Int,
|
||||
val aosEndMinute: Int,
|
||||
val invertAosTimeWindow: Boolean,
|
||||
val showDeepSpace: Boolean
|
||||
) : PassesAction
|
||||
data class FilterRadios(val modes: List<String>) : PassesAction
|
||||
data object RefreshPasses : PassesAction
|
||||
data object TogglePassesDialog : PassesAction
|
||||
|
||||
+82
-8
@@ -54,6 +54,11 @@ class PassesViewModel(
|
||||
nextPass = defaultPass,
|
||||
hours = settingsRepo.passesSettings.value.hoursAhead,
|
||||
elevation = settingsRepo.passesSettings.value.minElevation,
|
||||
lowElevation = settingsRepo.otherSettings.value.lowElevation,
|
||||
highElevation = settingsRepo.otherSettings.value.highElevation,
|
||||
aosStartMinute = settingsRepo.passesSettings.value.aosStartMinute,
|
||||
aosEndMinute = settingsRepo.passesSettings.value.aosEndMinute,
|
||||
invertAosTimeWindow = settingsRepo.passesSettings.value.invertAosTimeWindow,
|
||||
showDeepSpace = settingsRepo.passesSettings.value.showDeepSpace,
|
||||
modes = settingsRepo.passesSettings.value.selectedModes,
|
||||
shouldSeeWhatsNew = settingsRepo.otherSettings.value.shouldSeeWhatsNew
|
||||
@@ -71,7 +76,14 @@ class PassesViewModel(
|
||||
// React to settings changes: update UTC flag and whatsNew
|
||||
viewModelScope.launch {
|
||||
settingsRepo.otherSettings.collectLatest { settings ->
|
||||
_uiState.update { it.copy(isUtc = settings.stateOfUtc, shouldSeeWhatsNew = settings.shouldSeeWhatsNew) }
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isUtc = settings.stateOfUtc,
|
||||
shouldSeeWhatsNew = settings.shouldSeeWhatsNew,
|
||||
lowElevation = settings.lowElevation,
|
||||
highElevation = settings.highElevation
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Main tick loop: reacts to new passes, then ticks every second
|
||||
@@ -107,9 +119,29 @@ class PassesViewModel(
|
||||
when (action) {
|
||||
PassesAction.DismissWhatsNew -> settingsRepo.setWhatsNewDismissed()
|
||||
is PassesAction.FilterPasses ->
|
||||
applyFilter(action.hoursAhead, action.minElevation, action.showDeepSpace, _uiState.value.modes)
|
||||
applyFilter(
|
||||
hoursAhead = action.hoursAhead,
|
||||
minElevation = action.minElevation,
|
||||
lowElevation = action.lowElevation,
|
||||
highElevation = action.highElevation,
|
||||
aosStartMinute = action.aosStartMinute,
|
||||
aosEndMinute = action.aosEndMinute,
|
||||
invertAosTimeWindow = action.invertAosTimeWindow,
|
||||
showDeepSpace = action.showDeepSpace,
|
||||
modes = _uiState.value.modes
|
||||
)
|
||||
is PassesAction.FilterRadios ->
|
||||
applyFilter(_uiState.value.hours, _uiState.value.elevation, _uiState.value.showDeepSpace, action.modes)
|
||||
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
|
||||
)
|
||||
PassesAction.RefreshPasses -> refreshPasses()
|
||||
PassesAction.TogglePassesDialog ->
|
||||
_uiState.update { it.copy(isPassesDialogShown = !it.isPassesDialogShown) }
|
||||
@@ -198,19 +230,61 @@ class PassesViewModel(
|
||||
private fun applyFilter(
|
||||
hoursAhead: Int,
|
||||
minElevation: Double,
|
||||
lowElevation: Double,
|
||||
highElevation: Double,
|
||||
aosStartMinute: Int,
|
||||
aosEndMinute: Int,
|
||||
invertAosTimeWindow: Boolean,
|
||||
showDeepSpace: Boolean,
|
||||
modes: List<String>
|
||||
) = viewModelScope.launch {
|
||||
settingsRepo.setPassesSettings(PassesSettings(showDeepSpace, hoursAhead, minElevation, modes))
|
||||
settingsRepo.setPassesSettings(
|
||||
PassesSettings(
|
||||
showDeepSpace,
|
||||
hoursAhead,
|
||||
minElevation,
|
||||
aosStartMinute,
|
||||
aosEndMinute,
|
||||
invertAosTimeWindow,
|
||||
modes
|
||||
)
|
||||
)
|
||||
settingsRepo.updateOtherSettings { it.copy(lowElevation = lowElevation, highElevation = highElevation) }
|
||||
_uiState.update {
|
||||
it.copy(hours = hoursAhead, elevation = minElevation, showDeepSpace = showDeepSpace, modes = modes)
|
||||
it.copy(
|
||||
hours = hoursAhead,
|
||||
elevation = minElevation,
|
||||
lowElevation = lowElevation,
|
||||
highElevation = highElevation,
|
||||
aosStartMinute = aosStartMinute,
|
||||
aosEndMinute = aosEndMinute,
|
||||
invertAosTimeWindow = invertAosTimeWindow,
|
||||
showDeepSpace = showDeepSpace,
|
||||
modes = modes
|
||||
)
|
||||
}
|
||||
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes)
|
||||
satelliteRepo.calculatePasses(
|
||||
time = System.currentTimeMillis(),
|
||||
hoursAhead = hoursAhead,
|
||||
minElevation = minElevation,
|
||||
aosStartMinute = aosStartMinute,
|
||||
aosEndMinute = aosEndMinute,
|
||||
invertAosTimeWindow = invertAosTimeWindow,
|
||||
modes = modes
|
||||
)
|
||||
}
|
||||
|
||||
private fun refreshPasses() = viewModelScope.launch {
|
||||
val (_, hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value
|
||||
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes)
|
||||
val settings = settingsRepo.passesSettings.value
|
||||
satelliteRepo.calculatePasses(
|
||||
time = System.currentTimeMillis(),
|
||||
hoursAhead = settings.hoursAhead,
|
||||
minElevation = settings.minElevation,
|
||||
aosStartMinute = settings.aosStartMinute,
|
||||
aosEndMinute = settings.aosEndMinute,
|
||||
invertAosTimeWindow = settings.invertAosTimeWindow,
|
||||
modes = settings.selectedModes
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -53,10 +53,10 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||
import com.rtbishop.look4sat.core.presentation.OutlinedText
|
||||
import com.rtbishop.look4sat.core.presentation.R
|
||||
import com.rtbishop.look4sat.core.presentation.SharedDialog
|
||||
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -95,53 +95,44 @@ internal fun SstvPage(
|
||||
|
||||
if (showModeDialog.value) {
|
||||
val allModes = remember(sstv.supportedModes) { listOf("Auto") + sstv.supportedModes }
|
||||
Dialog(onDismissRequest = { showModeDialog.value = false }) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(vertical = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "SSTV Mode",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(bottom = 12.dp)
|
||||
)
|
||||
LazyColumn(
|
||||
val dismiss = { showModeDialog.value = false }
|
||||
SharedDialog(
|
||||
title = "SSTV Mode",
|
||||
onDismissRequest = dismiss,
|
||||
onAccept = dismiss
|
||||
) { _ ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight(0.7f)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
items(allModes) { mode ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight(0.5f)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||
) {
|
||||
items(allModes) { mode ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable {
|
||||
onAction(RadarAction.SstvSelectMode(mode))
|
||||
showModeDialog.value = false
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = mode,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
RadioButton(
|
||||
selected = mode == sstv.selectedMode,
|
||||
onClick = null,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable {
|
||||
onAction(RadarAction.SstvSelectMode(mode))
|
||||
showModeDialog.value = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = mode,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
RadioButton(
|
||||
selected = mode == sstv.selectedMode,
|
||||
onClick = null,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -62,10 +62,10 @@ import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||
import com.rtbishop.look4sat.core.presentation.CardLoadingIndicator
|
||||
import com.rtbishop.look4sat.core.presentation.EmptyListCard
|
||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||
import com.rtbishop.look4sat.core.presentation.InfoDialog
|
||||
import com.rtbishop.look4sat.core.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.core.presentation.PrimaryIconCard
|
||||
import com.rtbishop.look4sat.core.presentation.R
|
||||
import com.rtbishop.look4sat.core.presentation.SharedDialog
|
||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
||||
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
||||
@@ -95,11 +95,19 @@ private fun SatellitesScreen(
|
||||
)
|
||||
}
|
||||
if (uiState.shouldSeeWarning) {
|
||||
InfoDialog(
|
||||
stringResource(R.string.sat_warning_title),
|
||||
stringResource(R.string.sat_warning_message)
|
||||
) {
|
||||
onAction(SatellitesAction.DismissWarning)
|
||||
val dismiss = { onAction(SatellitesAction.DismissWarning) }
|
||||
SharedDialog(
|
||||
title = stringResource(R.string.sat_warning_title),
|
||||
onDismissRequest = dismiss,
|
||||
onAccept = dismiss,
|
||||
titleFontSize = 18
|
||||
) { padding ->
|
||||
Text(
|
||||
text = stringResource(R.string.sat_warning_message),
|
||||
fontSize = 16.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(horizontal = padding)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -69,13 +69,16 @@ fun PositionDialog(lat: Double, lon: Double, dismiss: () -> Unit, save: (Double,
|
||||
OutlinedTextField(
|
||||
value = latValue.value,
|
||||
onValueChange = { latValue.value = it },
|
||||
label = { Text(text = stringResource(id = R.string.prefs_station_lat_text)) }
|
||||
label = { Text(text = stringResource(id = R.string.prefs_station_lat_text)) },
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = LocalSpacing.current.large),
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = lonValue.value,
|
||||
onValueChange = { lonValue.value = it },
|
||||
label = { Text(text = stringResource(id = R.string.prefs_station_lon_text)) }
|
||||
label = { Text(text = stringResource(id = R.string.prefs_station_lon_text)) },
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = LocalSpacing.current.large),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(0.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +104,10 @@ fun LocatorDialog(qthLocator: String, dismiss: () -> Unit, save: (String) -> Uni
|
||||
OutlinedTextField(
|
||||
value = locator.value,
|
||||
onValueChange = { locator.value = it },
|
||||
label = { Text(text = stringResource(id = R.string.prefs_locator_text)) }
|
||||
label = { Text(text = stringResource(id = R.string.prefs_locator_text)) },
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = LocalSpacing.current.large),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(0.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +213,7 @@ fun DataSourcesDialog(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isEnabledCustomTransceivers.value,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,6 +304,7 @@ fun NetworkOutputDialog(
|
||||
onFormatChange = { frequencyFormat.value = it },
|
||||
formatLabel = stringResource(R.string.prefs_net_frequency_format_hint)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,6 +398,7 @@ fun BluetoothOutputDialog(
|
||||
onFormatChange = { frequencyFormat.value = it },
|
||||
formatLabel = stringResource(R.string.prefs_bt_frequency_output_hint)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,6 +650,7 @@ fun RadioControlDialog(
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user