mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Add modern battery info component (#2801)
This commit is contained in:
parent
7e55729ee1
commit
1a9771ea28
2 changed files with 313 additions and 0 deletions
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.common.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.BatteryUnknown
|
||||
import androidx.compose.material.icons.rounded.BatteryUnknown
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.ui.common.icons.BatteryEmpty
|
||||
import com.geeksville.mesh.ui.common.icons.BatteryUnknown
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusOrange
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
|
||||
private const val FORMAT = "%d%%"
|
||||
private const val SIZE_ICON = 20
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun MaterialBatteryInfo(modifier: Modifier = Modifier, level: Int) {
|
||||
val levelString = FORMAT.format(level)
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
if (level > 100) {
|
||||
Icon(
|
||||
modifier = Modifier.size(SIZE_ICON.dp).rotate(90f),
|
||||
imageVector = Icons.Rounded.Power,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(text = "PWD", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.labelLarge)
|
||||
} else if (level < 0) {
|
||||
Icon(
|
||||
modifier = Modifier.size(SIZE_ICON.dp),
|
||||
imageVector = BatteryUnknown,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
// Map battery percentage to color
|
||||
val fillColor =
|
||||
when (level) {
|
||||
in 0..19 -> MaterialTheme.colorScheme.StatusRed
|
||||
in 20..39 -> MaterialTheme.colorScheme.StatusOrange
|
||||
else -> MaterialTheme.colorScheme.StatusGreen
|
||||
}
|
||||
|
||||
Icon(
|
||||
modifier =
|
||||
Modifier.size(SIZE_ICON.dp).drawBehind {
|
||||
val insetVertical = size.height * .28f
|
||||
val insetLeft = size.width * .11f
|
||||
val insetRight = size.width * .22f
|
||||
|
||||
val availableWidth = size.width - (insetLeft + insetRight)
|
||||
val availableHeight = size.height - (insetVertical * 2)
|
||||
|
||||
// Fill (grow from left to right)
|
||||
val fillWidth = availableWidth * (level / 100f)
|
||||
|
||||
drawRect(
|
||||
color = fillColor,
|
||||
topLeft = Offset(insetLeft, insetVertical),
|
||||
size = Size(fillWidth, availableHeight),
|
||||
)
|
||||
},
|
||||
imageVector = BatteryEmpty,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = levelString,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BatteryLevelProvider : PreviewParameterProvider<Int> {
|
||||
override val values: Sequence<Int> = sequenceOf(-1, 19, 39, 90, 101)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
fun MaterialBatteryInfoPreview(@PreviewParameter(BatteryLevelProvider::class) batteryLevel: Int) {
|
||||
AppTheme { MaterialBatteryInfo(level = batteryLevel) }
|
||||
}
|
||||
184
app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt
Normal file
184
app/src/main/java/com/geeksville/mesh/ui/common/icons/Battery.kt
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.common.icons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* This is from Material Symbols.
|
||||
*
|
||||
* @see
|
||||
* [battery_android_0](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:battery_android_0:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=battery&icon.set=Material+Symbols&icon.size=24&icon.color=%23000000&icon.platform=android)
|
||||
*/
|
||||
val BatteryEmpty: ImageVector
|
||||
get() {
|
||||
if (batteryEmpty != null) {
|
||||
return batteryEmpty!!
|
||||
}
|
||||
batteryEmpty =
|
||||
ImageVector.Builder(
|
||||
name = "BatteryEmpty",
|
||||
defaultWidth = 24.dp,
|
||||
defaultHeight = 24.dp,
|
||||
viewportWidth = 960f,
|
||||
viewportHeight = 960f,
|
||||
)
|
||||
.apply {
|
||||
path(fill = SolidColor(Color.Black)) {
|
||||
moveTo(160f, 720f)
|
||||
quadToRelative(-50f, 0f, -85f, -35f)
|
||||
reflectiveQuadToRelative(-35f, -85f)
|
||||
verticalLineToRelative(-240f)
|
||||
quadToRelative(0f, -50f, 35f, -85f)
|
||||
reflectiveQuadToRelative(85f, -35f)
|
||||
horizontalLineToRelative(540f)
|
||||
quadToRelative(50f, 0f, 85f, 35f)
|
||||
reflectiveQuadToRelative(35f, 85f)
|
||||
verticalLineToRelative(240f)
|
||||
quadToRelative(0f, 50f, -35f, 85f)
|
||||
reflectiveQuadToRelative(-85f, 35f)
|
||||
lineTo(160f, 720f)
|
||||
close()
|
||||
moveTo(160f, 640f)
|
||||
horizontalLineToRelative(540f)
|
||||
quadToRelative(17f, 0f, 28.5f, -11.5f)
|
||||
reflectiveQuadTo(740f, 600f)
|
||||
verticalLineToRelative(-240f)
|
||||
quadToRelative(0f, -17f, -11.5f, -28.5f)
|
||||
reflectiveQuadTo(700f, 320f)
|
||||
lineTo(160f, 320f)
|
||||
quadToRelative(-17f, 0f, -28.5f, 11.5f)
|
||||
reflectiveQuadTo(120f, 360f)
|
||||
verticalLineToRelative(240f)
|
||||
quadToRelative(0f, 17f, 11.5f, 28.5f)
|
||||
reflectiveQuadTo(160f, 640f)
|
||||
close()
|
||||
moveTo(860f, 580f)
|
||||
verticalLineToRelative(-200f)
|
||||
horizontalLineToRelative(20f)
|
||||
quadToRelative(17f, 0f, 28.5f, 11.5f)
|
||||
reflectiveQuadTo(920f, 420f)
|
||||
verticalLineToRelative(120f)
|
||||
quadToRelative(0f, 17f, -11.5f, 28.5f)
|
||||
reflectiveQuadTo(880f, 580f)
|
||||
horizontalLineToRelative(-20f)
|
||||
close()
|
||||
moveTo(120f, 640f)
|
||||
verticalLineToRelative(-320f)
|
||||
verticalLineToRelative(320f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
return batteryEmpty!!
|
||||
}
|
||||
|
||||
private var batteryEmpty: ImageVector? = null
|
||||
|
||||
/**
|
||||
* This is from Material Symbols.
|
||||
*
|
||||
* @see
|
||||
* [battery_android_question](https://fonts.google.com/icons?selected=Material+Symbols+Outlined:battery_android_question:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=battery&icon.set=Material+Symbols&icon.size=24&icon.color=%23000000&icon.platform=android)
|
||||
*/
|
||||
val BatteryUnknown: ImageVector
|
||||
get() {
|
||||
if (batteryUnknown != null) {
|
||||
return batteryUnknown!!
|
||||
}
|
||||
batteryUnknown =
|
||||
ImageVector.Builder(
|
||||
name = "BatteryUnknown",
|
||||
defaultWidth = 24.dp,
|
||||
defaultHeight = 24.dp,
|
||||
viewportWidth = 960f,
|
||||
viewportHeight = 960f,
|
||||
)
|
||||
.apply {
|
||||
path(fill = SolidColor(Color.Black)) {
|
||||
moveTo(120f, 640f)
|
||||
verticalLineToRelative(-320f)
|
||||
verticalLineToRelative(320f)
|
||||
close()
|
||||
moveTo(726f, 720f)
|
||||
lineTo(160f, 720f)
|
||||
quadToRelative(-50f, 0f, -85f, -35f)
|
||||
reflectiveQuadToRelative(-35f, -85f)
|
||||
verticalLineToRelative(-240f)
|
||||
quadToRelative(0f, -50f, 35f, -85f)
|
||||
reflectiveQuadToRelative(85f, -35f)
|
||||
horizontalLineToRelative(521f)
|
||||
quadToRelative(-20f, 16f, -35f, 36f)
|
||||
reflectiveQuadToRelative(-25f, 44f)
|
||||
lineTo(160f, 320f)
|
||||
quadToRelative(-17f, 0f, -28.5f, 11.5f)
|
||||
reflectiveQuadTo(120f, 360f)
|
||||
verticalLineToRelative(240f)
|
||||
quadToRelative(0f, 17f, 11.5f, 28.5f)
|
||||
reflectiveQuadTo(160f, 640f)
|
||||
horizontalLineToRelative(520f)
|
||||
quadToRelative(2f, 25f, 14.5f, 45.5f)
|
||||
reflectiveQuadTo(726f, 720f)
|
||||
close()
|
||||
moveTo(800f, 660f)
|
||||
quadToRelative(17f, 0f, 28.5f, -11.5f)
|
||||
reflectiveQuadTo(840f, 620f)
|
||||
quadToRelative(0f, -17f, -11.5f, -28.5f)
|
||||
reflectiveQuadTo(800f, 580f)
|
||||
quadToRelative(-17f, 0f, -28.5f, 11.5f)
|
||||
reflectiveQuadTo(760f, 620f)
|
||||
quadToRelative(0f, 17f, 11.5f, 28.5f)
|
||||
reflectiveQuadTo(800f, 660f)
|
||||
close()
|
||||
moveTo(772f, 538f)
|
||||
horizontalLineToRelative(57f)
|
||||
verticalLineToRelative(-21f)
|
||||
quadToRelative(0f, -10f, 5f, -19f)
|
||||
quadToRelative(6f, -13f, 15.5f, -22f)
|
||||
reflectiveQuadToRelative(19.5f, -19f)
|
||||
quadToRelative(17f, -17f, 28.5f, -37f)
|
||||
reflectiveQuadToRelative(11.5f, -43f)
|
||||
quadToRelative(0f, -42f, -32.5f, -69.5f)
|
||||
reflectiveQuadTo(800f, 280f)
|
||||
quadToRelative(-38f, 0f, -68f, 22f)
|
||||
reflectiveQuadToRelative(-40f, 58f)
|
||||
lineToRelative(51f, 21f)
|
||||
quadToRelative(6f, -20f, 21.5f, -33f)
|
||||
reflectiveQuadToRelative(35.5f, -13f)
|
||||
quadToRelative(21f, 0f, 36.5f, 12f)
|
||||
reflectiveQuadToRelative(15.5f, 32f)
|
||||
quadToRelative(0f, 17f, -10f, 30.5f)
|
||||
reflectiveQuadTo(820f, 434f)
|
||||
quadToRelative(-11f, 11f, -22.5f, 21.5f)
|
||||
reflectiveQuadTo(779f, 480f)
|
||||
quadToRelative(-6f, 14f, -6.5f, 28.5f)
|
||||
reflectiveQuadTo(772f, 538f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
return batteryUnknown!!
|
||||
}
|
||||
|
||||
private var batteryUnknown: ImageVector? = null
|
||||
Loading…
Add table
Add a link
Reference in a new issue