mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
feat(widget): Add 1x1 size support to battery widget
Introduce BatteryLayout enum with size-driven dispatch. Drops minWidth from 80dp to 40dp so the battery widget can be placed at one cell. At 1-cell-wide placements render a compact icon+percent stack; wider sizes keep the current NARROW/WIDE layouts.
This commit is contained in:
@@ -158,7 +158,7 @@ class AncGlanceWidget : GlanceAppWidget() {
|
|||||||
|
|
||||||
private fun getCellsForSize(size: Int): Int {
|
private fun getCellsForSize(size: Int): Int {
|
||||||
var n = 2
|
var n = 2
|
||||||
while (70 * n - 30 < size) {
|
while (70 * n - 30 <= size) {
|
||||||
++n
|
++n
|
||||||
}
|
}
|
||||||
return n - 1
|
return n - 1
|
||||||
|
|||||||
@@ -57,12 +57,14 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log(TAG, ERROR) { "provideGlance setup failed: ${e.asLog()}" }
|
log(TAG, ERROR) { "provideGlance setup failed: ${e.asLog()}" }
|
||||||
provideContent {
|
provideContent {
|
||||||
|
val layout = BatteryLayout.forCells(getCellsForSize(LocalSize.current.width.value.toInt()))
|
||||||
GlanceWidgetContent(
|
GlanceWidgetContent(
|
||||||
state = WidgetRenderState.Message(
|
state = WidgetRenderState.Message(
|
||||||
theme = WidgetTheme.DEFAULT,
|
theme = WidgetTheme.DEFAULT,
|
||||||
resolvedBgColor = WidgetRenderStateMapper.resolvedBgColor(context, WidgetTheme.DEFAULT),
|
resolvedBgColor = WidgetRenderStateMapper.resolvedBgColor(context, WidgetTheme.DEFAULT),
|
||||||
resolvedTextColor = WidgetRenderStateMapper.resolvedTextColor(context, WidgetTheme.DEFAULT),
|
resolvedTextColor = WidgetRenderStateMapper.resolvedTextColor(context, WidgetTheme.DEFAULT),
|
||||||
resolvedIconColor = WidgetRenderStateMapper.resolvedIconColor(context, WidgetTheme.DEFAULT),
|
resolvedIconColor = WidgetRenderStateMapper.resolvedIconColor(context, WidgetTheme.DEFAULT),
|
||||||
|
layout = layout,
|
||||||
primaryText = context.getString(eu.darken.capod.R.string.widget_error_loading_label),
|
primaryText = context.getString(eu.darken.capod.R.string.widget_error_loading_label),
|
||||||
),
|
),
|
||||||
context = context,
|
context = context,
|
||||||
@@ -77,6 +79,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
val profiles by ep.deviceProfilesRepo().profiles.collectAsState(initial = emptyList())
|
val profiles by ep.deviceProfilesRepo().profiles.collectAsState(initial = emptyList())
|
||||||
val upgradeInfo by ep.upgradeRepo().upgradeInfo.collectAsState(initial = null)
|
val upgradeInfo by ep.upgradeRepo().upgradeInfo.collectAsState(initial = null)
|
||||||
val widthDp = LocalSize.current.width
|
val widthDp = LocalSize.current.width
|
||||||
|
val layout = BatteryLayout.forCells(getCellsForSize(widthDp.value.toInt()))
|
||||||
|
|
||||||
val state = try {
|
val state = try {
|
||||||
val profileId = ep.widgetSettings().getWidgetProfile(appWidgetId)
|
val profileId = ep.widgetSettings().getWidgetProfile(appWidgetId)
|
||||||
@@ -93,8 +96,6 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
profiles.firstOrNull { it.id == pid }?.label
|
profiles.firstOrNull { it.id == pid }?.label
|
||||||
}
|
}
|
||||||
|
|
||||||
val isWide = getCellsForSize(widthDp.value.toInt()) >= 5
|
|
||||||
|
|
||||||
WidgetRenderStateMapper.map(
|
WidgetRenderStateMapper.map(
|
||||||
context = context,
|
context = context,
|
||||||
device = device,
|
device = device,
|
||||||
@@ -102,7 +103,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
isPro = isPro,
|
isPro = isPro,
|
||||||
hasConfiguredProfile = profileId != null,
|
hasConfiguredProfile = profileId != null,
|
||||||
profileLabel = profileLabel,
|
profileLabel = profileLabel,
|
||||||
isWide = isWide,
|
layout = layout,
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log(TAG, ERROR) { "provideGlance failed: ${e.asLog()}" }
|
log(TAG, ERROR) { "provideGlance failed: ${e.asLog()}" }
|
||||||
@@ -111,6 +112,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
resolvedBgColor = WidgetRenderStateMapper.resolvedBgColor(context, WidgetTheme.DEFAULT),
|
resolvedBgColor = WidgetRenderStateMapper.resolvedBgColor(context, WidgetTheme.DEFAULT),
|
||||||
resolvedTextColor = WidgetRenderStateMapper.resolvedTextColor(context, WidgetTheme.DEFAULT),
|
resolvedTextColor = WidgetRenderStateMapper.resolvedTextColor(context, WidgetTheme.DEFAULT),
|
||||||
resolvedIconColor = WidgetRenderStateMapper.resolvedIconColor(context, WidgetTheme.DEFAULT),
|
resolvedIconColor = WidgetRenderStateMapper.resolvedIconColor(context, WidgetTheme.DEFAULT),
|
||||||
|
layout = layout,
|
||||||
primaryText = context.getString(eu.darken.capod.R.string.widget_error_loading_label),
|
primaryText = context.getString(eu.darken.capod.R.string.widget_error_loading_label),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -137,7 +139,7 @@ class BatteryGlanceWidget : GlanceAppWidget() {
|
|||||||
*/
|
*/
|
||||||
private fun getCellsForSize(size: Int): Int {
|
private fun getCellsForSize(size: Int): Int {
|
||||||
var n = 2
|
var n = 2
|
||||||
while (70 * n - 30 < size) {
|
while (70 * n - 30 <= size) {
|
||||||
++n
|
++n
|
||||||
}
|
}
|
||||||
return n - 1
|
return n - 1
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
@@ -87,86 +88,51 @@ private fun DualPodPreview(
|
|||||||
val iconColor = Color(state.resolvedIconColor)
|
val iconColor = Color(state.resolvedIconColor)
|
||||||
val iconTint = ColorFilter.tint(iconColor)
|
val iconTint = ColorFilter.tint(iconColor)
|
||||||
|
|
||||||
if (state.isWide) {
|
when (state.layout) {
|
||||||
// Wide layout: left | case | right in a horizontal row
|
BatteryLayout.WIDE -> {
|
||||||
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
|
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
// Left pod
|
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint, iconSize = 40, modifier = Modifier.padding(end = 12.dp))
|
||||||
PodItemRow(
|
PodItemRow(state.caseIcon, state.casePercent, state.caseCharging, false, textColor, iconTint, iconSize = 40, modifier = Modifier.padding(end = 12.dp))
|
||||||
icon = state.leftIcon,
|
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint, iconSize = 40)
|
||||||
percent = state.leftPercent,
|
}
|
||||||
charging = state.leftCharging,
|
DeviceLabel(
|
||||||
inEar = state.leftInEar,
|
label = state.deviceLabel,
|
||||||
|
visible = state.theme.showDeviceLabel,
|
||||||
textColor = textColor,
|
textColor = textColor,
|
||||||
iconTint = iconTint,
|
modifier = Modifier.padding(top = 4.dp, bottom = 8.dp),
|
||||||
iconSize = 40,
|
|
||||||
modifier = Modifier.padding(end = 12.dp),
|
|
||||||
)
|
|
||||||
// Case
|
|
||||||
PodItemRow(
|
|
||||||
icon = state.caseIcon,
|
|
||||||
percent = state.casePercent,
|
|
||||||
charging = state.caseCharging,
|
|
||||||
inEar = false,
|
|
||||||
textColor = textColor,
|
|
||||||
iconTint = iconTint,
|
|
||||||
iconSize = 40,
|
|
||||||
modifier = Modifier.padding(end = 12.dp),
|
|
||||||
)
|
|
||||||
// Right pod
|
|
||||||
PodItemRow(
|
|
||||||
icon = state.rightIcon,
|
|
||||||
percent = state.rightPercent,
|
|
||||||
charging = state.rightCharging,
|
|
||||||
inEar = state.rightInEar,
|
|
||||||
textColor = textColor,
|
|
||||||
iconTint = iconTint,
|
|
||||||
iconSize = 40,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DeviceLabel(
|
|
||||||
label = state.deviceLabel,
|
|
||||||
visible = state.theme.showDeviceLabel,
|
|
||||||
textColor = textColor,
|
|
||||||
modifier = Modifier.padding(top = 4.dp, bottom = 8.dp),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Compact layout: vertical stack
|
BatteryLayout.NARROW -> {
|
||||||
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
||||||
PodItemRow(
|
PodItemRow(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textColor, iconTint)
|
||||||
icon = state.leftIcon,
|
PodItemRow(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textColor, iconTint)
|
||||||
percent = state.leftPercent,
|
PodItemRow(state.caseIcon, state.casePercent, state.caseCharging, false, textColor, iconTint)
|
||||||
charging = state.leftCharging,
|
DeviceLabel(
|
||||||
inEar = state.leftInEar,
|
label = state.deviceLabel,
|
||||||
textColor = textColor,
|
visible = state.theme.showDeviceLabel,
|
||||||
iconTint = iconTint,
|
textColor = textColor,
|
||||||
)
|
)
|
||||||
PodItemRow(
|
}
|
||||||
icon = state.rightIcon,
|
}
|
||||||
percent = state.rightPercent,
|
|
||||||
charging = state.rightCharging,
|
BatteryLayout.TINY_COLUMN -> {
|
||||||
inEar = state.rightInEar,
|
WidgetContainer(
|
||||||
textColor = textColor,
|
bgColor = bgColor,
|
||||||
iconTint = iconTint,
|
modifier = modifier,
|
||||||
)
|
horizontalPadding = 4.dp,
|
||||||
PodItemRow(
|
verticalPadding = 0.dp,
|
||||||
icon = state.caseIcon,
|
) {
|
||||||
percent = state.casePercent,
|
TinyPodItem(state.leftIcon, state.leftPercent, textColor, iconTint)
|
||||||
charging = state.caseCharging,
|
TinyPodItem(state.rightIcon, state.rightPercent, textColor, iconTint)
|
||||||
inEar = false,
|
TinyPodItem(state.caseIcon, state.casePercent, textColor, iconTint)
|
||||||
textColor = textColor,
|
}
|
||||||
iconTint = iconTint,
|
|
||||||
)
|
|
||||||
DeviceLabel(
|
|
||||||
label = state.deviceLabel,
|
|
||||||
visible = state.theme.showDeviceLabel,
|
|
||||||
textColor = textColor,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,45 +146,60 @@ private fun SinglePodPreview(
|
|||||||
val textColor = Color(state.resolvedTextColor)
|
val textColor = Color(state.resolvedTextColor)
|
||||||
val iconTint = ColorFilter.tint(Color(state.resolvedIconColor))
|
val iconTint = ColorFilter.tint(Color(state.resolvedIconColor))
|
||||||
|
|
||||||
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
when (state.layout) {
|
||||||
Row(
|
BatteryLayout.WIDE, BatteryLayout.NARROW -> {
|
||||||
horizontalArrangement = Arrangement.Center,
|
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Row(
|
||||||
) {
|
horizontalArrangement = Arrangement.Center,
|
||||||
Image(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
painter = painterResource(state.batteryIcon),
|
) {
|
||||||
contentDescription = null,
|
Image(
|
||||||
modifier = Modifier.size(20.dp),
|
painter = painterResource(state.batteryIcon),
|
||||||
colorFilter = iconTint,
|
contentDescription = null,
|
||||||
)
|
modifier = Modifier.size(20.dp),
|
||||||
Text(
|
colorFilter = iconTint,
|
||||||
text = formatPercent(state.percent.toBatteryOrNull()),
|
)
|
||||||
fontSize = 12.sp,
|
Text(
|
||||||
color = textColor,
|
text = formatPercent(state.percent.toBatteryOrNull()),
|
||||||
modifier = Modifier.padding(horizontal = 8.dp),
|
fontSize = 12.sp,
|
||||||
)
|
color = textColor,
|
||||||
if (state.charging) {
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
Image(
|
)
|
||||||
painter = painterResource(R.drawable.ic_baseline_power_24),
|
if (state.charging) {
|
||||||
contentDescription = null,
|
Image(
|
||||||
modifier = Modifier.size(20.dp),
|
painter = painterResource(R.drawable.ic_baseline_power_24),
|
||||||
colorFilter = iconTint,
|
contentDescription = null,
|
||||||
)
|
modifier = Modifier.size(20.dp),
|
||||||
}
|
colorFilter = iconTint,
|
||||||
if (state.worn) {
|
)
|
||||||
Image(
|
}
|
||||||
painter = painterResource(R.drawable.ic_baseline_hearing_24),
|
if (state.worn) {
|
||||||
contentDescription = null,
|
Image(
|
||||||
modifier = Modifier.size(20.dp),
|
painter = painterResource(R.drawable.ic_baseline_hearing_24),
|
||||||
colorFilter = iconTint,
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
colorFilter = iconTint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeviceLabel(
|
||||||
|
label = state.deviceLabel,
|
||||||
|
visible = state.theme.showDeviceLabel,
|
||||||
|
textColor = textColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeviceLabel(
|
|
||||||
label = state.deviceLabel,
|
BatteryLayout.TINY_COLUMN -> {
|
||||||
visible = state.theme.showDeviceLabel,
|
WidgetContainer(
|
||||||
textColor = textColor,
|
bgColor = bgColor,
|
||||||
)
|
modifier = modifier,
|
||||||
|
horizontalPadding = 4.dp,
|
||||||
|
verticalPadding = 0.dp,
|
||||||
|
) {
|
||||||
|
TinyPodItem(state.batteryIcon, state.percent, textColor, iconTint)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,17 +210,25 @@ private fun MessagePreview(
|
|||||||
) {
|
) {
|
||||||
val bgColor = Color(state.resolvedBgColor)
|
val bgColor = Color(state.resolvedBgColor)
|
||||||
val textColor = Color(state.resolvedTextColor)
|
val textColor = Color(state.resolvedTextColor)
|
||||||
|
val isCompact = state.layout == BatteryLayout.TINY_COLUMN
|
||||||
|
|
||||||
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
WidgetContainer(
|
||||||
|
bgColor = bgColor,
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalPadding = if (isCompact) 4.dp else 16.dp,
|
||||||
|
verticalPadding = if (isCompact) 0.dp else 4.dp,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = state.primaryText,
|
text = state.primaryText,
|
||||||
fontSize = 12.sp,
|
fontSize = if (isCompact) 10.sp else 12.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = if (isCompact) 2 else Int.MAX_VALUE,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
if (state.secondaryText != null) {
|
if (!isCompact && state.secondaryText != null) {
|
||||||
Text(
|
Text(
|
||||||
text = state.secondaryText,
|
text = state.secondaryText,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
@@ -260,11 +249,17 @@ private fun LoadingPreview(
|
|||||||
) {
|
) {
|
||||||
val bgColor = Color(state.resolvedBgColor)
|
val bgColor = Color(state.resolvedBgColor)
|
||||||
val textColor = Color(state.resolvedTextColor)
|
val textColor = Color(state.resolvedTextColor)
|
||||||
|
val isCompact = state.layout == BatteryLayout.TINY_COLUMN
|
||||||
|
|
||||||
WidgetContainer(bgColor = bgColor, modifier = modifier) {
|
WidgetContainer(
|
||||||
|
bgColor = bgColor,
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalPadding = if (isCompact) 4.dp else 16.dp,
|
||||||
|
verticalPadding = if (isCompact) 0.dp else 4.dp,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "…",
|
text = "…",
|
||||||
fontSize = 12.sp,
|
fontSize = if (isCompact) 10.sp else 12.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
@@ -277,13 +272,15 @@ private fun LoadingPreview(
|
|||||||
private fun WidgetContainer(
|
private fun WidgetContainer(
|
||||||
bgColor: Color,
|
bgColor: Color,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
horizontalPadding: Dp = 16.dp,
|
||||||
|
verticalPadding: Dp = 4.dp,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(bgColor)
|
.background(bgColor)
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
.padding(horizontal = horizontalPadding, vertical = verticalPadding),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
@@ -338,6 +335,34 @@ private fun PodItemRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TinyPodItem(
|
||||||
|
icon: Int,
|
||||||
|
percent: Float,
|
||||||
|
textColor: Color,
|
||||||
|
iconTint: ColorFilter,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(icon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(14.dp),
|
||||||
|
colorFilter = iconTint,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = formatPercent(percent.toBatteryOrNull()),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = textColor,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.padding(start = 2.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DeviceLabel(
|
private fun DeviceLabel(
|
||||||
label: String?,
|
label: String?,
|
||||||
@@ -363,12 +388,30 @@ private fun formatPercent(percent: Float?): String {
|
|||||||
|
|
||||||
@Preview2
|
@Preview2
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreviewDualCompact() = PreviewWrapper {
|
private fun PreviewDualTiny11() = PreviewWrapper {
|
||||||
ComposeWidgetPreview(state = WidgetRenderState.previewDualPod())
|
ComposeWidgetPreview(
|
||||||
|
state = WidgetRenderState.previewDualPod(layout = BatteryLayout.TINY_COLUMN),
|
||||||
|
modifier = Modifier.size(40.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview2
|
||||||
|
@Composable
|
||||||
|
private fun PreviewDualTinyTall() = PreviewWrapper {
|
||||||
|
ComposeWidgetPreview(
|
||||||
|
state = WidgetRenderState.previewDualPod(layout = BatteryLayout.TINY_COLUMN),
|
||||||
|
modifier = Modifier.size(width = 40.dp, height = 110.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview2
|
||||||
|
@Composable
|
||||||
|
private fun PreviewDualNarrow() = PreviewWrapper {
|
||||||
|
ComposeWidgetPreview(state = WidgetRenderState.previewDualPod(layout = BatteryLayout.NARROW))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview2
|
@Preview2
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreviewDualWide() = PreviewWrapper {
|
private fun PreviewDualWide() = PreviewWrapper {
|
||||||
ComposeWidgetPreview(state = WidgetRenderState.previewDualPod(isWide = true))
|
ComposeWidgetPreview(state = WidgetRenderState.previewDualPod(layout = BatteryLayout.WIDE))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package eu.darken.capod.main.ui.widget
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.glance.ColorFilter
|
import androidx.glance.ColorFilter
|
||||||
@@ -53,31 +54,47 @@ private fun GlanceDualPod(
|
|||||||
state: WidgetRenderState.DualPod,
|
state: WidgetRenderState.DualPod,
|
||||||
clickModifier: GlanceModifier,
|
clickModifier: GlanceModifier,
|
||||||
) {
|
) {
|
||||||
val textStyle = TextStyle(
|
|
||||||
color = fixedColor(state.resolvedTextColor),
|
|
||||||
fontSize = 12.sp,
|
|
||||||
)
|
|
||||||
val iconTint = ColorFilter.tint(fixedColor(state.resolvedIconColor))
|
val iconTint = ColorFilter.tint(fixedColor(state.resolvedIconColor))
|
||||||
|
|
||||||
if (state.isWide) {
|
when (state.layout) {
|
||||||
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
BatteryLayout.WIDE -> {
|
||||||
Row(
|
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
|
||||||
modifier = GlanceModifier.fillMaxWidth().padding(top = 8.dp, bottom = 4.dp),
|
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = GlanceModifier.fillMaxWidth().padding(top = 8.dp, bottom = 4.dp),
|
||||||
) {
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
|
) {
|
||||||
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint, iconSize = 40)
|
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
|
||||||
|
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint, iconSize = 40, modifier = GlanceModifier.padding(end = 12.dp))
|
||||||
|
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint, iconSize = 40)
|
||||||
|
}
|
||||||
|
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
||||||
}
|
}
|
||||||
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
BatteryLayout.NARROW -> {
|
||||||
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint)
|
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
|
||||||
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint)
|
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
||||||
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint)
|
GlancePodItem(state.leftIcon, state.leftPercent, state.leftCharging, state.leftInEar, textStyle, iconTint)
|
||||||
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
GlancePodItem(state.rightIcon, state.rightPercent, state.rightCharging, state.rightInEar, textStyle, iconTint)
|
||||||
|
GlancePodItem(state.caseIcon, state.casePercent, state.caseCharging, false, textStyle, iconTint)
|
||||||
|
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryLayout.TINY_COLUMN -> {
|
||||||
|
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
|
||||||
|
GlanceWidgetRoot(
|
||||||
|
bgColor = state.resolvedBgColor,
|
||||||
|
clickModifier = clickModifier,
|
||||||
|
horizontalPadding = 4.dp,
|
||||||
|
verticalPadding = 0.dp,
|
||||||
|
) {
|
||||||
|
GlanceTinyPodItem(state.leftIcon, state.leftPercent, textStyle, iconTint)
|
||||||
|
GlanceTinyPodItem(state.rightIcon, state.rightPercent, textStyle, iconTint)
|
||||||
|
GlanceTinyPodItem(state.caseIcon, state.casePercent, textStyle, iconTint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,47 +104,60 @@ private fun GlanceSinglePod(
|
|||||||
state: WidgetRenderState.SinglePod,
|
state: WidgetRenderState.SinglePod,
|
||||||
clickModifier: GlanceModifier,
|
clickModifier: GlanceModifier,
|
||||||
) {
|
) {
|
||||||
val textStyle = TextStyle(
|
|
||||||
color = fixedColor(state.resolvedTextColor),
|
|
||||||
fontSize = 12.sp,
|
|
||||||
)
|
|
||||||
val iconTint = ColorFilter.tint(fixedColor(state.resolvedIconColor))
|
val iconTint = ColorFilter.tint(fixedColor(state.resolvedIconColor))
|
||||||
|
|
||||||
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
when (state.layout) {
|
||||||
Row(
|
BatteryLayout.WIDE, BatteryLayout.NARROW -> {
|
||||||
modifier = GlanceModifier.fillMaxWidth(),
|
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Row(
|
||||||
) {
|
modifier = GlanceModifier.fillMaxWidth(),
|
||||||
Image(
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
provider = ImageProvider(state.batteryIcon),
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
contentDescription = null,
|
) {
|
||||||
modifier = GlanceModifier.size(20.dp),
|
Image(
|
||||||
colorFilter = iconTint,
|
provider = ImageProvider(state.batteryIcon),
|
||||||
)
|
contentDescription = null,
|
||||||
Text(
|
modifier = GlanceModifier.size(20.dp),
|
||||||
text = formatGlancePercent(state.percent.toBatteryOrNull()),
|
colorFilter = iconTint,
|
||||||
style = textStyle,
|
)
|
||||||
modifier = GlanceModifier.padding(horizontal = 8.dp),
|
Text(
|
||||||
)
|
text = formatGlancePercent(state.percent.toBatteryOrNull()),
|
||||||
if (state.charging) {
|
style = textStyle,
|
||||||
Image(
|
modifier = GlanceModifier.padding(horizontal = 8.dp),
|
||||||
provider = ImageProvider(R.drawable.ic_baseline_power_24),
|
)
|
||||||
contentDescription = null,
|
if (state.charging) {
|
||||||
modifier = GlanceModifier.size(20.dp),
|
Image(
|
||||||
colorFilter = iconTint,
|
provider = ImageProvider(R.drawable.ic_baseline_power_24),
|
||||||
)
|
contentDescription = null,
|
||||||
|
modifier = GlanceModifier.size(20.dp),
|
||||||
|
colorFilter = iconTint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (state.worn) {
|
||||||
|
Image(
|
||||||
|
provider = ImageProvider(R.drawable.ic_baseline_hearing_24),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = GlanceModifier.size(20.dp),
|
||||||
|
colorFilter = iconTint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
||||||
}
|
}
|
||||||
if (state.worn) {
|
}
|
||||||
Image(
|
|
||||||
provider = ImageProvider(R.drawable.ic_baseline_hearing_24),
|
BatteryLayout.TINY_COLUMN -> {
|
||||||
contentDescription = null,
|
val textStyle = TextStyle(color = fixedColor(state.resolvedTextColor), fontSize = 12.sp)
|
||||||
modifier = GlanceModifier.size(20.dp),
|
GlanceWidgetRoot(
|
||||||
colorFilter = iconTint,
|
bgColor = state.resolvedBgColor,
|
||||||
)
|
clickModifier = clickModifier,
|
||||||
|
horizontalPadding = 4.dp,
|
||||||
|
verticalPadding = 0.dp,
|
||||||
|
) {
|
||||||
|
GlanceTinyPodItem(state.batteryIcon, state.percent, textStyle, iconTint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlanceDeviceLabel(state.deviceLabel, state.theme.showDeviceLabel, state.resolvedTextColor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,20 +166,29 @@ private fun GlanceMessage(
|
|||||||
state: WidgetRenderState.Message,
|
state: WidgetRenderState.Message,
|
||||||
clickModifier: GlanceModifier,
|
clickModifier: GlanceModifier,
|
||||||
) {
|
) {
|
||||||
val textStyle = TextStyle(
|
val isCompact = state.layout == BatteryLayout.TINY_COLUMN
|
||||||
color = fixedColor(state.resolvedTextColor),
|
val fontSize = if (isCompact) 10.sp else 12.sp
|
||||||
fontSize = 12.sp,
|
val hPad = if (isCompact) 4.dp else 16.dp
|
||||||
fontWeight = FontWeight.Bold,
|
val vPad = if (isCompact) 0.dp else 4.dp
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
)
|
|
||||||
|
|
||||||
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
GlanceWidgetRoot(
|
||||||
|
bgColor = state.resolvedBgColor,
|
||||||
|
clickModifier = clickModifier,
|
||||||
|
horizontalPadding = hPad,
|
||||||
|
verticalPadding = vPad,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = state.primaryText,
|
text = state.primaryText,
|
||||||
style = textStyle,
|
style = TextStyle(
|
||||||
|
color = fixedColor(state.resolvedTextColor),
|
||||||
|
fontSize = fontSize,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
),
|
||||||
modifier = GlanceModifier.fillMaxWidth(),
|
modifier = GlanceModifier.fillMaxWidth(),
|
||||||
|
maxLines = if (isCompact) 2 else Int.MAX_VALUE,
|
||||||
)
|
)
|
||||||
if (state.secondaryText != null) {
|
if (!isCompact && state.secondaryText != null) {
|
||||||
Text(
|
Text(
|
||||||
text = state.secondaryText,
|
text = state.secondaryText,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
@@ -169,12 +208,18 @@ private fun GlanceLoading(
|
|||||||
state: WidgetRenderState.Loading,
|
state: WidgetRenderState.Loading,
|
||||||
clickModifier: GlanceModifier,
|
clickModifier: GlanceModifier,
|
||||||
) {
|
) {
|
||||||
GlanceWidgetRoot(state.resolvedBgColor, clickModifier) {
|
val isCompact = state.layout == BatteryLayout.TINY_COLUMN
|
||||||
|
GlanceWidgetRoot(
|
||||||
|
bgColor = state.resolvedBgColor,
|
||||||
|
clickModifier = clickModifier,
|
||||||
|
horizontalPadding = if (isCompact) 4.dp else 16.dp,
|
||||||
|
verticalPadding = if (isCompact) 0.dp else 4.dp,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "…",
|
text = "…",
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = fixedColor(state.resolvedTextColor),
|
color = fixedColor(state.resolvedTextColor),
|
||||||
fontSize = 12.sp,
|
fontSize = if (isCompact) 10.sp else 12.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
),
|
),
|
||||||
@@ -187,13 +232,15 @@ private fun GlanceLoading(
|
|||||||
private fun GlanceWidgetRoot(
|
private fun GlanceWidgetRoot(
|
||||||
bgColor: Int,
|
bgColor: Int,
|
||||||
clickModifier: GlanceModifier,
|
clickModifier: GlanceModifier,
|
||||||
|
horizontalPadding: Dp = 16.dp,
|
||||||
|
verticalPadding: Dp = 4.dp,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = clickModifier
|
modifier = clickModifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(fixedColor(bgColor))
|
.background(fixedColor(bgColor))
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
.padding(horizontal = horizontalPadding, vertical = verticalPadding),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
@@ -246,6 +293,33 @@ private fun GlancePodItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun GlanceTinyPodItem(
|
||||||
|
icon: Int,
|
||||||
|
percent: Float,
|
||||||
|
textStyle: TextStyle,
|
||||||
|
iconTint: ColorFilter,
|
||||||
|
modifier: GlanceModifier = GlanceModifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
provider = ImageProvider(icon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = GlanceModifier.size(14.dp),
|
||||||
|
colorFilter = iconTint,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = formatGlancePercent(percent.toBatteryOrNull()),
|
||||||
|
style = textStyle,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = GlanceModifier.padding(start = 4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun GlanceDeviceLabel(
|
private fun GlanceDeviceLabel(
|
||||||
label: String?,
|
label: String?,
|
||||||
|
|||||||
@@ -4,18 +4,33 @@ import androidx.annotation.ColorInt
|
|||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
|
|
||||||
|
enum class BatteryLayout {
|
||||||
|
TINY_COLUMN,
|
||||||
|
NARROW,
|
||||||
|
WIDE;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun forCells(widthCells: Int): BatteryLayout = when {
|
||||||
|
widthCells <= 1 -> TINY_COLUMN
|
||||||
|
widthCells >= 5 -> WIDE
|
||||||
|
else -> NARROW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed class WidgetRenderState {
|
sealed class WidgetRenderState {
|
||||||
abstract val theme: WidgetTheme
|
abstract val theme: WidgetTheme
|
||||||
@get:ColorInt abstract val resolvedBgColor: Int
|
@get:ColorInt abstract val resolvedBgColor: Int
|
||||||
@get:ColorInt abstract val resolvedTextColor: Int
|
@get:ColorInt abstract val resolvedTextColor: Int
|
||||||
@get:ColorInt abstract val resolvedIconColor: Int
|
@get:ColorInt abstract val resolvedIconColor: Int
|
||||||
|
abstract val layout: BatteryLayout
|
||||||
|
|
||||||
data class DualPod(
|
data class DualPod(
|
||||||
override val theme: WidgetTheme,
|
override val theme: WidgetTheme,
|
||||||
@ColorInt override val resolvedBgColor: Int,
|
@ColorInt override val resolvedBgColor: Int,
|
||||||
@ColorInt override val resolvedTextColor: Int,
|
@ColorInt override val resolvedTextColor: Int,
|
||||||
@ColorInt override val resolvedIconColor: Int,
|
@ColorInt override val resolvedIconColor: Int,
|
||||||
val isWide: Boolean,
|
override val layout: BatteryLayout,
|
||||||
val deviceLabel: String?,
|
val deviceLabel: String?,
|
||||||
@DrawableRes val leftIcon: Int,
|
@DrawableRes val leftIcon: Int,
|
||||||
val leftPercent: Float,
|
val leftPercent: Float,
|
||||||
@@ -35,6 +50,7 @@ sealed class WidgetRenderState {
|
|||||||
@ColorInt override val resolvedBgColor: Int,
|
@ColorInt override val resolvedBgColor: Int,
|
||||||
@ColorInt override val resolvedTextColor: Int,
|
@ColorInt override val resolvedTextColor: Int,
|
||||||
@ColorInt override val resolvedIconColor: Int,
|
@ColorInt override val resolvedIconColor: Int,
|
||||||
|
override val layout: BatteryLayout,
|
||||||
val deviceLabel: String?,
|
val deviceLabel: String?,
|
||||||
@DrawableRes val headsetIcon: Int,
|
@DrawableRes val headsetIcon: Int,
|
||||||
val percent: Float,
|
val percent: Float,
|
||||||
@@ -48,6 +64,7 @@ sealed class WidgetRenderState {
|
|||||||
@ColorInt override val resolvedBgColor: Int,
|
@ColorInt override val resolvedBgColor: Int,
|
||||||
@ColorInt override val resolvedTextColor: Int,
|
@ColorInt override val resolvedTextColor: Int,
|
||||||
@ColorInt override val resolvedIconColor: Int,
|
@ColorInt override val resolvedIconColor: Int,
|
||||||
|
override val layout: BatteryLayout,
|
||||||
val primaryText: String,
|
val primaryText: String,
|
||||||
val secondaryText: String? = null,
|
val secondaryText: String? = null,
|
||||||
) : WidgetRenderState()
|
) : WidgetRenderState()
|
||||||
@@ -57,6 +74,7 @@ sealed class WidgetRenderState {
|
|||||||
@ColorInt override val resolvedBgColor: Int,
|
@ColorInt override val resolvedBgColor: Int,
|
||||||
@ColorInt override val resolvedTextColor: Int,
|
@ColorInt override val resolvedTextColor: Int,
|
||||||
@ColorInt override val resolvedIconColor: Int,
|
@ColorInt override val resolvedIconColor: Int,
|
||||||
|
override val layout: BatteryLayout,
|
||||||
) : WidgetRenderState()
|
) : WidgetRenderState()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -65,13 +83,13 @@ sealed class WidgetRenderState {
|
|||||||
@ColorInt bgColor: Int = 0xFFFFFFFF.toInt(),
|
@ColorInt bgColor: Int = 0xFFFFFFFF.toInt(),
|
||||||
@ColorInt textColor: Int = 0xFF1E1E1E.toInt(),
|
@ColorInt textColor: Int = 0xFF1E1E1E.toInt(),
|
||||||
@ColorInt iconColor: Int = 0xFF1E1E1E.toInt(),
|
@ColorInt iconColor: Int = 0xFF1E1E1E.toInt(),
|
||||||
isWide: Boolean = false,
|
layout: BatteryLayout = BatteryLayout.NARROW,
|
||||||
): DualPod = DualPod(
|
): DualPod = DualPod(
|
||||||
theme = theme,
|
theme = theme,
|
||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
isWide = isWide,
|
layout = layout,
|
||||||
deviceLabel = "My AirPods Pro",
|
deviceLabel = "My AirPods Pro",
|
||||||
leftIcon = R.drawable.device_airpods_pro2_left,
|
leftIcon = R.drawable.device_airpods_pro2_left,
|
||||||
leftPercent = 0.85f,
|
leftPercent = 0.85f,
|
||||||
@@ -85,5 +103,25 @@ sealed class WidgetRenderState {
|
|||||||
casePercent = 1.0f,
|
casePercent = 1.0f,
|
||||||
caseCharging = false,
|
caseCharging = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun previewSinglePod(
|
||||||
|
theme: WidgetTheme = WidgetTheme.DEFAULT,
|
||||||
|
@ColorInt bgColor: Int = 0xFFFFFFFF.toInt(),
|
||||||
|
@ColorInt textColor: Int = 0xFF1E1E1E.toInt(),
|
||||||
|
@ColorInt iconColor: Int = 0xFF1E1E1E.toInt(),
|
||||||
|
layout: BatteryLayout = BatteryLayout.NARROW,
|
||||||
|
): SinglePod = SinglePod(
|
||||||
|
theme = theme,
|
||||||
|
resolvedBgColor = bgColor,
|
||||||
|
resolvedTextColor = textColor,
|
||||||
|
resolvedIconColor = iconColor,
|
||||||
|
layout = layout,
|
||||||
|
deviceLabel = "My AirPods Max",
|
||||||
|
headsetIcon = R.drawable.device_airpods_max,
|
||||||
|
percent = 0.72f,
|
||||||
|
batteryIcon = R.drawable.ic_baseline_battery_3_bar_24,
|
||||||
|
charging = false,
|
||||||
|
worn = true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ object WidgetRenderStateMapper {
|
|||||||
isPro: Boolean,
|
isPro: Boolean,
|
||||||
hasConfiguredProfile: Boolean,
|
hasConfiguredProfile: Boolean,
|
||||||
profileLabel: String?,
|
profileLabel: String?,
|
||||||
isWide: Boolean = false,
|
layout: BatteryLayout = BatteryLayout.NARROW,
|
||||||
): WidgetRenderState {
|
): WidgetRenderState {
|
||||||
val bgColor = resolvedBgColor(context, theme)
|
val bgColor = resolvedBgColor(context, theme)
|
||||||
val textColor = resolvedTextColor(context, theme)
|
val textColor = resolvedTextColor(context, theme)
|
||||||
@@ -30,6 +30,7 @@ object WidgetRenderStateMapper {
|
|||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
|
layout = layout,
|
||||||
primaryText = context.getString(R.string.upgrade_capod_label),
|
primaryText = context.getString(R.string.upgrade_capod_label),
|
||||||
secondaryText = context.getString(R.string.upgrade_capod_description),
|
secondaryText = context.getString(R.string.upgrade_capod_description),
|
||||||
)
|
)
|
||||||
@@ -39,7 +40,7 @@ object WidgetRenderStateMapper {
|
|||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
isWide = isWide,
|
layout = layout,
|
||||||
deviceLabel = profileLabel ?: device.getLabel(context),
|
deviceLabel = profileLabel ?: device.getLabel(context),
|
||||||
leftIcon = device.leftPodIcon,
|
leftIcon = device.leftPodIcon,
|
||||||
leftPercent = device.batteryLeft.toBatteryFloat(),
|
leftPercent = device.batteryLeft.toBatteryFloat(),
|
||||||
@@ -59,6 +60,7 @@ object WidgetRenderStateMapper {
|
|||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
|
layout = layout,
|
||||||
deviceLabel = profileLabel ?: device.getLabel(context),
|
deviceLabel = profileLabel ?: device.getLabel(context),
|
||||||
headsetIcon = device.iconRes,
|
headsetIcon = device.iconRes,
|
||||||
percent = device.batteryHeadset.toBatteryFloat(),
|
percent = device.batteryHeadset.toBatteryFloat(),
|
||||||
@@ -72,6 +74,7 @@ object WidgetRenderStateMapper {
|
|||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
|
layout = layout,
|
||||||
primaryText = context.getString(R.string.pods_unknown_label),
|
primaryText = context.getString(R.string.pods_unknown_label),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,6 +89,7 @@ object WidgetRenderStateMapper {
|
|||||||
resolvedBgColor = bgColor,
|
resolvedBgColor = bgColor,
|
||||||
resolvedTextColor = textColor,
|
resolvedTextColor = textColor,
|
||||||
resolvedIconColor = iconColor,
|
resolvedIconColor = iconColor,
|
||||||
|
layout = layout,
|
||||||
primaryText = context.getString(messageRes),
|
primaryText = context.getString(messageRes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:minWidth="80dp"
|
android:minWidth="40dp"
|
||||||
android:minHeight="40dp"
|
android:minHeight="40dp"
|
||||||
android:targetCellWidth="2"
|
android:targetCellWidth="2"
|
||||||
android:targetCellHeight="1"
|
android:targetCellHeight="1"
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package eu.darken.capod.main.ui.widget
|
||||||
|
|
||||||
|
import eu.darken.capod.main.ui.widget.BatteryLayout.NARROW
|
||||||
|
import eu.darken.capod.main.ui.widget.BatteryLayout.TINY_COLUMN
|
||||||
|
import eu.darken.capod.main.ui.widget.BatteryLayout.WIDE
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testhelpers.BaseTest
|
||||||
|
|
||||||
|
class BatteryLayoutTest : BaseTest() {
|
||||||
|
|
||||||
|
@Test fun `1 cell wide resolves to tiny column`() {
|
||||||
|
BatteryLayout.forCells(1) shouldBe TINY_COLUMN
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `2 cells wide resolves to narrow`() {
|
||||||
|
BatteryLayout.forCells(2) shouldBe NARROW
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `3 cells wide resolves to narrow`() {
|
||||||
|
BatteryLayout.forCells(3) shouldBe NARROW
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `4 cells wide resolves to narrow`() {
|
||||||
|
BatteryLayout.forCells(4) shouldBe NARROW
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `5 cells wide resolves to wide`() {
|
||||||
|
BatteryLayout.forCells(5) shouldBe WIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `8 cells wide resolves to wide`() {
|
||||||
|
BatteryLayout.forCells(8) shouldBe WIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `zero cells resolves to tiny column not thrown`() {
|
||||||
|
BatteryLayout.forCells(0) shouldBe TINY_COLUMN
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user