diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index b1ef31ce..41ab65d9 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -540,4 +540,10 @@
You haven\'t connected a Google account yet
Connect account
+
+ Charging
+
+ - Full in %1$s minute
+ - Full in %1$s minutes
+
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/icons/BatteryIcons.kt b/ui/src/main/java/de/mm20/launcher2/ui/icons/BatteryIcons.kt
new file mode 100644
index 00000000..45d2b5b0
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/icons/BatteryIcons.kt
@@ -0,0 +1,538 @@
+package de.mm20.launcher2.ui.icons
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.materialIcon
+import androidx.compose.material.icons.materialPath
+import androidx.compose.ui.graphics.vector.group
+
+val Icons.Rounded.Battery0Bar
+ get() = materialIcon("Icons.Rounded.Battery0Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(14f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery1Bar
+ get() = materialIcon("Icons.Rounded.Battery1Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(12f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery2Bar
+ get() = materialIcon("Icons.Rounded.Battery2Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(10f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery3Bar
+ get() = materialIcon("Icons.Rounded.Battery3Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(8f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery4Bar
+ get() = materialIcon("Icons.Rounded.Battery4Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(6f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery5Bar
+ get() = materialIcon("Icons.Rounded.Battery5Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(4f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.Battery6Bar
+ get() = materialIcon("Icons.Rounded.Battery6Bar") {
+ materialPath {
+ moveTo(17f, 5f)
+ verticalLineToRelative(16f)
+ curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
+ horizontalLineTo(8f)
+ curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
+ verticalLineTo(5f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ verticalLineTo(3f)
+ curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
+ horizontalLineToRelative(2f)
+ curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
+ verticalLineToRelative(1f)
+ horizontalLineToRelative(2f)
+ curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
+ close()
+ moveTo(15f, 6f)
+ horizontalLineTo(9f)
+ verticalLineToRelative(2f)
+ horizontalLineToRelative(6f)
+ verticalLineTo(6f)
+ close()
+ }
+ }
+
+val Icons.Rounded.BatteryCharging0Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging0Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 75.589844f)
+ lineTo(34.015625f, 75.589844f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(47.253906f, 32.126953f)
+ curveTo(46.605486f, 32.112843f, 45.950535f, 32.41843f, 45.582031f, 33.109375f)
+ lineTo(35.490234f, 52.005859f)
+ curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(41.574219f, 68.03125f)
+ curveTo(41.574219f, 69.996604f, 44.219867f, 70.640241f, 45.126953f, 68.939453f)
+ lineTo(55.21875f, 50.041016f)
+ curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.848696f, 48.201598f, 32.147575f, 47.253906f, 32.126953f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging1Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging1Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 68.03125f)
+ lineTo(45.611328f, 68.03125f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(34.015625f, 68.03125f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(41.574219f, 68.03125f)
+ lineTo(45.611328f, 68.03125f)
+ lineTo(55.21875f, 50.041016f)
+ curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
+ lineTo(35.490234f, 52.005859f)
+ curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(41.574219f, 68.03125f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging2Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging2Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 60.472656f)
+ lineTo(49.648438f, 60.472656f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(41.574219f, 60.472656f)
+ lineTo(34.015625f, 60.472656f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(41.574219f, 60.472656f)
+ lineTo(49.648438f, 60.472656f)
+ lineTo(55.21875f, 50.041016f)
+ curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
+ lineTo(35.490234f, 52.005859f)
+ curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(41.574219f, 60.472656f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging3Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging3Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 52.914062f)
+ lineTo(53.683594f, 52.914062f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(37.152344f, 54.802734f)
+ curveTo(36.048819f, 54.802734f, 35.258483f, 53.910469f, 35.259766f, 52.914062f)
+ lineTo(34.015625f, 52.914062f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(35.259766f, 52.914062f)
+ lineTo(53.683594f, 52.914062f)
+ lineTo(55.21875f, 50.041016f)
+ curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
+ lineTo(35.490234f, 52.005859f)
+ curveTo(35.332642f, 52.303535f, 35.260152f, 52.613661f, 35.259766f, 52.914062f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging4Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging4Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 45.353516f)
+ lineTo(49.134766f, 45.353516f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(53.556641f, 47.244141f)
+ curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(37.152344f, 54.802734f)
+ curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
+ lineTo(39.042969f, 45.353516f)
+ lineTo(34.015625f, 45.353516f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(39.042969f, 45.353516f)
+ lineTo(49.134766f, 45.353516f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
+ lineTo(39.042969f, 45.353516f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging5Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging5Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 37.794922f)
+ lineTo(49.134766f, 37.794922f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(53.556641f, 47.244141f)
+ curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(37.152344f, 54.802734f)
+ curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
+ lineTo(43.080078f, 37.794922f)
+ lineTo(34.015625f, 37.794922f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(43.080078f, 37.794922f)
+ lineTo(49.134766f, 37.794922f)
+ lineTo(49.134766f, 34.015625f)
+ curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
+ lineTo(43.080078f, 37.794922f)
+ close()
+ }
+ }
+ }
+
+val Icons.Rounded.BatteryCharging6Bar
+ get() = materialIcon("Icons.Rounded.BatteryCharging6Bar") {
+ group(
+ scaleX = 0.26458333f,
+ scaleY = 0.26458333f,
+ ) {
+ materialPath {
+ moveTo(41.574219f, 7.5585938f)
+ curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
+ lineTo(37.794922f, 15.117188f)
+ lineTo(30.236328f, 15.117188f)
+ curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
+ lineTo(26.457031f, 79.369141f)
+ curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
+ lineTo(60.472656f, 83.150391f)
+ curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
+ lineTo(64.251953f, 18.898438f)
+ curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
+ lineTo(52.914062f, 15.117188f)
+ lineTo(52.914062f, 11.337891f)
+ curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
+ lineTo(41.574219f, 7.5585938f)
+ close()
+ moveTo(34.015625f, 22.677734f)
+ lineTo(56.693359f, 22.677734f)
+ lineTo(56.693359f, 30.236328f)
+ lineTo(34.015625f, 30.236328f)
+ lineTo(34.015625f, 22.677734f)
+ close()
+ moveTo(47.253906f, 32.126953f)
+ curveTo(48.201598f, 32.147575f, 49.134766f, 32.848696f, 49.134766f, 34.015625f)
+ lineTo(49.134766f, 47.244141f)
+ lineTo(53.556641f, 47.244141f)
+ curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
+ lineTo(45.126953f, 68.939453f)
+ curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
+ lineTo(41.574219f, 54.802734f)
+ lineTo(37.152344f, 54.802734f)
+ curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
+ lineTo(45.582031f, 33.109375f)
+ curveTo(45.950535f, 32.41843f, 46.605486f, 32.112843f, 47.253906f, 32.126953f)
+ close()
+ }
+ }
+ }
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
index 5e37970a..620fc994 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt
@@ -10,10 +10,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.preferences.LauncherDataStore
-import de.mm20.launcher2.ui.launcher.widgets.clock.parts.AlarmPartProvider
-import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider
-import de.mm20.launcher2.ui.launcher.widgets.clock.parts.MusicPartProvider
-import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
+import de.mm20.launcher2.ui.launcher.widgets.clock.parts.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.*
@@ -29,7 +26,8 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
partProviders.value = listOf(
DatePartProvider(),
MusicPartProvider(),
- AlarmPartProvider()
+ AlarmPartProvider(),
+ BatteryPartProvider(),
)
}
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt
new file mode 100644
index 00000000..49f8c4cf
--- /dev/null
+++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt
@@ -0,0 +1,193 @@
+package de.mm20.launcher2.ui.launcher.widgets.clock.parts
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.BatteryManager
+import android.os.Build
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.BatteryChargingFull
+import androidx.compose.material.icons.rounded.BatteryFull
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.core.content.getSystemService
+import de.mm20.launcher2.preferences.Settings
+import de.mm20.launcher2.ui.R
+import de.mm20.launcher2.ui.icons.*
+import de.mm20.launcher2.ui.pluralResource
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.*
+
+class BatteryPartProvider : PartProvider {
+
+ private val batteryInfo = MutableStateFlow(null)
+
+ override fun getRanking(context: Context): Flow = channelFlow {
+ val chargingInfo = getChargingInfo(context)
+
+ chargingInfo.collectLatest {
+ batteryInfo.value = it
+ if (it.charging) {
+ send(10)
+ } else if (it.level <= 15) {
+ send(55)
+ } else {
+ send(0)
+ }
+ }
+ }
+
+ private val time = MutableStateFlow(System.currentTimeMillis())
+
+ override fun setTime(time: Long) {
+ super.setTime(time)
+ this.time.value = time
+ }
+
+ @Composable
+ override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) {
+
+ val batteryInfo by this.batteryInfo.collectAsState(null)
+
+ batteryInfo?.let {
+
+ if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) {
+ Row(
+ Modifier.padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = getBatteryIcon(it),
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ text = "${it.level} %",
+ style = MaterialTheme.typography.titleMedium,
+ )
+ if (it.charging) {
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ text = it.fullIn?.let {
+ val m = (it / 60000).toInt()
+ pluralResource(R.plurals.battery_part_remaining_charge_time, m, m)
+ } ?: stringResource(R.string.battery_part_charging),
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+ }
+ }
+ if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Horizontal) {
+ Row(
+ Modifier.padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ modifier = Modifier.padding(end = 12.dp).size(36.dp),
+ imageVector = getBatteryIcon(it),
+ contentDescription = null
+ )
+ Column {
+ Text(
+ text = "${it.level} %",
+ style = MaterialTheme.typography.titleLarge,
+ )
+ if (it.charging) {
+ Text(
+ text = it.fullIn?.let {
+ val m = (it / 60000).toInt()
+ pluralResource(R.plurals.battery_part_remaining_charge_time, m, m)
+ } ?: stringResource(R.string.battery_part_charging),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun getBatteryIcon(batteryInfo: BatteryInfo): ImageVector {
+ return if (batteryInfo.charging) {
+ when (batteryInfo.level) {
+ in 0..12 -> Icons.Rounded.BatteryCharging0Bar
+ in 13..25 -> Icons.Rounded.BatteryCharging1Bar
+ in 26..37 -> Icons.Rounded.BatteryCharging2Bar
+ in 38..50 -> Icons.Rounded.BatteryCharging3Bar
+ in 51..63 -> Icons.Rounded.BatteryCharging4Bar
+ in 64..75 -> Icons.Rounded.BatteryCharging5Bar
+ in 76..88 -> Icons.Rounded.BatteryCharging6Bar
+ else -> Icons.Rounded.BatteryChargingFull
+ }
+ } else {
+ when (batteryInfo.level) {
+ in 0..12 -> Icons.Rounded.Battery0Bar
+ in 13..25 -> Icons.Rounded.Battery1Bar
+ in 26..37 -> Icons.Rounded.Battery2Bar
+ in 38..50 -> Icons.Rounded.Battery3Bar
+ in 51..63 -> Icons.Rounded.Battery4Bar
+ in 64..75 -> Icons.Rounded.Battery5Bar
+ in 76..88 -> Icons.Rounded.Battery6Bar
+ else -> Icons.Rounded.BatteryFull
+ }
+ }
+ }
+
+ private fun getChargingInfo(context: Context): Flow = callbackFlow {
+ val batteryManager: BatteryManager = context.getSystemService() ?: return@callbackFlow
+
+ trySendBlocking(
+ BatteryInfo(
+ level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY),
+ charging = batteryManager.isCharging,
+ fullIn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ batteryManager.computeChargeTimeRemaining().takeIf { it >= 0 }
+ } else null,
+ )
+ )
+
+ val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ trySendBlocking(
+ BatteryInfo(
+ level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY),
+ charging = intent?.getIntExtra(
+ BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_UNKNOWN
+ ) == BatteryManager.BATTERY_STATUS_CHARGING,
+ fullIn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ batteryManager.computeChargeTimeRemaining().takeIf { it >= 0 }
+ } else null,
+ )
+ )
+ }
+ }
+ context.registerReceiver(receiver, IntentFilter().apply {
+ addAction(Intent.ACTION_BATTERY_CHANGED)
+ })
+ awaitClose {
+ context.unregisterReceiver(receiver)
+ }
+ }
+}
+
+private data class BatteryInfo(
+ val level: Int,
+ val charging: Boolean,
+ val fullIn: Long?,
+)
\ No newline at end of file