Skip to content

Commit

Permalink
fix #17: Adds optional custom range inputs for power, heart rate (#18)
Browse files Browse the repository at this point in the history
* fix #17: Adds optional custom range inputs for power, heart rate data sources

* Update karoo-ext

* Fix display of overlay value if out of range

* Fix custom speed range
  • Loading branch information
timklge authored Jan 24, 2025
1 parent 4bbf79e commit c7686e4
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 49 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
[![GitHub Downloads (specific asset, all releases)](https://img.shields.io/github/downloads/timklge/karoo-powerbar/app-release.apk)](https://github.com/timklge/karoo-powerbar/releases)
![GitHub License](https://img.shields.io/github/license/timklge/karoo-powerbar)

Simple karoo extension that shows an overlay power bar at the edge of the screen. For Karoo 2 and Karoo 3 devices.
Simple karoo extension that shows an overlay power bar at the edge of the screen, comparable to the
dedicated LEDs featured on Wahoo devices.
For Karoo 2 and Karoo 3 devices.

![Powerbar](powerbar0.png)
![Settings](powerbar1.png)
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
applicationId = "de.timklge.karoopowerbar"
minSdk = 26
targetSdk = 33
versionCode = 11
versionName = "1.3.2"
versionCode = 12
versionName = "1.3.3"
}

signingConfigs {
Expand Down
6 changes: 3 additions & 3 deletions app/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.3.2",
"latestVersionCode": 11,
"latestVersion": "1.3.3",
"latestVersionCode": 12,
"developer": "timklge",
"description": "Adds a colored power bar to the bottom of the screen",
"releaseNotes": "Add size setting, cadence and speed data sources with custom ranges"
"releaseNotes": "Adds option to set a custom range for power and heart rate bar"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ enum class CustomProgressBarSize(val id: String, val label: String, val fontSize
class CustomProgressBar @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : View(context, attrs) {
var progress: Double = 0.5
var showValueIfNull: Boolean = false
var progress: Double? = 0.5
var location: PowerbarLocation = PowerbarLocation.BOTTOM
var label: String = ""
var showLabel: Boolean = true
Expand Down Expand Up @@ -99,13 +98,13 @@ class CustomProgressBar @JvmOverloads constructor(
val rect = RectF(
1f,
15f,
((canvas.width.toDouble() - 1f) * progress.coerceIn(0.0, 1.0)).toFloat(),
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(0.0, 1.0)).toFloat(),
15f + size.barHeight
)

canvas.drawRect(0f, 15f, canvas.width.toFloat(), 15f + size.barHeight, backgroundPaint)

if (progress > 0.0 || showValueIfNull) {
if (progress != null) {
canvas.drawRoundRect(rect, 2f, 2f, blurPaint)
canvas.drawRoundRect(rect, 2f, 2f, linePaint)

Expand Down Expand Up @@ -135,13 +134,13 @@ class CustomProgressBar @JvmOverloads constructor(
val rect = RectF(
1f,
canvas.height.toFloat() - 1f - size.barHeight,
((canvas.width.toDouble() - 1f) * progress.coerceIn(0.0, 1.0)).toFloat(),
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(0.0, 1.0)).toFloat(),
canvas.height.toFloat()
)

canvas.drawRect(0f, canvas.height.toFloat() - size.barHeight, canvas.width.toFloat(), canvas.height.toFloat(), backgroundPaint)

if (progress > 0.0 || showValueIfNull) {
if (progress != null) {
canvas.drawRoundRect(rect, 2f, 2f, blurPaint)
canvas.drawRoundRect(rect, 2f, 2f, linePaint)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

class KarooPowerbarExtension : KarooExtension("karoo-powerbar", "1.3.2") {
class KarooPowerbarExtension : KarooExtension("karoo-powerbar", "1.3.3") {

companion object {
const val TAG = "karoo-powerbar"
Expand Down
11 changes: 7 additions & 4 deletions app/src/main/kotlin/de/timklge/karoopowerbar/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ data class PowerbarSettings(

val minCadence: Int = defaultMinCadence, val maxCadence: Int = defaultMaxCadence,
val minSpeed: Float = defaultMinSpeedMs, val maxSpeed: Float = defaultMaxSpeedMs, // 50 km/h in m/s
val minPower: Int? = null, val maxPower: Int? = null,
val minHr: Int? = null, val maxHr: Int? = null,
val useCustomHrRange: Boolean = false, val useCustomPowerRange: Boolean = false
){
companion object {
val defaultSettings = Json.encodeToString(PowerbarSettings())
val defaultMinSpeedMs = 0f
val defaultMaxSpeedMs = 13.89f
val defaultMinCadence = 50
val defaultMaxCadence = 120
const val defaultMinSpeedMs = 0f
const val defaultMaxSpeedMs = 13.89f
const val defaultMinCadence = 50
const val defaultMaxCadence = 120
}
}

Expand Down
42 changes: 20 additions & 22 deletions app/src/main/kotlin/de/timklge/karoopowerbar/Window.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class Window(
layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
rootView = layoutInflater.inflate(R.layout.popup_window, null)
powerbar = rootView.findViewById(R.id.progressBar)
powerbar.progress = 0.0
powerbar.progress = null

windowManager = context.getSystemService(WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
Expand Down Expand Up @@ -119,7 +119,7 @@ class Window(
}

powerbar.progressColor = context.resources.getColor(R.color.zone7)
powerbar.progress = 0.0
powerbar.progress = null
powerbar.location = powerbarLocation
powerbar.showLabel = showLabel
powerbar.size = powerbarSize
Expand Down Expand Up @@ -175,7 +175,6 @@ class Window(
val maxSpeed = streamData.settings?.maxSpeed ?: PowerbarSettings.defaultMaxSpeedMs
val progress =
remap(valueMetersPerSecond, minSpeed.toDouble(), maxSpeed.toDouble(), 0.0, 1.0)
powerbar.showValueIfNull = valueMetersPerSecond != 0.0

@ColorRes val zoneColorRes = Zone.entries[(progress * Zone.entries.size).roundToInt().coerceIn(0..<Zone.entries.size)].colorResource

Expand All @@ -184,14 +183,13 @@ class Window(
} else {
context.getColor(R.color.zone0)
}
powerbar.progress = progress
powerbar.progress = if (value > 0) progress else null
powerbar.label = "$value"

Log.d(TAG, "Speed: $value min: $minSpeed max: $maxSpeed")
} else {
powerbar.progressColor = context.getColor(R.color.zone0)
powerbar.progress = 0.0
powerbar.showValueIfNull = false
powerbar.progress = null
powerbar.label = "?"

Log.d(TAG, "Speed: Unavailable")
Expand Down Expand Up @@ -223,20 +221,18 @@ class Window(

@ColorRes val zoneColorRes = Zone.entries[(progress * Zone.entries.size).roundToInt().coerceIn(0..<Zone.entries.size)].colorResource

powerbar.showValueIfNull = value != 0
powerbar.progressColor = if (streamData.settings?.useZoneColors == true) {
context.getColor(zoneColorRes)
} else {
context.getColor(R.color.zone0)
}
powerbar.progress = progress
powerbar.progress = if (value > 0) progress else null
powerbar.label = "$value"

Log.d(TAG, "Cadence: $value min: $minCadence max: $maxCadence")
} else {
powerbar.progressColor = context.getColor(R.color.zone0)
powerbar.progress = 0.0
powerbar.showValueIfNull = false
powerbar.progress = null
powerbar.label = "?"

Log.d(TAG, "Cadence: Unavailable")
Expand All @@ -261,23 +257,24 @@ class Window(
val value = streamData.value?.roundToInt()

if (value != null) {
val minHr = streamData.userProfile.restingHr
val maxHr = streamData.userProfile.maxHr
val progress =
remap(value.toDouble(), minHr.toDouble(), maxHr.toDouble(), 0.0, 1.0)
val customMinHr = if (streamData.settings?.useCustomHrRange == true) streamData.settings.minHr else null
val customMaxHr = if (streamData.settings?.useCustomHrRange == true) streamData.settings.maxHr else null
val minHr = customMinHr ?: streamData.userProfile.restingHr
val maxHr = customMaxHr ?: streamData.userProfile.maxHr
val progress = remap(value.toDouble(), minHr.toDouble(), maxHr.toDouble(), 0.0, 1.0)

powerbar.progressColor = if (streamData.settings?.useZoneColors == true) {
context.getColor(getZone(streamData.userProfile.heartRateZones, value)?.colorResource ?: R.color.zone7)
} else {
context.getColor(R.color.zone0)
}
powerbar.progress = progress
powerbar.progress = if (value > 0) progress else null
powerbar.label = "$value"

Log.d(TAG, "Hr: $value min: $minHr max: $maxHr")
} else {
powerbar.progressColor = context.getColor(R.color.zone0)
powerbar.progress = 0.0
powerbar.progress = null
powerbar.label = "?"

Log.d(TAG, "Hr: Unavailable")
Expand Down Expand Up @@ -308,23 +305,24 @@ class Window(
val value = streamData.value?.roundToInt()

if (value != null) {
val minPower = streamData.userProfile.powerZones.first().min
val maxPower = streamData.userProfile.powerZones.last().min + 50
val progress =
remap(value.toDouble(), minPower.toDouble(), maxPower.toDouble(), 0.0, 1.0)
val customMinPower = if (streamData.settings?.useCustomPowerRange == true) streamData.settings.minPower else null
val customMaxPower = if (streamData.settings?.useCustomPowerRange == true) streamData.settings.maxPower else null
val minPower = customMinPower ?: streamData.userProfile.powerZones.first().min
val maxPower = customMaxPower ?: (streamData.userProfile.powerZones.last().min + 50)
val progress = remap(value.toDouble(), minPower.toDouble(), maxPower.toDouble(), 0.0, 1.0)

powerbar.progressColor = if (streamData.settings?.useZoneColors == true) {
context.getColor(getZone(streamData.userProfile.powerZones, value)?.colorResource ?: R.color.zone7)
} else {
context.getColor(R.color.zone0)
}
powerbar.progress = progress
powerbar.progress = if (value > 0) progress else null
powerbar.label = "${value}W"

Log.d(TAG, "Power: $value min: $minPower max: $maxPower")
} else {
powerbar.progressColor = context.getColor(R.color.zone0)
powerbar.progress = 0.0
powerbar.progress = null
powerbar.label = "?"

Log.d(TAG, "Power: Unavailable")
Expand Down
Loading

0 comments on commit c7686e4

Please sign in to comment.