Fix gps flow reset
This commit is contained in:
parent
d3109e459c
commit
e6ee80e60e
@ -7,5 +7,5 @@
|
|||||||
"latestVersionCode": 7,
|
"latestVersionCode": 7,
|
||||||
"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": "Add hourly forecast and temperature datafields. Show error message in fields if no weather data or gps is available."
|
"releaseNotes": "Adds hourly forecast. Shows error message in fields if weather data or gps are unavailable and remembers last known gps position."
|
||||||
}
|
}
|
||||||
@ -39,13 +39,13 @@ import kotlinx.coroutines.FlowPreview
|
|||||||
import kotlinx.coroutines.TimeoutCancellationException
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.channels.trySendBlocking
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.emitAll
|
||||||
import kotlinx.coroutines.flow.filterNot
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
@ -124,10 +124,8 @@ suspend fun getErrorWidget(glance: GlanceRemoteViews, context: Context, settings
|
|||||||
"Headwind app not set up"
|
"Headwind app not set up"
|
||||||
} else if (headingResponse is HeadingResponse.NoGps){
|
} else if (headingResponse is HeadingResponse.NoGps){
|
||||||
"No GPS signal"
|
"No GPS signal"
|
||||||
} else if (headingResponse is HeadingResponse.NoWeatherData) {
|
|
||||||
"No weather data"
|
|
||||||
} else {
|
} else {
|
||||||
"Unknown error"
|
"Weather data download failed"
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage")
|
Log.d(KarooHeadwindExtension.TAG, "Error widget: $errorMessage")
|
||||||
@ -207,7 +205,7 @@ fun Context.streamStats(): Flow<HeadwindStats> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun Context.getLastKnownPosition(): GpsCoordinates? {
|
suspend fun Context.getLastKnownPosition(): GpsCoordinates? {
|
||||||
val settingsJson = dataStore.data.first()
|
val settingsJson = dataStore.data.first()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val lastKnownPositionString = settingsJson[lastKnownPositionKey] ?: return null
|
val lastKnownPositionString = settingsJson[lastKnownPositionKey] ?: return null
|
||||||
@ -334,47 +332,61 @@ fun KarooSystemService.getHeadingFlow(context: Context): Flow<HeadingResponse> {
|
|||||||
if (newAcc.size > 3) newAcc.drop(1) else newAcc
|
if (newAcc.size > 3) newAcc.drop(1) else newAcc
|
||||||
}
|
}
|
||||||
.map { data ->
|
.map { data ->
|
||||||
if (data.isEmpty()) return@map HeadingResponse.NoGps
|
Log.i(KarooHeadwindExtension.TAG, "Heading value: $data")
|
||||||
|
|
||||||
if (data.all { it is HeadingResponse.Value }) {
|
if (data.isEmpty()) return@map HeadingResponse.NoGps
|
||||||
val avg = data.mapNotNull { (it as? HeadingResponse.Value)?.diff }.average()
|
if (data.firstOrNull() !is HeadingResponse.Value) return@map data.first()
|
||||||
HeadingResponse.Value(avg)
|
|
||||||
} else {
|
val avgValues = data.mapNotNull { (it as? HeadingResponse.Value)?.diff }
|
||||||
data.first()
|
|
||||||
}
|
if (avgValues.isEmpty()) return@map HeadingResponse.NoGps
|
||||||
|
|
||||||
|
val avg = avgValues.average()
|
||||||
|
|
||||||
|
HeadingResponse.Value(avg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> concatenate(vararg flows: Flow<T>) = flow {
|
fun <T> concatenate(vararg flows: Flow<T>) = flow {
|
||||||
var hadNullValue = false
|
|
||||||
|
|
||||||
for (flow in flows) {
|
for (flow in flows) {
|
||||||
flow.collect { value ->
|
emitAll(flow)
|
||||||
if (!hadNullValue) {
|
}
|
||||||
emit(value)
|
}
|
||||||
if (value == null) hadNullValue = true
|
|
||||||
} else {
|
fun<T> Flow<T>.dropNullsIfNullEncountered(): Flow<T?> = flow {
|
||||||
if (value != null) emit(value)
|
var hadValue = false
|
||||||
}
|
|
||||||
|
collect { value ->
|
||||||
|
if (!hadValue) {
|
||||||
|
emit(value)
|
||||||
|
if (value != null) hadValue = true
|
||||||
|
} else {
|
||||||
|
if (value != null) emit(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
suspend fun KarooSystemService.updateLastKnownGps(context: Context) {
|
suspend fun KarooSystemService.updateLastKnownGps(context: Context) {
|
||||||
getGpsCoordinateFlow(context)
|
while (true) {
|
||||||
.filterNotNull()
|
getGpsCoordinateFlow(context)
|
||||||
.throttle(60 * 1_000) // Only update last known gps position once every minute
|
.filterNotNull()
|
||||||
.collect { gps ->
|
.throttle(60 * 1_000) // Only update last known gps position once every minute
|
||||||
saveLastKnownPosition(context, gps)
|
.collect { gps ->
|
||||||
}
|
saveLastKnownPosition(context, gps)
|
||||||
|
}
|
||||||
|
delay(1_000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@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 flowOf(GpsCoordinates(52.5164069,13.3784))
|
||||||
|
|
||||||
val initialFlow = flow<GpsCoordinates> { context.getLastKnownPosition() }
|
val initialFlow = flow {
|
||||||
|
val lastKnownPosition = context.getLastKnownPosition()
|
||||||
|
if (lastKnownPosition != null) emit(lastKnownPosition)
|
||||||
|
}
|
||||||
|
|
||||||
val gpsFlow = streamDataFlow(DataType.Type.LOCATION)
|
val gpsFlow = streamDataFlow(DataType.Type.LOCATION)
|
||||||
.map { (it as? StreamState.Streaming)?.dataPoint?.values }
|
.map { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||||
@ -384,10 +396,10 @@ fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinat
|
|||||||
val bearing = values?.get(DataType.Field.LOC_BEARING)
|
val bearing = values?.get(DataType.Field.LOC_BEARING)
|
||||||
|
|
||||||
if (lat != null && lon != null){
|
if (lat != null && lon != null){
|
||||||
Log.d(KarooHeadwindExtension.TAG, "Updated gps coordinates: $lat $lon")
|
// Log.d(KarooHeadwindExtension.TAG, "Updated gps coordinates: $lat $lon")
|
||||||
GpsCoordinates(lat, lon, bearing)
|
GpsCoordinates(lat, lon, bearing)
|
||||||
} else {
|
} else {
|
||||||
Log.w(KarooHeadwindExtension.TAG, "Gps unavailable: $values")
|
// Log.w(KarooHeadwindExtension.TAG, "Gps unavailable: $values")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,16 +409,7 @@ fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinat
|
|||||||
return concatenatedFlow
|
return concatenatedFlow
|
||||||
.combine(context.streamSettings(this)) { gps, settings -> gps to settings }
|
.combine(context.streamSettings(this)) { gps, settings -> gps to settings }
|
||||||
.map { (gps, settings) ->
|
.map { (gps, settings) ->
|
||||||
val rounded = gps?.round(settings.roundLocationTo.km.toDouble())
|
gps?.round(settings.roundLocationTo.km.toDouble())
|
||||||
if (rounded != null) Log.d(KarooHeadwindExtension.TAG, "Round location to ${settings.roundLocationTo.km} - $rounded")
|
|
||||||
rounded
|
|
||||||
}
|
}
|
||||||
.distinctUntilChanged { old, new ->
|
.dropNullsIfNullEncountered()
|
||||||
if (old != null && new != null) {
|
|
||||||
old.distanceTo(new).absoluteValue < 0.001
|
|
||||||
} else {
|
|
||||||
old == new
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.debounce(Duration.ofSeconds(10))
|
|
||||||
}
|
}
|
||||||
@ -23,15 +23,20 @@ import io.hammerhead.karooext.models.UserProfile
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.retry
|
import kotlinx.coroutines.flow.retry
|
||||||
import kotlinx.coroutines.flow.transformLatest
|
import kotlinx.coroutines.flow.transformLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.time.debounce
|
||||||
|
import java.time.Duration
|
||||||
|
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
|
||||||
|
|
||||||
@ -67,7 +72,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.3") {
|
|||||||
data class StreamData(val settings: HeadwindSettings, val gps: GpsCoordinates?,
|
data class StreamData(val settings: HeadwindSettings, val gps: GpsCoordinates?,
|
||||||
val profile: UserProfile? = null)
|
val profile: UserProfile? = null)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
@ -86,6 +91,14 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.1.3") {
|
|||||||
|
|
||||||
val gpsFlow = karooSystem
|
val gpsFlow = karooSystem
|
||||||
.getGpsCoordinateFlow(this@KarooHeadwindExtension)
|
.getGpsCoordinateFlow(this@KarooHeadwindExtension)
|
||||||
|
.distinctUntilChanged { old, new ->
|
||||||
|
if (old != null && new != null) {
|
||||||
|
old.distanceTo(new).absoluteValue < 0.001
|
||||||
|
} else {
|
||||||
|
old == new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.debounce(Duration.ofSeconds(5))
|
||||||
.transformLatest { value: GpsCoordinates? ->
|
.transformLatest { value: GpsCoordinates? ->
|
||||||
while(true){
|
while(true){
|
||||||
emit(value)
|
emit(value)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user