Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/domain/interactor/BatteryInteractor.kt +22 −7 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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 Loading @@ -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 Loading @@ -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 } } Loading @@ -97,4 +111,5 @@ enum class BatteryAttributionModel { Defend, PowerSave, Charging, Unknown, } packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/shared/ui/BatteryFrame.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 } /** Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/shared/ui/BatteryGlyph.kt +12 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt +32 −28 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ fun BatteryCanvas( innerWidth: Float, innerHeight: Float, glyphs: List<BatteryGlyph>, level: Int, level: Int?, isFull: Boolean, colorsProvider: () -> BatteryColors, modifier: Modifier = Modifier, Loading Loading @@ -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, Loading @@ -119,6 +120,7 @@ fun BatteryCanvas( ) } } } // Now draw the glyphs var horizontalOffset = (BatteryFrame.innerWidth - totalWidth) / 2 Loading Loading @@ -190,7 +192,7 @@ fun UnifiedBattery( @Composable fun BatteryLayout( attribution: BatteryGlyph?, levelProvider: () -> Int, levelProvider: () -> Int?, isFullProvider: () -> Boolean, glyphsProvider: () -> List<BatteryGlyph>, colorsProvider: () -> BatteryColors, Loading Loading @@ -329,7 +331,7 @@ class BatteryMeasurePolicy : MeasurePolicy { @Composable fun BatteryBody( pathSpec: PathSpec, levelProvider: () -> Int, levelProvider: () -> Int?, glyphsProvider: () -> List<BatteryGlyph>, isFullProvider: () -> Boolean, colorsProvider: () -> BatteryColors, Loading Loading @@ -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, Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt +21 −17 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -111,6 +112,8 @@ sealed class BatteryViewModel( Defend -> BatteryGlyph.Defend Unknown -> BatteryGlyph.Question else -> null } } Loading Loading @@ -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, Loading @@ -188,8 +184,7 @@ sealed class BatteryViewModel( ContentDescription.Loaded(descr) } attr == Charging -> { Charging -> { val descr = context.getString( R.string.accessibility_battery_level_charging, Loading @@ -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, Loading @@ -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) Loading Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/domain/interactor/BatteryInteractor.kt +22 −7 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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 Loading @@ -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 Loading @@ -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 } } Loading @@ -97,4 +111,5 @@ enum class BatteryAttributionModel { Defend, PowerSave, Charging, Unknown, }
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/shared/ui/BatteryFrame.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 } /** Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/shared/ui/BatteryGlyph.kt +12 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt +32 −28 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ fun BatteryCanvas( innerWidth: Float, innerHeight: Float, glyphs: List<BatteryGlyph>, level: Int, level: Int?, isFull: Boolean, colorsProvider: () -> BatteryColors, modifier: Modifier = Modifier, Loading Loading @@ -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, Loading @@ -119,6 +120,7 @@ fun BatteryCanvas( ) } } } // Now draw the glyphs var horizontalOffset = (BatteryFrame.innerWidth - totalWidth) / 2 Loading Loading @@ -190,7 +192,7 @@ fun UnifiedBattery( @Composable fun BatteryLayout( attribution: BatteryGlyph?, levelProvider: () -> Int, levelProvider: () -> Int?, isFullProvider: () -> Boolean, glyphsProvider: () -> List<BatteryGlyph>, colorsProvider: () -> BatteryColors, Loading Loading @@ -329,7 +331,7 @@ class BatteryMeasurePolicy : MeasurePolicy { @Composable fun BatteryBody( pathSpec: PathSpec, levelProvider: () -> Int, levelProvider: () -> Int?, glyphsProvider: () -> List<BatteryGlyph>, isFullProvider: () -> Boolean, colorsProvider: () -> BatteryColors, Loading Loading @@ -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, Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt +21 −17 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -111,6 +112,8 @@ sealed class BatteryViewModel( Defend -> BatteryGlyph.Defend Unknown -> BatteryGlyph.Question else -> null } } Loading Loading @@ -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, Loading @@ -188,8 +184,7 @@ sealed class BatteryViewModel( ContentDescription.Loaded(descr) } attr == Charging -> { Charging -> { val descr = context.getString( R.string.accessibility_battery_level_charging, Loading @@ -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, Loading @@ -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) Loading Loading @@ -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