From 1bed7849a3c940bdf85d4ca42cc2b9ab91eb8aeb Mon Sep 17 00:00:00 2001 From: atsunatsu Date: Wed, 16 Sep 2026 11:57:03 +0800 Subject: [PATCH] feat(mutual): mutual page times follow the UTC setting The pass card time range and the elevation-curve X-axis labels were hardcoded to the device timezone. MutualUiState now carries isUtc, initialized and kept in sync with OtherSettings.stateOfUtc (same pattern as PassesViewModel), and both the card timeFormat and the chart axis use UTC when the setting is on. Also fixes pre-existing test-fake compile breakage: FakeSettingsRepo built OtherSettings with positional args missing shouldSeeWhatsNew, and never implemented the wavelogSettings/lotwSettings/worked-grids members added by later interface extensions. --- .../feature/mutual/ElevationCurveChart.kt | 8 +++++- .../look4sat/feature/mutual/MutualScreen.kt | 10 +++++++- .../feature/mutual/MutualViewModel.kt | 11 +++++++- .../rtbishop/look4sat/feature/mutual/Fakes.kt | 25 ++++++++++++++++++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/ElevationCurveChart.kt b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/ElevationCurveChart.kt index 7822d896..ead04626 100644 --- a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/ElevationCurveChart.kt +++ b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/ElevationCurveChart.kt @@ -45,6 +45,7 @@ import com.rtbishop.look4sat.core.presentation.R import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import java.util.TimeZone import kotlin.math.roundToInt /** @@ -58,6 +59,7 @@ fun ElevationCurveChart( startTime: Long, endTime: Long, maxElev: Double, + isUtc: Boolean = false, progress: Float = 0.5f, onProgressChange: (Float) -> Unit = {}, modifier: Modifier = Modifier @@ -74,7 +76,11 @@ fun ElevationCurveChart( val textColorArgb = textColor.toArgb() val onSurfaceArgb = onSurfaceColor.toArgb() val gridColorArgb = gridColor.toArgb() - val timeFormat = remember { SimpleDateFormat("HH:mm", Locale.getDefault()) } + val timeFormat = remember(isUtc) { + SimpleDateFormat("HH:mm", Locale.getDefault()).apply { + if (isUtc) timeZone = TimeZone.getTimeZone("UTC") + } + } Column(modifier = modifier.fillMaxWidth()) { Canvas( diff --git a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt index 698a17e0..c2fecfa1 100644 --- a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt +++ b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt @@ -71,6 +71,7 @@ import com.rtbishop.look4sat.core.presentation.isVerticalLayout import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import java.util.TimeZone @Composable fun MutualScreen( @@ -186,7 +187,11 @@ private fun MutualContent( onHoursAhead: (Int) -> Unit, onClearError: () -> Unit ) { - val timeFormat = remember { SimpleDateFormat("MM/dd HH:mm", Locale.getDefault()) } + val timeFormat = remember(state.isUtc) { + SimpleDateFormat("MM/dd HH:mm", Locale.getDefault()).apply { + if (state.isUtc) timeZone = TimeZone.getTimeZone("UTC") + } + } LazyColumn( modifier = Modifier.fillMaxSize(), @@ -323,6 +328,7 @@ private fun MutualContent( pass = pass, isExpanded = state.selectedPassIndex == index, timeFormat = timeFormat, + isUtc = state.isUtc, minElevA = state.stationAMinElev, minElevB = state.stationBMinElev, onClick = { onSelectPass(if (state.selectedPassIndex == index) -1 else index) }, @@ -527,6 +533,7 @@ private fun MutualPassCard( pass: MutualPass, isExpanded: Boolean, timeFormat: SimpleDateFormat, + isUtc: Boolean, minElevA: Double, minElevB: Double, onClick: () -> Unit, @@ -622,6 +629,7 @@ private fun MutualPassCard( startTime = visibleStart, endTime = visibleEnd, maxElev = adjustedMaxElev, + isUtc = isUtc, progress = dragProgress, onProgressChange = { dragProgress = it } ) diff --git a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualViewModel.kt b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualViewModel.kt index f7ed0659..7520f5a9 100644 --- a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualViewModel.kt +++ b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualViewModel.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -53,6 +54,7 @@ data class MutualUiState( val isCalculating: Boolean = false, val hasSearched: Boolean = false, val selectedPassIndex: Int = -1, + val isUtc: Boolean = false, val errorMessage: String? = null ) @@ -77,8 +79,15 @@ class MutualViewModel( stationALat = "%.4f".format(pos.latitude), stationALon = "%.4f".format(pos.longitude), stationAMinElev = 0.0, - stationBMinElev = 0.0 + stationBMinElev = 0.0, + isUtc = settingsRepo.otherSettings.value.stateOfUtc ) } + // Keep the time display in sync with the UTC toggle in Settings + viewModelScope.launch { + settingsRepo.otherSettings.collectLatest { settings -> + _uiState.update { it.copy(isUtc = settings.stateOfUtc) } + } + } } fun onStationALat(value: String) { diff --git a/feature/mutual/src/test/java/com/rtbishop/look4sat/feature/mutual/Fakes.kt b/feature/mutual/src/test/java/com/rtbishop/look4sat/feature/mutual/Fakes.kt index b4a035d0..ca481be9 100644 --- a/feature/mutual/src/test/java/com/rtbishop/look4sat/feature/mutual/Fakes.kt +++ b/feature/mutual/src/test/java/com/rtbishop/look4sat/feature/mutual/Fakes.kt @@ -2,11 +2,14 @@ package com.rtbishop.look4sat.feature.mutual import com.rtbishop.look4sat.core.domain.model.DataSourcesSettings import com.rtbishop.look4sat.core.domain.model.DatabaseState +import com.rtbishop.look4sat.core.domain.model.GridQso +import com.rtbishop.look4sat.core.domain.model.LoTWSettings import com.rtbishop.look4sat.core.domain.model.OtherSettings import com.rtbishop.look4sat.core.domain.model.PassesSettings import com.rtbishop.look4sat.core.domain.model.RCSettings import com.rtbishop.look4sat.core.domain.model.RadioControlSettings import com.rtbishop.look4sat.core.domain.model.SatRadio +import com.rtbishop.look4sat.core.domain.model.WavelogSettings import com.rtbishop.look4sat.core.domain.predict.GeoPos import com.rtbishop.look4sat.core.domain.predict.OrbitalObject import com.rtbishop.look4sat.core.domain.predict.OrbitalPass @@ -62,7 +65,17 @@ class FakeSettingsRepo(initialPosition: GeoPos = GeoPos(23.13, 113.26)) : ISetti RCSettings(false, "", "", "", false, "", "", "", 0L, false, "", "", "", false, "", "") ) override val otherSettings: StateFlow = MutableStateFlow( - OtherSettings(false, false, false, false, false, false, false, false) + OtherSettings( + stateOfAutoUpdate = false, + stateOfSensors = false, + stateOfSweep = false, + stateOfUtc = false, + stateOfLightTheme = false, + stateOfNightMode = false, + stateOfMapGrid = false, + shouldSeeWarning = false, + shouldSeeWhatsNew = false + ) ) override val dataSourcesSettings: StateFlow = MutableStateFlow( DataSourcesSettings(satelliteUrls = emptyList(), transceiversUrls = emptyList()) @@ -70,6 +83,8 @@ class FakeSettingsRepo(initialPosition: GeoPos = GeoPos(23.13, 113.26)) : ISetti override val radioControlSettings: StateFlow = MutableStateFlow( RadioControlSettings(false, RadioControlSettings.MODEL_YAESU_FT817, "", "", "", "", 9600) ) + override val wavelogSettings: StateFlow = MutableStateFlow(WavelogSettings()) + override val lotwSettings: StateFlow = MutableStateFlow(LoTWSettings()) override fun setSelectedIds(ids: List) = TODO() override fun setSelectedTypes(types: List) = TODO() @@ -93,4 +108,12 @@ class FakeSettingsRepo(initialPosition: GeoPos = GeoPos(23.13, 113.26)) : ISetti override fun setSatelliteOffset(catnum: Int, offset: String) = TODO() override fun getAmSatCallsign(): String = "" override fun setAmSatCallsign(callsign: String) = TODO() + override fun updateWavelogSettings(settings: WavelogSettings) = TODO() + override fun getWorkedGrids(): Set = TODO() + override fun setWorkedGrids(grids: Set) = TODO() + override fun getWorkedGridQsos(): Map> = TODO() + override fun setWorkedGridQsos(qsos: Map>) = TODO() + override fun getRoamedGrids(): Set = TODO() + override fun setRoamedGrids(grids: Set) = TODO() + override fun updateLoTWSettings(settings: LoTWSettings) = TODO() } \ No newline at end of file