fix #18: Add option for headwind widget to use absolute wind direction

This commit is contained in:
Tim Kluge 2024-12-20 17:23:20 +01:00
parent 72daf42938
commit b7000428ae
8 changed files with 40 additions and 18 deletions

View File

@ -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.

View File

@ -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 {

View File

@ -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."
}

View File

@ -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"
}

View File

@ -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<StreamData> {
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

View File

@ -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 {

View File

@ -2,7 +2,7 @@
<string name="app_name">Headwind</string>
<string name="extension_name">headwind</string>
<string name="headwind">Headwind</string>
<string name="headwind_description">Current headwind direction</string>
<string name="headwind_description">Current headwind direction and speed</string>
<string name="relativeHumidity">Humidity</string>
<string name="relativeHumidity_description">Relative humidity in percent</string>
<string name="cloudCover">Cloud cover</string>