diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt b/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt index 32f8c3d..1affac7 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/Extensions.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.single import kotlinx.coroutines.flow.timeout import kotlinx.coroutines.time.debounce @@ -167,24 +168,36 @@ fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow { return getHeadingFlow() .filter { it >= 0 } - .combine(currentWeatherData) { cardinalDirection, data -> cardinalDirection to data } - .map { (cardinalDirection, data) -> - val bearing = cardinalDirection * 45.0 + .combine(currentWeatherData) { bearing, data -> bearing to data } + .map { (bearing, data) -> val windBearing = data.current.windDirection + 180 - val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360 + val diff = signedAngleDifference(bearing, windBearing) Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff") diff } } -fun KarooSystemService.getHeadingFlow(): Flow { - // return flowOf(2) +fun Double.lerp(target: Double, alpha: Double): Double { + return this + (target - this) * alpha +} - return streamDataFlow(DataType.Type.HEADING) - .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } - .map { it.roundToInt() } +fun KarooSystemService.getHeadingFlow(): Flow { + //return flowOf(20.0) + + return streamDataFlow("TYPE_LOCATION_ID") + .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.values } + .map { values -> + val heading = values[DataType.Field.LOC_BEARING] + + heading ?: 0.0 + } .distinctUntilChanged() + .scan(emptyList()) { acc, value -> + val newAcc = acc + value + if (newAcc.size > 3) newAcc.drop(1) else newAcc + } + .map { it.average() } } @OptIn(FlowPreview::class) 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 d9b303e..75c6543 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt @@ -7,7 +7,6 @@ import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi import androidx.glance.appwidget.GlanceRemoteViews import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse -import de.timklge.karooheadwind.getHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.screens.HeadwindSettings import de.timklge.karooheadwind.streamCurrentWeatherData 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 791c6a6..2840757 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionView.kt @@ -1,7 +1,15 @@ package de.timklge.karooheadwind.datatypes +import android.R +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp @@ -9,6 +17,7 @@ import androidx.glance.ColorFilter import androidx.glance.GlanceModifier import androidx.glance.Image import androidx.glance.ImageProvider +import androidx.glance.LocalContext import androidx.glance.background import androidx.glance.color.ColorProvider import androidx.glance.layout.Alignment @@ -20,27 +29,45 @@ import androidx.glance.preview.ExperimentalGlancePreviewApi import androidx.glance.preview.Preview import androidx.glance.text.Text import androidx.glance.text.TextStyle -import de.timklge.karooheadwind.R import kotlin.math.roundToInt -fun getArrowResourceByBearing(bearing: Int): Int { - val oclock = ((bearing % 360) / 30.0).roundToInt() - return when (oclock){ - 0 -> R.drawable.arrow_0 - 1 -> R.drawable.arrow_1 - 2 -> R.drawable.arrow_2 - 3 -> R.drawable.arrow_3 - 4 -> R.drawable.arrow_4 - 5 -> R.drawable.arrow_5 - 6 -> R.drawable.arrow_6 - 7 -> R.drawable.arrow_7 - 8 -> R.drawable.arrow_8 - 9 -> R.drawable.arrow_9 - 10 -> R.drawable.arrow_10 - 11 -> R.drawable.arrow_11 - 12 -> R.drawable.arrow_0 - else -> error("Bearing $bearing out of range") +val bitmapsByBearing = mutableMapOf() + +fun getArrowBitmapByBearing(bearing: Int): Bitmap { + synchronized(bitmapsByBearing) { + val bearingRounded = (((bearing + 360) / 5.0).roundToInt() * 5) % 360 + + val storedBitmap = bitmapsByBearing[bearingRounded] + if (storedBitmap != null) return storedBitmap + + val bitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + + val paint = Paint().apply { + color = android.graphics.Color.WHITE + style = Paint.Style.STROKE + strokeWidth = 15f + isAntiAlias = true + } + + val path = Path().apply { + moveTo(64f, 0f) // Top point of the arrow + lineTo(128f, 128f) // Bottom right point of the arrow + lineTo(64f, 96f) // Middle bottom point of the arrow + lineTo(0f, 128f) // Bottom left point of the arrow + close() // Close the path to form the arrow shape + } + + canvas.save() + canvas.rotate(bearing.toFloat(), 64f, 64f) // Rotate the canvas based on the bearing + canvas.scale(0.75f, 0.75f, 64f, 64f) // Scale the arrow down to fit the canvas + canvas.drawPath(path, paint) + canvas.restore() + + bitmapsByBearing[bearingRounded] = bitmap + + return bitmap } } @@ -57,7 +84,7 @@ fun HeadwindDirection(bearing: Int, fontSize: Int, overlayText: String? = null) ) { Image( modifier = GlanceModifier.fillMaxSize(), - provider = ImageProvider(getArrowResourceByBearing(bearing)), + provider = ImageProvider(getArrowBitmapByBearing(bearing)), contentDescription = "Relative wind direction indicator", contentScale = ContentScale.Fit, colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White)) 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 33c0fe1..84c5626 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherDataType.kt @@ -8,7 +8,6 @@ import androidx.glance.appwidget.GlanceRemoteViews import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.WeatherInterpretation -import de.timklge.karooheadwind.getHeadingFlow import de.timklge.karooheadwind.screens.HeadwindSettings import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamSettings @@ -25,7 +24,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.launch import kotlin.math.roundToInt diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherView.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherView.kt index b261fff..01306fb 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherView.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/WeatherView.kt @@ -9,6 +9,7 @@ import androidx.glance.ColorFilter import androidx.glance.GlanceModifier import androidx.glance.Image import androidx.glance.ImageProvider +import androidx.glance.LocalContext import androidx.glance.color.ColorProvider import androidx.glance.layout.Alignment import androidx.glance.layout.Column @@ -57,7 +58,7 @@ fun Weather(current: WeatherInterpretation, windBearing: Int, windSpeed: Int, wi Row(horizontalAlignment = Alignment.CenterHorizontally, verticalAlignment = Alignment.CenterVertically) { Image( modifier = GlanceModifier.height(20.dp).width(12.dp), - provider = ImageProvider(getArrowResourceByBearing(windBearing)), + provider = ImageProvider(getArrowBitmapByBearing(windBearing)), contentDescription = "Current wind direction", contentScale = ContentScale.Fit, colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White)) diff --git a/app/src/main/res/drawable/arrow_0.png b/app/src/main/res/drawable/arrow_0.png deleted file mode 100644 index a2013b4..0000000 Binary files a/app/src/main/res/drawable/arrow_0.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_1.png b/app/src/main/res/drawable/arrow_1.png deleted file mode 100644 index d1c41dc..0000000 Binary files a/app/src/main/res/drawable/arrow_1.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_10.png b/app/src/main/res/drawable/arrow_10.png deleted file mode 100644 index 89a020f..0000000 Binary files a/app/src/main/res/drawable/arrow_10.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_11.png b/app/src/main/res/drawable/arrow_11.png deleted file mode 100644 index 7bc493c..0000000 Binary files a/app/src/main/res/drawable/arrow_11.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_2.png b/app/src/main/res/drawable/arrow_2.png deleted file mode 100644 index e358916..0000000 Binary files a/app/src/main/res/drawable/arrow_2.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_3.png b/app/src/main/res/drawable/arrow_3.png deleted file mode 100644 index 28249a9..0000000 Binary files a/app/src/main/res/drawable/arrow_3.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_4.png b/app/src/main/res/drawable/arrow_4.png deleted file mode 100644 index 30488ce..0000000 Binary files a/app/src/main/res/drawable/arrow_4.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_5.png b/app/src/main/res/drawable/arrow_5.png deleted file mode 100644 index d0c4b85..0000000 Binary files a/app/src/main/res/drawable/arrow_5.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_6.png b/app/src/main/res/drawable/arrow_6.png deleted file mode 100644 index f61ea2f..0000000 Binary files a/app/src/main/res/drawable/arrow_6.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_7.png b/app/src/main/res/drawable/arrow_7.png deleted file mode 100644 index f0e841c..0000000 Binary files a/app/src/main/res/drawable/arrow_7.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_8.png b/app/src/main/res/drawable/arrow_8.png deleted file mode 100644 index fdfc94a..0000000 Binary files a/app/src/main/res/drawable/arrow_8.png and /dev/null differ diff --git a/app/src/main/res/drawable/arrow_9.png b/app/src/main/res/drawable/arrow_9.png deleted file mode 100644 index a383c2f..0000000 Binary files a/app/src/main/res/drawable/arrow_9.png and /dev/null differ