diff --git a/README.md b/README.md index 3a51def..ec3191e 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ If you are using a Karoo 2, you can use manual sideloading: After installing this app on your Karoo and opening it once from the main menu, you can add the following new data fields to your data pages: -- Headwind (graphical, 1x1 field): Shows the headwind direction and speed as a circle with a triangular direction indicator. The speed is shown at the center in your set unit of measurement (default is kilometers per hour if you have set up metric units in your Karoo, otherwise miles per hour). Both direction and speed are relative to the current riding direction by default, i. e., riding directly into a wind of 20 km/h will show a headwind speed of 20 km/h, while riding in the same direction will show -20 km/h. You can change this behavior in the app settings to show the absolute wind speed instead. +- Headwind (graphical, 1x1 field): Shows the headwind direction and speed as a circle with a triangular direction indicator. The speed is shown at the center in your set unit of measurement (default is kilometers per hour if you have set up metric units in your Karoo, otherwise miles per hour). Both direction and speed are relative to the current riding direction by default, i. e., riding directly into a wind of 20 km/h will show a headwind speed of 20 km/h, while riding in the same direction will show -20 km/h. You can change this behavior in the app settings to show the absolute wind direction and speed instead. - Weather forecast (graphical, 2x1 field): Shows three columns indicating the current weather conditions (sunny, cloudy, ...), wind direction, precipitation and temperature forecasted for the next three hours. Tap on this widget to cycle through the 12 hour forecast. - Additionally, data fields that only show the current data value for headwind speed, humidity, cloud cover, absolute wind speed, absolute wind gust speed, absolute wind direction, rainfall and surface pressure can be added if desired. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1423e34..120f0fb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,8 +15,8 @@ android { applicationId = "de.timklge.karooheadwind" minSdk = 26 targetSdk = 35 - versionCode = 5 - versionName = "1.1.1" + versionCode = 6 + versionName = "1.1.2" } signingConfigs { diff --git a/app/manifest.json b/app/manifest.json index 5a03b35..2def8fe 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -3,9 +3,9 @@ "packageName": "de.timklge.karooheadwind", "iconUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/karoo-headwind.png", "latestApkUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/app-release.apk", - "latestVersion": "1.1.1", - "latestVersionCode": 5, + "latestVersion": "1.1.2", + "latestVersionCode": 6, "developer": "timklge", "description": "Provides headwind direction, wind speed and other weather data fields", - "releaseNotes": "Add hourly forecast and temperature datafields" + "releaseNotes": "Add hourly forecast and temperature datafields. Add setting to use absolute wind direction on headwind datafield." } \ No newline at end of file diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt b/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt index d289248..b37a53e 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/KarooHeadwindExtension.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes -class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.1") { +class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.2") { companion object { const val TAG = "karoo-headwind" } 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 317cd40..3482a6c 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt @@ -9,6 +9,7 @@ import androidx.glance.appwidget.GlanceRemoteViews import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.screens.HeadwindSettings +import de.timklge.karooheadwind.screens.WindDirectionIndicatorSetting import de.timklge.karooheadwind.screens.WindDirectionIndicatorTextSetting import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamDataFlow @@ -54,7 +55,7 @@ class HeadwindDirectionDataType( } } - data class StreamData(val value: Double, val windSpeed: Double, val settings: HeadwindSettings) + data class StreamData(val value: Double, val absoluteWindDirection: Double, val windSpeed: Double, val settings: HeadwindSettings) private fun previewFlow(): Flow { return flow { @@ -62,7 +63,7 @@ class HeadwindDirectionDataType( val bearing = (0..360).random().toDouble() val windSpeed = (-20..20).random() - emit(StreamData(bearing, windSpeed.toDouble(), HeadwindSettings())) + emit(StreamData(bearing, bearing, windSpeed.toDouble(), HeadwindSettings())) delay(2_000) } } @@ -89,7 +90,7 @@ class HeadwindDirectionDataType( .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .combine(context.streamCurrentWeatherData()) { value, data -> value to data } .combine(context.streamSettings(karooSystem)) { (value, data), settings -> - StreamData(value, data.current.windSpeed, settings) + StreamData(value, data.current.windDirection, data.current.windSpeed, settings) } } @@ -102,7 +103,10 @@ class HeadwindDirectionDataType( .collect { streamData -> Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view") val windSpeed = streamData.windSpeed - val windDirection = streamData.value + val windDirection = when (streamData.settings.windDirectionIndicatorSetting){ + WindDirectionIndicatorSetting.HEADWIND_DIRECTION -> streamData.value + WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180 + } val text = when (streamData.settings.windDirectionIndicatorTextSetting) { WindDirectionIndicatorTextSetting.HEADWIND_SPEED -> { val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt index 31cc88d..209e519 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt @@ -88,11 +88,11 @@ fun HeadwindDirection(baseBitmap: Bitmap, bearing: Int, fontSize: Int, overlayTe ) if (overlayText.isNotEmpty()){ - Text( - overlayText, - style = TextStyle(ColorProvider(Color.Black, Color.White), fontSize = (0.6 * fontSize).sp, fontFamily = FontFamily.Monospace), - modifier = GlanceModifier.background(Color(1f, 1f, 1f, 0.4f), Color(0f, 0f, 0f, 0.4f)).padding(1.dp) - ) + Text( + overlayText, + style = TextStyle(ColorProvider(Color.Black, Color.White), fontSize = (0.6 * fontSize).sp, fontFamily = FontFamily.Monospace), + modifier = GlanceModifier.background(Color(1f, 1f, 1f, 0.4f), Color(0f, 0f, 0f, 0.4f)).padding(1.dp) + ) } } } \ No newline at end of file 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 06d71d9..706a194 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/screens/MainScreen.kt @@ -66,6 +66,11 @@ enum class WindDirectionIndicatorTextSetting(val id: String, val label: String){ NONE("none", "None") } +enum class WindDirectionIndicatorSetting(val id: String, val label: String){ + HEADWIND_DIRECTION("headwind-direction", "Headwind"), + WIND_DIRECTION("wind-direction", "Absolute wind direction"), +} + enum class TemperatureUnit(val id: String, val label: String, val unitDisplay: String){ CELSIUS("celsius", "Celsius (°C)", "°C"), FAHRENHEIT("fahrenheit", "Fahrenheit (°F)", "°F") @@ -82,6 +87,7 @@ data class HeadwindSettings( val windUnit: WindUnit = WindUnit.KILOMETERS_PER_HOUR, val welcomeDialogAccepted: Boolean = false, val windDirectionIndicatorTextSetting: WindDirectionIndicatorTextSetting = WindDirectionIndicatorTextSetting.HEADWIND_SPEED, + val windDirectionIndicatorSetting: WindDirectionIndicatorSetting = WindDirectionIndicatorSetting.HEADWIND_DIRECTION, val roundLocationTo: RoundLocationSetting = RoundLocationSetting.KM_2 ){ companion object { @@ -120,6 +126,7 @@ fun MainScreen() { var selectedWindUnit by remember { mutableStateOf(WindUnit.KILOMETERS_PER_HOUR) } var welcomeDialogVisible by remember { mutableStateOf(false) } var selectedWindDirectionIndicatorTextSetting by remember { mutableStateOf(WindDirectionIndicatorTextSetting.HEADWIND_SPEED) } + var selectedWindDirectionIndicatorSetting by remember { mutableStateOf(WindDirectionIndicatorSetting.HEADWIND_DIRECTION) } var selectedRoundLocationSetting by remember { mutableStateOf(RoundLocationSetting.KM_2) } val stats by ctx.streamStats().collectAsState(HeadwindStats()) @@ -132,6 +139,7 @@ fun MainScreen() { selectedWindUnit = settings.windUnit welcomeDialogVisible = !settings.welcomeDialogAccepted selectedWindDirectionIndicatorTextSetting = settings.windDirectionIndicatorTextSetting + selectedWindDirectionIndicatorSetting = settings.windDirectionIndicatorSetting selectedRoundLocationSetting = settings.roundLocationTo } } @@ -151,6 +159,14 @@ fun MainScreen() { .verticalScroll(rememberScrollState()) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) { + val windDirectionIndicatorSettingDropdownOptions = WindDirectionIndicatorSetting.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val windDirectionIndicatorSettingSelection by remember(selectedWindDirectionIndicatorSetting) { + mutableStateOf(windDirectionIndicatorSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorSetting.id }!!) + } + Dropdown(label = "Wind direction indicator", options = windDirectionIndicatorSettingDropdownOptions, selected = windDirectionIndicatorSettingSelection) { selectedOption -> + selectedWindDirectionIndicatorSetting = WindDirectionIndicatorSetting.entries.find { unit -> unit.id == selectedOption.id }!! + } + val windDirectionIndicatorTextSettingDropdownOptions = WindDirectionIndicatorTextSetting.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } val windDirectionIndicatorTextSettingSelection by remember(selectedWindDirectionIndicatorTextSetting) { mutableStateOf(windDirectionIndicatorTextSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorTextSetting.id }!!) @@ -180,7 +196,9 @@ fun MainScreen() { .fillMaxWidth() .height(50.dp), onClick = { val newSettings = HeadwindSettings(windUnit = selectedWindUnit, - welcomeDialogAccepted = true, windDirectionIndicatorTextSetting = selectedWindDirectionIndicatorTextSetting, + welcomeDialogAccepted = true, + windDirectionIndicatorSetting = selectedWindDirectionIndicatorSetting, + windDirectionIndicatorTextSetting = selectedWindDirectionIndicatorTextSetting, roundLocationTo = selectedRoundLocationSetting) coroutineScope.launch { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 817ac64..f180113 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,7 @@ Headwind headwind Headwind - Current headwind direction + Current headwind direction and speed Humidity Relative humidity in percent Cloud cover