Only refresh graphical data fields on the currently opened data page (#133)

This commit is contained in:
timklge 2025-05-11 19:36:15 +02:00 committed by GitHub
parent f7cd264e0c
commit 9772347a61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 167 additions and 68 deletions

View File

@ -1,6 +1,7 @@
package de.timklge.karooheadwind
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.ActiveRidePage
import io.hammerhead.karooext.models.OnLocationChanged
import io.hammerhead.karooext.models.OnNavigationState
import io.hammerhead.karooext.models.OnStreamState
@ -12,6 +13,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.flow.transform
@ -65,3 +67,22 @@ fun<T> Flow<T>.throttle(timeout: Long): Flow<T> = this
emit(it)
delay(timeout)
}
fun KarooSystemService.streamActiveRidePage(): Flow<ActiveRidePage> {
return callbackFlow {
val listenerId = addConsumer { activeRidePage: ActiveRidePage ->
trySendBlocking(activeRidePage)
}
awaitClose {
removeConsumer(listenerId)
}
}
}
fun KarooSystemService.streamDatatypeIsVisible(
datatype: String,
): Flow<Boolean> {
return streamActiveRidePage().map { page ->
page.page.elements.any { it.dataTypeId == datatype }
}
}

View File

@ -31,6 +31,7 @@ import de.timklge.karooheadwind.UpcomingRoute
import de.timklge.karooheadwind.WeatherDataProvider
import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.streamCurrentForecastWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUpcomingRoute
import de.timklge.karooheadwind.streamUserProfile
@ -54,6 +55,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
@ -90,7 +92,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
data class StreamData(val data: WeatherDataResponse?, val settings: SettingsAndProfile,
val widgetSettings: HeadwindWidgetSettings? = null,
val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null)
val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null, val isVisible: Boolean)
data class SettingsAndProfile(val settings: HeadwindSettings, val isImperial: Boolean, val isImperialTemperature: Boolean)
@ -164,7 +166,8 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
HeadwindSettings(),
settingsAndProfile?.isImperial == true,
settingsAndProfile?.isImperialTemperature == true
)
),
isVisible = true
)
)
@ -206,14 +209,23 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
if (oldDistance == null || newDistance == null) return@distinctUntilChanged false
abs(oldDistance - newDistance) < 1_000
}
) { weatherData, settings, widgetSettings, heading, upcomingRoute ->
},
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data ->
val weatherData = data[0] as WeatherDataResponse?
val settings = data[1] as SettingsAndProfile
val widgetSettings = data[2] as HeadwindWidgetSettings?
val heading = data[3] as HeadingResponse?
val upcomingRoute = data[4] as UpcomingRoute?
val isVisible = data[5] as Boolean
StreamData(
data = weatherData,
settings = settings,
widgetSettings = widgetSettings,
headingResponse = heading,
upcomingRoute = upcomingRoute
upcomingRoute = upcomingRoute,
isVisible = isVisible
)
}
}
@ -221,7 +233,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
val viewJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(ShowCustomStreamState("", null))
dataFlow.collect { (allData, settingsAndProfile, widgetSettings, headingResponse, upcomingRoute) ->
dataFlow.filter { it.isVisible }.collect { (allData, settingsAndProfile, widgetSettings, headingResponse, upcomingRoute) ->
Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view")
if (allData?.data.isNullOrEmpty()){

View File

@ -12,6 +12,7 @@ import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.WindDirectionIndicatorSetting
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.throttle
import io.hammerhead.karooext.KarooSystemService
@ -31,6 +32,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
@ -43,13 +45,21 @@ class HeadwindDirectionDataType(
) : DataTypeImpl("karoo-headwind", "headwind") {
private val glance = GlanceRemoteViews()
data class StreamData(val headingResponse: HeadingResponse, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings)
private fun streamValues(): Flow<Double> = flow {
karooSystem.getRelativeHeadingFlow(applicationContext)
.combine(applicationContext.streamCurrentWeatherData(karooSystem)) { headingResponse, data ->
StreamData(headingResponse, data?.windDirection, data?.windSpeed)
}
.combine(applicationContext.streamSettings(karooSystem)) { data, settings -> data.copy(settings = settings) }
.collect { streamData ->
combine(
karooSystem.getRelativeHeadingFlow(applicationContext),
applicationContext.streamCurrentWeatherData(karooSystem),
applicationContext.streamSettings(karooSystem),
) { headingResponse, currentWeather, settings ->
StreamData(
headingResponse,
currentWeather?.windDirection,
currentWeather?.windSpeed,
settings,
)
}.collect { streamData ->
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
var returnValue = 0.0
@ -96,9 +106,7 @@ class HeadwindDirectionDataType(
}
}
data class StreamData(val headingResponse: HeadingResponse?, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings? = null)
data class DirectionAndSpeed(val bearing: Double, val speed: Double?)
data class DirectionAndSpeed(val bearing: Double, val speed: Double?, val isVisible: Boolean)
private fun previewFlow(): Flow<DirectionAndSpeed> {
return flow {
@ -106,7 +114,7 @@ class HeadwindDirectionDataType(
val bearing = (0..360).random().toDouble()
val windSpeed = (0..20).random()
emit(DirectionAndSpeed(bearing, windSpeed.toDouble()))
emit(DirectionAndSpeed(bearing, windSpeed.toDouble(), true))
delay(2_000)
}
@ -136,15 +144,15 @@ class HeadwindDirectionDataType(
emitAll(UserWindSpeedDataType.streamValues(context, karooSystem))
}
combine(directionFlow, speedFlow) { direction, speed ->
DirectionAndSpeed(direction, speed)
combine(directionFlow, speedFlow, karooSystem.streamDatatypeIsVisible(dataTypeId)) { direction, speed, isVisible ->
DirectionAndSpeed(direction, speed, isVisible)
}
}
val viewJob = CoroutineScope(Dispatchers.IO).launch {
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
flow.throttle(refreshRate).collect { streamData ->
flow.filter { it.isVisible }.throttle(refreshRate).collect { streamData ->
Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null }

View File

@ -21,9 +21,11 @@ import de.timklge.karooheadwind.datatypes.TailwindDataType.StreamData
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.ViewEmitter
@ -85,7 +87,7 @@ class TailwindAndRideSpeedDataType(
val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10)
val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial))
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial, isVisible = true))
delay(2_000)
}
@ -113,7 +115,20 @@ class TailwindAndRideSpeedDataType(
val flow = if (config.preview) {
previewFlow(karooSystem.streamUserProfile())
} else {
combine(karooSystem.getRelativeHeadingFlow(context), context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), streamSpeedInMs()) { headingResponse, weatherData, settings, userProfile, rideSpeedInMs ->
combine(karooSystem.getRelativeHeadingFlow(context),
context.streamCurrentWeatherData(karooSystem),
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
streamSpeedInMs(),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data ->
val headingResponse = data[0] as HeadingResponse
val weatherData = data[1] as? WeatherData
val settings = data[2] as HeadwindSettings
val userProfile = data[3] as UserProfile
val rideSpeedInMs = data[4] as Double
val isVisible = data[5] as Boolean
val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
val absoluteWindDirection = weatherData?.windDirection
val windSpeed = weatherData?.windSpeed
@ -124,7 +139,7 @@ class TailwindAndRideSpeedDataType(
rideSpeedInMs * 3.6
}
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed)
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible)
}
}

View File

@ -17,9 +17,11 @@ import de.timklge.karooheadwind.WindDirectionIndicatorTextSetting
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.ViewEmitter
@ -35,6 +37,7 @@ import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
@ -56,7 +59,8 @@ class TailwindDataType(
val settings: HeadwindSettings,
val rideSpeed: Double?,
val gustSpeed: Double?,
val isImperial: Boolean)
val isImperial: Boolean,
val isVisible: Boolean)
private fun previewFlow(profileFlow: Flow<UserProfile>): Flow<StreamData> {
return flow {
@ -69,7 +73,7 @@ class TailwindDataType(
val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10)
val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial))
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial, isVisible = true))
delay(2_000)
}
@ -98,7 +102,20 @@ class TailwindDataType(
val flow = if (config.preview) {
previewFlow(karooSystem.streamUserProfile())
} else {
combine(karooSystem.getRelativeHeadingFlow(context), context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), streamSpeedInMs()) { headingResponse, weatherData, settings, userProfile, rideSpeedInMs ->
combine(karooSystem.getRelativeHeadingFlow(context),
context.streamCurrentWeatherData(karooSystem),
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
streamSpeedInMs(),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data ->
val headingResponse = data[0] as HeadingResponse
val weatherData = data[1] as? WeatherData
val settings = data[2] as HeadwindSettings
val userProfile = data[3] as UserProfile
val rideSpeedInMs = data[4] as Double
val isVisible = data[5] as Boolean
val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
val absoluteWindDirection = weatherData?.windDirection
val windSpeed = weatherData?.windSpeed
@ -109,7 +126,7 @@ class TailwindDataType(
rideSpeedInMs * 3.6
}
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed)
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible)
}
}
@ -117,7 +134,7 @@ class TailwindDataType(
emitter.onNext(ShowCustomStreamState("", null))
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
flow.throttle(refreshRate).collect { streamData ->
flow.filter { it.isVisible }.throttle(refreshRate).collect { streamData ->
Log.d(KarooHeadwindExtension.TAG, "Updating tailwind direction view")
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff

View File

@ -20,6 +20,7 @@ import de.timklge.karooheadwind.R
import de.timklge.karooheadwind.TemperatureUnit
import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
@ -42,6 +43,7 @@ import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import java.time.Instant
@ -75,7 +77,8 @@ class WeatherDataType(
}
data class StreamData(val data: WeatherData?, val settings: HeadwindSettings,
val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null)
val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null,
val isVisible: Boolean)
private fun previewFlow(): Flow<StreamData> = flow {
while (true){
@ -85,7 +88,7 @@ class WeatherDataType(
20.0, 50.0, 3.0, 0.0, 1013.25, 980.0, 15.0, 30.0, 30.0,
WeatherInterpretation.getKnownWeatherCodes().random(), isForecast = false,
isNight = listOf(true, false).random()
), HeadwindSettings()))
), HeadwindSettings(), isVisible = true))
delay(5_000)
}
@ -106,8 +109,14 @@ class WeatherDataType(
val dataFlow = if (config.preview){
previewFlow()
} else {
combine(context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), karooSystem.getHeadingFlow(context)) { data, settings, profile, heading ->
StreamData(data, settings, profile, heading)
combine(
context.streamCurrentWeatherData(karooSystem),
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
karooSystem.getHeadingFlow(context),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data, settings, profile, heading, isVisible ->
StreamData(data, settings, profile, heading, isVisible)
}
}
@ -116,7 +125,7 @@ class WeatherDataType(
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
dataFlow.throttle(refreshRate).collect { (data, settings, userProfile, headingResponse) ->
dataFlow.filter { it.isVisible }.throttle(refreshRate).collect { (data, settings, userProfile, headingResponse) ->
Log.d(KarooHeadwindExtension.TAG, "Updating weather view")
if (data == null){

View File

@ -17,9 +17,10 @@ import androidx.glance.text.FontFamily
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.internal.ViewEmitter
import io.hammerhead.karooext.models.ShowCustomStreamState
@ -31,8 +32,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@ -53,10 +55,18 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
return data.windDirection
}
private fun previewFlow(): Flow<Double> {
data class StreamData(
val windBearing: Double,
val isVisible: Boolean
)
private fun previewFlow(): Flow<StreamData> {
return flow {
while (true) {
emit((0..360).random().toDouble())
emit(StreamData(
(0..360).random().toDouble(),
true
))
delay(1_000)
}
}
@ -75,17 +85,24 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
val flow = if (config.preview){
previewFlow()
} else {
karooSystem.streamDataFlow(dataTypeId)
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue ?: 0.0 }
combine(
karooSystem.streamDataFlow(dataTypeId),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { windBearing, isVisible ->
StreamData(
windBearing = (windBearing as? StreamState.Streaming)?.dataPoint?.singleValue ?: 0.0,
isVisible = isVisible
)
}
}
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
flow.throttle(refreshRate).collect { windBearing ->
flow.filter { it.isVisible }.throttle(refreshRate).collect { (windBearing, isVisible) ->
val windCardinalDirectionIndex = ((windBearing % 360) / 22.5).roundToInt() % 16
val text = windDirections[windCardinalDirectionIndex]
Log.d( KarooHeadwindExtension.TAG,"Updating wind direction view")
Log.d(KarooHeadwindExtension.TAG,"Updating wind direction view")
val result = glance.compose(context, DpSize.Unspecified) {
Box(modifier = GlanceModifier.fillMaxSize(),
contentAlignment = Alignment(