Compare commits
14 Commits
master
...
1.4.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
| b48138f196 | |||
| 2bb9c59bff | |||
| 6db3097e66 | |||
| da448b7407 | |||
| 76def03651 | |||
| cdafc7cedd | |||
| f020cc4e2f | |||
| bc1d03f053 | |||
| a54abf2667 | |||
| 7ffb6867b7 | |||
| dfedd1afe5 | |||
| 65e7ea4d14 | |||
| 3e1fa2169b | |||
| 72b9a0f57e |
@ -72,7 +72,7 @@ tasks.register("generateManifest") {
|
||||
"latestVersionCode" to android.defaultConfig.versionCode,
|
||||
"developer" to "github.com/timklge",
|
||||
"description" to "Open-source extension that adds colored power or heart rate progress bars to the edges of the screen, similar to the LEDs on Wahoo computers",
|
||||
"releaseNotes" to "* Add route progress data source\n* Add workout target range indicator\n* Make bars transparent by default\n* Split size setting",
|
||||
"releaseNotes" to "* Add route progress data source\n* Add workout target range indicator\n* Replace dropdown popup with fullscreen dialog",
|
||||
"screenshotUrls" to listOf(
|
||||
"$baseUrl/powerbar_min.gif",
|
||||
"$baseUrl/powerbar0.png",
|
||||
|
||||
@ -11,6 +11,14 @@ import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class CustomProgressBarSize(val id: String, val label: String, val fontSize: Float, val barHeight: Float) {
|
||||
SMALL("small", "Small", 35f, 10f),
|
||||
MEDIUM("medium", "Medium", 40f, 15f),
|
||||
LARGE("large", "Large", 60f, 25f),
|
||||
}
|
||||
|
||||
class CustomProgressBar @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
@ -22,30 +30,22 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
var maxTarget: Double? = null
|
||||
var target: Double? = null
|
||||
var showLabel: Boolean = true
|
||||
var barBackground: Boolean = false
|
||||
@ColorInt var progressColor: Int = 0xFF2b86e6.toInt()
|
||||
|
||||
var fontSize = CustomProgressBarFontSize.MEDIUM
|
||||
var size = CustomProgressBarSize.MEDIUM
|
||||
set(value) {
|
||||
field = value
|
||||
textPaint.textSize = value.fontSize
|
||||
invalidate() // Redraw to apply new font size
|
||||
}
|
||||
|
||||
var barSize = CustomProgressBarBarSize.MEDIUM
|
||||
set(value) {
|
||||
field = value
|
||||
targetZoneStrokePaint.strokeWidth = when(value){
|
||||
CustomProgressBarBarSize.NONE, CustomProgressBarBarSize.SMALL -> 3f
|
||||
CustomProgressBarBarSize.MEDIUM -> 6f
|
||||
CustomProgressBarBarSize.LARGE -> 8f
|
||||
CustomProgressBarSize.SMALL -> 3f
|
||||
CustomProgressBarSize.MEDIUM -> 6f
|
||||
CustomProgressBarSize.LARGE -> 8f
|
||||
}
|
||||
targetIndicatorPaint.strokeWidth = when(value){
|
||||
CustomProgressBarBarSize.NONE, CustomProgressBarBarSize.SMALL -> 6f
|
||||
CustomProgressBarBarSize.MEDIUM -> 8f
|
||||
CustomProgressBarBarSize.LARGE -> 10f
|
||||
CustomProgressBarSize.SMALL -> 6f
|
||||
CustomProgressBarSize.MEDIUM -> 8f
|
||||
CustomProgressBarSize.LARGE -> 10f
|
||||
}
|
||||
invalidate() // Redraw to apply new bar size
|
||||
}
|
||||
|
||||
private val targetColor = 0xFF9933FF.toInt()
|
||||
@ -80,7 +80,7 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
|
||||
private val blurPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
strokeWidth = 6f
|
||||
strokeWidth = 4f
|
||||
style = Paint.Style.STROKE
|
||||
color = progressColor
|
||||
maskFilter = BlurMaskFilter(3f, BlurMaskFilter.Blur.NORMAL)
|
||||
@ -88,7 +88,7 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
|
||||
private val blurPaintHighlight = Paint().apply {
|
||||
isAntiAlias = true
|
||||
strokeWidth = 10f
|
||||
strokeWidth = 8f
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
color = ColorUtils.blendARGB(progressColor, 0xFFFFFF, 0.5f)
|
||||
maskFilter = BlurMaskFilter(6f, BlurMaskFilter.Blur.NORMAL)
|
||||
@ -109,7 +109,7 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
private val textPaint = Paint().apply {
|
||||
color = Color.WHITE
|
||||
strokeWidth = 3f
|
||||
textSize = fontSize.fontSize
|
||||
textSize = size.fontSize
|
||||
typeface = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)
|
||||
textAlign = Paint.Align.CENTER
|
||||
}
|
||||
@ -134,18 +134,19 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
|
||||
when (location) {
|
||||
PowerbarLocation.TOP -> {
|
||||
val barTop = 15f
|
||||
val barBottom = barTop + size.barHeight
|
||||
val rect = RectF(
|
||||
1f,
|
||||
15f,
|
||||
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(0.0, 1.0)).toFloat(),
|
||||
15f + barSize.barHeight // barSize.barHeight will be 0f if NONE
|
||||
barTop,
|
||||
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(
|
||||
0.0,
|
||||
1.0
|
||||
)).toFloat(),
|
||||
barBottom
|
||||
)
|
||||
|
||||
// Draw bar components only if barSize is not NONE
|
||||
if (barSize != CustomProgressBarBarSize.NONE) {
|
||||
if (barBackground){
|
||||
canvas.drawRect(0f, 15f, canvas.width.toFloat(), 15f + barSize.barHeight, backgroundPaint)
|
||||
}
|
||||
canvas.drawRect(0f, barTop, canvas.width.toFloat(), barBottom, backgroundPaint)
|
||||
|
||||
// Draw target zone fill behind the progress bar
|
||||
if (minTarget != null && maxTarget != null) {
|
||||
@ -153,9 +154,9 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
val maxTargetX = (canvas.width * maxTarget!!).toFloat()
|
||||
canvas.drawRoundRect(
|
||||
minTargetX,
|
||||
15f,
|
||||
barTop,
|
||||
maxTargetX,
|
||||
15f + barSize.barHeight,
|
||||
barBottom,
|
||||
2f,
|
||||
2f,
|
||||
targetZoneFillPaint
|
||||
@ -165,11 +166,17 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
if (progress != null) {
|
||||
canvas.drawRoundRect(rect, 2f, 2f, blurPaint)
|
||||
canvas.drawRoundRect(rect, 2f, 2f, linePaint)
|
||||
canvas.drawRoundRect(rect.right-4, rect.top, rect.right+4, rect.bottom, 2f, 2f, blurPaintHighlight)
|
||||
}
|
||||
}
|
||||
// Draw label (if progress is not null and showLabel is true)
|
||||
if (progress != null) {
|
||||
|
||||
canvas.drawRoundRect(
|
||||
rect.right - 4,
|
||||
rect.top,
|
||||
rect.right + 4,
|
||||
rect.bottom,
|
||||
2f,
|
||||
2f,
|
||||
blurPaintHighlight
|
||||
)
|
||||
|
||||
// Draw target zone stroke after progress bar, before label
|
||||
if (minTarget != null && maxTarget != null) {
|
||||
val minTargetX = (canvas.width * minTarget!!).toFloat()
|
||||
@ -177,9 +184,9 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
// Draw stroked rounded rectangle for the target zone
|
||||
canvas.drawRoundRect(
|
||||
minTargetX,
|
||||
15f,
|
||||
barTop,
|
||||
maxTargetX,
|
||||
15f + barSize.barHeight,
|
||||
barBottom,
|
||||
2f,
|
||||
2f,
|
||||
targetZoneStrokePaint
|
||||
@ -190,54 +197,101 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
if (target != null) {
|
||||
val targetX = (canvas.width * target!!).toFloat()
|
||||
targetIndicatorPaint.color = if (isTargetMet) Color.GREEN else Color.RED
|
||||
canvas.drawLine(targetX, 15f, targetX, 15f + barSize.barHeight, targetIndicatorPaint)
|
||||
canvas.drawLine(targetX, barTop, targetX, barBottom, targetIndicatorPaint)
|
||||
}
|
||||
|
||||
if (showLabel) {
|
||||
val textContent =
|
||||
label // Store original label, as textPaint.measureText can be slow
|
||||
val measuredTextWidth = textPaint.measureText(textContent)
|
||||
val labelBoxWidth = (measuredTextWidth + 20).coerceAtLeast(10f)
|
||||
val labelBoxHeight = size.fontSize + 10f // Consistent height with padding
|
||||
|
||||
// Calculate horizontal position for the label box (centered around progress end, clamped)
|
||||
val labelBoxLeft = (rect.right - labelBoxWidth / 2f).coerceIn(
|
||||
0f,
|
||||
canvas.width - labelBoxWidth
|
||||
)
|
||||
|
||||
var labelBoxTop: Float
|
||||
var labelBoxBottom: Float
|
||||
var textYPosition: Float
|
||||
|
||||
if (target != null) { // If workout target is present, move label BELOW the bar
|
||||
val labelPadding = 5f // Padding between bar and label box
|
||||
labelBoxTop = barBottom + labelPadding
|
||||
labelBoxBottom = labelBoxTop + labelBoxHeight
|
||||
// Vertically center text in the new box
|
||||
val labelBoxCenterY = labelBoxTop + labelBoxHeight / 2f
|
||||
textYPosition =
|
||||
labelBoxCenterY - (textPaint.ascent() + textPaint.descent()) / 2f
|
||||
} else { // Original position for TOP
|
||||
val yOffsetOriginal = when (size) {
|
||||
CustomProgressBarSize.SMALL -> (size.fontSize - size.barHeight) / 2 + 2f
|
||||
CustomProgressBarSize.MEDIUM, CustomProgressBarSize.LARGE -> (size.fontSize - size.barHeight) / 2
|
||||
}
|
||||
labelBoxTop = barTop - yOffsetOriginal
|
||||
labelBoxBottom =
|
||||
barBottom + yOffsetOriginal // Original calculation was based on rect.bottom which is barBottom
|
||||
textYPosition = barBottom + 6f // Original text Y
|
||||
}
|
||||
|
||||
if (showLabel){
|
||||
lineStrokePaint.color = if (target != null){
|
||||
if (isTargetMet) Color.GREEN else Color.RED
|
||||
} else progressColor
|
||||
|
||||
blurPaint.color = lineStrokePaint.color
|
||||
blurPaintHighlight.color = ColorUtils.blendARGB(lineStrokePaint.color, 0xFFFFFF, 0.5f)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
textBackgroundPaint
|
||||
)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
blurPaint
|
||||
)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
lineStrokePaint
|
||||
)
|
||||
|
||||
val textBounds = textPaint.measureText(label)
|
||||
val xOffset = (textBounds + 20).coerceAtLeast(10f) / 2f
|
||||
val x = (rect.right - xOffset).coerceIn(0f..canvas.width-xOffset*2f)
|
||||
val r = x + xOffset * 2
|
||||
|
||||
val fm = textPaint.fontMetrics
|
||||
// barCenterY calculation uses barSize.barHeight, which is 0f for NONE,
|
||||
// correctly centering the label on the 15f line.
|
||||
val barCenterY = rect.top + barSize.barHeight / 2f
|
||||
val centeredTextBaselineY = barCenterY - (fm.ascent + fm.descent) / 2f
|
||||
val calculatedTextBoxTop = centeredTextBaselineY + fm.ascent
|
||||
val finalTextBoxTop = calculatedTextBoxTop.coerceAtLeast(0f)
|
||||
val finalTextBaselineY = finalTextBoxTop - fm.ascent
|
||||
val finalTextBoxBottom = finalTextBaselineY + fm.descent
|
||||
|
||||
canvas.drawRoundRect(x, finalTextBoxTop, r, finalTextBoxBottom, 2f, 2f, textBackgroundPaint)
|
||||
canvas.drawRoundRect(x, finalTextBoxTop, r, finalTextBoxBottom, 2f, 2f, blurPaint)
|
||||
canvas.drawRoundRect(x, finalTextBoxTop, r, finalTextBoxBottom, 2f, 2f, lineStrokePaint)
|
||||
canvas.drawText(label, x + xOffset, finalTextBaselineY, textPaint)
|
||||
canvas.drawText(
|
||||
textContent,
|
||||
labelBoxLeft + labelBoxWidth / 2f,
|
||||
textYPosition,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PowerbarLocation.BOTTOM -> {
|
||||
val barTop = canvas.height.toFloat() - 1f - size.barHeight
|
||||
val barBottom = canvas.height.toFloat()
|
||||
val rect = RectF(
|
||||
1f,
|
||||
canvas.height.toFloat() - 1f - barSize.barHeight, // barSize.barHeight will be 0f if NONE
|
||||
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(0.0, 1.0)).toFloat(),
|
||||
canvas.height.toFloat()
|
||||
barTop,
|
||||
((canvas.width.toDouble() - 1f) * (progress ?: 0.0).coerceIn(
|
||||
0.0,
|
||||
1.0
|
||||
)).toFloat(),
|
||||
barBottom
|
||||
)
|
||||
|
||||
// Draw bar components only if barSize is not NONE
|
||||
if (barSize != CustomProgressBarBarSize.NONE) {
|
||||
if (barBackground){
|
||||
// Use barSize.barHeight for background top calculation
|
||||
canvas.drawRect(0f, canvas.height.toFloat() - barSize.barHeight, canvas.width.toFloat(), canvas.height.toFloat(), backgroundPaint)
|
||||
}
|
||||
canvas.drawRect(0f, barTop, canvas.width.toFloat(), barBottom, backgroundPaint)
|
||||
|
||||
// Draw target zone fill behind the progress bar
|
||||
if (minTarget != null && maxTarget != null) {
|
||||
@ -245,9 +299,9 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
val maxTargetX = (canvas.width * maxTarget!!).toFloat()
|
||||
canvas.drawRoundRect(
|
||||
minTargetX,
|
||||
canvas.height.toFloat() - barSize.barHeight,
|
||||
barTop,
|
||||
maxTargetX,
|
||||
canvas.height.toFloat(),
|
||||
barBottom,
|
||||
2f,
|
||||
2f,
|
||||
targetZoneFillPaint
|
||||
@ -257,12 +311,17 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
if (progress != null) {
|
||||
canvas.drawRoundRect(rect, 2f, 2f, blurPaint)
|
||||
canvas.drawRoundRect(rect, 2f, 2f, linePaint)
|
||||
canvas.drawRoundRect(rect.right-4, rect.top, rect.right+4, rect.bottom, 2f, 2f, blurPaintHighlight)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw label (if progress is not null and showLabel is true)
|
||||
if (progress != null) {
|
||||
canvas.drawRoundRect(
|
||||
rect.right - 4,
|
||||
rect.top,
|
||||
rect.right + 4,
|
||||
rect.bottom,
|
||||
2f,
|
||||
2f,
|
||||
blurPaintHighlight
|
||||
)
|
||||
|
||||
// Draw target zone stroke after progress bar, before label
|
||||
if (minTarget != null && maxTarget != null) {
|
||||
val minTargetX = (canvas.width * minTarget!!).toFloat()
|
||||
@ -270,9 +329,9 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
// Draw stroked rounded rectangle for the target zone
|
||||
canvas.drawRoundRect(
|
||||
minTargetX,
|
||||
canvas.height.toFloat() - barSize.barHeight,
|
||||
barTop,
|
||||
maxTargetX,
|
||||
canvas.height.toFloat(),
|
||||
barBottom,
|
||||
2f,
|
||||
2f,
|
||||
targetZoneStrokePaint
|
||||
@ -283,33 +342,83 @@ class CustomProgressBar @JvmOverloads constructor(
|
||||
if (target != null) {
|
||||
val targetX = (canvas.width * target!!).toFloat()
|
||||
targetIndicatorPaint.color = if (isTargetMet) Color.GREEN else Color.RED
|
||||
canvas.drawLine(targetX, canvas.height.toFloat() - barSize.barHeight, targetX, canvas.height.toFloat(), targetIndicatorPaint)
|
||||
canvas.drawLine(targetX, barTop, targetX, barBottom, targetIndicatorPaint)
|
||||
}
|
||||
|
||||
if (showLabel) {
|
||||
val textContent = label // Store original label
|
||||
val measuredTextWidth = textPaint.measureText(textContent)
|
||||
val labelBoxWidth = (measuredTextWidth + 20).coerceAtLeast(10f)
|
||||
val labelBoxHeight = size.fontSize + 10f // Consistent height with padding
|
||||
|
||||
// Calculate horizontal position for the label box (centered around progress end, clamped)
|
||||
val labelBoxLeft = (rect.right - labelBoxWidth / 2f).coerceIn(
|
||||
0f,
|
||||
canvas.width - labelBoxWidth
|
||||
)
|
||||
|
||||
var labelBoxTop: Float
|
||||
var labelBoxBottom: Float
|
||||
var textYPosition: Float
|
||||
|
||||
if (target != null) { // If workout target is present, move label ABOVE the bar
|
||||
val labelPadding = 5f // Padding between bar and label box
|
||||
labelBoxBottom = barTop - labelPadding
|
||||
labelBoxTop = labelBoxBottom - labelBoxHeight
|
||||
// Vertically center text in the new box
|
||||
val labelBoxCenterY = labelBoxTop + labelBoxHeight / 2f
|
||||
textYPosition =
|
||||
labelBoxCenterY - (textPaint.ascent() + textPaint.descent()) / 2f
|
||||
} else { // Original position for BOTTOM
|
||||
val yOffsetOriginal = when (size) {
|
||||
CustomProgressBarSize.SMALL -> size.fontSize / 2 + 2f
|
||||
CustomProgressBarSize.MEDIUM -> size.fontSize / 2
|
||||
CustomProgressBarSize.LARGE -> size.fontSize / 2 - 5f
|
||||
}
|
||||
labelBoxTop = barTop - yOffsetOriginal
|
||||
labelBoxBottom = barBottom + 5f // Original 'b' calculation
|
||||
textYPosition =
|
||||
barBottom - 1f // Original text Y (rect.top + size.barHeight -1)
|
||||
}
|
||||
|
||||
if (showLabel){
|
||||
lineStrokePaint.color = if (target != null){
|
||||
if (isTargetMet) Color.GREEN else Color.RED
|
||||
} else progressColor
|
||||
|
||||
blurPaint.color = lineStrokePaint.color
|
||||
blurPaintHighlight.color = ColorUtils.blendARGB(lineStrokePaint.color, 0xFFFFFF, 0.5f)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
textBackgroundPaint
|
||||
)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
blurPaint
|
||||
)
|
||||
canvas.drawRoundRect(
|
||||
labelBoxLeft,
|
||||
labelBoxTop,
|
||||
labelBoxLeft + labelBoxWidth,
|
||||
labelBoxBottom,
|
||||
2f,
|
||||
2f,
|
||||
lineStrokePaint
|
||||
)
|
||||
|
||||
val textBounds = textPaint.measureText(label)
|
||||
val xOffset = (textBounds + 20).coerceAtLeast(10f) / 2f
|
||||
val x = (rect.right - xOffset).coerceIn(0f..canvas.width-xOffset*2f)
|
||||
val r = x + xOffset * 2
|
||||
|
||||
// textDrawBaselineY calculation uses rect.top and barSize.barHeight.
|
||||
// If NONE, barSize.barHeight is 0f. rect.top becomes canvas.height - 1f.
|
||||
// So, baseline is (canvas.height - 1f) + 0f - 1f = canvas.height - 2f.
|
||||
val textDrawBaselineY = rect.top + barSize.barHeight - 1f
|
||||
val yBox = textDrawBaselineY + textPaint.ascent()
|
||||
val bBox = textDrawBaselineY + textPaint.descent()
|
||||
|
||||
canvas.drawRoundRect(x, yBox, r, bBox, 2f, 2f, textBackgroundPaint)
|
||||
canvas.drawRoundRect(x, yBox, r, bBox, 2f, 2f, blurPaint)
|
||||
canvas.drawRoundRect(x, yBox, r, bBox, 2f, 2f, lineStrokePaint)
|
||||
canvas.drawText(label, x + xOffset, textDrawBaselineY, textPaint)
|
||||
canvas.drawText(
|
||||
textContent,
|
||||
labelBoxLeft + labelBoxWidth / 2f,
|
||||
textYPosition,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
package de.timklge.karoopowerbar
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class CustomProgressBarSize(val id: String, val label: String, val fontSize: Float, val barHeight: Float) {
|
||||
SMALL("small", "Small", 35f, 10f),
|
||||
MEDIUM("medium", "Medium", 40f, 15f),
|
||||
LARGE("large", "Large", 60f, 25f),
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class CustomProgressBarFontSize(val id: String, val label: String, val fontSize: Float) {
|
||||
SMALL("small", "Small", 35f),
|
||||
MEDIUM("medium", "Medium", 40f),
|
||||
LARGE("large", "Large", 60f);
|
||||
|
||||
companion object {
|
||||
fun fromSize(size: CustomProgressBarSize): CustomProgressBarFontSize {
|
||||
return when (size) {
|
||||
CustomProgressBarSize.SMALL -> SMALL
|
||||
CustomProgressBarSize.MEDIUM -> MEDIUM
|
||||
CustomProgressBarSize.LARGE -> LARGE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class CustomProgressBarBarSize(val id: String, val label: String, val barHeight: Float) {
|
||||
NONE("none", "None", 0f),
|
||||
SMALL("small", "Small", 10f),
|
||||
MEDIUM("medium", "Medium", 15f),
|
||||
LARGE("large", "Large", 25f);
|
||||
|
||||
companion object {
|
||||
fun fromSize(size: CustomProgressBarSize): CustomProgressBarBarSize {
|
||||
return when (size) {
|
||||
CustomProgressBarSize.SMALL -> SMALL
|
||||
CustomProgressBarSize.MEDIUM -> MEDIUM
|
||||
CustomProgressBarSize.LARGE -> LARGE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +53,7 @@ class ForegroundService : Service() {
|
||||
windows.clear()
|
||||
|
||||
if (settings.source != SelectedSource.NONE && showBars) {
|
||||
Window(this@ForegroundService, PowerbarLocation.BOTTOM, settings.showLabelOnBars, settings.barBackground, settings.barBarSize, settings.barFontSize).apply {
|
||||
Window(this@ForegroundService, PowerbarLocation.BOTTOM, settings.showLabelOnBars, settings.barSize).apply {
|
||||
selectedSource = settings.source
|
||||
windows.add(this)
|
||||
open()
|
||||
@ -61,7 +61,7 @@ class ForegroundService : Service() {
|
||||
}
|
||||
|
||||
if (settings.topBarSource != SelectedSource.NONE && showBars){
|
||||
Window(this@ForegroundService, PowerbarLocation.TOP, settings.showLabelOnBars, settings.barBackground, settings.barBarSize, settings.barFontSize).apply {
|
||||
Window(this@ForegroundService, PowerbarLocation.TOP, settings.showLabelOnBars, settings.barSize).apply {
|
||||
selectedSource = settings.topBarSource
|
||||
open()
|
||||
windows.add(this)
|
||||
|
||||
@ -2,6 +2,7 @@ package de.timklge.karoopowerbar
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import de.timklge.karoopowerbar.screens.SelectedSource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -20,10 +21,7 @@ data class PowerbarSettings(
|
||||
val onlyShowWhileRiding: Boolean = true,
|
||||
val showLabelOnBars: Boolean = true,
|
||||
val useZoneColors: Boolean = true,
|
||||
val barBackground: Boolean = false,
|
||||
val barSize: CustomProgressBarSize = CustomProgressBarSize.MEDIUM,
|
||||
val barFontSize: CustomProgressBarFontSize = CustomProgressBarFontSize.fromSize(barSize),
|
||||
val barBarSize: CustomProgressBarBarSize = CustomProgressBarBarSize.fromSize(barSize),
|
||||
|
||||
val minCadence: Int = defaultMinCadence, val maxCadence: Int = defaultMaxCadence,
|
||||
val minSpeed: Float = defaultMinSpeedMs, val maxSpeed: Float = defaultMaxSpeedMs, // 50 km/h in m/s
|
||||
|
||||
@ -54,9 +54,7 @@ class Window(
|
||||
private val context: Context,
|
||||
val powerbarLocation: PowerbarLocation = PowerbarLocation.BOTTOM,
|
||||
val showLabel: Boolean,
|
||||
val barBackground: Boolean,
|
||||
val powerbarBarSize: CustomProgressBarBarSize,
|
||||
val powerbarFontSize: CustomProgressBarFontSize,
|
||||
val powerbarSize: CustomProgressBarSize
|
||||
) {
|
||||
companion object {
|
||||
val FIELD_TARGET_VALUE_ID = "FIELD_WORKOUT_TARGET_VALUE_ID";
|
||||
@ -136,9 +134,7 @@ class Window(
|
||||
powerbar.progress = null
|
||||
powerbar.location = powerbarLocation
|
||||
powerbar.showLabel = showLabel
|
||||
powerbar.barBackground = barBackground
|
||||
powerbar.fontSize = powerbarFontSize
|
||||
powerbar.barSize = powerbarBarSize
|
||||
powerbar.size = powerbarSize
|
||||
powerbar.invalidate()
|
||||
|
||||
Log.i(TAG, "Streaming $selectedSource")
|
||||
|
||||
@ -57,8 +57,6 @@ import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import de.timklge.karoopowerbar.CustomProgressBarBarSize
|
||||
import de.timklge.karoopowerbar.CustomProgressBarFontSize
|
||||
import de.timklge.karoopowerbar.CustomProgressBarSize
|
||||
import de.timklge.karoopowerbar.KarooPowerbarExtension
|
||||
import de.timklge.karoopowerbar.PowerbarSettings
|
||||
@ -68,6 +66,8 @@ import de.timklge.karoopowerbar.settingsKey
|
||||
import de.timklge.karoopowerbar.streamSettings
|
||||
import de.timklge.karoopowerbar.streamUserProfile
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.HardwareType
|
||||
import io.hammerhead.karooext.models.PlayBeepPattern
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@ -147,9 +147,7 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
var onlyShowWhileRiding by remember { mutableStateOf(false) }
|
||||
var colorBasedOnZones by remember { mutableStateOf(false) }
|
||||
var showLabelOnBars by remember { mutableStateOf(true) }
|
||||
var barBarSize by remember { mutableStateOf(CustomProgressBarBarSize.MEDIUM) }
|
||||
var barFontSize by remember { mutableStateOf(CustomProgressBarFontSize.MEDIUM) }
|
||||
var barBackground by remember { mutableStateOf(false) }
|
||||
var barSize by remember { mutableStateOf(CustomProgressBarSize.MEDIUM) }
|
||||
|
||||
var minCadence by remember { mutableStateOf("0") }
|
||||
var maxCadence by remember { mutableStateOf("0") }
|
||||
@ -179,7 +177,6 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
val newSettings = PowerbarSettings(
|
||||
source = bottomSelectedSource, topBarSource = topSelectedSource,
|
||||
onlyShowWhileRiding = onlyShowWhileRiding, showLabelOnBars = showLabelOnBars,
|
||||
barBackground = barBackground,
|
||||
useZoneColors = colorBasedOnZones,
|
||||
minCadence = minCadence.toIntOrNull() ?: PowerbarSettings.defaultMinCadence,
|
||||
maxCadence = maxCadence.toIntOrNull() ?: PowerbarSettings.defaultMaxCadence,
|
||||
@ -188,8 +185,7 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
maxPower = customMaxPower.toIntOrNull(),
|
||||
minHr = customMinHr.toIntOrNull(),
|
||||
maxHr = customMaxHr.toIntOrNull(),
|
||||
barBarSize = barBarSize,
|
||||
barFontSize = barFontSize,
|
||||
barSize = barSize,
|
||||
useCustomPowerRange = useCustomPowerRange,
|
||||
useCustomHrRange = useCustomHrRange,
|
||||
)
|
||||
@ -231,9 +227,7 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
onlyShowWhileRiding = settings.onlyShowWhileRiding
|
||||
showLabelOnBars = settings.showLabelOnBars
|
||||
colorBasedOnZones = settings.useZoneColors
|
||||
barBarSize = settings.barBarSize
|
||||
barFontSize = settings.barFontSize
|
||||
barBackground = settings.barBackground
|
||||
barSize = settings.barSize
|
||||
minCadence = settings.minCadence.toString()
|
||||
maxCadence = settings.maxCadence.toString()
|
||||
isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
|
||||
@ -315,23 +309,12 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
}
|
||||
|
||||
apply {
|
||||
val dropdownOptions = CustomProgressBarBarSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) }
|
||||
val dropdownInitialSelection by remember(barBarSize) {
|
||||
mutableStateOf(dropdownOptions.find { option -> option.id == barBarSize.id }!!)
|
||||
val dropdownOptions = CustomProgressBarSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) }
|
||||
val dropdownInitialSelection by remember(barSize) {
|
||||
mutableStateOf(dropdownOptions.find { option -> option.id == barSize.id }!!)
|
||||
}
|
||||
Dropdown(label = "Bar Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption ->
|
||||
barBarSize = CustomProgressBarBarSize.entries.find { unit -> unit.id == selectedOption.id }!!
|
||||
coroutineScope.launch { updateSettings() }
|
||||
}
|
||||
}
|
||||
|
||||
apply {
|
||||
val dropdownOptions = CustomProgressBarFontSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) }
|
||||
val dropdownInitialSelection by remember(barFontSize) {
|
||||
mutableStateOf(dropdownOptions.find { option -> option.id == barFontSize.id }!!)
|
||||
}
|
||||
Dropdown(label = "Text Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption ->
|
||||
barFontSize = CustomProgressBarFontSize.entries.find { unit -> unit.id == selectedOption.id }!!
|
||||
barSize = CustomProgressBarSize.entries.find { unit -> unit.id == selectedOption.id }!!
|
||||
coroutineScope.launch { updateSettings() }
|
||||
}
|
||||
}
|
||||
@ -488,15 +471,6 @@ fun MainScreen(onFinish: () -> Unit) {
|
||||
Text("Show value on bars")
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Switch(checked = barBackground, onCheckedChange = {
|
||||
barBackground = it
|
||||
coroutineScope.launch { updateSettings() }
|
||||
})
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text("Opaque background")
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Switch(checked = onlyShowWhileRiding, onCheckedChange = {
|
||||
onlyShowWhileRiding = it
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user