diff --git a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Components.kt b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Components.kt index f39359c0..d66d7099 100644 --- a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Components.kt +++ b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Components.kt @@ -45,6 +45,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -62,8 +63,7 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPass import java.text.SimpleDateFormat import java.util.Date import java.util.Locale - -private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH) +import java.util.TimeZone @Composable @Preview(showBackground = true) @@ -118,7 +118,13 @@ private fun NextPassRowPreview() = MainTheme { } @Composable -fun RowScope.NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier) { +fun RowScope.NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier, isUtc: Boolean = false) { + val timeZone = remember(isUtc) { + if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault() + } + val sdfTime = remember(isUtc) { + SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).also { it.timeZone = timeZone } + } ElevatedCard( modifier = modifier .height(48.dp) diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt index 67a8fc0f..cf17593f 100644 --- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt +++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapScreen.kt @@ -140,12 +140,12 @@ private fun MapScreen(uiState: MapState, mapView: MapView) { TimerRow(timeString = timeString, isTimeAos = isTimeAos) IconCard(action = selectNext, resId = R.drawable.ic_arrow) } - TopBar { NextPassRow(pass = uiState.orbitalPass) } + TopBar { NextPassRow(pass = uiState.orbitalPass, isUtc = uiState.isUtc) } } else { TopBar { IconCard(action = selectPrev, resId = R.drawable.ic_arrow, modifier = rotateMod) TimerRow(timeString = timeString, isTimeAos = isTimeAos) - NextPassRow(pass = uiState.orbitalPass, modifier = Modifier.weight(1f)) + NextPassRow(pass = uiState.orbitalPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc) IconCard(action = selectNext, resId = R.drawable.ic_arrow) } } diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapState.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapState.kt index eb76bfe9..85adefbd 100644 --- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapState.kt +++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapState.kt @@ -25,6 +25,7 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPos data class MapState( val mapData: MapData?, val isLightUi: Boolean, + val isUtc: Boolean, val stationPosition: GeoPos?, val orbitalPass: OrbitalPass, val track: List>?, diff --git a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt index 851f41e7..c573352e 100644 --- a/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt +++ b/feature/map/src/main/java/com/rtbishop/look4sat/feature/map/MapViewModel.kt @@ -43,12 +43,13 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.util.Date -class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISettingsRepo) : +class MapViewModel(private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo) : ViewModel() { private val stationPos = settingsRepo.stationPosition.value @@ -57,6 +58,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet MapState( mapData = null, isLightUi = settingsRepo.otherSettings.value.stateOfLightTheme, + isUtc = settingsRepo.otherSettings.value.stateOfUtc, stationPosition = null, orbitalPass = defaultPass, track = null, @@ -73,6 +75,11 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet val uiState: StateFlow = _uiState init { + viewModelScope.launch { + settingsRepo.otherSettings.collectLatest { settings -> + _uiState.update { it.copy(isUtc = settings.stateOfUtc) } + } + } selectDefaultSatellite(-1) } diff --git a/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesScreen.kt b/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesScreen.kt index b789d714..e140939f 100644 --- a/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesScreen.kt +++ b/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesScreen.kt @@ -77,9 +77,7 @@ import com.rtbishop.look4sat.core.presentation.isVerticalLayout import java.text.SimpleDateFormat import java.util.Date import java.util.Locale - -private val sdfDate = SimpleDateFormat("EEE dd MMM", Locale.ENGLISH) -private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH) +import java.util.TimeZone fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) { composable(Screen.Passes.route) { @@ -131,7 +129,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos) }, bottomInfo = { - NextPassRow(pass = uiState.nextPass) + NextPassRow(pass = uiState.nextPass, isUtc = uiState.isUtc) }, endAction = { IconCard(action = showRadiosDialog, resId = R.drawable.ic_radios) @@ -141,6 +139,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U ) { _ -> PassesList( isRefreshing = uiState.isRefreshing, + isUtc = uiState.isUtc, passes = uiState.itemsList, navigateToRadar = navigateToRadar, refreshPasses = refreshList, @@ -153,6 +152,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U @Composable private fun PassesList( isRefreshing: Boolean, + isUtc: Boolean, passes: List, navigateToRadar: (Int, Long) -> Unit, refreshPasses: () -> Unit, @@ -188,7 +188,8 @@ private fun PassesList( pass = pass, navigateToRadar = navigateToRadar, modifier = Modifier.animateItem(), - isVerticalLayout = isVerticalLayout + isVerticalLayout = isVerticalLayout, + isUtc = isUtc ) } } @@ -220,14 +221,24 @@ private fun NearEarthPass( pass: OrbitalPass, navigateToRadar: (Int, Long) -> Unit, modifier: Modifier = Modifier, - isVerticalLayout: Boolean = true + isVerticalLayout: Boolean = true, + isUtc: Boolean = false ) { val passSatId = stringResource(id = R.string.pass_satId, pass.catNum) val horizontalPadding = if (isVerticalLayout) 6.dp else 10.dp + val timeZone = remember(isUtc) { + if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault() + } + val sdfDate = remember(isUtc) { + SimpleDateFormat("EEE dd MMM", Locale.ENGLISH).also { it.timeZone = timeZone } + } + val sdfTime = remember(isUtc) { + SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).also { it.timeZone = timeZone } + } // Cache formatted date/time strings — aosTime/losTime never change for a given pass - val aosDateStr = remember(pass.aosTime) { sdfDate.format(Date(pass.aosTime)) } - val aosTimeStr = remember(pass.aosTime) { sdfTime.format(Date(pass.aosTime)) } - val losTimeStr = remember(pass.losTime) { sdfTime.format(Date(pass.losTime)) } + val aosDateStr = remember(pass.aosTime, isUtc) { sdfDate.format(Date(pass.aosTime)) } + val aosTimeStr = remember(pass.aosTime, isUtc) { sdfTime.format(Date(pass.aosTime)) } + val losTimeStr = remember(pass.losTime, isUtc) { sdfTime.format(Date(pass.losTime)) } Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { Surface(modifier = Modifier .padding(bottom = 2.dp) diff --git a/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesViewModel.kt b/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesViewModel.kt index 6ac89421..18331cc2 100644 --- a/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesViewModel.kt +++ b/feature/passes/src/main/java/com/rtbishop/look4sat/feature/passes/PassesViewModel.kt @@ -93,7 +93,12 @@ class PassesViewModel( } viewModelScope.launch { settingsRepo.otherSettings.collectLatest { settings -> - _uiState.update { it.copy(shouldSeeWhatsNew = settings.shouldSeeWhatsNew) } + _uiState.update { + it.copy( + isUtc = settings.stateOfUtc, + shouldSeeWhatsNew = settings.shouldSeeWhatsNew + ) + } } } } diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt index c7487d25..c36e321d 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt @@ -113,12 +113,12 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) { TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos) IconCard(action = addToCalendar, resId = R.drawable.ic_calendar) } - TopBar { NextPassRow(pass = upcomingPass) } + TopBar { NextPassRow(pass = upcomingPass, isUtc = uiState.isUtc) } } else { TopBar { IconCard(action = navigateUp, resId = R.drawable.ic_back) TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos) - NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f)) + NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc) IconCard(action = addToCalendar, resId = R.drawable.ic_calendar) } } diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarState.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarState.kt index d5df63ea..bf2b4e12 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarState.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarState.kt @@ -25,6 +25,7 @@ data class RadarState( val currentPass: OrbitalPass?, val currentTime: String, val isCurrentTimeAos: Boolean, + val isUtc: Boolean, val orientationValues: Pair, val orbitalPos: OrbitalPos?, val satTrack: List, diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarViewModel.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarViewModel.kt index ea88383c..42d3c4cf 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarViewModel.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarViewModel.kt @@ -39,6 +39,7 @@ import com.rtbishop.look4sat.core.domain.utility.toTimerString import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -58,6 +59,7 @@ class RadarViewModel( currentPass = null, currentTime = "00:00:00", isCurrentTimeAos = true, + isUtc = settingsRepo.otherSettings.value.stateOfUtc, orientationValues = sensorsRepo.orientation.value, orbitalPos = null, satTrack = emptyList(), @@ -84,6 +86,12 @@ class RadarViewModel( } } } + // React to UTC setting changes + viewModelScope.launch { + settingsRepo.otherSettings.collectLatest { settings -> + _uiState.update { it.copy(isUtc = settings.stateOfUtc) } + } + } // Resolve which pass we're tracking viewModelScope.launch { val catNum = savedStateHandle.get("catNum") ?: 0