diff options
17 files changed, 238 insertions, 30 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt index 57b7df7a8d31..31f8590c0378 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt @@ -51,11 +51,13 @@ import com.android.systemui.res.R import com.android.systemui.shade.ShadeViewStateProvider import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore 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.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -85,6 +87,7 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) +@DisableFlags(NewStatusBarIcons.FLAG_NAME) class KeyguardStatusBarViewControllerTest : SysuiTestCase() { private lateinit var kosmos: Kosmos private lateinit var testScope: TestScope @@ -190,6 +193,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() { statusBarIconController, iconManagerFactory, batteryMeterViewController, + kosmos.batteryViewModelFactory, shadeViewStateProvider, keyguardStateController, keyguardBypassController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt index 4759c081de5f..183cd8f1ae8b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import android.graphics.Color import android.graphics.Rect import android.view.View +import androidx.compose.runtime.getValue +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.lifecycle.Hydrator import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel @@ -26,15 +29,20 @@ import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChip 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.shared.ui.model.SystemInfoCombinedVisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import org.mockito.Mockito.mock class FakeHomeStatusBarViewModel( override val operatorNameViewModel: StatusBarOperatorNameViewModel -) : HomeStatusBarViewModel { +) : HomeStatusBarViewModel, ExclusiveActivatable() { + private val hydrator = Hydrator("FakeHomeStatusBarViewModel.hydrator") + override val areNotificationsLightsOut = MutableStateFlow(false) override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) @@ -56,6 +64,11 @@ class FakeHomeStatusBarViewModel( override val isHomeStatusBarAllowedByScene = MutableStateFlow(false) + override val batteryViewModelFactory: BatteryViewModel.Factory = + object : BatteryViewModel.Factory { + override fun create(): BatteryViewModel = mock(BatteryViewModel::class.java) + } + override val shouldShowOperatorNameView = MutableStateFlow(false) override val isClockVisible = @@ -80,6 +93,7 @@ class FakeHomeStatusBarViewModel( var darkIconTint = Color.BLACK var lightIconTint = Color.WHITE + var darkIntensity = 0f override val areaTint: Flow<StatusBarTintColor> = MutableStateFlow( @@ -91,4 +105,22 @@ class FakeHomeStatusBarViewModel( } } ) + + val isAreaDarkSource = + MutableStateFlow( + IsAreaDark { viewBounds -> + if (DarkIconDispatcher.isInAreas(darkRegions, viewBounds)) { + darkIntensity < 0.5f + } else { + false + } + } + ) + + override val areaDark: IsAreaDark by + hydrator.hydratedStateOf(traceName = "areaDark", source = isAreaDarkSource) + + override suspend fun onActivated(): Nothing { + hydrator.activate() + } } diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index b9ef88eea6b9..32407c6ae1bf 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -147,6 +147,7 @@ frame when animating QS <-> QQS transition android:id="@+id/batteryRemainingIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:visibility="gone" app:textAppearance="@style/TextAppearance.QS.Status" /> </LinearLayout> </FrameLayout> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index c28dc50cc1dc..bb99d581c0b0 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -34,12 +34,17 @@ android:orientation="horizontal"/> <!-- PaddingEnd is added to balance hover padding, compensating for paddingStart in statusIcons. - See b/339589733 --> + See b/339589733. + + Default visibility is now "gone" to make space for the new battery icon + --> <com.android.systemui.battery.BatteryMeterView android:id="@+id/battery" android:layout_height="wrap_content" android:layout_width="wrap_content" android:clipToPadding="false" android:clipChildren="false" android:paddingEnd="@dimen/status_bar_battery_end_padding" + android:visibility="gone" systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" /> + </LinearLayout> diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index 9a30c213a2f9..fcf51051940c 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -46,7 +46,10 @@ import java.io.PrintWriter; import javax.inject.Inject; -/** Controller for {@link BatteryMeterView}. **/ +/** + * Controller for {@link BatteryMeterView}. + * @deprecated once [NewStatusBarIcons] is rolled out, this class is no longer needed + */ public class BatteryMeterViewController extends ViewController<BatteryMeterView> { private final ConfigurationController mConfigurationController; private final TunerService mTunerService; diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 9a79e1a45505..ce48c85d57ae 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -31,16 +31,26 @@ import android.os.Trace.TRACE_TAG_APP import android.provider.AlarmClock import android.view.DisplayCutout import android.view.View +import android.view.ViewGroup import android.view.WindowInsets import android.widget.TextView import androidx.annotation.VisibleForTesting +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.unit.dp import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.view.doOnLayout +import androidx.core.view.isVisible +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.battery.BatteryMeterView +import com.android.systemui.battery.BatteryMeterView.MODE_ESTIMATE import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.demomode.DemoMode @@ -60,12 +70,15 @@ import com.android.systemui.shade.carrier.ShadeCarrierGroup 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.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory 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.BatteryWithEstimate +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.NextAlarmController @@ -76,6 +89,7 @@ import dagger.Lazy import java.io.PrintWriter import javax.inject.Inject import javax.inject.Named +import kotlinx.coroutines.flow.MutableStateFlow /** * Controller for QS header. @@ -100,6 +114,7 @@ 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 dumpManager: DumpManager, private val shadeCarrierGroupControllerBuilder: ShadeCarrierGroupController.Builder, private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager, @@ -162,6 +177,8 @@ constructor( private var lastInsets: WindowInsets? = null private var nextAlarmIntent: PendingIntent? = null + private val showBatteryEstimate = MutableStateFlow(false) + private var qsDisabled = false private var visible = false set(value) { @@ -323,10 +340,6 @@ constructor( override fun onInit() { variableDateViewControllerFactory.create(date as VariableDateView).init() - batteryMeterViewController.init() - - // battery settings same as in QS icons - batteryMeterViewController.ignoreTunerUpdates() val fgColor = Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary) @@ -336,11 +349,36 @@ constructor( iconManager = tintedIconManagerFactory.create(iconContainer, StatusBarLocation.QS) iconManager.setTint(fgColor, bgColor) - batteryIcon.updateColors( - fgColor /* foreground */, - bgColor /* background */, - fgColor, /* single tone (current default) */ - ) + if (!NewStatusBarIcons.isEnabled) { + batteryMeterViewController.init() + + // battery settings same as in QS icons + batteryMeterViewController.ignoreTunerUpdates() + + batteryIcon.isVisible = true + batteryIcon.updateColors( + fgColor /* foreground */, + bgColor /* background */, + fgColor, /* single tone (current default) */ + ) + } else { + // Configure the compose battery view + val batteryComposeView = + ComposeView(mView.context).apply { + setContent { + val showBatteryEstimate by showBatteryEstimate.collectAsStateWithLifecycle() + BatteryWithEstimate( + modifier = Modifier.height(17.dp).wrapContentWidth(), + viewModelFactory = batteryViewModelFactory, + isDark = { true }, + showEstimate = showBatteryEstimate, + ) + } + } + mView.requireViewById<ViewGroup>(R.id.hover_system_icons_container).apply { + addView(batteryComposeView, -1) + } + } carrierIconSlots = listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile)) @@ -474,7 +512,11 @@ constructor( private fun updateBatteryMode() { qsBatteryModeController.getBatteryMode(cutout, qsExpandedFraction)?.let { - batteryIcon.setPercentShowMode(it) + if (NewStatusBarIcons.isEnabled) { + showBatteryEstimate.value = it == MODE_ESTIMATE + } else { + batteryIcon.setPercentShowMode(it) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 58326dbb3a34..fa4fe46e690c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -47,6 +47,7 @@ import com.android.settingslib.Utils; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.res.R; +import com.android.systemui.statusbar.core.NewStatusBarIcons; import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider; import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange; import com.android.systemui.statusbar.phone.ui.TintedIconManager; @@ -79,7 +80,7 @@ public class KeyguardStatusBarView extends RelativeLayout { private TextView mCarrierLabel; private ImageView mMultiUserAvatar; - private BatteryMeterView mBatteryView; + @Nullable private BatteryMeterView mBatteryView; private StatusIconContainer mStatusIconContainer; private StatusBarUserSwitcherContainer mUserSwitcherContainer; @@ -131,6 +132,11 @@ public class KeyguardStatusBarView extends RelativeLayout { mMultiUserAvatar = findViewById(R.id.multi_user_avatar); mCarrierLabel = findViewById(R.id.keyguard_carrier_text); mBatteryView = mSystemIconsContainer.findViewById(R.id.battery); + if (NewStatusBarIcons.isEnabled()) { + // When this flag is rolled forward, this whole view can be removed + mBatteryView.setVisibility(View.GONE); + mBatteryView = null; + } mCutoutSpace = findViewById(R.id.cutout_space_view); mStatusIconArea = findViewById(R.id.status_icon_area); mStatusIconContainer = findViewById(R.id.statusIcons); @@ -259,7 +265,10 @@ public class KeyguardStatusBarView extends RelativeLayout { mMultiUserAvatar.setVisibility(View.GONE); } } - mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable); + + if (mBatteryView != null) { + mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable); + } } private void updateSystemIconsLayoutParams() { @@ -442,7 +451,9 @@ public class KeyguardStatusBarView extends RelativeLayout { /** Should only be called from {@link KeyguardStatusBarViewController}. */ void onThemeChanged(TintedIconManager iconManager) { - mBatteryView.setColorsFromContext(mContext); + if (mBatteryView != null) { + mBatteryView.setColorsFromContext(mContext); + } updateIconsAndTextColors(iconManager); } @@ -450,7 +461,9 @@ public class KeyguardStatusBarView extends RelativeLayout { void onOverlayChanged() { final int carrierTheme = R.style.TextAppearance_StatusBar_Clock; mCarrierLabel.setTextAppearance(carrierTheme); - mBatteryView.updatePercentView(); + if (mBatteryView != null) { + mBatteryView.updatePercentView(); + } final int userSwitcherTheme = R.style.TextAppearance_StatusBar_UserChip; TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 40245aef4f67..de7215461c80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -34,10 +34,12 @@ import android.provider.Settings; import android.util.MathUtils; import android.view.DisplayCutout; import android.view.View; +import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.compose.ui.platform.ComposeView; import androidx.core.animation.Animator; import androidx.core.animation.AnimatorListenerAdapter; import androidx.core.animation.ValueAnimator; @@ -62,6 +64,7 @@ import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.core.NewStatusBarIcons; import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore; import com.android.systemui.statusbar.disableflags.DisableStateTracker; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; @@ -71,10 +74,13 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor; import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt; import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; import com.android.systemui.statusbar.phone.ui.TintedIconManager; +import com.android.systemui.statusbar.pipeline.battery.ui.binder.UnifiedBatteryViewBinder; +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -125,6 +131,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final StatusBarIconController mStatusBarIconController; private final TintedIconManager.Factory mTintedIconManagerFactory; private final BatteryMeterViewController mBatteryMeterViewController; + private final BatteryViewModel.Factory mBatteryViewModelFactory; private final ShadeViewStateProvider mShadeViewStateProvider; private final KeyguardStateController mKeyguardStateController; private final KeyguardBypassController mKeyguardBypassController; @@ -145,7 +152,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final GlanceableHubToLockscreenTransitionViewModel mHubToLockscreenTransitionViewModel; private final LockscreenToGlanceableHubTransitionViewModel mLockscreenToHubTransitionViewModel; - private View mSystemIconsContainer; + private ViewGroup mSystemIconsContainer; private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; private final ConfigurationController.ConfigurationListener mConfigurationListener = @@ -327,6 +334,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat StatusBarIconController statusBarIconController, TintedIconManager.Factory tintedIconManagerFactory, BatteryMeterViewController batteryMeterViewController, + BatteryViewModel.Factory batteryViewModelFactory, ShadeViewStateProvider shadeViewStateProvider, KeyguardStateController keyguardStateController, KeyguardBypassController bypassController, @@ -360,6 +368,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mStatusBarIconController = statusBarIconController; mTintedIconManagerFactory = tintedIconManagerFactory; mBatteryMeterViewController = batteryMeterViewController; + mBatteryViewModelFactory = batteryViewModelFactory; mShadeViewStateProvider = shadeViewStateProvider; mKeyguardStateController = keyguardStateController; mKeyguardBypassController = bypassController; @@ -417,7 +426,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat protected void onInit() { super.onInit(); mCarrierTextController.init(); - mBatteryMeterViewController.init(); + if (!NewStatusBarIcons.isEnabled()) { + mBatteryMeterViewController.init(); + } if (isMigrationEnabled()) { KeyguardStatusBarViewBinder.bind(mView, mKeyguardStatusBarViewModel); } @@ -469,6 +480,15 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher); collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(), mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher); + if (NewStatusBarIcons.isEnabled()) { + ComposeView batteryComposeView = new ComposeView(mContext); + UnifiedBatteryViewBinder.bind( + batteryComposeView, + mBatteryViewModelFactory, + DarkIconInteractor.toIsAreaDark(mView.darkChangeFlow())); + + mSystemIconsContainer.addView(batteryComposeView, -1); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java index 7207d0aef3ee..4d531b512dd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java @@ -20,6 +20,7 @@ import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.statusbar.core.NewStatusBarIcons; import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.layout.StatusBarBoundsProvider; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; @@ -85,7 +86,9 @@ public interface HomeStatusBarComponent { default void init() { // No one accesses these controllers, so we need to make sure we reference them here so they // do get initialized. - getBatteryMeterViewController().init(); + if (!NewStatusBarIcons.isEnabled()) { + getBatteryMeterViewController().init(); + } getHeadsUpAppearanceController().init(); getPhoneStatusBarViewController().init(); if (!NotificationsLiveDataStoreRefactor.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt index 903844efa3f0..9665c33ac4c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/binder/UnifiedBatteryViewBinder.kt @@ -16,7 +16,10 @@ package com.android.systemui.statusbar.pipeline.battery.ui.binder +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.view.isVisible @@ -27,6 +30,8 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.pipeline.battery.ui.composable.UnifiedBattery import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel.Companion.STATUS_BAR_BATTERY_HEIGHT +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel.Companion.STATUS_BAR_BATTERY_WIDTH import kotlinx.coroutines.flow.Flow /** In cases where the battery needs to be bound to an existing android view */ @@ -47,7 +52,13 @@ object UnifiedBatteryViewBinder { ) setContent { val isDark by isAreaDark.collectAsStateWithLifecycle(IsAreaDark { true }) - UnifiedBattery(viewModelFactory = viewModelFactory, isDark = isDark) + UnifiedBattery( + modifier = + Modifier.height(STATUS_BAR_BATTERY_HEIGHT) + .width(STATUS_BAR_BATTERY_WIDTH), + viewModelFactory = viewModelFactory, + isDark = isDark, + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt index 2ee86ee0a679..ac793a9c97e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/BatteryWithEstimate.kt @@ -54,7 +54,7 @@ fun BatteryWithEstimate( ) if (showEstimate) { viewModel.batteryTimeRemainingEstimate?.let { - Spacer(modifier.width(2.dp)) + Spacer(modifier.width(4.dp)) Text(text = it, color = Color.White) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt index d0d099e74cb1..afd4bb1f36c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.battery.ui.viewmodel import android.content.Context import androidx.compose.runtime.getValue +import androidx.compose.ui.unit.dp import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.lifecycle.ExclusiveActivatable @@ -223,6 +224,10 @@ constructor(interactor: BatteryInteractor, @Application context: Context) : Excl } companion object { + // Status bar battery height, based on a 21x12 base canvas + val STATUS_BAR_BATTERY_HEIGHT = 13.dp + val STATUS_BAR_BATTERY_WIDTH = 22.75.dp + fun Int.glyphRepresentation(): List<BatteryGlyph> = toString().map { it.toGlyph() } private fun Char.toGlyph(): BatteryGlyph = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index 9d72daf01831..c34fa464cc3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -23,6 +23,8 @@ import android.widget.LinearLayout import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -34,9 +36,11 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme import com.android.keyguard.AlphaOptimizedLinearLayout +import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.chips.ui.compose.OngoingActivityChips +import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor @@ -51,6 +55,9 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.statusbar.phone.ui.DarkIconManager import com.android.systemui.statusbar.phone.ui.StatusBarIconController +import com.android.systemui.statusbar.pipeline.battery.ui.composable.UnifiedBattery +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel.Companion.STATUS_BAR_BATTERY_HEIGHT +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel.Companion.STATUS_BAR_BATTERY_WIDTH import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarIconBlockListBinder import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel @@ -73,14 +80,13 @@ constructor( ) { fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { val composeView = ComposeView(root.context) - val displayId = root.context.displayId val darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId) ?: return composeView composeView.apply { setContent { StatusBarRoot( parent = root, - statusBarViewModel = homeStatusBarViewModelFactory.create(displayId), + statusBarViewModelFactory = homeStatusBarViewModelFactory, statusBarViewBinder = homeStatusBarViewBinder, notificationIconsBinder = notificationIconsBinder, darkIconManagerFactory = darkIconManagerFactory, @@ -110,7 +116,7 @@ constructor( @Composable fun StatusBarRoot( parent: ViewGroup, - statusBarViewModel: HomeStatusBarViewModel, + statusBarViewModelFactory: HomeStatusBarViewModelFactory, statusBarViewBinder: HomeStatusBarViewBinder, notificationIconsBinder: NotificationIconContainerStatusBarViewBinder, darkIconManagerFactory: DarkIconManager.Factory, @@ -120,6 +126,10 @@ fun StatusBarRoot( eventAnimationInteractor: SystemStatusEventAnimationInteractor, onViewCreated: (ViewGroup) -> Unit, ) { + val displayId = parent.context.displayId + val statusBarViewModel = + rememberViewModel("HomeStatusBar") { statusBarViewModelFactory.create(displayId) } + Box(Modifier.fillMaxSize()) { // TODO(b/364360986): remove this before rolling the flag forward if (StatusBarRootModernization.SHOW_DISAMBIGUATION) { @@ -159,10 +169,6 @@ fun StatusBarRoot( LinearLayout.LayoutParams.WRAP_CONTENT, ) - setViewCompositionStrategy( - ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed - ) - setContent { PlatformTheme { val chips by @@ -241,6 +247,12 @@ fun StatusBarRoot( endSideContent.addView(composeView, 0) } + // If the flag is enabled, create and add a compose battery view to the end + // of the system_icons container + if (NewStatusBarIcons.isEnabled) { + addBatteryComposable(phoneStatusBarView, statusBarViewModel) + } + notificationIconsBinder.bindWhileAttached( notificationIconContainer, context.displayId, @@ -263,6 +275,27 @@ fun StatusBarRoot( } } +/** Create a new [UnifiedBattery] and add it to the end of the system_icons container */ +private fun addBatteryComposable( + phoneStatusBarView: PhoneStatusBarView, + statusBarViewModel: HomeStatusBarViewModel, +) { + val batteryComposeView = + ComposeView(phoneStatusBarView.context).apply { + setContent { + UnifiedBattery( + modifier = + Modifier.height(STATUS_BAR_BATTERY_HEIGHT).width(STATUS_BAR_BATTERY_WIDTH), + viewModelFactory = statusBarViewModel.batteryViewModelFactory, + isDark = statusBarViewModel.areaDark, + ) + } + } + phoneStatusBarView.findViewById<ViewGroup>(R.id.system_icons).apply { + addView(batteryComposeView, -1) + } +} + /** * This is our analog of the flexi "ribbon", which just shows some text so we know if the flag is on */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index 1bc45a95044c..f396cbfc8000 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import android.annotation.ColorInt import android.graphics.Rect import android.view.View +import androidx.compose.runtime.getValue import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -28,6 +29,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.lifecycle.Hydrator import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.plugins.DarkIconDispatcher @@ -55,8 +58,10 @@ import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNoti import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor +import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarIconBlockListInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarInteractor import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel @@ -90,6 +95,9 @@ import kotlinx.coroutines.flow.stateIn * so that it's all in one place and easily testable outside of the fragment. */ interface HomeStatusBarViewModel { + /** Factory to create the view model for the battery icon */ + val batteryViewModelFactory: BatteryViewModel.Factory + /** * True if the device is currently transitioning from lockscreen to occluded and false * otherwise. @@ -171,6 +179,9 @@ interface HomeStatusBarViewModel { */ val areaTint: Flow<StatusBarTintColor> + /** [IsAreaDark] applicable for this status bar's display and content area */ + val areaDark: IsAreaDark + /** Interface for the assisted factory, to allow for providing a fake in tests */ interface HomeStatusBarViewModelFactory { fun create(displayId: Int): HomeStatusBarViewModel @@ -181,6 +192,7 @@ class HomeStatusBarViewModelImpl @AssistedInject constructor( @Assisted thisDisplayId: Int, + override val batteryViewModelFactory: BatteryViewModel.Factory, tableLoggerFactory: TableLogBufferFactory, homeStatusBarInteractor: HomeStatusBarInteractor, homeStatusBarIconBlockListInteractor: HomeStatusBarIconBlockListInteractor, @@ -201,7 +213,9 @@ constructor( statusBarContentInsetsViewModelStore: StatusBarContentInsetsViewModelStore, @Background bgScope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, -) : HomeStatusBarViewModel { +) : HomeStatusBarViewModel, ExclusiveActivatable() { + + private val hydrator = Hydrator(traceName = "HomeStatusBarViewModel.hydrator") val tableLogger = tableLoggerFactory.getOrCreate(tableLogBufferName(thisDisplayId), 200) @@ -294,6 +308,13 @@ constructor( .distinctUntilChanged() .flowOn(bgDispatcher) + override val areaDark: IsAreaDark by + hydrator.hydratedStateOf( + traceName = "areaDark", + initialValue = IsAreaDark { true }, + source = darkIconInteractor.isAreaDark(thisDisplayId), + ) + /** * True if the current SysUI state can show the home status bar (aka this status bar), and false * if we shouldn't be showing any part of the home status bar. @@ -473,6 +494,10 @@ constructor( @View.Visibility private fun Boolean.toVisibleOrInvisible(): Int = if (this) View.VISIBLE else View.INVISIBLE + override suspend fun onActivated(): Nothing { + hydrator.activate() + } + /** Inject this to create the display-dependent view model */ @AssistedFactory interface HomeStatusBarViewModelFactoryImpl : diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index edb0f352a64a..f3af794f776b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory 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.policy.Clock import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.statusbar.policy.NextAlarmController @@ -202,6 +203,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { Lazy { kosmos.shadeDisplaysRepository }, variableDateViewControllerFactory, batteryMeterViewController, + kosmos.batteryViewModelFactory, dumpManager, mShadeCarrierGroupControllerBuilder, combinedShadeHeadersConstraintManager, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModelKosmos.kt index c6cf0063986a..7dd0103e9f5a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/battery/ui/viewmodel/BatteryViewModelKosmos.kt @@ -22,3 +22,10 @@ import com.android.systemui.statusbar.pipeline.battery.domain.interactor.battery val Kosmos.batteryViewModel by Kosmos.Fixture { BatteryViewModel(batteryInteractor, testableContext) } + +val Kosmos.batteryViewModelFactory by + Kosmos.Fixture { + object : BatteryViewModel.Factory { + override fun create(): BatteryViewModel = batteryViewModel + } + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt index bc29dba3442c..fbada934c9d4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor +import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.batteryViewModelFactory import com.android.systemui.statusbar.pipeline.shared.domain.interactor.homeStatusBarIconBlockListInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.homeStatusBarInteractor @@ -42,6 +43,7 @@ var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by Kosmos.Fixture { HomeStatusBarViewModelImpl( testableContext.displayId, + batteryViewModelFactory, tableLogBufferFactory, homeStatusBarInteractor, homeStatusBarIconBlockListInteractor, |