From a4347511a573dafa3f818020a2917db8dce8eb29 Mon Sep 17 00:00:00 2001 From: Tim Kluge Date: Fri, 13 Dec 2024 17:56:20 +0100 Subject: [PATCH] fix #7: Set units of measurement to karoo profile preferences on first load --- .../de/timklge/karooheadwind/Extensions.kt | 33 ++++++++++++++++--- .../karooheadwind/KarooHeadwindExtension.kt | 2 +- .../datatypes/HeadwindDirectionDataType.kt | 4 +-- .../datatypes/HeadwindSpeedDataType.kt | 2 +- .../datatypes/WeatherDataType.kt | 2 +- .../timklge/karooheadwind/screens/Dropdown.kt | 7 ++-- .../karooheadwind/screens/MainScreen.kt | 14 +++++--- 7 files changed, 45 insertions(+), 19 deletions(-) diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt b/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt index 1affac7..4a241c8 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt @@ -7,12 +7,15 @@ import androidx.datastore.preferences.core.stringPreferencesKey import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.screens.HeadwindSettings import de.timklge.karooheadwind.screens.HeadwindStats +import de.timklge.karooheadwind.screens.PrecipitationUnit +import de.timklge.karooheadwind.screens.WindUnit import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.HttpResponseState import io.hammerhead.karooext.models.OnHttpResponse import io.hammerhead.karooext.models.OnStreamState import io.hammerhead.karooext.models.StreamState +import io.hammerhead.karooext.models.UserProfile import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.channels.awaitClose @@ -24,6 +27,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull @@ -86,12 +90,22 @@ fun Context.streamCurrentWeatherData(): Flow { }.filterNotNull().distinctUntilChanged().filter { it.current.time * 1000 >= System.currentTimeMillis() - (1000 * 60 * 60 * 12) } } -fun Context.streamSettings(): Flow { +fun Context.streamSettings(karooSystemService: KarooSystemService): Flow { return dataStore.data.map { settingsJson -> try { - jsonWithUnknownKeys.decodeFromString( - settingsJson[settingsKey] ?: HeadwindSettings.defaultSettings - ) + if (settingsJson.contains(settingsKey)){ + jsonWithUnknownKeys.decodeFromString(settingsJson[settingsKey]!!) + } else { + val defaultSettings = jsonWithUnknownKeys.decodeFromString(HeadwindSettings.defaultSettings) + + val preferredUnits = karooSystemService.streamUserProfile().first().preferredUnit + val preferredMetric = preferredUnits.distance == UserProfile.PreferredUnit.UnitType.METRIC + + defaultSettings.copy( + windUnit = if (preferredMetric) WindUnit.KILOMETERS_PER_HOUR else WindUnit.MILES_PER_HOUR, + precipitationUnit = if (preferredMetric) PrecipitationUnit.MILLIMETERS else PrecipitationUnit.INCH + ) + } } catch(e: Throwable){ Log.e(KarooHeadwindExtension.TAG, "Failed to read preferences", e) jsonWithUnknownKeys.decodeFromString(HeadwindSettings.defaultSettings) @@ -112,6 +126,17 @@ fun Context.streamStats(): Flow { }.distinctUntilChanged() } +fun KarooSystemService.streamUserProfile(): Flow { + return callbackFlow { + val listenerId = addConsumer { userProfile: UserProfile -> + trySendBlocking(userProfile) + } + awaitClose { + removeConsumer(listenerId) + } + } +} + @OptIn(FlowPreview::class) suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoordinates, settings: HeadwindSettings): HttpResponseState.Complete { return callbackFlow { diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt b/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt index c5a42df..ceacaf4 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt @@ -75,7 +75,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.0.0-beta2") { } } - streamSettings() + streamSettings(karooSystem) .filter { it.welcomeDialogAccepted } .combine(gpsFlow) { settings, gps -> settings to gps } .map { (settings, gps) -> diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt index 0ffdcb2..4408b27 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt @@ -72,7 +72,7 @@ class HeadwindDirectionDataType( karooSystem.streamDataFlow(dataTypeId) .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .combine(context.streamCurrentWeatherData()) { value, data -> value to data } - .combine(context.streamSettings()) { (value, data), settings -> StreamData(value, data, settings) } + .combine(context.streamSettings(karooSystem)) { (value, data), settings -> StreamData(value, data, settings) } .onCompletion { // Clear view on completion val result = glance.compose(context, DpSize.Unspecified) { } @@ -83,7 +83,7 @@ class HeadwindDirectionDataType( val windSpeed = streamData.data.current.windSpeed val windDirection = streamData.value val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed - val windSpeedText = "${headwindSpeed.roundToInt()}" + val windSpeedText = headwindSpeed.roundToInt().toString() val result = glance.compose(context, DpSize.Unspecified) { HeadwindDirection(baseBitmap, windDirection.roundToInt(), config.textSize, windSpeedText) diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindSpeedDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindSpeedDataType.kt index d1ce203..2af4a6e 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindSpeedDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindSpeedDataType.kt @@ -28,7 +28,7 @@ class HeadwindSpeedDataType( val job = CoroutineScope(Dispatchers.IO).launch { karooSystem.getRelativeHeadingFlow(context) .combine(context.streamCurrentWeatherData()) { value, data -> value to data } - .combine(context.streamSettings()) { (value, data), settings -> StreamData(value, data, settings) } + .combine(context.streamSettings(karooSystem)) { (value, data), settings -> StreamData(value, data, settings) } .collect { streamData -> val windSpeed = streamData.data.current.windSpeed val windDirection = streamData.value diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt index 82314dd..673c0b0 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt @@ -68,7 +68,7 @@ class WeatherDataType( val viewJob = CoroutineScope(Dispatchers.IO).launch { context.streamCurrentWeatherData() - .combine(context.streamSettings()) { data, settings -> StreamData(data, settings) } + .combine(context.streamSettings(karooSystem)) { data, settings -> StreamData(data, settings) } .onCompletion { // Clear view on completion val result = glance.compose(context, DpSize.Unspecified) { } diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/screens/Dropdown.kt b/app/src/main/kotlin/de/timklge/karooheadwind/screens/Dropdown.kt index ff01152..7dbe519 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/screens/Dropdown.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/screens/Dropdown.kt @@ -17,14 +17,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier - data class DropdownOption(val id: String, val name: String) @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Dropdown(label: String, options: List, initialSelection: DropdownOption, onSelect: (selectedOption: DropdownOption) -> Unit) { +fun Dropdown(label: String, options: List, selected: DropdownOption, onSelect: (selectedOption: DropdownOption) -> Unit) { var expanded by remember { mutableStateOf(false) } - var selected by remember { mutableStateOf(initialSelection) } ExposedDropdownMenuBox( expanded = expanded, @@ -51,9 +49,8 @@ fun Dropdown(label: String, options: List, initialSelection: Dro DropdownMenuItem( text = { Text(option.name, style = MaterialTheme.typography.bodyLarge) }, onClick = { - selected = option expanded = false - onSelect(selected) + onSelect(option) }, contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, ) diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt b/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt index 66b03a7..2af2d3a 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt @@ -100,7 +100,7 @@ fun MainScreen() { var savedDialogVisible by remember { mutableStateOf(false) } LaunchedEffect(Unit) { - ctx.streamSettings().collect { settings -> + ctx.streamSettings(karooSystem).collect { settings -> selectedWindUnit = settings.windUnit selectedPrecipitationUnit = settings.precipitationUnit welcomeDialogVisible = !settings.welcomeDialogAccepted @@ -123,14 +123,18 @@ fun MainScreen() { .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) { val windSpeedUnitDropdownOptions = WindUnit.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val windSpeedUnitInitialSelection = windSpeedUnitDropdownOptions.find { option -> option.id == selectedWindUnit.id }!! - Dropdown(label = "Wind Speed Unit", options = windSpeedUnitDropdownOptions, initialSelection = windSpeedUnitInitialSelection) { selectedOption -> + val windSpeedUnitInitialSelection by remember(selectedWindUnit) { + mutableStateOf(windSpeedUnitDropdownOptions.find { option -> option.id == selectedWindUnit.id }!!) + } + Dropdown(label = "Wind Speed Unit", options = windSpeedUnitDropdownOptions, selected = windSpeedUnitInitialSelection) { selectedOption -> selectedWindUnit = WindUnit.entries.find { unit -> unit.id == selectedOption.id }!! } val precipitationUnitDropdownOptions = PrecipitationUnit.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val precipitationUnitInitialSelection = precipitationUnitDropdownOptions.find { option -> option.id == selectedPrecipitationUnit.id }!! - Dropdown(label = "Precipitation Unit", options = precipitationUnitDropdownOptions, initialSelection = precipitationUnitInitialSelection) { selectedOption -> + val precipitationUnitInitialSelection by remember(selectedPrecipitationUnit) { + mutableStateOf(precipitationUnitDropdownOptions.find { option -> option.id == selectedPrecipitationUnit.id }!!) + } + Dropdown(label = "Precipitation Unit", options = precipitationUnitDropdownOptions, selected = precipitationUnitInitialSelection) { selectedOption -> selectedPrecipitationUnit = PrecipitationUnit.entries.find { unit -> unit.id == selectedOption.id }!! }