Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06acf088 authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge "[battery] add '?' state" into main

parents 1d58ef74 80c77b7a
Loading
Loading
Loading
Loading
+22 −7
Original line number Diff line number Diff line
@@ -19,14 +19,22 @@ package com.android.systemui.statusbar.pipeline.battery.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.pipeline.battery.data.repository.BatteryRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map

@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class BatteryInteractor @Inject constructor(repo: BatteryRepository) {
    /** The current level in the range of [0-100] */
    val level = repo.level.filterNotNull()
    /** The current level in the range of [0-100], or null if we don't know the level yet */
    val level =
        combine(repo.isStateUnknown, repo.level) { unknown, level ->
            if (unknown) {
                null
            } else {
                level
            }
        }

    /** Whether the battery has been fully charged */
    val isFull = level.map { isBatteryFull(it) }
@@ -47,7 +55,7 @@ class BatteryInteractor @Inject constructor(repo: BatteryRepository) {
     * The critical level (see [CRITICAL_LEVEL]) defines the level below which we might want to
     * display an error UI. E.g., show the battery as red.
     */
    val isCritical = level.map { it <= CRITICAL_LEVEL }
    val isCritical = level.map { it != null && it <= CRITICAL_LEVEL }

    /** @see [BatteryRepository.isStateUnknown] for docs. The battery cannot be detected */
    val isStateUnknown = repo.isStateUnknown
@@ -69,8 +77,14 @@ class BatteryInteractor @Inject constructor(repo: BatteryRepository) {
     * This flow can be used to canonically describe the battery state charging state.
     */
    val batteryAttributionType =
        combine(isCharging, powerSave, isBatteryDefenderEnabled) { charging, powerSave, defend ->
            if (powerSave) {
        combine(isCharging, powerSave, isBatteryDefenderEnabled, isStateUnknown) {
            charging,
            powerSave,
            defend,
            unknown ->
            if (unknown) {
                BatteryAttributionModel.Unknown
            } else if (powerSave) {
                BatteryAttributionModel.PowerSave
            } else if (defend) {
                BatteryAttributionModel.Defend
@@ -88,7 +102,7 @@ class BatteryInteractor @Inject constructor(repo: BatteryRepository) {
        /** Level below which we consider to be critically low */
        private const val CRITICAL_LEVEL = 20

        fun isBatteryFull(level: Int) = level >= 100
        fun isBatteryFull(level: Int?) = level != null && level >= 100
    }
}

@@ -97,4 +111,5 @@ enum class BatteryAttributionModel {
    Defend,
    PowerSave,
    Charging,
    Unknown,
}
+3 −0
Original line number Diff line number Diff line
@@ -75,6 +75,9 @@ object BatteryFrame {

    /** The height of the drawable that is usable for inside elements */
    const val innerHeight = 13f

    /** Corner radius for the battery body */
    const val cornerRadius = 4f
}

/**
+12 −0
Original line number Diff line number Diff line
@@ -134,6 +134,18 @@ sealed interface BatteryGlyph : Glyph {
        override val height: Float = 9.00f
    }

    data object Question : BatteryGlyph {
        override val path: Path =
            Path().apply {
                addSvg(
                    "M2.85,6.438C2.591,6.438 2.363,6.356 2.167,6.193C1.975,6.025 1.879,5.823 1.879,5.588V5.557C1.879,5.209 1.958,4.911 2.117,4.663C2.276,4.416 2.545,4.143 2.925,3.845C3.276,3.572 3.537,3.346 3.708,3.166C3.883,2.985 3.971,2.792 3.971,2.587C3.971,2.31 3.869,2.091 3.664,1.932C3.464,1.768 3.188,1.687 2.837,1.687C2.616,1.687 2.418,1.722 2.242,1.794C2.067,1.865 1.919,1.961 1.798,2.083C1.677,2.205 1.568,2.322 1.472,2.435C1.38,2.545 1.242,2.62 1.059,2.662C0.879,2.7 0.687,2.67 0.482,2.574C0.282,2.477 0.14,2.32 0.057,2.102C-0.023,1.884 -0.019,1.668 0.069,1.454C0.161,1.24 0.34,1.015 0.608,0.78C0.879,0.541 1.207,0.352 1.591,0.214C1.975,0.071 2.426,0 2.944,0C3.866,0 4.605,0.231 5.161,0.692C5.72,1.15 6,1.739 6,2.461C6,2.897 5.889,3.287 5.668,3.631C5.451,3.971 5.098,4.315 4.61,4.663C4.326,4.869 4.127,5.049 4.015,5.205C3.902,5.36 3.835,5.546 3.814,5.765V5.777C3.781,5.945 3.679,6.098 3.507,6.237C3.336,6.371 3.117,6.438 2.85,6.438ZM2.837,10C2.495,10 2.205,9.885 1.967,9.654C1.733,9.423 1.616,9.14 1.616,8.804C1.616,8.477 1.733,8.2 1.967,7.974C2.205,7.747 2.495,7.634 2.837,7.634C3.18,7.634 3.47,7.747 3.708,7.974C3.95,8.2 4.071,8.477 4.071,8.804C4.071,9.14 3.952,9.423 3.714,9.654C3.476,9.885 3.184,10 2.837,10Z"
                )
            }

        override val width: Float = 6.00f
        override val height: Float = 10.00f
    }

    data object Zero : BatteryGlyph {
        override val path: Path =
            Path().apply {
+32 −28
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ fun BatteryCanvas(
    innerWidth: Float,
    innerHeight: Float,
    glyphs: List<BatteryGlyph>,
    level: Int,
    level: Int?,
    isFull: Boolean,
    colorsProvider: () -> BatteryColors,
    modifier: Modifier = Modifier,
@@ -110,6 +110,7 @@ fun BatteryCanvas(
                    }
                drawPath(path.path, bgColor)
                // Then draw the body, clipped to the fill level
                if (level != null && level > 0) {
                    clipRect(0f, 0f, level.scaledLevel(), innerHeight) {
                        drawRoundRect(
                            color = colors.fill,
@@ -119,6 +120,7 @@ fun BatteryCanvas(
                        )
                    }
                }
            }

            // Now draw the glyphs
            var horizontalOffset = (BatteryFrame.innerWidth - totalWidth) / 2
@@ -190,7 +192,7 @@ fun UnifiedBattery(
@Composable
fun BatteryLayout(
    attribution: BatteryGlyph?,
    levelProvider: () -> Int,
    levelProvider: () -> Int?,
    isFullProvider: () -> Boolean,
    glyphsProvider: () -> List<BatteryGlyph>,
    colorsProvider: () -> BatteryColors,
@@ -329,7 +331,7 @@ class BatteryMeasurePolicy : MeasurePolicy {
@Composable
fun BatteryBody(
    pathSpec: PathSpec,
    levelProvider: () -> Int,
    levelProvider: () -> Int?,
    glyphsProvider: () -> List<BatteryGlyph>,
    isFullProvider: () -> Boolean,
    colorsProvider: () -> BatteryColors,
@@ -368,14 +370,15 @@ fun BatteryBody(
                // 2. draw body
                drawPath(pathSpec.path, color)

                // 3. clip the fill to the level
                // 3. clip the fill to the level if we have it
                if (level != null && level > 0) {
                    clipRect(
                        left = 0f,
                        top = 0f,
                        right = level.scaledLevel(),
                        bottom = BatteryFrame.innerHeight,
                    ) {
                    // 4 Draw the rounded rect fill fully, it'll be clipped above
                        // 4. Draw the rounded rect fill fully, it'll be clipped above
                        drawRoundRect(
                            color = colors.fill,
                            topLeft = Offset.Zero,
@@ -384,10 +387,11 @@ fun BatteryBody(
                                    width = BatteryFrame.innerWidth,
                                    height = BatteryFrame.innerHeight,
                                ),
                        CornerRadius(x = 4f),
                            CornerRadius(x = BatteryFrame.cornerRadius),
                        )
                    }
                }
            }

            // Next: draw the glyphs
            var horizontalOffset = (BatteryFrame.innerWidth - totalGlyphWidth) / 2f
+21 −17
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.battery.domain.interactor.BatteryAttributionModel.Charging
import com.android.systemui.statusbar.pipeline.battery.domain.interactor.BatteryAttributionModel.Defend
import com.android.systemui.statusbar.pipeline.battery.domain.interactor.BatteryAttributionModel.PowerSave
import com.android.systemui.statusbar.pipeline.battery.domain.interactor.BatteryAttributionModel.Unknown
import com.android.systemui.statusbar.pipeline.battery.domain.interactor.BatteryInteractor
import com.android.systemui.statusbar.pipeline.battery.shared.ui.BatteryColors
import com.android.systemui.statusbar.pipeline.battery.shared.ui.BatteryFrame
@@ -79,7 +80,7 @@ sealed class BatteryViewModel(

    /** A [List<BatteryGlyph>] representation of the current [level] */
    private val levelGlyphs: Flow<List<BatteryGlyph>> =
        interactor.level.map { it.glyphRepresentation() }
        interactor.level.map { it?.glyphRepresentation() ?: emptyList() }

    private val _glyphList: Flow<List<BatteryGlyph>> =
        shouldShowPercent.flatMapLatest {
@@ -111,6 +112,8 @@ sealed class BatteryViewModel(

                Defend -> BatteryGlyph.Defend

                Unknown -> BatteryGlyph.Question

                else -> null
            }
        }
@@ -170,16 +173,9 @@ sealed class BatteryViewModel(
            traceName = "contentDescription",
            initialValue = ContentDescription.Loaded(null),
            source =
                combine(
                    interactor.batteryAttributionType,
                    interactor.isStateUnknown,
                    interactor.level,
                ) { attr, isUnknown, level ->
                    when {
                        isUnknown ->
                            ContentDescription.Resource(R.string.accessibility_battery_unknown)

                        attr == Defend -> {
                combine(interactor.batteryAttributionType, interactor.level) { attr, level ->
                    when (attr) {
                        Defend -> {
                            val descr =
                                context.getString(
                                    R.string.accessibility_battery_level_charging_paused,
@@ -188,8 +184,7 @@ sealed class BatteryViewModel(

                            ContentDescription.Loaded(descr)
                        }

                        attr == Charging -> {
                        Charging -> {
                            val descr =
                                context.getString(
                                    R.string.accessibility_battery_level_charging,
@@ -197,8 +192,7 @@ sealed class BatteryViewModel(
                                )
                            ContentDescription.Loaded(descr)
                        }

                        attr == PowerSave -> {
                        PowerSave -> {
                            val descr =
                                context.getString(
                                    R.string.accessibility_battery_level_battery_saver_with_percent,
@@ -206,7 +200,10 @@ sealed class BatteryViewModel(
                                )
                            ContentDescription.Loaded(descr)
                        }

                        Unknown -> {
                            val descr = context.getString(R.string.accessibility_battery_unknown)
                            ContentDescription.Loaded(descr)
                        }
                        else -> {
                            val descr =
                                context.getString(R.string.accessibility_battery_level, level)
@@ -338,7 +335,14 @@ constructor(interactor: BatteryInteractor, @Application context: Context) :
        hydrator.hydratedStateOf(
            traceName = "batteryPercent",
            initialValue = null,
            source = interactor.level.map { NumberFormat.getPercentInstance().format(it / 100f) },
            source =
                interactor.level.map { level ->
                    if (level == null) {
                        null
                    } else {
                        NumberFormat.getPercentInstance().format(level / 100f)
                    }
                },
        )

    private val _attributionAsList: Flow<List<BatteryGlyph>> =
Loading