Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -686,6 +686,16 @@ flag { bug: "364360986" } flag { name: "status_bar_rudimentary_battery" namespace: "systemui" description: "Bugfix flag to control the percentage-outside-battery behavior" bug: "408730362" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "new_volume_panel" namespace: "systemui" Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt +4 −2 Original line number Diff line number Diff line Loading @@ -57,7 +57,8 @@ import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler import com.android.systemui.statusbar.layout.mockStatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.batteryViewModelFactory import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.batteryWithPercentViewModelFactory import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.unifiedBatteryViewModelFactory import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController Loading Loading @@ -195,7 +196,8 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() { statusBarIconController, iconManagerFactory, batteryMeterViewController, kosmos.batteryViewModelFactory, kosmos.batteryWithPercentViewModelFactory, kosmos.unifiedBatteryViewModelFactory, shadeViewStateProvider, keyguardStateController, keyguardBypassController, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt +12 −4 Original line number Diff line number Diff line Loading @@ -31,7 +31,8 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryNextToPercentViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.UnifiedBatteryViewModel import com.android.systemui.statusbar.pipeline.shared.ui.model.ChipsVisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel Loading Loading @@ -72,9 +73,16 @@ class FakeHomeStatusBarViewModel( override val canShowOngoingActivityChips: Flow<Boolean> = MutableStateFlow(false) override val batteryViewModelFactory: BatteryViewModel.Factory = object : BatteryViewModel.Factory { override fun create(): BatteryViewModel = mock(BatteryViewModel::class.java) override val batteryNextToPercentViewModel: BatteryNextToPercentViewModel.Factory = object : BatteryNextToPercentViewModel.Factory { override fun create(): BatteryNextToPercentViewModel = mock(BatteryNextToPercentViewModel::class.java) } override val unifiedBatteryViewModel: UnifiedBatteryViewModel.Factory = object : UnifiedBatteryViewModel.Factory { override fun create(): UnifiedBatteryViewModel = mock(UnifiedBatteryViewModel::class.java) } override val systemStatusIconsViewModelFactory: SystemStatusIconsViewModel.Factory = object : SystemStatusIconsViewModel.Factory { Loading packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +45 −15 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import androidx.core.view.doOnLayout import androidx.core.view.isVisible import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.app.animation.Interpolators import com.android.compose.theme.PlatformTheme import com.android.keyguard.AlphaOptimizedLinearLayout import com.android.settingslib.Utils import com.android.systemui.Dumpable Loading @@ -70,6 +71,7 @@ import com.android.systemui.shade.carrier.ShadeCarrierGroupController import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.RudimentaryBattery import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer Loading @@ -77,8 +79,11 @@ import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithChargeStatus import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithEstimate import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.pipeline.battery.ui.composable.ShowPercentMode import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryNextToPercentViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.UnifiedBatteryViewModel import com.android.systemui.statusbar.pipeline.shared.ui.view.SystemStatusIconsLayoutHelper import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.ConfigurationController Loading Loading @@ -115,7 +120,8 @@ constructor( private val shadeDisplaysRepositoryLazy: Lazy<ShadeDisplaysRepository>, private val variableDateViewControllerFactory: VariableDateViewController.Factory, @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController, private val batteryViewModelFactory: BatteryViewModel.Factory, private val unifiedBatteryViewModelFactory: UnifiedBatteryViewModel.Factory, private val tandemBatteryViewModelFactory: BatteryNextToPercentViewModel.Factory, private val dumpManager: DumpManager, private val shadeCarrierGroupControllerBuilder: ShadeCarrierGroupController.Builder, private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager, Loading Loading @@ -364,19 +370,7 @@ constructor( SystemStatusIconsLayoutHelper.configurePaddingForNewStatusBarIcons(statusIcons) // Configure the compose battery view val batteryComposeView = ComposeView(mView.context).apply { setContent { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithEstimate( modifier = Modifier.wrapContentSize(), viewModelFactory = batteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showEstimate = showBatteryEstimate, ) } } val batteryComposeView = createBatteryComposeView() mView.requireViewById<ViewGroup>(R.id.hover_system_icons_container).apply { addView(batteryComposeView, -1) } Loading @@ -393,6 +387,42 @@ constructor( privacyIconsController.onParentVisible() } private fun createBatteryComposeView(): ComposeView { return if (RudimentaryBattery.isEnabled) { ComposeView(mView.context).apply { setContent { PlatformTheme { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithChargeStatus( modifier = Modifier.wrapContentSize(), viewModelFactory = tandemBatteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showPercentMode = if (showBatteryEstimate) ShowPercentMode.PreferEstimate else ShowPercentMode.Always, ) } } } } else { ComposeView(mView.context).apply { setContent { PlatformTheme { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithEstimate( modifier = Modifier.wrapContentSize(), viewModelFactory = unifiedBatteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showEstimate = showBatteryEstimate, ) } } } } } override fun onViewAttached() { privacyIconsController.chipVisibilityListener = chipVisibilityListener updateVisibility() Loading packages/SystemUI/src/com/android/systemui/statusbar/core/RudimentaryBattery.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.core import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** * A rudimentary battery eschews the mid-century-modern approach of percent-in-battery and instead * yields a battery pure in its iconography. Next to it is a Text object which handles the more * mundane task of relaying a textual vibe on the overall percentage therein as likewise represented * as the fill level of the aforementioned "battery" (henceforth known as "battery") icon. */ object RudimentaryBattery { /** Aconfig flag for percent-outside-battery */ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RUDIMENTARY_BATTERY /** Is the refactor enabled. Dependency on [StatusBarRootModernization] */ @JvmStatic inline val isEnabled get() = Flags.statusBarRudimentaryBattery() && StatusBarRootModernization.isEnabled && NewStatusBarIcons.isEnabled /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is not enabled to ensure that the refactor author catches issues in testing. * Caution!! Using this check incorrectly will cause crashes in nextfood builds! */ @JvmStatic @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return")) inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) } Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -686,6 +686,16 @@ flag { bug: "364360986" } flag { name: "status_bar_rudimentary_battery" namespace: "systemui" description: "Bugfix flag to control the percentage-outside-battery behavior" bug: "408730362" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "new_volume_panel" namespace: "systemui" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt +4 −2 Original line number Diff line number Diff line Loading @@ -57,7 +57,8 @@ import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler import com.android.systemui.statusbar.layout.mockStatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.batteryViewModelFactory import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.batteryWithPercentViewModelFactory import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.unifiedBatteryViewModelFactory import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController Loading Loading @@ -195,7 +196,8 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() { statusBarIconController, iconManagerFactory, batteryMeterViewController, kosmos.batteryViewModelFactory, kosmos.batteryWithPercentViewModelFactory, kosmos.unifiedBatteryViewModelFactory, shadeViewStateProvider, keyguardStateController, keyguardBypassController, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt +12 −4 Original line number Diff line number Diff line Loading @@ -31,7 +31,8 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryNextToPercentViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.UnifiedBatteryViewModel import com.android.systemui.statusbar.pipeline.shared.ui.model.ChipsVisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel Loading Loading @@ -72,9 +73,16 @@ class FakeHomeStatusBarViewModel( override val canShowOngoingActivityChips: Flow<Boolean> = MutableStateFlow(false) override val batteryViewModelFactory: BatteryViewModel.Factory = object : BatteryViewModel.Factory { override fun create(): BatteryViewModel = mock(BatteryViewModel::class.java) override val batteryNextToPercentViewModel: BatteryNextToPercentViewModel.Factory = object : BatteryNextToPercentViewModel.Factory { override fun create(): BatteryNextToPercentViewModel = mock(BatteryNextToPercentViewModel::class.java) } override val unifiedBatteryViewModel: UnifiedBatteryViewModel.Factory = object : UnifiedBatteryViewModel.Factory { override fun create(): UnifiedBatteryViewModel = mock(UnifiedBatteryViewModel::class.java) } override val systemStatusIconsViewModelFactory: SystemStatusIconsViewModel.Factory = object : SystemStatusIconsViewModel.Factory { Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +45 −15 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import androidx.core.view.doOnLayout import androidx.core.view.isVisible import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.app.animation.Interpolators import com.android.compose.theme.PlatformTheme import com.android.keyguard.AlphaOptimizedLinearLayout import com.android.settingslib.Utils import com.android.systemui.Dumpable Loading @@ -70,6 +71,7 @@ import com.android.systemui.shade.carrier.ShadeCarrierGroupController import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.RudimentaryBattery import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer Loading @@ -77,8 +79,11 @@ import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithChargeStatus import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithEstimate import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.pipeline.battery.ui.composable.ShowPercentMode import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryNextToPercentViewModel import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.UnifiedBatteryViewModel import com.android.systemui.statusbar.pipeline.shared.ui.view.SystemStatusIconsLayoutHelper import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.ConfigurationController Loading Loading @@ -115,7 +120,8 @@ constructor( private val shadeDisplaysRepositoryLazy: Lazy<ShadeDisplaysRepository>, private val variableDateViewControllerFactory: VariableDateViewController.Factory, @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController, private val batteryViewModelFactory: BatteryViewModel.Factory, private val unifiedBatteryViewModelFactory: UnifiedBatteryViewModel.Factory, private val tandemBatteryViewModelFactory: BatteryNextToPercentViewModel.Factory, private val dumpManager: DumpManager, private val shadeCarrierGroupControllerBuilder: ShadeCarrierGroupController.Builder, private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager, Loading Loading @@ -364,19 +370,7 @@ constructor( SystemStatusIconsLayoutHelper.configurePaddingForNewStatusBarIcons(statusIcons) // Configure the compose battery view val batteryComposeView = ComposeView(mView.context).apply { setContent { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithEstimate( modifier = Modifier.wrapContentSize(), viewModelFactory = batteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showEstimate = showBatteryEstimate, ) } } val batteryComposeView = createBatteryComposeView() mView.requireViewById<ViewGroup>(R.id.hover_system_icons_container).apply { addView(batteryComposeView, -1) } Loading @@ -393,6 +387,42 @@ constructor( privacyIconsController.onParentVisible() } private fun createBatteryComposeView(): ComposeView { return if (RudimentaryBattery.isEnabled) { ComposeView(mView.context).apply { setContent { PlatformTheme { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithChargeStatus( modifier = Modifier.wrapContentSize(), viewModelFactory = tandemBatteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showPercentMode = if (showBatteryEstimate) ShowPercentMode.PreferEstimate else ShowPercentMode.Always, ) } } } } else { ComposeView(mView.context).apply { setContent { PlatformTheme { id = R.id.battery_meter_composable_view val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() BatteryWithEstimate( modifier = Modifier.wrapContentSize(), viewModelFactory = unifiedBatteryViewModelFactory, isDarkProvider = { IsAreaDark { true } }, showEstimate = showBatteryEstimate, ) } } } } } override fun onViewAttached() { privacyIconsController.chipVisibilityListener = chipVisibilityListener updateVisibility() Loading
packages/SystemUI/src/com/android/systemui/statusbar/core/RudimentaryBattery.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.core import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** * A rudimentary battery eschews the mid-century-modern approach of percent-in-battery and instead * yields a battery pure in its iconography. Next to it is a Text object which handles the more * mundane task of relaying a textual vibe on the overall percentage therein as likewise represented * as the fill level of the aforementioned "battery" (henceforth known as "battery") icon. */ object RudimentaryBattery { /** Aconfig flag for percent-outside-battery */ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RUDIMENTARY_BATTERY /** Is the refactor enabled. Dependency on [StatusBarRootModernization] */ @JvmStatic inline val isEnabled get() = Flags.statusBarRudimentaryBattery() && StatusBarRootModernization.isEnabled && NewStatusBarIcons.isEnabled /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is not enabled to ensure that the refactor author catches issues in testing. * Caution!! Using this check incorrectly will cause crashes in nextfood builds! */ @JvmStatic @Deprecated("Avoid crashing.", ReplaceWith("if (this.isUnexpectedlyInLegacyMode()) return")) inline fun unsafeAssertInNewMode() = RefactorFlagUtils.unsafeAssertInNewMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) }