fix #3: Fix handling of manually configured power and hr zones

This commit is contained in:
Tim Kluge 2024-12-08 23:34:41 +01:00
parent d9b8fac445
commit bc782e7f90
6 changed files with 56 additions and 53 deletions

View File

@ -13,8 +13,8 @@ android {
applicationId = "de.timklge.karoopowerbar"
minSdk = 26
targetSdk = 33
versionCode = 3
versionName = "1.1.0"
versionCode = 4
versionName = "1.1.1"
}
buildTypes {

View File

@ -3,9 +3,9 @@
"packageName": "de.timklge.karoopowerbar",
"iconUrl": "https://github.com/timklge/karoo-powerbar/releases/latest/download/karoo-powerbar.png",
"latestApkUrl": "https://github.com/timklge/karoo-powerbar/releases/latest/download/app-release.apk",
"latestVersion": "1.1.0",
"latestVersionCode": 3,
"latestVersion": "1.1.1",
"latestVersionCode": 4,
"developer": "timklge",
"description": "Adds a colored power bar to the bottom of the screen",
"releaseNotes": "Add options to add secondary power bar and to hide bar when not riding"
"releaseNotes": "Add options to add secondary power bar and to hide bar when not riding. Fix manually set up power/hr zones."
}

View File

@ -10,7 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class KarooPowerbarExtension : KarooExtension("karoo-powerbar", "1.1.0") {
class KarooPowerbarExtension : KarooExtension("karoo-powerbar", "1.1.1") {
companion object {
const val TAG = "karoo-powerbar"

View File

@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt
fun remap(value: Double, fromMin: Double, fromMax: Double, toMin: Double, toMax: Double): Double {
return (value - fromMin) * (toMax - toMin) / (fromMax - fromMin) + toMin
@ -102,11 +103,11 @@ class Window(
Log.i(TAG, "Karoo system service connected: $connected")
}
powerbar.progressColor = context.resources.getColor(R.color.zoneAerobic)
powerbar.progressColor = context.resources.getColor(R.color.zone7)
powerbar.progress = 0.0
powerbar.invalidate()
Log.i(KarooPowerbarExtension.TAG, "Streaming $selectedSource")
Log.i(TAG, "Streaming $selectedSource")
when (selectedSource){
SelectedSource.POWER -> streamPower(PowerStreamSmoothing.RAW)
@ -124,7 +125,7 @@ class Window(
}
}
} catch (e: Exception) {
Log.e(KarooPowerbarExtension.TAG, e.toString())
Log.e(TAG, e.toString())
}
}
@ -139,20 +140,21 @@ class Window(
.map { (userProfile, hr) -> StreamData(userProfile, hr) }
.distinctUntilChanged()
.collect { streamData ->
val value = streamData.value.roundToInt()
val color = context.getColor(
streamData.userProfile.getUserHrZone(streamData.value.toInt())?.colorResource
?: R.color.zoneAerobic
streamData.userProfile.getZone(streamData.userProfile.heartRateZones, value)?.colorResource
?: R.color.zone7
)
val minHr = streamData.userProfile.restingHr
val maxHr = streamData.userProfile.maxHr
val progress =
remap(streamData.value, minHr.toDouble(), maxHr.toDouble(), 0.0, 1.0)
remap(value.toDouble(), minHr.toDouble(), maxHr.toDouble(), 0.0, 1.0)
powerbar.progressColor = color
powerbar.progress = progress
powerbar.invalidate()
Log.d(KarooPowerbarExtension.TAG, "Hr: ${streamData.value} min: $minHr max: $maxHr")
Log.d(TAG, "Hr: $value min: $minHr max: $maxHr")
}
}
@ -173,20 +175,21 @@ class Window(
.map { (userProfile, power) -> StreamData(userProfile, power) }
.distinctUntilChanged()
.collect { streamData ->
val value = streamData.value.roundToInt()
val color = context.getColor(
streamData.userProfile.getUserPowerZone(streamData.value.toInt())?.colorResource
?: R.color.zoneAerobic
streamData.userProfile.getZone(streamData.userProfile.powerZones, value)?.colorResource
?: R.color.zone7
)
val minPower = streamData.userProfile.powerZones.first().min
val maxPower = streamData.userProfile.powerZones.last().min + 50
val progress =
remap(streamData.value, minPower.toDouble(), maxPower.toDouble(), 0.0, 1.0)
remap(value.toDouble(), minPower.toDouble(), maxPower.toDouble(), 0.0, 1.0)
powerbar.progressColor = color
powerbar.progress = progress
powerbar.invalidate()
Log.d(KarooPowerbarExtension.TAG, "Power: ${streamData.value} min: $minPower max: $maxPower")
Log.d(TAG, "Power: ${value} min: $minPower max: $maxPower")
}
}
@ -197,7 +200,7 @@ class Window(
rootView.invalidate()
(rootView.parent as ViewGroup).removeAllViews()
} catch (e: Exception) {
Log.d(KarooPowerbarExtension.TAG, e.toString())
Log.d(TAG, e.toString())
}
}
}

View File

@ -2,38 +2,36 @@ package de.timklge.karoopowerbar
import io.hammerhead.karooext.models.UserProfile
enum class PowerZone(val colorResource: Int) {
ACTIVE_RECOVERY(R.color.zoneActiveRecovery),
ENDURANCE(R.color.zoneEndurance),
TEMPO(R.color.zoneTempo),
THRESHOLD(R.color.zoneThreshold),
VO2_MAX(R.color.zoneVO2Max),
AEROBIC_CAPACITY(R.color.zoneAerobic),
ANAEROBIC_CAPACITY(R.color.zoneAnaerobic),
enum class Zone(val colorResource: Int){
Zone0(R.color.zone0),
Zone1(R.color.zone1),
Zone2(R.color.zone2),
Zone3(R.color.zone3),
Zone4(R.color.zone4),
Zone5(R.color.zone5),
Zone6(R.color.zone6),
Zone7(R.color.zone7),
Zone8(R.color.zone8),
}
enum class HrZone(val colorResource: Int) {
ACTIVE_RECOVERY(R.color.zoneActiveRecovery),
ENDURANCE(R.color.zoneEndurance),
TEMPO(R.color.zoneTempo),
THRESHOLD(R.color.zoneThreshold),
VO2_MAX(R.color.zoneAerobic),
}
val zones = mapOf(
1 to listOf(Zone.Zone7),
2 to listOf(Zone.Zone1, Zone.Zone7),
3 to listOf(Zone.Zone1, Zone.Zone3, Zone.Zone7),
4 to listOf(Zone.Zone1, Zone.Zone3, Zone.Zone5, Zone.Zone7),
5 to listOf(Zone.Zone1, Zone.Zone2, Zone.Zone3, Zone.Zone5, Zone.Zone7),
6 to listOf(Zone.Zone1, Zone.Zone2, Zone.Zone3, Zone.Zone5, Zone.Zone7, Zone.Zone8),
7 to listOf(Zone.Zone1, Zone.Zone2, Zone.Zone3, Zone.Zone5, Zone.Zone6, Zone.Zone7, Zone.Zone8),
8 to listOf(Zone.Zone0, Zone.Zone1, Zone.Zone2, Zone.Zone3, Zone.Zone5, Zone.Zone6, Zone.Zone7, Zone.Zone8),
9 to listOf(Zone.Zone0, Zone.Zone1, Zone.Zone2, Zone.Zone3, Zone.Zone4, Zone.Zone5, Zone.Zone6, Zone.Zone7, Zone.Zone8)
)
fun UserProfile.getUserPowerZone(power: Int): PowerZone? {
powerZones.forEachIndexed { index, zone ->
if (power in zone.min..zone.max) {
return PowerZone.entries[index]
}
}
fun UserProfile.getZone(userZones: List<UserProfile.Zone>, value: Int): Zone? {
val zoneList = zones[userZones.size] ?: return null
return null
}
fun UserProfile.getUserHrZone(hr: Int): HrZone? {
heartRateZones.forEachIndexed { index, zone ->
if (hr in zone.min..zone.max) {
return HrZone.entries[index]
userZones.forEachIndexed { index, zone ->
if (value in zone.min..zone.max) {
return zoneList.getOrNull(index) ?: Zone.Zone7
}
}

View File

@ -3,11 +3,13 @@
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#2600B3</color>
<color name="zoneActiveRecovery">#00B988</color>
<color name="zoneEndurance">#60EEB2</color>
<color name="zoneTempo">#FFF500</color>
<color name="zoneThreshold">#FB8C65</color>
<color name="zoneVO2Max">#FE581F</color>
<color name="zoneAerobic">#D60404</color>
<color name="zoneAnaerobic">#B700A2</color>
<color name="zone0">#2386D9</color>
<color name="zone1">#00B988</color>
<color name="zone2">#60EEB2</color>
<color name="zone3">#FFF500</color>
<color name="zone4">#FDC84C</color>
<color name="zone5">#FB8C65</color>
<color name="zone6">#FE581F</color>
<color name="zone7">#D60404</color>
<color name="zone8">#B700A2</color>
</resources>