ref #35: Set headwind data type field to direction according to user setting, add userwind data type (#36)

* ref #35: Set headwind data type fields to values according to user settings

* Use single datafield per type, add userwindSpeed data type to expose (head-)wind speed depending on user setting

* Enable minification for release builds

* Fix signed angle difference in headwind view

* Remove bitmap cache

* Format
This commit is contained in:
timklge 2025-02-08 14:28:30 +01:00 committed by GitHub
parent 0869121176
commit 01959ce3b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 233 additions and 89 deletions

View File

@ -45,3 +45,14 @@ The app will automatically attempt to download weather data for your current app
- Icons are from [boxicons.com](https://boxicons.com) ([MIT-licensed](icon_credits.txt)) - Icons are from [boxicons.com](https://boxicons.com) ([MIT-licensed](icon_credits.txt))
- Made possible by the generous usage terms of [open-meteo.com](https://open-meteo.com) - Made possible by the generous usage terms of [open-meteo.com](https://open-meteo.com)
- Uses [karoo-ext](https://github.com/hammerheadnav/karoo-ext) (Apache2-licensed) - Uses [karoo-ext](https://github.com/hammerheadnav/karoo-ext) (Apache2-licensed)
## Extension Developers: Headwind Data Type
If the user has installed the headwind extension on his karoo, you can stream the headwind data type from other extensions via `karoo-ext`.
Use extension id `karoo-headwind` with datatype ids `headwind` and `userwindSpeed`.
- The `headwind` datatype contains a single field that either represents an error code or the wind direction. A `-1.0` indicates missing gps receiption, `-2.0` no weather data, `-3.0` that the headwind extension
has not been set up. Otherwise, the value is the wind direction in degrees; if the user has set the headwind indicator to depict the absolute wind direction, the field will contain the absolute wind direction; otherwise
it will contain the headwind direction.
- The `userwindSpeed` datatype contains a single field with the wind speed in the user's defined unit. If the user has set the headwind indicator to show the absolute wind speed,
this field will contain the absolute wind speed; otherwise it will contain the headwind speed.

View File

@ -15,8 +15,8 @@ android {
applicationId = "de.timklge.karooheadwind" applicationId = "de.timklge.karooheadwind"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 10 versionCode = 11
versionName = "1.2.2" versionName = "1.2.3"
} }
signingConfigs { signingConfigs {
@ -38,7 +38,7 @@ android {
} }
release { release {
signingConfig = signingConfigs.getByName("release") signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = false isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
} }
} }

View File

@ -3,9 +3,9 @@
"packageName": "de.timklge.karooheadwind", "packageName": "de.timklge.karooheadwind",
"iconUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/karoo-headwind.png", "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", "latestApkUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/app-release.apk",
"latestVersion": "1.2.2", "latestVersion": "1.2.3",
"latestVersionCode": 10, "latestVersionCode": 11,
"developer": "timklge", "developer": "timklge",
"description": "Provides headwind direction, wind speed and other weather data fields", "description": "Provides headwind direction, wind speed and other weather data fields",
"releaseNotes": "Reduce font size in tailwind indicators, update gps bearing retrieval" "releaseNotes": "Update gps bearing retrieval, data type exposure for other extensions, optimize build"
} }

View File

@ -4,8 +4,6 @@ import android.content.Context
import android.util.Log import android.util.Log
import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.StreamState
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -15,7 +13,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.scan
sealed class HeadingResponse { sealed class HeadingResponse {
@ -35,7 +32,7 @@ fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow<HeadingRes
val windBearing = data.current.windDirection + 180 val windBearing = data.current.windDirection + 180
val diff = signedAngleDifference(bearing.diff, windBearing) val diff = signedAngleDifference(bearing.diff, windBearing)
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff") Log.d(KarooHeadwindExtension.TAG, "Wind bearing: Heading $bearing vs wind $windBearing => $diff")
HeadingResponse.Value(diff) HeadingResponse.Value(diff)
} }

View File

@ -12,6 +12,7 @@ import de.timklge.karooheadwind.datatypes.HeadwindSpeedDataType
import de.timklge.karooheadwind.datatypes.TailwindAndRideSpeedDataType import de.timklge.karooheadwind.datatypes.TailwindAndRideSpeedDataType
import de.timklge.karooheadwind.datatypes.HeadwindDirectionDataType import de.timklge.karooheadwind.datatypes.HeadwindDirectionDataType
import de.timklge.karooheadwind.datatypes.TemperatureDataType import de.timklge.karooheadwind.datatypes.TemperatureDataType
import de.timklge.karooheadwind.datatypes.UserWindSpeedDataType
import de.timklge.karooheadwind.datatypes.WeatherDataType import de.timklge.karooheadwind.datatypes.WeatherDataType
import de.timklge.karooheadwind.datatypes.WeatherForecastDataType import de.timklge.karooheadwind.datatypes.WeatherForecastDataType
import de.timklge.karooheadwind.datatypes.WindSpeedDataType import de.timklge.karooheadwind.datatypes.WindSpeedDataType
@ -41,12 +42,11 @@ import kotlin.math.absoluteValue
import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.2.2") { class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.2.3") {
companion object { companion object {
const val TAG = "karoo-headwind" const val TAG = "karoo-headwind"
} }
lateinit var karooSystem: KarooSystemService lateinit var karooSystem: KarooSystemService
private var updateLastKnownGpsJob: Job? = null private var updateLastKnownGpsJob: Job? = null
@ -67,7 +67,8 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.2.2") {
TemperatureDataType(applicationContext), TemperatureDataType(applicationContext),
WindDirectionDataType(karooSystem, applicationContext), WindDirectionDataType(karooSystem, applicationContext),
PrecipitationDataType(applicationContext), PrecipitationDataType(applicationContext),
SurfacePressureDataType(applicationContext) SurfacePressureDataType(applicationContext),
UserWindSpeedDataType(karooSystem, applicationContext)
) )
} }

View File

@ -11,8 +11,8 @@ import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.screens.HeadwindSettings import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karooheadwind.screens.WindDirectionIndicatorSetting import de.timklge.karooheadwind.screens.WindDirectionIndicatorSetting
import de.timklge.karooheadwind.screens.WindDirectionIndicatorTextSetting
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
@ -30,8 +30,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.cos
import kotlin.math.roundToInt import kotlin.math.roundToInt
@OptIn(ExperimentalGlanceRemoteViewsApi::class) @OptIn(ExperimentalGlanceRemoteViewsApi::class)
@ -44,9 +45,41 @@ class HeadwindDirectionDataType(
override fun startStream(emitter: Emitter<StreamState>) { override fun startStream(emitter: Emitter<StreamState>) {
val job = CoroutineScope(Dispatchers.IO).launch { val job = CoroutineScope(Dispatchers.IO).launch {
karooSystem.getRelativeHeadingFlow(applicationContext) karooSystem.getRelativeHeadingFlow(applicationContext)
.collect { diff -> .combine(applicationContext.streamCurrentWeatherData()) { headingResponse, data -> StreamData(headingResponse, data?.current?.windDirection, data?.current?.windSpeed) }
val value = (diff as? HeadingResponse.Value)?.diff ?: 0.0 .combine(applicationContext.streamSettings(karooSystem)) { data, settings -> data.copy(settings = settings) }
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value)))) .collect { streamData ->
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
var returnValue = 0.0
if (value == null || streamData.absoluteWindDirection == null || streamData.settings == null || streamData.windSpeed == null){
var errorCode = 1.0
var headingResponse = streamData.headingResponse
if (headingResponse is HeadingResponse.Value && (streamData.absoluteWindDirection == null || streamData.windSpeed == null)){
headingResponse = HeadingResponse.NoWeatherData
}
if (streamData.settings?.welcomeDialogAccepted == false){
errorCode = ERROR_APP_NOT_SET_UP.toDouble()
} else if (headingResponse is HeadingResponse.NoGps){
errorCode = ERROR_NO_GPS.toDouble()
} else {
errorCode = ERROR_NO_WEATHER_DATA.toDouble()
}
returnValue = errorCode
} else {
var windDirection = when (streamData.settings.windDirectionIndicatorSetting){
WindDirectionIndicatorSetting.HEADWIND_DIRECTION -> value
WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180
}
if (windDirection < 0) windDirection += 360
returnValue = windDirection
}
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to returnValue))))
} }
} }
emitter.setCancellable { emitter.setCancellable {
@ -56,13 +89,16 @@ class HeadwindDirectionDataType(
data class StreamData(val headingResponse: HeadingResponse?, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings? = null) data class StreamData(val headingResponse: HeadingResponse?, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings? = null)
private fun previewFlow(): Flow<StreamData> { data class DirectionAndSpeed(val bearing: Double, val speed: Double?)
private fun previewFlow(): Flow<DirectionAndSpeed> {
return flow { return flow {
while (true) { while (true) {
val bearing = (0..360).random().toDouble() val bearing = (0..360).random().toDouble()
val windSpeed = (0..20).random() val windSpeed = (0..20).random()
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings())) emit(DirectionAndSpeed(bearing, windSpeed.toDouble()))
delay(2_000) delay(2_000)
} }
} }
@ -85,44 +121,34 @@ class HeadwindDirectionDataType(
val flow = if (config.preview) { val flow = if (config.preview) {
previewFlow() previewFlow()
} else { } else {
karooSystem.getRelativeHeadingFlow(context) val directionFlow = karooSystem.streamDataFlow(dataTypeId).mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
.combine(context.streamCurrentWeatherData()) { headingResponse, data -> StreamData(headingResponse, data?.current?.windDirection, data?.current?.windSpeed) } val speedFlow = karooSystem.streamDataFlow(DataType.dataTypeId("karoo-headwind", "userwindSpeed")).map { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
.combine(context.streamSettings(karooSystem)) { data, settings -> data.copy(settings = settings) }
combine(directionFlow, speedFlow) { direction, speed ->
DirectionAndSpeed(direction, speed)
}
} }
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
flow.collect { streamData -> flow.collect { streamData ->
Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view") Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null }
if (value == null || streamData.absoluteWindDirection == null || streamData.settings == null || streamData.windSpeed == null){ if (errorCode != null) {
var headingResponse = streamData.headingResponse emitter.updateView(getErrorWidget(glance, context, errorCode).remoteViews)
if (headingResponse is HeadingResponse.Value && (streamData.absoluteWindDirection == null || streamData.windSpeed == null)){
headingResponse = HeadingResponse.NoWeatherData
}
emitter.updateView(getErrorWidget(glance, context, streamData.settings, headingResponse).remoteViews)
return@collect return@collect
} }
val windSpeed = streamData.windSpeed val windDirection = streamData.bearing
val windDirection = when (streamData.settings.windDirectionIndicatorSetting){ val windSpeed = streamData.speed
WindDirectionIndicatorSetting.HEADWIND_DIRECTION -> 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
headwindSpeed.roundToInt().toString()
}
WindDirectionIndicatorTextSetting.WIND_SPEED -> windSpeed.roundToInt().toString()
WindDirectionIndicatorTextSetting.NONE -> ""
}
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
HeadwindDirection(baseBitmap, windDirection.roundToInt(), config.textSize, text, viewSize = config.viewSize) HeadwindDirection(
baseBitmap,
windDirection.roundToInt(),
config.textSize,
windSpeed?.toInt()?.toString() ?: ""
)
} }
emitter.updateView(result.remoteViews) emitter.updateView(result.remoteViews)
@ -134,4 +160,10 @@ class HeadwindDirectionDataType(
viewJob.cancel() viewJob.cancel()
} }
} }
companion object {
const val ERROR_NO_GPS = -1
const val ERROR_NO_WEATHER_DATA = -2
const val ERROR_APP_NOT_SET_UP = -3
}
} }

View File

@ -22,7 +22,6 @@ import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding import androidx.glance.layout.padding
import androidx.glance.layout.size import androidx.glance.layout.size
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.text.FontFamily import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight import androidx.glance.text.FontWeight
import androidx.glance.text.Text import androidx.glance.text.Text
@ -30,47 +29,34 @@ import androidx.glance.text.TextStyle
import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import kotlin.math.roundToInt import kotlin.math.roundToInt
data class BitmapWithBearing(val bitmap: Bitmap, val bearing: Int)
val bitmapsByBearing = mutableMapOf<BitmapWithBearing, Bitmap>()
fun getArrowBitmapByBearing(baseBitmap: Bitmap, bearing: Int): Bitmap { fun getArrowBitmapByBearing(baseBitmap: Bitmap, bearing: Int): Bitmap {
synchronized(bitmapsByBearing) {
val bearingRounded = (((bearing + 360) / 10.0).roundToInt() * 10) % 360 val bearingRounded = (((bearing + 360) / 10.0).roundToInt() * 10) % 360
val bitmapWithBearing = BitmapWithBearing(baseBitmap, bearingRounded)
val storedBitmap = bitmapsByBearing[bitmapWithBearing]
if (storedBitmap != null) return storedBitmap
val bitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.ARGB_8888) val bitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap) val canvas = Canvas(bitmap)
val paint = Paint().apply { val paint = Paint().apply {
color = android.graphics.Color.BLACK color = android.graphics.Color.BLACK
style = Paint.Style.STROKE style = Paint.Style.STROKE
// strokeWidth = 15f
isAntiAlias = true isAntiAlias = true
} }
canvas.save() canvas.save()
canvas.scale((bitmap.width / baseBitmap.width.toFloat()), (bitmap.height / baseBitmap.height.toFloat()), (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat()) canvas.scale((bitmap.width / baseBitmap.width.toFloat()), (bitmap.height / baseBitmap.height.toFloat()), (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat())
Log.d(KarooHeadwindExtension.TAG, "Drawing arrow at $bearingRounded") Log.d(KarooHeadwindExtension.TAG, "Drawing arrow at $bearingRounded")
canvas.rotate(bearing.toFloat(), (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat()) canvas.rotate(bearingRounded.toFloat(), (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat())
canvas.drawBitmap(baseBitmap, ((bitmap.width - baseBitmap.width) / 2).toFloat(), ((bitmap.height - baseBitmap.height) / 2).toFloat(), paint) canvas.drawBitmap(baseBitmap, ((bitmap.width - baseBitmap.width) / 2).toFloat(), ((bitmap.height - baseBitmap.height) / 2).toFloat(), paint)
canvas.restore() canvas.restore()
bitmapsByBearing[bitmapWithBearing] = bitmap
return bitmap return bitmap
} }
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Composable @Composable
fun HeadwindDirection(baseBitmap: Bitmap, bearing: Int, fontSize: Int, fun HeadwindDirection(
baseBitmap: Bitmap, bearing: Int, fontSize: Int,
overlayText: String, overlaySubText: String? = null, overlayText: String, overlaySubText: String? = null,
dayColor: Color = Color.Black, nightColor: Color = Color.White, dayColor: Color = Color.Black, nightColor: Color = Color.White
viewSize: Pair<Int, Int>) { ) {
Box( Box(
modifier = GlanceModifier.fillMaxSize().padding(5.dp), modifier = GlanceModifier.fillMaxSize().padding(5.dp),
contentAlignment = Alignment( contentAlignment = Alignment(

View File

@ -46,3 +46,4 @@ class HeadwindSpeedDataType(
} }
} }
} }

View File

@ -199,8 +199,15 @@ class TailwindAndRideSpeedDataType(
} }
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
HeadwindDirection(baseBitmap, windDirection.roundToInt(), config.textSize, text, subtextWithSign, HeadwindDirection(
dayColor, nightColor, viewSize = config.viewSize) baseBitmap,
windDirection.roundToInt(),
config.textSize,
text,
subtextWithSign,
dayColor,
nightColor
)
} }
emitter.updateView(result.remoteViews) emitter.updateView(result.remoteViews)

View File

@ -0,0 +1,69 @@
package de.timklge.karooheadwind.datatypes
import android.content.Context
import de.timklge.karooheadwind.HeadingResponse
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karooheadwind.screens.WindDirectionIndicatorTextSetting
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamSettings
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter
import io.hammerhead.karooext.models.DataPoint
import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.StreamState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlin.math.cos
class UserWindSpeedDataType(
private val karooSystem: KarooSystemService,
private val context: Context
) : DataTypeImpl("karoo-headwind", "userwindSpeed"){
data class StreamData(val headingResponse: HeadingResponse, val weatherResponse: OpenMeteoCurrentWeatherResponse?, val settings: HeadwindSettings)
override fun startStream(emitter: Emitter<StreamState>) {
val job = CoroutineScope(Dispatchers.IO).launch {
karooSystem.getRelativeHeadingFlow(context)
.combine(context.streamCurrentWeatherData()) { value, data -> value to data }
.combine(context.streamSettings(karooSystem)) { (value, data), settings -> StreamData(value, data, settings) }
.filter { it.weatherResponse != null }
.collect { streamData ->
val windSpeed = streamData.weatherResponse?.current?.windSpeed ?: 0.0
val windDirection = (streamData.headingResponse as? HeadingResponse.Value)?.diff ?: 0.0
if (streamData.settings.windDirectionIndicatorTextSetting == WindDirectionIndicatorTextSetting.HEADWIND_SPEED){
val headwindSpeed = cos((windDirection + 180) * Math.PI / 180.0) * windSpeed
emitter.onNext(
StreamState.Streaming(
DataPoint(
dataTypeId,
mapOf(DataType.Field.SINGLE to headwindSpeed)
)
)
)
} else {
emitter.onNext(
StreamState.Streaming(
DataPoint(
dataTypeId,
mapOf(DataType.Field.SINGLE to windSpeed)
)
)
)
}
}
}
emitter.setCancellable {
job.cancel()
}
}
}

View File

@ -37,7 +37,38 @@ suspend fun getErrorWidget(glance: GlanceRemoteViews, context: Context, settings
Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage") Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage")
Text(text = errorMessage, style = TextStyle(fontSize = TextUnit(16f, TextUnitType.Sp), Text(text = errorMessage,
style = TextStyle(
fontSize = TextUnit(16f, TextUnitType.Sp),
textAlign = TextAlign.Center,
color = ColorProvider(Color.Black, Color.White)
)
)
}
}
}
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
suspend fun getErrorWidget(glance: GlanceRemoteViews, context: Context, errorCode: Int): RemoteViewsCompositionResult {
return glance.compose(context, DpSize.Unspecified) {
Box(modifier = GlanceModifier.fillMaxSize().padding(5.dp), contentAlignment = Alignment.Center) {
val errorMessage = when (errorCode) {
HeadwindDirectionDataType.ERROR_APP_NOT_SET_UP -> {
"Headwind app not set up"
}
HeadwindDirectionDataType.ERROR_NO_GPS -> {
"No GPS signal"
}
else -> {
"Weather data download failed"
}
}
Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage")
Text(text = errorMessage,
style = TextStyle(
fontSize = TextUnit(16f, TextUnitType.Sp),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = ColorProvider(Color.Black, Color.White) color = ColorProvider(Color.Black, Color.White)
) )

View File

@ -27,4 +27,6 @@
<string name="headwind_speed_description">Current headwind speed</string> <string name="headwind_speed_description">Current headwind speed</string>
<string name="temperature">Temperature</string> <string name="temperature">Temperature</string>
<string name="temperature_description">Current temperature in configured unit</string> <string name="temperature_description">Current temperature in configured unit</string>
<string name="userwind_speed_description">Current headwind or wind speed based on user setting</string>
<string name="userwind_speed">Set wind speed</string>
</resources> </resources>

View File

@ -15,7 +15,7 @@
description="@string/tailwind_and_speed_description" description="@string/tailwind_and_speed_description"
displayName="@string/tailwind_and_speed" displayName="@string/tailwind_and_speed"
graphical="true" graphical="true"
icon="@drawable/ic_launcher" icon="@drawable/wind"
typeId="tailwind-and-ride-speed" /> typeId="tailwind-and-ride-speed" />
<DataType <DataType
@ -39,6 +39,13 @@
icon="@drawable/wind" icon="@drawable/wind"
typeId="headwindSpeed" /> typeId="headwindSpeed" />
<DataType
description="@string/userwind_speed_description"
displayName="@string/userwind_speed"
graphical="false"
icon="@drawable/wind"
typeId="userwindSpeed" />
<DataType <DataType
description="@string/relativeHumidity_description" description="@string/relativeHumidity_description"
displayName="@string/relativeHumidity" displayName="@string/relativeHumidity"