Show no gps, no weather data error messages in visual data fields
This commit is contained in:
parent
543bcaf478
commit
407755e94b
@ -15,8 +15,8 @@ android {
|
||||
applicationId = "de.timklge.karooheadwind"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 6
|
||||
versionName = "1.1.2"
|
||||
versionCode = 7
|
||||
versionName = "1.1.3"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@ -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.2",
|
||||
"latestVersionCode": 6,
|
||||
"latestVersion": "1.1.3",
|
||||
"latestVersionCode": 7,
|
||||
"developer": "timklge",
|
||||
"description": "Provides headwind direction, wind speed and other weather data fields",
|
||||
"releaseNotes": "Add hourly forecast and temperature datafields. Add setting to use absolute wind direction on headwind datafield."
|
||||
"releaseNotes": "Add hourly forecast and temperature datafields. Show error message in fields if no weather data or gps is available."
|
||||
}
|
||||
@ -2,8 +2,25 @@ package de.timklge.karooheadwind
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi
|
||||
import androidx.glance.appwidget.GlanceRemoteViews
|
||||
import androidx.glance.appwidget.RemoteViewsCompositionResult
|
||||
import androidx.glance.color.ColorProvider
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextAlign
|
||||
import androidx.glance.text.TextStyle
|
||||
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.screens.HeadwindStats
|
||||
@ -28,6 +45,7 @@ import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
@ -86,7 +104,30 @@ fun KarooSystemService.streamDataFlow(dataTypeId: String): Flow<StreamState> {
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.streamCurrentWeatherData(): Flow<OpenMeteoCurrentWeatherResponse> {
|
||||
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
|
||||
suspend fun getErrorWidget(glance: GlanceRemoteViews, context: Context, settings: HeadwindSettings?, headingResponse: HeadingResponse?): RemoteViewsCompositionResult {
|
||||
return glance.compose(context, DpSize.Unspecified) {
|
||||
Box(modifier = GlanceModifier.fillMaxSize().padding(5.dp), contentAlignment = Alignment.Center) {
|
||||
val errorMessage = if (settings?.welcomeDialogAccepted == false) {
|
||||
"Headwind app not set up"
|
||||
} else if (headingResponse is HeadingResponse.NoGps){
|
||||
"No GPS signal"
|
||||
} else if (headingResponse is HeadingResponse.NoWeatherData) {
|
||||
"No weather data"
|
||||
} else {
|
||||
"Unknown error"
|
||||
}
|
||||
|
||||
Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage")
|
||||
|
||||
Text(text = errorMessage, style = TextStyle(fontSize = TextUnit(16f, TextUnitType.Sp),
|
||||
textAlign = TextAlign.Center,
|
||||
color = ColorProvider(Color.Black, Color.White)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.streamCurrentWeatherData(): Flow<OpenMeteoCurrentWeatherResponse?> {
|
||||
return dataStore.data.map { settingsJson ->
|
||||
try {
|
||||
val data = settingsJson[currentDataKey]
|
||||
@ -95,7 +136,13 @@ fun Context.streamCurrentWeatherData(): Flow<OpenMeteoCurrentWeatherResponse> {
|
||||
Log.e(KarooHeadwindExtension.TAG, "Failed to read weather data", e)
|
||||
null
|
||||
}
|
||||
}.filterNotNull().distinctUntilChanged().filter { it.current.time * 1000 >= System.currentTimeMillis() - (1000 * 60 * 60 * 12) }
|
||||
}.distinctUntilChanged().map { response ->
|
||||
if (response != null && response.current.time * 1000 >= System.currentTimeMillis() - (1000 * 60 * 60 * 12)){
|
||||
response
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.streamWidgetSettings(): Flow<HeadwindWidgetSettings> {
|
||||
@ -212,63 +259,95 @@ fun signedAngleDifference(angle1: Double, angle2: Double): Double {
|
||||
return sign * diff
|
||||
}
|
||||
|
||||
fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow<Double> {
|
||||
sealed class HeadingResponse {
|
||||
data object NoGps: HeadingResponse()
|
||||
data object NoWeatherData: HeadingResponse()
|
||||
data class Value(val diff: Double): HeadingResponse()
|
||||
}
|
||||
|
||||
fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow<HeadingResponse> {
|
||||
val currentWeatherData = context.streamCurrentWeatherData()
|
||||
|
||||
return getHeadingFlow()
|
||||
.filter { it >= 0 }
|
||||
.combine(currentWeatherData) { bearing, data -> bearing to data }
|
||||
.map { (bearing, data) ->
|
||||
val windBearing = data.current.windDirection + 180
|
||||
val diff = signedAngleDifference(bearing, windBearing)
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
|
||||
when {
|
||||
bearing is HeadingResponse.Value && data != null -> {
|
||||
val windBearing = data.current.windDirection + 180
|
||||
val diff = signedAngleDifference(bearing.diff, windBearing)
|
||||
|
||||
diff
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
|
||||
|
||||
HeadingResponse.Value(diff)
|
||||
}
|
||||
bearing is HeadingResponse.NoGps -> HeadingResponse.NoGps
|
||||
bearing is HeadingResponse.NoWeatherData || data == null -> HeadingResponse.NoWeatherData
|
||||
else -> bearing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KarooSystemService.getHeadingFlow(): Flow<Double> {
|
||||
// return flowOf(20.0)
|
||||
fun KarooSystemService.getHeadingFlow(): Flow<HeadingResponse> {
|
||||
// return flowOf(HeadingResponse.Value(20.0))
|
||||
|
||||
return streamDataFlow(DataType.Type.LOCATION)
|
||||
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||
.map { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||
.map { values ->
|
||||
val heading = values[DataType.Field.LOC_BEARING]
|
||||
val heading = values?.get(DataType.Field.LOC_BEARING)
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updated gps bearing: $heading")
|
||||
heading ?: 0.0
|
||||
val headingValue = heading?.let { HeadingResponse.Value(it) }
|
||||
|
||||
headingValue ?: HeadingResponse.NoGps
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.scan(emptyList<Double>()) { acc, value -> /* Average over 3 values */
|
||||
.scan(emptyList<HeadingResponse>()) { acc, value -> /* Average over 3 values */
|
||||
if (value !is HeadingResponse.Value) return@scan listOf(value)
|
||||
|
||||
val newAcc = acc + value
|
||||
if (newAcc.size > 3) newAcc.drop(1) else newAcc
|
||||
}
|
||||
.map { it.average() }
|
||||
.map { data ->
|
||||
if (data.isEmpty()) return@map HeadingResponse.NoGps
|
||||
|
||||
if (data.all { it is HeadingResponse.Value }) {
|
||||
val avg = data.mapNotNull { (it as? HeadingResponse.Value)?.diff }.average()
|
||||
HeadingResponse.Value(avg)
|
||||
} else {
|
||||
data.first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinates> {
|
||||
fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinates?> {
|
||||
// return flowOf(GpsCoordinates(52.5164069,13.3784))
|
||||
|
||||
return streamDataFlow(DataType.Type.LOCATION)
|
||||
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||
.mapNotNull { values ->
|
||||
val lat = values[DataType.Field.LOC_LATITUDE]
|
||||
val lon = values[DataType.Field.LOC_LONGITUDE]
|
||||
.map { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||
.map { values ->
|
||||
val lat = values?.get(DataType.Field.LOC_LATITUDE)
|
||||
val lon = values?.get(DataType.Field.LOC_LONGITUDE)
|
||||
|
||||
if (lat != null && lon != null){
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updated gps coords: $lat $lon")
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updated gps coordinates: $lat $lon")
|
||||
GpsCoordinates(lat, lon)
|
||||
} else {
|
||||
Log.e(KarooHeadwindExtension.TAG, "Missing gps values: $values")
|
||||
Log.w(KarooHeadwindExtension.TAG, "Gps unavailable: $values")
|
||||
null
|
||||
}
|
||||
}
|
||||
.combine(context.streamSettings(this)) { gps, settings -> gps to settings }
|
||||
.map { (gps, settings) ->
|
||||
val rounded = gps.round(settings.roundLocationTo.km.toDouble())
|
||||
Log.d(KarooHeadwindExtension.TAG, "Round location to ${settings.roundLocationTo.km} - $rounded")
|
||||
val rounded = gps?.round(settings.roundLocationTo.km.toDouble())
|
||||
if (rounded != null) Log.d(KarooHeadwindExtension.TAG, "Round location to ${settings.roundLocationTo.km} - $rounded")
|
||||
rounded
|
||||
}
|
||||
.distinctUntilChanged { old, new -> old.distanceTo(new).absoluteValue < 0.001 }
|
||||
.distinctUntilChanged { old, new ->
|
||||
if (old != null && new != null) {
|
||||
old.distanceTo(new).absoluteValue < 0.001
|
||||
} else {
|
||||
old == new
|
||||
}
|
||||
}
|
||||
.debounce(Duration.ofSeconds(10))
|
||||
}
|
||||
@ -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.2") {
|
||||
class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.3") {
|
||||
companion object {
|
||||
const val TAG = "karoo-headwind"
|
||||
}
|
||||
@ -62,7 +62,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.2") {
|
||||
)
|
||||
}
|
||||
|
||||
data class StreamData(val settings: HeadwindSettings, val gps: GpsCoordinates,
|
||||
data class StreamData(val settings: HeadwindSettings, val gps: GpsCoordinates?,
|
||||
val profile: UserProfile? = null)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@ -80,7 +80,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.2") {
|
||||
|
||||
val gpsFlow = karooSystem
|
||||
.getGpsCoordinateFlow(this@KarooHeadwindExtension)
|
||||
.transformLatest { value: GpsCoordinates ->
|
||||
.transformLatest { value: GpsCoordinates? ->
|
||||
while(true){
|
||||
emit(value)
|
||||
delay(1.hours)
|
||||
@ -101,6 +101,10 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.2") {
|
||||
HeadwindStats()
|
||||
}
|
||||
|
||||
if (gps == null){
|
||||
error("No GPS coordinates available")
|
||||
}
|
||||
|
||||
val response = karooSystem.makeOpenMeteoHttpRequest(gps, settings, profile)
|
||||
if (response.error != null){
|
||||
try {
|
||||
|
||||
@ -12,6 +12,7 @@ import io.hammerhead.karooext.models.DataType
|
||||
import io.hammerhead.karooext.models.StreamState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseDataType(
|
||||
@ -25,10 +26,12 @@ abstract class BaseDataType(
|
||||
val job = CoroutineScope(Dispatchers.IO).launch {
|
||||
val currentWeatherData = applicationContext.streamCurrentWeatherData()
|
||||
|
||||
currentWeatherData.collect { data ->
|
||||
val value = getValue(data)
|
||||
Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value")
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value))))
|
||||
currentWeatherData
|
||||
.filterNotNull()
|
||||
.collect { data ->
|
||||
val value = getValue(data)
|
||||
Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value")
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value))))
|
||||
}
|
||||
}
|
||||
emitter.setCancellable {
|
||||
|
||||
@ -6,7 +6,9 @@ import android.util.Log
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi
|
||||
import androidx.glance.appwidget.GlanceRemoteViews
|
||||
import de.timklge.karooheadwind.HeadingResponse
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.getErrorWidget
|
||||
import de.timklge.karooheadwind.getRelativeHeadingFlow
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.screens.WindDirectionIndicatorSetting
|
||||
@ -47,7 +49,8 @@ class HeadwindDirectionDataType(
|
||||
val job = CoroutineScope(Dispatchers.IO).launch {
|
||||
karooSystem.getRelativeHeadingFlow(applicationContext)
|
||||
.collect { diff ->
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to diff))))
|
||||
val value = (diff as? HeadingResponse.Value)?.diff ?: 0.0
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value))))
|
||||
}
|
||||
}
|
||||
emitter.setCancellable {
|
||||
@ -55,7 +58,7 @@ class HeadwindDirectionDataType(
|
||||
}
|
||||
}
|
||||
|
||||
data class StreamData(val value: Double, val absoluteWindDirection: Double, val windSpeed: Double, val settings: HeadwindSettings)
|
||||
data class StreamData(val headingResponse: HeadingResponse?, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings? = null)
|
||||
|
||||
private fun previewFlow(): Flow<StreamData> {
|
||||
return flow {
|
||||
@ -63,7 +66,7 @@ class HeadwindDirectionDataType(
|
||||
val bearing = (0..360).random().toDouble()
|
||||
val windSpeed = (-20..20).random()
|
||||
|
||||
emit(StreamData(bearing, bearing, windSpeed.toDouble(), HeadwindSettings()))
|
||||
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings()))
|
||||
delay(2_000)
|
||||
}
|
||||
}
|
||||
@ -86,36 +89,47 @@ class HeadwindDirectionDataType(
|
||||
val flow = if (config.preview) {
|
||||
previewFlow()
|
||||
} else {
|
||||
karooSystem.streamDataFlow(dataTypeId)
|
||||
.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.windDirection, data.current.windSpeed, settings)
|
||||
}
|
||||
karooSystem.getRelativeHeadingFlow(context)
|
||||
.combine(context.streamCurrentWeatherData()) { headingResponse, data -> StreamData(headingResponse, data?.current?.windDirection, data?.current?.windSpeed) }
|
||||
.combine(context.streamSettings(karooSystem)) { data, settings -> data.copy(settings = settings) }
|
||||
}
|
||||
|
||||
val viewJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
flow.collect { streamData ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
|
||||
val windSpeed = streamData.windSpeed
|
||||
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
|
||||
headwindSpeed.roundToInt().toString()
|
||||
}
|
||||
WindDirectionIndicatorTextSetting.WIND_SPEED -> windSpeed.roundToInt().toString()
|
||||
WindDirectionIndicatorTextSetting.NONE -> ""
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
|
||||
|
||||
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
|
||||
if (value == null || streamData.absoluteWindDirection == null || streamData.settings == null || streamData.windSpeed == null){
|
||||
var headingResponse = streamData.headingResponse
|
||||
|
||||
if (headingResponse is HeadingResponse.Value && (streamData.absoluteWindDirection == null || streamData.windSpeed == null)){
|
||||
headingResponse = HeadingResponse.NoWeatherData
|
||||
}
|
||||
|
||||
val result = glance.compose(context, DpSize.Unspecified) {
|
||||
HeadwindDirection(baseBitmap, windDirection.roundToInt(), config.textSize, text)
|
||||
}
|
||||
emitter.updateView(getErrorWidget(glance, context, streamData.settings, headingResponse).remoteViews)
|
||||
|
||||
emitter.updateView(result.remoteViews)
|
||||
return@collect
|
||||
}
|
||||
|
||||
val windSpeed = streamData.windSpeed
|
||||
val windDirection = when (streamData.settings.windDirectionIndicatorSetting){
|
||||
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) {
|
||||
HeadwindDirection(baseBitmap, windDirection.roundToInt(), config.textSize, text)
|
||||
}
|
||||
|
||||
emitter.updateView(result.remoteViews)
|
||||
}
|
||||
}
|
||||
emitter.setCancellable {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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
|
||||
@ -15,6 +16,7 @@ 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
|
||||
|
||||
@ -22,16 +24,17 @@ class HeadwindSpeedDataType(
|
||||
private val karooSystem: KarooSystemService,
|
||||
private val context: Context) : DataTypeImpl("karoo-headwind", "headwindSpeed"){
|
||||
|
||||
data class StreamData(val value: Double, val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings)
|
||||
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.data.current.windSpeed
|
||||
val windDirection = streamData.value
|
||||
val windSpeed = streamData.weatherResponse?.current?.windSpeed ?: 0.0
|
||||
val windDirection = (streamData.headingResponse as? HeadingResponse.Value)?.diff ?: 0.0
|
||||
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
|
||||
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to headwindSpeed))))
|
||||
|
||||
@ -4,15 +4,22 @@ import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi
|
||||
import androidx.glance.appwidget.GlanceRemoteViews
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import de.timklge.karooheadwind.HeadingResponse
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
|
||||
import de.timklge.karooheadwind.WeatherInterpretation
|
||||
import de.timklge.karooheadwind.getErrorWidget
|
||||
import de.timklge.karooheadwind.getHeadingFlow
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.screens.PrecipitationUnit
|
||||
import de.timklge.karooheadwind.screens.TemperatureUnit
|
||||
@ -33,6 +40,8 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.Instant
|
||||
@ -57,6 +66,7 @@ class WeatherDataType(
|
||||
val currentWeatherData = applicationContext.streamCurrentWeatherData()
|
||||
|
||||
currentWeatherData
|
||||
.filterNotNull()
|
||||
.collect { data ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data.current.weatherCode}")
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to data.current.weatherCode.toDouble()))))
|
||||
@ -79,15 +89,23 @@ class WeatherDataType(
|
||||
de.timklge.karooheadwind.R.drawable.arrow_0
|
||||
)
|
||||
|
||||
data class StreamData(val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings,
|
||||
val profile: UserProfile? = null)
|
||||
data class StreamData(val data: OpenMeteoCurrentWeatherResponse?, val settings: HeadwindSettings,
|
||||
val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null)
|
||||
|
||||
val viewJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
context.streamCurrentWeatherData()
|
||||
.combine(context.streamSettings(karooSystem)) { data, settings -> StreamData(data, settings) }
|
||||
.combine(karooSystem.streamUserProfile()) { data, profile -> data.copy(profile = profile) }
|
||||
.collect { (data, settings, userProfile) ->
|
||||
.combine(karooSystem.getHeadingFlow()) { data, heading -> data.copy(headingResponse = heading) }
|
||||
.collect { (data, settings, userProfile, headingResponse) ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updating weather view")
|
||||
|
||||
if (data == null){
|
||||
emitter.updateView(getErrorWidget(glance, context, settings, headingResponse).remoteViews)
|
||||
|
||||
return@collect
|
||||
}
|
||||
|
||||
val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode)
|
||||
val formattedTime = timeFormatter.format(Instant.ofEpochSecond(data.current.time))
|
||||
|
||||
|
||||
@ -3,10 +3,6 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -25,12 +21,13 @@ import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.Spacer
|
||||
import androidx.glance.layout.fillMaxHeight
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.layout.width
|
||||
import de.timklge.karooheadwind.HeadingResponse
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
|
||||
import de.timklge.karooheadwind.WeatherInterpretation
|
||||
import de.timklge.karooheadwind.saveSettings
|
||||
import de.timklge.karooheadwind.getErrorWidget
|
||||
import de.timklge.karooheadwind.getHeadingFlow
|
||||
import de.timklge.karooheadwind.saveWidgetSettings
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.screens.HeadwindWidgetSettings
|
||||
@ -55,7 +52,6 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
@ -74,7 +70,7 @@ class CycleHoursAction : ActionCallback {
|
||||
val data = context.streamCurrentWeatherData().first()
|
||||
|
||||
var hourOffset = currentSettings.currentForecastHourOffset + 3
|
||||
if (hourOffset >= data.forecastData.weatherCode.size) {
|
||||
if (data == null || hourOffset >= data.forecastData.weatherCode.size) {
|
||||
hourOffset = 0
|
||||
}
|
||||
|
||||
@ -101,8 +97,8 @@ class WeatherForecastDataType(
|
||||
|
||||
currentWeatherData
|
||||
.collect { data ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data.current.weatherCode}")
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to data.current.weatherCode.toDouble()))))
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data?.current?.weatherCode}")
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to (data?.current?.weatherCode?.toDouble() ?: 0.0)))))
|
||||
}
|
||||
}
|
||||
emitter.setCancellable {
|
||||
@ -122,16 +118,23 @@ class WeatherForecastDataType(
|
||||
de.timklge.karooheadwind.R.drawable.arrow_0
|
||||
)
|
||||
|
||||
data class StreamData(val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings,
|
||||
val widgetSettings: HeadwindWidgetSettings? = null, val profile: UserProfile? = null)
|
||||
data class StreamData(val data: OpenMeteoCurrentWeatherResponse?, val settings: HeadwindSettings,
|
||||
val widgetSettings: HeadwindWidgetSettings? = null, val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null)
|
||||
|
||||
val viewJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
context.streamCurrentWeatherData()
|
||||
.combine(context.streamSettings(karooSystem)) { data, settings -> StreamData(data, settings) }
|
||||
.combine(karooSystem.streamUserProfile()) { data, profile -> data.copy(profile = profile) }
|
||||
.combine(context.streamWidgetSettings()) { data, widgetSettings -> data.copy(widgetSettings = widgetSettings) }
|
||||
.collect { (data, settings, widgetSettings, userProfile) ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updating weather view")
|
||||
.combine(karooSystem.getHeadingFlow()) { data, headingResponse -> data.copy(headingResponse = headingResponse) }
|
||||
.collect { (data, settings, widgetSettings, userProfile, headingResponse) ->
|
||||
Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view")
|
||||
|
||||
if (data == null){
|
||||
emitter.updateView(getErrorWidget(glance, context, settings, headingResponse).remoteViews)
|
||||
|
||||
return@collect
|
||||
}
|
||||
|
||||
val result = glance.compose(context, DpSize.Unspecified) {
|
||||
Row(modifier = GlanceModifier.fillMaxSize().clickable(onClick = actionRunCallback<CycleHoursAction>()), horizontalAlignment = Alignment.Horizontal.CenterHorizontally) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user