Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.DualApplePods.LidState
import eu.darken.capod.pods.core.firstSeenFormatted
import eu.darken.capod.pods.core.formatBatteryPercent
import eu.darken.capod.pods.core.toBatteryFloat
import eu.darken.capod.pods.core.toBatteryOrNull
import eu.darken.capod.pods.core.getSignalQuality
import eu.darken.capod.pods.core.lastSeenFormatted
import java.time.Duration
Expand Down Expand Up @@ -164,7 +166,7 @@ fun DualPodsCard(
) {
PodGauge(
iconRes = device.leftPodIcon,
batteryPercent = device.batteryLeftPodPercent,
batteryPercent = device.batteryLeftPodPercent.toBatteryFloat(),
isCharging = (device as? HasChargeDetectionDual)?.isLeftPodCharging ?: false,
isInEar = (device as? HasEarDetectionDual)?.isLeftPodInEar ?: false,
showEarDetection = device is HasEarDetectionDual,
Expand All @@ -175,7 +177,7 @@ fun DualPodsCard(

PodGauge(
iconRes = device.rightPodIcon,
batteryPercent = device.batteryRightPodPercent,
batteryPercent = device.batteryRightPodPercent.toBatteryFloat(),
isCharging = (device as? HasChargeDetectionDual)?.isRightPodCharging ?: false,
isInEar = (device as? HasEarDetectionDual)?.isRightPodInEar ?: false,
showEarDetection = device is HasEarDetectionDual,
Expand Down Expand Up @@ -218,7 +220,7 @@ fun DualPodsCard(
@Composable
private fun PodGauge(
iconRes: Int,
batteryPercent: Float?,
batteryPercent: Float,
isCharging: Boolean,
isInEar: Boolean,
showEarDetection: Boolean,
Expand All @@ -227,15 +229,15 @@ private fun PodGauge(
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val clamped = batteryPercent?.coerceIn(0f, 1f)
val clamped = if (batteryPercent >= 0f) batteryPercent.coerceIn(0f, 1f) else -1f
val animatedProgress by animateFloatAsState(
targetValue = clamped ?: 0f,
targetValue = if (clamped >= 0f) clamped else 0f,
animationSpec = tween(600, easing = FastOutSlowInEasing),
label = "gaugeProgress",
)

val ringColor = when {
clamped == null -> MaterialTheme.colorScheme.surfaceVariant
clamped < 0f -> MaterialTheme.colorScheme.surfaceVariant
clamped > 0.30f -> MaterialTheme.colorScheme.primary
clamped >= 0.15f -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.error
Expand All @@ -261,7 +263,7 @@ private fun PodGauge(
)

// Progress ring
if (clamped != null) {
if (clamped >= 0f) {
CircularProgressIndicator(
progress = { animatedProgress },
modifier = Modifier.size(80.dp),
Expand All @@ -284,9 +286,9 @@ private fun PodGauge(

// Battery percentage
Text(
text = formatBatteryPercent(context, batteryPercent),
text = formatBatteryPercent(context, batteryPercent.toBatteryOrNull()),
style = MaterialTheme.typography.titleMedium,
color = if (batteryPercent != null) {
color = if (batteryPercent >= 0f) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurfaceVariant
Expand Down Expand Up @@ -335,7 +337,7 @@ private fun CaseRow(
)

BatteryCapsule(
percent = device.batteryCasePercent,
percent = device.batteryCasePercent.toBatteryFloat(),
modifier = Modifier
.weight(1f)
.height(8.dp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ private val CapsuleShape = RoundedCornerShape(6.dp)

@Composable
fun BatteryCapsule(
percent: Float?,
percent: Float,
modifier: Modifier = Modifier,
) {
val clamped = percent?.coerceIn(0f, 1f)
val clamped = if (percent >= 0f) percent.coerceIn(0f, 1f) else -1f
val animatedFraction by animateFloatAsState(
targetValue = clamped ?: 0f,
targetValue = if (clamped >= 0f) clamped else 0f,
animationSpec = tween(600, easing = FastOutSlowInEasing),
label = "batteryFill",
)

val barColor = when {
clamped == null -> MaterialTheme.colorScheme.surfaceVariant
clamped < 0f -> MaterialTheme.colorScheme.surfaceVariant
clamped > 0.30f -> MaterialTheme.colorScheme.primary
clamped >= 0.15f -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.error
Expand All @@ -62,7 +62,7 @@ fun BatteryCapsule(
.clip(CapsuleShape)
.background(MaterialTheme.colorScheme.surfaceVariant),
) {
if (clamped != null) {
if (clamped >= 0f) {
Box(
modifier = Modifier
.fillMaxHeight()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.compose.ui.unit.sp
import eu.darken.capod.R
import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.pods.core.toBatteryOrNull
import kotlin.math.roundToInt

@Composable
Expand Down Expand Up @@ -191,7 +192,7 @@ private fun SinglePodPreview(
colorFilter = iconTint,
)
Text(
text = formatPercent(state.percent),
text = formatPercent(state.percent.toBatteryOrNull()),
fontSize = 12.sp,
color = textColor,
modifier = Modifier.padding(horizontal = 8.dp),
Expand Down Expand Up @@ -293,7 +294,7 @@ private fun WidgetContainer(
@Composable
private fun PodItemRow(
icon: Int,
percent: Float?,
percent: Float,
charging: Boolean,
inEar: Boolean,
textColor: Color,
Expand All @@ -313,7 +314,7 @@ private fun PodItemRow(
colorFilter = iconTint,
)
Text(
text = formatPercent(percent),
text = formatPercent(percent.toBatteryOrNull()),
fontSize = 12.sp,
color = textColor,
modifier = Modifier.padding(horizontal = 4.dp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import eu.darken.capod.R
import eu.darken.capod.main.ui.MainActivity
import eu.darken.capod.pods.core.toBatteryOrNull
import kotlin.math.roundToInt

@Composable
Expand Down Expand Up @@ -105,7 +106,7 @@ private fun GlanceSinglePod(
colorFilter = iconTint,
)
Text(
text = formatGlancePercent(state.percent),
text = formatGlancePercent(state.percent.toBatteryOrNull()),
style = textStyle,
modifier = GlanceModifier.padding(horizontal = 8.dp),
)
Expand Down Expand Up @@ -203,7 +204,7 @@ private fun GlanceWidgetRoot(
@Composable
private fun GlancePodItem(
icon: Int,
percent: Float?,
percent: Float,
charging: Boolean,
inEar: Boolean,
textStyle: TextStyle,
Expand All @@ -222,7 +223,7 @@ private fun GlancePodItem(
colorFilter = iconTint,
)
Text(
text = formatGlancePercent(percent),
text = formatGlancePercent(percent.toBatteryOrNull()),
style = textStyle,
modifier = GlanceModifier.padding(horizontal = 4.dp),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ sealed class WidgetRenderState {
val isWide: Boolean,
val deviceLabel: String?,
@DrawableRes val leftIcon: Int,
val leftPercent: Float?,
val leftPercent: Float,
val leftCharging: Boolean,
val leftInEar: Boolean,
@DrawableRes val rightIcon: Int,
val rightPercent: Float?,
val rightPercent: Float,
val rightCharging: Boolean,
val rightInEar: Boolean,
@DrawableRes val caseIcon: Int,
val casePercent: Float?,
val casePercent: Float,
val caseCharging: Boolean,
) : WidgetRenderState()

Expand All @@ -37,7 +37,7 @@ sealed class WidgetRenderState {
@ColorInt override val resolvedIconColor: Int,
val deviceLabel: String?,
@DrawableRes val headsetIcon: Int,
val percent: Float?,
val percent: Float,
@DrawableRes val batteryIcon: Int,
val charging: Boolean,
val worn: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import eu.darken.capod.pods.core.HasEarDetectionDual
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.SinglePodDevice
import eu.darken.capod.pods.core.getBatteryDrawable
import eu.darken.capod.pods.core.toBatteryFloat

object WidgetRenderStateMapper {

Expand Down Expand Up @@ -46,15 +47,15 @@ object WidgetRenderStateMapper {
isWide = isWide,
deviceLabel = profileLabel ?: device.getLabel(context),
leftIcon = device.leftPodIcon,
leftPercent = device.batteryLeftPodPercent,
leftPercent = device.batteryLeftPodPercent.toBatteryFloat(),
leftCharging = device is HasChargeDetectionDual && device.isLeftPodCharging,
leftInEar = device is HasEarDetectionDual && device.isLeftPodInEar,
rightIcon = device.rightPodIcon,
rightPercent = device.batteryRightPodPercent,
rightPercent = device.batteryRightPodPercent.toBatteryFloat(),
rightCharging = device is HasChargeDetectionDual && device.isRightPodCharging,
rightInEar = device is HasEarDetectionDual && device.isRightPodInEar,
caseIcon = (device as? HasCase)?.caseIcon ?: R.drawable.device_airpods_gen1_case,
casePercent = (device as? HasCase)?.batteryCasePercent,
casePercent = (device as? HasCase)?.batteryCasePercent.toBatteryFloat(),
caseCharging = device is HasCase && device.isCaseCharging,
)

Expand All @@ -65,7 +66,7 @@ object WidgetRenderStateMapper {
resolvedIconColor = iconColor,
deviceLabel = profileLabel ?: device.getLabel(context),
headsetIcon = device.iconRes,
percent = device.batteryHeadsetPercent,
percent = device.batteryHeadsetPercent.toBatteryFloat(),
batteryIcon = getBatteryDrawable(device.batteryHeadsetPercent),
charging = device is HasChargeDetectionDual && device.isHeadsetBeingCharged,
worn = device is HasEarDetection && device.isBeingWorn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import java.time.Duration
import java.time.Instant
import kotlin.math.roundToInt

const val BATTERY_UNKNOWN = -1f

fun Float?.toBatteryFloat(): Float = this ?: BATTERY_UNKNOWN

fun Float.toBatteryOrNull(): Float? = takeIf { it >= 0f }

fun formatBatteryPercent(context: Context, percent: Float?): String =
percent?.let { "${(it * 100).roundToInt()}%" }
?: context.getString(R.string.general_value_not_available_label)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.SinglePodDevice
import eu.darken.capod.pods.core.formatBatteryPercent
import eu.darken.capod.pods.core.getBatteryDrawable
import eu.darken.capod.pods.core.toBatteryFloat
import eu.darken.capod.pods.core.toBatteryOrNull
import eu.darken.capod.pods.core.getSignalQuality

@Composable
Expand Down Expand Up @@ -127,23 +129,23 @@ private fun DualPodContent(device: DualPodDevice) {
// Left pod
BatteryColumn(
iconRes = device.leftPodIcon,
batteryPercent = device.batteryLeftPodPercent,
batteryPercent = device.batteryLeftPodPercent.toBatteryFloat(),
modifier = Modifier.weight(1f),
)

// Case (only if device has one)
if (hasCase != null) {
BatteryColumn(
iconRes = hasCase.caseIcon,
batteryPercent = hasCase.batteryCasePercent,
batteryPercent = hasCase.batteryCasePercent.toBatteryFloat(),
modifier = Modifier.weight(1f),
)
}

// Right pod
BatteryColumn(
iconRes = device.rightPodIcon,
batteryPercent = device.batteryRightPodPercent,
batteryPercent = device.batteryRightPodPercent.toBatteryFloat(),
modifier = Modifier.weight(1f),
)
}
Expand All @@ -153,17 +155,18 @@ private fun DualPodContent(device: DualPodDevice) {
private fun SinglePodContent(device: SinglePodDevice) {
BatteryColumn(
iconRes = device.iconRes,
batteryPercent = device.batteryHeadsetPercent,
batteryPercent = device.batteryHeadsetPercent.toBatteryFloat(),
)
}

@Composable
private fun BatteryColumn(
iconRes: Int,
batteryPercent: Float?,
batteryPercent: Float,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val nullablePercent = batteryPercent.toBatteryOrNull()

Column(
modifier = modifier,
Expand All @@ -183,13 +186,13 @@ private fun BatteryColumn(
horizontalArrangement = Arrangement.Center,
) {
Icon(
painter = painterResource(getBatteryDrawable(batteryPercent)),
painter = painterResource(getBatteryDrawable(nullablePercent)),
contentDescription = null,
modifier = Modifier.size(16.dp),
)
Spacer(modifier = Modifier.width(2.dp))
Text(
text = formatBatteryPercent(context, batteryPercent),
text = formatBatteryPercent(context, nullablePercent),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
Expand Down