Merge "Refactor - Pull status bar "orchestration" logic out of CentralSurfaces" into main
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 55ccaa6..92bc95a 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -70,4 +70,12 @@
* {@link #onBootCompleted()} will never be called before {@link #start()}. */
default void onBootCompleted() {
}
+
+ /** No op implementation that can be used when feature flagging on the Dagger Module level. */
+ CoreStartable NOP = new Nop();
+
+ class Nop implements CoreStartable {
+ @Override
+ public void start() {}
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
new file mode 100644
index 0000000..57c8bc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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 android.app.StatusBarManager
+import android.content.Context
+import android.os.Binder
+import android.os.RemoteException
+import android.view.WindowInsets
+import com.android.internal.statusbar.IStatusBarService
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.systemui.CoreStartable
+import com.android.systemui.InitController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import dagger.Lazy
+import javax.inject.Inject
+
+@SysUISingleton
+class CommandQueueInitializer
+@Inject
+constructor(
+ private val context: Context,
+ private val commandQueue: CommandQueue,
+ private val commandQueueCallbacksLazy: Lazy<CommandQueue.Callbacks>,
+ private val statusBarModeRepository: StatusBarModeRepositoryStore,
+ private val initController: InitController,
+ private val barService: IStatusBarService,
+ private val navigationBarController: NavigationBarController,
+) : CoreStartable {
+
+ override fun start() {
+ StatusBarSimpleFragment.assertInNewMode()
+ val result: RegisterStatusBarResult =
+ try {
+ barService.registerStatusBar(commandQueue)
+ } catch (ex: RemoteException) {
+ ex.rethrowFromSystemServer()
+ return
+ }
+
+ createNavigationBar(result)
+
+ if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
+ statusBarModeRepository.defaultDisplay.showTransient()
+ }
+ val displayId = context.display.displayId
+ val commandQueueCallbacks = commandQueueCallbacksLazy.get()
+ commandQueueCallbacks.onSystemBarAttributesChanged(
+ displayId,
+ result.mAppearance,
+ result.mAppearanceRegions,
+ result.mNavbarColorManagedByIme,
+ result.mBehavior,
+ result.mRequestedVisibleTypes,
+ result.mPackageName,
+ result.mLetterboxDetails,
+ )
+
+ // StatusBarManagerService has a back up of IME token and it's restored here.
+ commandQueueCallbacks.setImeWindowStatus(
+ displayId,
+ result.mImeWindowVis,
+ result.mImeBackDisposition,
+ result.mShowImeSwitcher,
+ )
+
+ // Set up the initial icon state
+ val numIcons: Int = result.mIcons.size
+ for (i in 0 until numIcons) {
+ commandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i))
+ }
+
+ // set the initial view visibility
+ val disabledFlags1 = result.mDisabledFlags1
+ val disabledFlags2 = result.mDisabledFlags2
+ initController.addPostInitTask {
+ commandQueue.disable(displayId, disabledFlags1, disabledFlags2, /* animate= */ false)
+ try {
+ // NOTE(b/262059863): Force-update the disable flags after applying the flags
+ // returned from registerStatusBar(). The result's disabled flags may be stale
+ // if StatusBarManager's disabled flags are updated between registering the bar
+ // and this handling this post-init task. We force an update in this case, and use a
+ // new token to not conflict with any other disabled flags already requested by
+ // SysUI
+ val token = Binder()
+ barService.disable(StatusBarManager.DISABLE_HOME, token, context.packageName)
+ barService.disable(0, token, context.packageName)
+ } catch (ex: RemoteException) {
+ ex.rethrowFromSystemServer()
+ }
+ }
+ }
+
+ private fun createNavigationBar(result: RegisterStatusBarResult) {
+ navigationBarController.createNavigationBars(/* includeDefaultDisplay= */ true, result)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
new file mode 100644
index 0000000..8bd990b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 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 android.view.View
+import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.PluginDependencyProvider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.shade.NotificationShadeWindowViewController
+import com.android.systemui.shade.ShadeSurface
+import com.android.systemui.statusbar.AutoHideUiElement
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.phone.AutoHideController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.wm.shell.bubbles.Bubbles
+import dagger.Lazy
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.launch
+
+/**
+ * Class responsible for managing the lifecycle and state of the status bar.
+ *
+ * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
+ * plan is break it out into individual classes.
+ */
+@SysUISingleton
+class StatusBarOrchestrator
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val statusBarInitializer: StatusBarInitializer,
+ private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarModeRepository: StatusBarModeRepositoryStore,
+ private val demoModeController: DemoModeController,
+ private val pluginDependencyProvider: PluginDependencyProvider,
+ private val autoHideController: AutoHideController,
+ private val remoteInputManager: NotificationRemoteInputManager,
+ private val notificationShadeWindowViewControllerLazy:
+ Lazy<NotificationShadeWindowViewController>,
+ private val shadeSurface: ShadeSurface,
+ private val bubblesOptional: Optional<Bubbles>,
+ private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ powerInteractor: PowerInteractor,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+) : CoreStartable {
+
+ private val phoneStatusBarViewController =
+ MutableStateFlow<PhoneStatusBarViewController?>(value = null)
+
+ private val phoneStatusBarTransitions =
+ MutableStateFlow<PhoneStatusBarTransitions?>(value = null)
+
+ private val shouldAnimateNextBarModeChange =
+ combine(
+ statusBarModeRepository.defaultDisplay.isTransientShown,
+ powerInteractor.isAwake,
+ statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ ) { isTransientShown, isDeviceAwake, statusBarWindowState ->
+ !isTransientShown &&
+ isDeviceAwake &&
+ statusBarWindowState != StatusBarWindowState.Hidden
+ }
+
+ private val controllerAndBouncerShowing =
+ combine(
+ phoneStatusBarViewController.filterNotNull(),
+ primaryBouncerInteractor.isShowing,
+ ::Pair,
+ )
+
+ private val barTransitionsAndDeviceAsleep =
+ combine(phoneStatusBarTransitions.filterNotNull(), powerInteractor.isAsleep, ::Pair)
+
+ private val statusBarVisible =
+ combine(
+ statusBarModeRepository.defaultDisplay.statusBarMode,
+ statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ ) { mode, statusBarWindowState ->
+ mode != StatusBarMode.LIGHTS_OUT &&
+ mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
+ statusBarWindowState != StatusBarWindowState.Hidden
+ }
+
+ private val barModeUpdate =
+ combine(
+ shouldAnimateNextBarModeChange,
+ phoneStatusBarTransitions.filterNotNull(),
+ statusBarModeRepository.defaultDisplay.statusBarMode,
+ ::Triple,
+ )
+ .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
+ // We only want to collect when either bar transitions or status bar mode
+ // changed.
+ Pair(barTransitions, statusBarMode)
+ }
+
+ override fun start() {
+ StatusBarSimpleFragment.assertInNewMode()
+ applicationScope.launch {
+ launch {
+ controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+ setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+ }
+ }
+ launch {
+ barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+ if (deviceAsleep) {
+ barTransitions.finishAnimations()
+ }
+ }
+ }
+ launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+ launch {
+ barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+ updateBarMode(animate, barTransitions, statusBarMode)
+ }
+ }
+ }
+ createAndAddWindow()
+ setupPluginDependencies()
+ setUpAutoHide()
+ }
+
+ private fun createAndAddWindow() {
+ initializeStatusBarFragment()
+ statusBarWindowController.attach()
+ }
+
+ private fun initializeStatusBarFragment() {
+ statusBarInitializer.statusBarViewUpdatedListener =
+ object : StatusBarInitializer.OnStatusBarViewUpdatedListener {
+ override fun onStatusBarViewUpdated(
+ statusBarViewController: PhoneStatusBarViewController,
+ statusBarTransitions: PhoneStatusBarTransitions,
+ ) {
+ phoneStatusBarViewController.value = statusBarViewController
+ phoneStatusBarTransitions.value = statusBarTransitions
+
+ notificationShadeWindowViewControllerLazy
+ .get()
+ .setStatusBarViewController(statusBarViewController)
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ shadeSurface.updateExpansionAndVisibility()
+ }
+ }
+ }
+
+ private fun setupPluginDependencies() {
+ pluginDependencyProvider.allowPluginDependency(DarkIconDispatcher::class.java)
+ pluginDependencyProvider.allowPluginDependency(StatusBarStateController::class.java)
+ }
+
+ private fun setUpAutoHide() {
+ autoHideController.setStatusBar(
+ object : AutoHideUiElement {
+ override fun synchronizeState() {}
+
+ override fun shouldHideOnTouch(): Boolean {
+ return !remoteInputManager.isRemoteInputActive
+ }
+
+ override fun isVisible(): Boolean {
+ return statusBarModeRepository.defaultDisplay.isTransientShown.value
+ }
+
+ override fun hide() {
+ statusBarModeRepository.defaultDisplay.clearTransient()
+ }
+ })
+ }
+
+ private fun updateBarMode(
+ animate: Boolean,
+ barTransitions: PhoneStatusBarTransitions,
+ barMode: StatusBarMode,
+ ) {
+ if (!demoModeController.isInDemoMode) {
+ barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
+ }
+ autoHideController.touchAutoHide()
+ }
+
+ private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+ bubblesOptional.ifPresent { bubbles: Bubbles ->
+ bubbles.onStatusBarVisibilityChanged(statusBarVisible)
+ }
+ }
+
+ private fun setBouncerShowingForStatusBarComponents(
+ controller: PhoneStatusBarViewController,
+ bouncerShowing: Boolean,
+ ) {
+ val importance =
+ if (bouncerShowing) {
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ } else {
+ View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ }
+ controller.setImportantForAccessibility(importance)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+ CentralSurfaces.dumpBarTransitions(
+ pw,
+ "PhoneStatusBarTransitions",
+ phoneStatusBarTransitions.value,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 3903ff3..cf238d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -46,6 +46,7 @@
*/
@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
abstract class StatusBarModule {
+
@Binds
@IntoMap
@ClassKey(OngoingCallController::class)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 1d3f0e1..5f4f72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -241,10 +241,10 @@
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
import dalvik.annotation.optimization.NeverCompile;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
@@ -304,6 +304,7 @@
};
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
+ StatusBarSimpleFragment.assertInLegacyMode();
mStatusBarWindowState = state;
updateBubblesVisibility();
}
@@ -813,8 +814,9 @@
mStartingSurfaceOptional = startingSurfaceOptional;
mDreamManager = dreamManager;
lockscreenShadeTransitionController.setCentralSurfaces(this);
- statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
-
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
+ }
mScreenOffAnimationController = screenOffAnimationController;
ShadeExpansionListener shadeExpansionListener = this::onPanelExpansionChanged;
@@ -901,10 +903,12 @@
mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
RegisterStatusBarResult result = null;
- try {
- result = mBarService.registerStatusBar(mCommandQueue);
- } catch (RemoteException ex) {
- ex.rethrowFromSystemServer();
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ try {
+ result = mBarService.registerStatusBar(mCommandQueue);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
}
createAndAddWindows(result);
@@ -912,30 +916,45 @@
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
- if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
- mStatusBarModeRepository.getDefaultDisplay().showTransient();
- }
- mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
- result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
- result.mRequestedVisibleTypes, result.mPackageName, result.mLetterboxDetails);
-
- // StatusBarManagerService has a back up of IME token and it's restored here.
- mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeWindowVis,
- result.mImeBackDisposition, result.mShowImeSwitcher);
-
- // Set up the initial icon state
- int numIcons = result.mIcons.size();
- for (int i = 0; i < numIcons; i++) {
- mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
- }
-
- if (DEBUG) {
- Log.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
- numIcons,
- result.mDisabledFlags1,
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
+ mStatusBarModeRepository.getDefaultDisplay().showTransient();
+ }
+ mCommandQueueCallbacks.onSystemBarAttributesChanged(
+ mDisplayId,
result.mAppearance,
- result.mImeWindowVis));
+ result.mAppearanceRegions,
+ result.mNavbarColorManagedByIme,
+ result.mBehavior,
+ result.mRequestedVisibleTypes,
+ result.mPackageName,
+ result.mLetterboxDetails);
+
+ // StatusBarManagerService has a back up of IME token and it's restored here.
+ mCommandQueueCallbacks.setImeWindowStatus(
+ mDisplayId,
+ result.mImeWindowVis,
+ result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+
+ // Set up the initial icon state
+ int numIcons = result.mIcons.size();
+ for (int i = 0; i < numIcons; i++) {
+ mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
+ }
+
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
+ numIcons,
+ result.mDisabledFlags1,
+ result.mAppearance,
+ result.mImeWindowVis));
+ }
}
IntentFilter internalFilter = new IntentFilter();
@@ -1005,24 +1024,30 @@
mAccessibilityFloatingMenuController.init();
- // set the initial view visibility
- int disabledFlags1 = result.mDisabledFlags1;
- int disabledFlags2 = result.mDisabledFlags2;
- mInitController.addPostInitTask(() -> {
- setUpDisableFlags(disabledFlags1, disabledFlags2);
- try {
- // NOTE(b/262059863): Force-update the disable flags after applying the flags
- // returned from registerStatusBar(). The result's disabled flags may be stale
- // if StatusBarManager's disabled flags are updated between registering the bar and
- // this handling this post-init task. We force an update in this case, and use a new
- // token to not conflict with any other disabled flags already requested by SysUI
- Binder token = new Binder();
- mBarService.disable(DISABLE_HOME, token, mContext.getPackageName());
- mBarService.disable(0, token, mContext.getPackageName());
- } catch (RemoteException ex) {
- ex.rethrowFromSystemServer();
- }
- });
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ // set the initial view visibility
+ int disabledFlags1 = result.mDisabledFlags1;
+ int disabledFlags2 = result.mDisabledFlags2;
+ mInitController.addPostInitTask(
+ () -> {
+ setUpDisableFlags(disabledFlags1, disabledFlags2);
+ try {
+ // NOTE(b/262059863): Force-update the disable flags after applying the
+ // flags returned from registerStatusBar(). The result's disabled flags
+ // may be stale if StatusBarManager's disabled flags are updated between
+ // registering the bar and this handling this post-init task. We force
+ // an update in this case, and use a new token to not conflict with any
+ // other disabled flags already requested by SysUI
+ Binder token = new Binder();
+ mBarService.disable(DISABLE_HOME, token, mContext.getPackageName());
+ mBarService.disable(0, token, mContext.getPackageName());
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ });
+ }
registerCallbacks();
@@ -1101,7 +1126,7 @@
/**
* @deprecated use {@link
- * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
+ * WindowRootViewVisibilityInteractor#isLockscreenOrShadeVisible()} instead.
*/ @VisibleForTesting
@Deprecated
void initShadeVisibilityListener() {
@@ -1168,13 +1193,16 @@
mWallpaperController.setRootView(getNotificationShadeWindowView());
mDemoModeController.addCallback(mDemoModeCallback);
- mJavaAdapter.alwaysCollectFlow(
- mStatusBarModeRepository.getDefaultDisplay().isTransientShown(),
- this::onTransientShownChanged);
- mJavaAdapter.alwaysCollectFlow(
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(),
- this::updateBarMode);
-
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator.
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.getDefaultDisplay().isTransientShown(),
+ this::onTransientShownChanged);
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(),
+ this::updateBarMode);
+ }
mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
mCommandQueue.addCallback(mCommandQueueCallbacks);
@@ -1184,59 +1212,70 @@
mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
mWakeUpCoordinator.onPanelExpansionChanged(currentState);
- // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
- mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
- mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
-
- // Set up CollapsedStatusBarFragment and PhoneStatusBarView
- mStatusBarInitializer.setStatusBarViewUpdatedListener(
- (statusBarViewController, statusBarTransitions) -> {
- mPhoneStatusBarViewController = statusBarViewController;
- mStatusBarTransitions = statusBarTransitions;
- getNotificationShadeWindowViewController()
- .setStatusBarViewController(mPhoneStatusBarViewController);
- // Ensure we re-propagate panel expansion values to the panel controller and
- // any listeners it may have, such as PanelBar. This will also ensure we
- // re-display the notification panel if necessary (for example, if
- // a heads-up notification was being displayed and should continue being
- // displayed).
- mShadeSurface.updateExpansionAndVisibility();
- setBouncerShowingForStatusBarComponents(mBouncerShowing);
- checkBarModes();
- });
- // When the flag is on, we register the fragment as a core startable and this is not needed
+ // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in
+ // StatusBarOrchestrator.
if (!StatusBarSimpleFragment.isEnabled()) {
+ // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
+ mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
+ mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
+
+ // Set up CollapsedStatusBarFragment and PhoneStatusBarView
+ mStatusBarInitializer.setStatusBarViewUpdatedListener(
+ (statusBarViewController, statusBarTransitions) -> {
+
+ mPhoneStatusBarViewController = statusBarViewController;
+ mStatusBarTransitions = statusBarTransitions;
+ getNotificationShadeWindowViewController()
+ .setStatusBarViewController(mPhoneStatusBarViewController);
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mShadeSurface.updateExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
+ checkBarModes();
+ });
+ // When the flag is on, we register the fragment as a core startable and this is not
+ // needed
mStatusBarInitializer.initializeStatusBar();
}
mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
- createNavigationBar(result);
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ createNavigationBar(result);
+ }
mAmbientIndicationContainer = getNotificationShadeWindowView().findViewById(
R.id.ambient_indication_container);
- mAutoHideController.setStatusBar(new AutoHideUiElement() {
- @Override
- public void synchronizeState() {
- checkBarModes();
- }
+ // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in
+ // StatusBarOrchestrator.
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ mAutoHideController.setStatusBar(
+ new AutoHideUiElement() {
+ @Override
+ public void synchronizeState() {
+ checkBarModes();
+ }
- @Override
- public boolean shouldHideOnTouch() {
- return !mRemoteInputManager.isRemoteInputActive();
- }
+ @Override
+ public boolean shouldHideOnTouch() {
+ return !mRemoteInputManager.isRemoteInputActive();
+ }
- @Override
- public boolean isVisible() {
- return isTransientShown();
- }
+ @Override
+ public boolean isVisible() {
+ return isTransientShown();
+ }
- @Override
- public void hide() {
- mStatusBarModeRepository.getDefaultDisplay().clearTransient();
- }
- });
+ @Override
+ public void hide() {
+ mStatusBarModeRepository.getDefaultDisplay().clearTransient();
+ }
+ });
+ }
ScrimView scrimBehind = getNotificationShadeWindowView().findViewById(R.id.scrim_behind);
ScrimView notificationsScrim = getNotificationShadeWindowView()
@@ -1479,12 +1518,14 @@
* @param state2 disable2 flags
*/
protected void setUpDisableFlags(int state1, int state2) {
+ StatusBarSimpleFragment.assertInLegacyMode();
mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
}
// TODO(b/117478341): This was left such that CarStatusBar can override this method.
// Try to remove this.
protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
+ StatusBarSimpleFragment.assertInLegacyMode();
mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);
}
@@ -1697,14 +1738,16 @@
@Override
public void checkBarModes() {
if (mDemoModeController.isInDemoMode()) return;
- if (mStatusBarTransitions != null) {
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator.
+ if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) {
checkBarMode(
mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(),
mStatusBarWindowState,
mStatusBarTransitions);
+ mNoAnimationOnNextBarModeChange = false;
}
mNavigationBarController.checkNavBarModes(mDisplayId);
- mNoAnimationOnNextBarModeChange = false;
}
/** Temporarily hides Bubbles if the status bar is hidden. */
@@ -1728,7 +1771,9 @@
}
private void finishBarAnimations() {
- if (mStatusBarTransitions != null) {
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator.
+ if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) {
mStatusBarTransitions.finishAnimations();
}
mNavigationBarController.finishBarAnimations(mDisplayId);
@@ -1770,14 +1815,17 @@
}
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
- pw.print(" mStatusBarWindowState=");
- pw.println(windowStateToString(mStatusBarWindowState));
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ pw.print(" mStatusBarWindowState=");
+ pw.println(windowStateToString(mStatusBarWindowState));
+ }
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
- CentralSurfaces.dumpBarTransitions(
- pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
-
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ CentralSurfaces.dumpBarTransitions(
+ pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
+ }
pw.println(" mMediaManager: ");
if (mMediaManager != null) {
mMediaManager.dump(pw, args);
@@ -1850,7 +1898,11 @@
private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
- mStatusBarWindowController.attach();
+ // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+ // StatusBarOrchestrator
+ if (!StatusBarSimpleFragment.isEnabled()) {
+ mStatusBarWindowController.attach();
+ }
}
// called by makeStatusbar and also by PhoneStatusBarView
@@ -2475,7 +2527,7 @@
int importance = bouncerShowing
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- if (mPhoneStatusBarViewController != null) {
+ if (!StatusBarSimpleFragment.isEnabled() && mPhoneStatusBarViewController != null) {
mPhoneStatusBarViewController.setImportantForAccessibility(importance);
}
mShadeSurface.setImportantForAccessibility(importance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 13b651e8..5b03198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -16,10 +16,20 @@
package com.android.systemui.statusbar.phone.dagger
import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.core.CommandQueueInitializer
import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.core.StatusBarInitializerImpl
+import com.android.systemui.statusbar.core.StatusBarOrchestrator
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
import dagger.Binds
+import dagger.Lazy
import dagger.Module
+import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@@ -27,6 +37,16 @@
@Module
interface StatusBarPhoneModule {
+ @Binds
+ abstract fun windowStateRepoStore(
+ impl: StatusBarWindowStateRepositoryStoreImpl
+ ): StatusBarWindowStateRepositoryStore
+
+ @Binds
+ abstract fun commandQCallbacks(
+ impl: CentralSurfacesCommandQueueCallbacks
+ ): CommandQueue.Callbacks
+
/** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
@Binds
@IntoMap
@@ -34,4 +54,34 @@
fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable
@Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarOrchestrator::class)
+ fun orchestratorCoreStartable(
+ orchestratorLazy: Lazy<StatusBarOrchestrator>
+ ): CoreStartable {
+ return if (StatusBarSimpleFragment.isEnabled) {
+ orchestratorLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(CommandQueueInitializer::class)
+ fun commandQueueInitializerCoreStartable(
+ initializerLazy: Lazy<CommandQueueInitializer>
+ ): CoreStartable {
+ return if (StatusBarSimpleFragment.isEnabled) {
+ initializerLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
new file mode 100644
index 0000000..2a196c6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 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 android.internal.statusbar.fakeStatusBarService
+import android.platform.test.annotations.EnableFlags
+import android.view.WindowInsets
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.initController
+import com.android.systemui.keyguard.data.repository.fakeCommandQueue
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockCommandQueueCallbacks
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommandQueueInitializerTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val initController = kosmos.initController
+ private val commandQueue = kosmos.fakeCommandQueue
+ private val commandQueueCallbacks = kosmos.mockCommandQueueCallbacks
+ private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
+ private val fakeStatusBarService = kosmos.fakeStatusBarService
+ private val initializer = kosmos.commandQueueInitializer
+
+ @Test
+ fun start_registersStatusBar() {
+ initializer.start()
+
+ assertThat(fakeStatusBarService.registeredStatusBar).isNotNull()
+ }
+
+ @Test
+ fun start_barResultHasTransientStatusBar_transientStateIsTrue() {
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isTrue()
+ }
+
+ @Test
+ fun start_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun start_callsOnSystemBarAttributesChanged_basedOnRegisterBarResult() {
+ initializer.start()
+
+ verify(commandQueueCallbacks)
+ .onSystemBarAttributesChanged(
+ context.displayId,
+ fakeStatusBarService.appearance,
+ fakeStatusBarService.appearanceRegions,
+ fakeStatusBarService.navbarColorManagedByIme,
+ fakeStatusBarService.behavior,
+ fakeStatusBarService.requestedVisibleTypes,
+ fakeStatusBarService.packageName,
+ fakeStatusBarService.letterboxDetails,
+ )
+ }
+
+ @Test
+ fun start_callsSetIcon_basedOnRegisterBarResult() {
+ initializer.start()
+
+ assertThat(commandQueue.icons).isEqualTo(fakeStatusBarService.statusBarIcons)
+ }
+
+ @Test
+ fun start_callsSetImeWindowStatus_basedOnRegisterBarResult() {
+ initializer.start()
+
+ verify(commandQueueCallbacks)
+ .setImeWindowStatus(
+ context.displayId,
+ fakeStatusBarService.imeWindowVis,
+ fakeStatusBarService.imeBackDisposition,
+ fakeStatusBarService.showImeSwitcher,
+ )
+ }
+
+ @Test
+ fun start_afterPostInitTaskExecuted_callsDisableFlags_basedOnRegisterBarResult() {
+ initializer.start()
+
+ initController.executePostInitTasks()
+
+ assertThat(commandQueue.disableFlags1ForDisplay(context.displayId))
+ .isEqualTo(fakeStatusBarService.disabledFlags1)
+ assertThat(commandQueue.disableFlags2ForDisplay(context.displayId))
+ .isEqualTo(fakeStatusBarService.disabledFlags2)
+ }
+
+ @Test
+ fun start_beforePostInitTaskExecuted_doesNotCallsDisableFlags() {
+ initializer.start()
+
+ assertThat(commandQueue.disableFlags1ForDisplay(context.displayId)).isNull()
+ assertThat(commandQueue.disableFlags2ForDisplay(context.displayId)).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
new file mode 100644
index 0000000..5803365
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2024 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 android.platform.test.annotations.EnableFlags
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.mockPluginDependencyProvider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.shade.mockNotificationShadeWindowViewController
+import com.android.systemui.shade.mockShadeSurface
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT
+import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
+import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
+import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.testKosmos
+import com.android.wm.shell.bubbles.bubbles
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarOrchestratorTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().also {
+ it.testDispatcher = it.unconfinedTestDispatcher
+ it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
+ }
+ private val testScope = kosmos.testScope
+ private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
+ private val statusBarWindowController = kosmos.fakeStatusBarWindowController
+ private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
+ private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
+ private val notificationShadeWindowViewController =
+ kosmos.mockNotificationShadeWindowViewController
+ private val shadeSurface = kosmos.mockShadeSurface
+ private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
+ private val fakeStatusBarWindowStateRepositoryStore =
+ kosmos.fakeStatusBarWindowStateRepositoryStore
+ private val fakePowerRepository = kosmos.fakePowerRepository
+ private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
+ private val mockBubbles = kosmos.bubbles
+
+ private val orchestrator = kosmos.statusBarOrchestrator
+
+ @Test
+ fun start_setsUpPluginDependencies() {
+ orchestrator.start()
+
+ verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+ verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+ }
+
+ @Test
+ fun start_attachesWindow() {
+ orchestrator.start()
+
+ assertThat(statusBarWindowController.isAttached).isTrue()
+ }
+
+ @Test
+ fun start_setsStatusBarControllerOnShade() {
+ orchestrator.start()
+
+ verify(notificationShadeWindowViewController)
+ .setStatusBarViewController(statusBarViewController)
+ }
+
+ @Test
+ fun start_updatesShadeExpansion() {
+ orchestrator.start()
+
+ verify(shadeSurface).updateExpansionAndVisibility()
+ }
+
+ @Test
+ fun bouncerShowing_setsImportanceForA11yToNoHideDescendants() =
+ testScope.runTest {
+ orchestrator.start()
+
+ bouncerRepository.setPrimaryShow(isShowing = true)
+
+ verify(statusBarViewController)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
+ }
+
+ @Test
+ fun bouncerNotShowing_setsImportanceForA11yToNoHideDescendants() =
+ testScope.runTest {
+ orchestrator.start()
+
+ bouncerRepository.setPrimaryShow(isShowing = false)
+
+ verify(statusBarViewController)
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
+ }
+
+ @Test
+ fun deviceGoesToSleep_barTransitionsAnimationsAreFinished() =
+ testScope.runTest {
+ putDeviceToSleep()
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions).finishAnimations()
+ }
+
+ @Test
+ fun deviceIsAwake_barTransitionsAnimationsAreNotFinished() =
+ testScope.runTest {
+ awakeDevice()
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+ }
+
+ @Test
+ fun statusBarVisible_notifiesBubbles() =
+ testScope.runTest {
+ setStatusBarMode(TRANSPARENT)
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+
+ orchestrator.start()
+
+ verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ true)
+ }
+
+ @Test
+ fun statusBarInLightsOutMode_notifiesBubblesWithStatusBarInvisible() =
+ testScope.runTest {
+ setStatusBarMode(LIGHTS_OUT)
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+
+ orchestrator.start()
+
+ verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+ }
+
+ @Test
+ fun statusBarInLightsOutTransparentMode_notifiesBubblesWithStatusBarInvisible() =
+ testScope.runTest {
+ setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+
+ orchestrator.start()
+
+ verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+ }
+
+ @Test
+ fun statusBarWindowNotShowing_notifiesBubblesWithStatusBarInvisible() =
+ testScope.runTest {
+ setStatusBarMode(TRANSPARENT)
+ setStatusBarWindowState(StatusBarWindowState.Hidden)
+
+ orchestrator.start()
+
+ verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+ }
+
+ @Test
+ fun statusBarModeChange_transitionsToModeWithAnimation() =
+ testScope.runTest {
+ awakeDevice()
+ clearTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+ }
+
+ @Test
+ fun statusBarModeChange_keepsTransitioningAsModeChanges() =
+ testScope.runTest {
+ awakeDevice()
+ clearTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+
+ setStatusBarMode(OPAQUE)
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
+
+ setStatusBarMode(LIGHTS_OUT)
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
+
+ setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+ }
+
+ @Test
+ fun statusBarModeChange_transientIsShown_transitionsToModeWithoutAnimation() =
+ testScope.runTest {
+ awakeDevice()
+ setTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+ }
+
+ @Test
+ fun statusBarModeChange_windowIsHidden_transitionsToModeWithoutAnimation() =
+ testScope.runTest {
+ awakeDevice()
+ clearTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Hidden)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+ }
+
+ @Test
+ fun statusBarModeChange_deviceIsAsleep_transitionsToModeWithoutAnimation() =
+ testScope.runTest {
+ putDeviceToSleep()
+ clearTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ verify(mockPhoneStatusBarTransitions)
+ .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+ }
+
+ @Test
+ fun statusBarModeAnimationConditionsChange_withoutBarModeChange_noNewTransitionsHappen() =
+ testScope.runTest {
+ awakeDevice()
+ clearTransientStatusBar()
+ setStatusBarWindowState(StatusBarWindowState.Showing)
+ setStatusBarMode(TRANSPARENT)
+
+ orchestrator.start()
+
+ putDeviceToSleep()
+ awakeDevice()
+ setTransientStatusBar()
+ clearTransientStatusBar()
+
+ verify(mockPhoneStatusBarTransitions, times(1))
+ .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+ }
+
+ private fun putDeviceToSleep() {
+ fakePowerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.KEY,
+ lastSleepReason = WakeSleepReason.KEY,
+ powerButtonLaunchGestureTriggered = true,
+ )
+ }
+
+ private fun awakeDevice() {
+ fakePowerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.KEY,
+ lastSleepReason = WakeSleepReason.KEY,
+ powerButtonLaunchGestureTriggered = true,
+ )
+ }
+
+ private fun setTransientStatusBar() {
+ statusBarModeRepository.defaultDisplay.showTransient()
+ }
+
+ private fun clearTransientStatusBar() {
+ statusBarModeRepository.defaultDisplay.clearTransient()
+ }
+
+ private fun setStatusBarWindowState(state: StatusBarWindowState) {
+ fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+ }
+
+ private fun setStatusBarMode(statusBarMode: StatusBarMode) {
+ statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c710c56..15ea811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -169,6 +169,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
+import com.android.systemui.statusbar.core.StatusBarOrchestrator;
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -346,6 +347,7 @@
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
@Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ @Mock private StatusBarOrchestrator mStatusBarOrchestrator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
new file mode 100644
index 0000000..cc0597b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2024 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 android.internal.statusbar
+
+import android.app.Notification
+import android.content.ComponentName
+import android.graphics.Rect
+import android.graphics.drawable.Icon
+import android.hardware.biometrics.IBiometricContextListener
+import android.hardware.biometrics.IBiometricSysuiReceiver
+import android.hardware.biometrics.PromptInfo
+import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback
+import android.media.INearbyMediaDevicesProvider
+import android.media.MediaRoute2Info
+import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.os.UserHandle
+import android.util.ArrayMap
+import android.view.KeyEvent
+import com.android.internal.logging.InstanceId
+import com.android.internal.statusbar.IAddTileResultCallback
+import com.android.internal.statusbar.ISessionListener
+import com.android.internal.statusbar.IStatusBar
+import com.android.internal.statusbar.IStatusBarService
+import com.android.internal.statusbar.IUndoMediaTransferCallback
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.internal.view.AppearanceRegion
+import org.mockito.kotlin.mock
+
+class FakeStatusBarService : IStatusBarService.Stub() {
+
+ var registeredStatusBar: IStatusBar? = null
+ private set
+
+ var statusBarIcons =
+ ArrayMap<String, StatusBarIcon>().also {
+ it["slot1"] = mock<StatusBarIcon>()
+ it["slot2"] = mock<StatusBarIcon>()
+ }
+ var disabledFlags1 = 1234567
+ var appearance = 123
+ var appearanceRegions =
+ arrayOf(
+ AppearanceRegion(
+ /* appearance = */ 123,
+ /* bounds = */ Rect(/* left= */ 4, /* top= */ 3, /* right= */ 2, /* bottom= */ 1),
+ ),
+ AppearanceRegion(
+ /* appearance = */ 345,
+ /* bounds = */ Rect(/* left= */ 1, /* top= */ 2, /* right= */ 3, /* bottom= */ 4),
+ ),
+ )
+ var imeWindowVis = 987
+ var imeBackDisposition = 654
+ var showImeSwitcher = true
+ var disabledFlags2 = 7654321
+ var navbarColorManagedByIme = true
+ var behavior = 234
+ var requestedVisibleTypes = 345
+ var packageName = "fake.bar.ser.vice"
+ var transientBarTypes = 0
+ var letterboxDetails =
+ arrayOf(
+ LetterboxDetails(
+ /* letterboxInnerBounds = */ Rect(
+ /* left= */ 5,
+ /* top= */ 6,
+ /* right= */ 7,
+ /* bottom= */ 8,
+ ),
+ /* letterboxFullBounds = */ Rect(
+ /* left= */ 1,
+ /* top= */ 2,
+ /* right= */ 3,
+ /* bottom= */ 4,
+ ),
+ /* appAppearance = */ 123,
+ )
+ )
+
+ override fun expandNotificationsPanel() {}
+
+ override fun collapsePanels() {}
+
+ override fun togglePanel() {}
+
+ override fun disable(what: Int, token: IBinder, pkg: String) {
+ disableForUser(what, token, pkg, userId = 0)
+ }
+
+ override fun disableForUser(what: Int, token: IBinder, pkg: String, userId: Int) {}
+
+ override fun disable2(what: Int, token: IBinder, pkg: String) {
+ disable2ForUser(what, token, pkg, userId = 0)
+ }
+
+ override fun disable2ForUser(what: Int, token: IBinder, pkg: String, userId: Int) {}
+
+ override fun getDisableFlags(token: IBinder, userId: Int): IntArray {
+ return intArrayOf(disabledFlags1, disabledFlags2)
+ }
+
+ override fun setIcon(
+ slot: String,
+ iconPackage: String,
+ iconId: Int,
+ iconLevel: Int,
+ contentDescription: String,
+ ) {}
+
+ override fun setIconVisibility(slot: String, visible: Boolean) {}
+
+ override fun removeIcon(slot: String) {}
+
+ override fun setImeWindowStatus(
+ displayId: Int,
+ vis: Int,
+ backDisposition: Int,
+ showImeSwitcher: Boolean,
+ ) {}
+
+ override fun expandSettingsPanel(subPanel: String) {}
+
+ override fun registerStatusBar(callbacks: IStatusBar): RegisterStatusBarResult {
+ registeredStatusBar = callbacks
+ return RegisterStatusBarResult(
+ statusBarIcons,
+ disabledFlags1,
+ appearance,
+ appearanceRegions,
+ imeWindowVis,
+ imeBackDisposition,
+ showImeSwitcher,
+ disabledFlags2,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibleTypes,
+ packageName,
+ transientBarTypes,
+ letterboxDetails,
+ )
+ }
+
+ override fun onPanelRevealed(clearNotificationEffects: Boolean, numItems: Int) {}
+
+ override fun onPanelHidden() {}
+
+ override fun clearNotificationEffects() {}
+
+ override fun onNotificationClick(key: String, nv: NotificationVisibility) {}
+
+ override fun onNotificationActionClick(
+ key: String,
+ actionIndex: Int,
+ action: Notification.Action,
+ nv: NotificationVisibility,
+ generatedByAssistant: Boolean,
+ ) {}
+
+ override fun onNotificationError(
+ pkg: String,
+ tag: String,
+ id: Int,
+ uid: Int,
+ initialPid: Int,
+ message: String,
+ userId: Int,
+ ) {}
+
+ override fun onClearAllNotifications(userId: Int) {}
+
+ override fun onNotificationClear(
+ pkg: String,
+ userId: Int,
+ key: String,
+ dismissalSurface: Int,
+ dismissalSentiment: Int,
+ nv: NotificationVisibility,
+ ) {}
+
+ override fun onNotificationVisibilityChanged(
+ newlyVisibleKeys: Array<NotificationVisibility>,
+ noLongerVisibleKeys: Array<NotificationVisibility>,
+ ) {}
+
+ override fun onNotificationExpansionChanged(
+ key: String,
+ userAction: Boolean,
+ expanded: Boolean,
+ notificationLocation: Int,
+ ) {}
+
+ override fun onNotificationDirectReplied(key: String) {}
+
+ override fun onNotificationSmartSuggestionsAdded(
+ key: String,
+ smartReplyCount: Int,
+ smartActionCount: Int,
+ generatedByAssistant: Boolean,
+ editBeforeSending: Boolean,
+ ) {}
+
+ override fun onNotificationSmartReplySent(
+ key: String,
+ replyIndex: Int,
+ reply: CharSequence,
+ notificationLocation: Int,
+ modifiedBeforeSending: Boolean,
+ ) {}
+
+ override fun onNotificationSettingsViewed(key: String) {}
+
+ override fun onNotificationBubbleChanged(key: String, isBubble: Boolean, flags: Int) {}
+
+ override fun onBubbleMetadataFlagChanged(key: String, flags: Int) {}
+
+ override fun hideCurrentInputMethodForBubbles(displayId: Int) {}
+
+ override fun grantInlineReplyUriPermission(
+ key: String,
+ uri: Uri,
+ user: UserHandle,
+ packageName: String,
+ ) {}
+
+ override fun clearInlineReplyUriPermissions(key: String) {}
+
+ override fun onNotificationFeedbackReceived(key: String, feedback: Bundle) {}
+
+ override fun onGlobalActionsShown() {}
+
+ override fun onGlobalActionsHidden() {}
+
+ override fun shutdown() {}
+
+ override fun reboot(safeMode: Boolean) {}
+
+ override fun restart() {}
+
+ override fun addTile(tile: ComponentName) {}
+
+ override fun remTile(tile: ComponentName) {}
+
+ override fun clickTile(tile: ComponentName) {}
+
+ override fun handleSystemKey(key: KeyEvent) {}
+
+ override fun getLastSystemKey(): Int {
+ return -1
+ }
+
+ override fun showPinningEnterExitToast(entering: Boolean) {}
+
+ override fun showPinningEscapeToast() {}
+
+ override fun showAuthenticationDialog(
+ promptInfo: PromptInfo,
+ sysuiReceiver: IBiometricSysuiReceiver,
+ sensorIds: IntArray,
+ credentialAllowed: Boolean,
+ requireConfirmation: Boolean,
+ userId: Int,
+ operationId: Long,
+ opPackageName: String,
+ requestId: Long,
+ ) {}
+
+ override fun onBiometricAuthenticated(modality: Int) {}
+
+ override fun onBiometricHelp(modality: Int, message: String) {}
+
+ override fun onBiometricError(modality: Int, error: Int, vendorCode: Int) {}
+
+ override fun hideAuthenticationDialog(requestId: Long) {}
+
+ override fun setBiometicContextListener(listener: IBiometricContextListener) {}
+
+ override fun setUdfpsRefreshRateCallback(callback: IUdfpsRefreshRateRequestCallback) {}
+
+ override fun showInattentiveSleepWarning() {}
+
+ override fun dismissInattentiveSleepWarning(animated: Boolean) {}
+
+ override fun startTracing() {}
+
+ override fun stopTracing() {}
+
+ override fun isTracing(): Boolean {
+ return false
+ }
+
+ override fun suppressAmbientDisplay(suppress: Boolean) {}
+
+ override fun requestTileServiceListeningState(componentName: ComponentName, userId: Int) {}
+
+ override fun requestAddTile(
+ componentName: ComponentName,
+ label: CharSequence,
+ icon: Icon,
+ userId: Int,
+ callback: IAddTileResultCallback,
+ ) {}
+
+ override fun cancelRequestAddTile(packageName: String) {}
+
+ override fun setNavBarMode(navBarMode: Int) {}
+
+ override fun getNavBarMode(): Int {
+ return -1
+ }
+
+ override fun registerSessionListener(sessionFlags: Int, listener: ISessionListener) {}
+
+ override fun unregisterSessionListener(sessionFlags: Int, listener: ISessionListener) {}
+
+ override fun onSessionStarted(sessionType: Int, instanceId: InstanceId) {}
+
+ override fun onSessionEnded(sessionType: Int, instanceId: InstanceId) {}
+
+ override fun updateMediaTapToTransferSenderDisplay(
+ displayState: Int,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback,
+ ) {}
+
+ override fun updateMediaTapToTransferReceiverDisplay(
+ displayState: Int,
+ routeInfo: MediaRoute2Info,
+ appIcon: Icon,
+ appName: CharSequence,
+ ) {}
+
+ override fun registerNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
+
+ override fun unregisterNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
+
+ override fun showRearDisplayDialog(currentBaseState: Int) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt
new file mode 100644
index 0000000..1304161
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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 android.internal.statusbar
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeStatusBarService by Kosmos.Fixture { FakeStatusBarService() }
+
+var Kosmos.statusBarService by Kosmos.Fixture { fakeStatusBarService }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt
new file mode 100644
index 0000000..39384fd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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
+
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockDemoModeController by Kosmos.Fixture { mock<DemoModeController>() }
+
+var Kosmos.demoModeController by Kosmos.Fixture { mockDemoModeController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt
new file mode 100644
index 0000000..13169e1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.initController by Kosmos.Fixture { InitController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
index 3a59f6a..601c145 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
@@ -18,6 +18,8 @@
package com.android.systemui.keyguard.data.repository
import android.content.Context
+import androidx.collection.ArrayMap
+import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.CommandQueue
@@ -31,6 +33,11 @@
CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) {
private val callbacks = mutableListOf<Callbacks>()
+ val icons = ArrayMap<String, StatusBarIcon>()
+
+ private val perDisplayDisableFlags1 = mutableMapOf<Int, Int>()
+ private val perDisplayDisableFlags2 = mutableMapOf<Int, Int>()
+
override fun addCallback(callback: Callbacks) {
callbacks.add(callback)
}
@@ -44,6 +51,23 @@
}
fun callbackCount(): Int = callbacks.size
+
+ override fun setIcon(slot: String, icon: StatusBarIcon) {
+ icons[slot] = icon
+ }
+
+ override fun disable(displayId: Int, state1: Int, state2: Int, animate: Boolean) {
+ perDisplayDisableFlags1[displayId] = state1
+ perDisplayDisableFlags2[displayId] = state2
+ }
+
+ override fun disable(displayId: Int, state1: Int, state2: Int) {
+ disable(displayId, state1, state2, /* animate= */ false)
+ }
+
+ fun disableFlags1ForDisplay(displayId: Int) = perDisplayDisableFlags1[displayId]
+
+ fun disableFlags2ForDisplay(displayId: Int) = perDisplayDisableFlags2[displayId]
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt
new file mode 100644
index 0000000..9e2039e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.navigationbar
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockNavigationBarController by Kosmos.Fixture { mock<NavigationBarController>() }
+
+var Kosmos.navigationBarController by Kosmos.Fixture { mockNavigationBarController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt
new file mode 100644
index 0000000..f1388e9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.plugins
+
+import android.testing.LeakCheck
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.utils.leaks.FakePluginManager
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
+
+val Kosmos.leakCheck by Kosmos.Fixture { LeakCheck() }
+
+val Kosmos.fakePluginManager by Kosmos.Fixture { FakePluginManager(leakCheck) }
+
+var Kosmos.pluginManager by Kosmos.Fixture { fakePluginManager }
+
+val Kosmos.pluginDependencyProvider by Kosmos.Fixture { PluginDependencyProvider { pluginManager } }
+
+val Kosmos.mockPluginDependencyProvider by Kosmos.Fixture { mock<PluginDependencyProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
index 1ceab68..a9f9c82 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
@@ -20,3 +20,13 @@
import com.android.systemui.util.mockito.mock
var Kosmos.shadeViewController by Kosmos.Fixture { mock<ShadeViewController>() }
+
+val Kosmos.mockNotificationShadeWindowViewController by
+ Kosmos.Fixture { mock<NotificationShadeWindowViewController>() }
+
+var Kosmos.notificationShadeWindowViewController by
+ Kosmos.Fixture { mockNotificationShadeWindowViewController }
+
+val Kosmos.mockShadeSurface by Kosmos.Fixture { mock<ShadeSurface>() }
+
+var Kosmos.shadeSurface by Kosmos.Fixture { mockShadeSurface }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
index 27f7f68..f571c1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
@@ -19,4 +19,10 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
-var Kosmos.commandQueue by Kosmos.Fixture { mock<CommandQueue>() }
+val Kosmos.mockCommandQueue by Kosmos.Fixture { mock<CommandQueue>() }
+
+var Kosmos.commandQueue by Kosmos.Fixture { mockCommandQueue }
+
+val Kosmos.mockCommandQueueCallbacks by Kosmos.Fixture { mock<CommandQueue.Callbacks>() }
+
+var Kosmos.commandQueueCallbacks by Kosmos.Fixture { mockCommandQueue }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
index 554bdbe..d436cd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
@@ -19,5 +19,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
-var Kosmos.notificationRemoteInputManager by
+val Kosmos.mockNotificationRemoteInputManager by
Kosmos.Fixture { mock<NotificationRemoteInputManager>() }
+
+var Kosmos.notificationRemoteInputManager by Kosmos.Fixture { mockNotificationRemoteInputManager }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt
new file mode 100644
index 0000000..cba4e8e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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 android.content.testableContext
+import android.internal.statusbar.fakeStatusBarService
+import com.android.systemui.initController
+import com.android.systemui.keyguard.data.repository.fakeCommandQueue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.navigationbar.mockNavigationBarController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockCommandQueueCallbacks
+
+var Kosmos.commandQueueInitializer by
+ Kosmos.Fixture {
+ CommandQueueInitializer(
+ testableContext,
+ fakeCommandQueue,
+ { mockCommandQueueCallbacks },
+ fakeStatusBarModeRepository,
+ initController,
+ fakeStatusBarService,
+ mockNavigationBarController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
new file mode 100644
index 0000000..edd6604
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+
+class FakeStatusBarInitializer(
+ private val statusBarViewController: PhoneStatusBarViewController,
+ private val statusBarTransitions: PhoneStatusBarTransitions,
+) : StatusBarInitializer {
+
+ override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
+ set(value) {
+ field = value
+ value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
+ }
+
+ override fun initializeStatusBar() {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
new file mode 100644
index 0000000..d103200
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 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.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+
+val Kosmos.fakeStatusBarInitializer by
+ Kosmos.Fixture {
+ FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
+ }
+
+var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
new file mode 100644
index 0000000..c53e44d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.mockDemoModeController
+import com.android.systemui.plugins.mockPluginDependencyProvider
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.shade.mockNotificationShadeWindowViewController
+import com.android.systemui.shade.mockShadeSurface
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockNotificationRemoteInputManager
+import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.wm.shell.bubbles.bubblesOptional
+
+val Kosmos.statusBarOrchestrator by
+ Kosmos.Fixture {
+ StatusBarOrchestrator(
+ applicationCoroutineScope,
+ fakeStatusBarInitializer,
+ fakeStatusBarWindowController,
+ fakeStatusBarModeRepository,
+ mockDemoModeController,
+ mockPluginDependencyProvider,
+ mockAutoHideController,
+ mockNotificationRemoteInputManager,
+ { mockNotificationShadeWindowViewController },
+ mockShadeSurface,
+ bubblesOptional,
+ statusBarWindowStateRepositoryStore,
+ powerInteractor,
+ primaryBouncerInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
new file mode 100644
index 0000000..090ce31
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.phone
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockAutoHideController by Kosmos.Fixture { mock<AutoHideController>() }
+
+var Kosmos.autoHideController by Kosmos.Fixture { mockAutoHideController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt
new file mode 100644
index 0000000..603ee08
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.phone
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.Mockito.mock
+
+val Kosmos.mockPhoneStatusBarViewController: PhoneStatusBarViewController by
+ Kosmos.Fixture { mock(PhoneStatusBarViewController::class.java) }
+
+var Kosmos.phoneStatusBarViewController by Kosmos.Fixture { mockPhoneStatusBarViewController }
+
+val Kosmos.mockPhoneStatusBarTransitions: PhoneStatusBarTransitions by
+ Kosmos.Fixture { mock(PhoneStatusBarTransitions::class.java) }
+
+var Kosmos.phoneStatusBarTransitions by Kosmos.Fixture { mockPhoneStatusBarTransitions }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
new file mode 100644
index 0000000..528c9d9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.window
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.fragments.FragmentHostManager
+import java.util.Optional
+
+class FakeStatusBarWindowController : StatusBarWindowController {
+
+ var isAttached = false
+ private set
+
+ override val statusBarHeight: Int = 0
+
+ override fun refreshStatusBarHeight() {}
+
+ override fun attach() {
+ isAttached = true
+ }
+
+ override fun addViewToWindow(view: View, layoutParams: ViewGroup.LayoutParams) {}
+
+ override val backgroundView: View
+ get() = throw NotImplementedError()
+
+ override val fragmentHostManager: FragmentHostManager
+ get() = throw NotImplementedError()
+
+ override fun wrapAnimationControllerIfInStatusBar(
+ rootView: View,
+ animationController: ActivityTransitionAnimator.Controller,
+ ): Optional<ActivityTransitionAnimator.Controller> = Optional.empty()
+
+ override fun setForceStatusBarVisible(forceStatusBarVisible: Boolean) {}
+
+ override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
new file mode 100644
index 0000000..c198b35
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.window
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() }
+
+var Kosmos.statusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
new file mode 100644
index 0000000..6532a7e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.window.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeStatusBarWindowStateRepositoryStore : StatusBarWindowStateRepositoryStore {
+
+ private val perDisplayRepos = mutableMapOf<Int, FakeStatusBarWindowStatePerDisplayRepository>()
+
+ override val defaultDisplay: FakeStatusBarWindowStatePerDisplayRepository =
+ forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): FakeStatusBarWindowStatePerDisplayRepository =
+ perDisplayRepos.computeIfAbsent(displayId) {
+ FakeStatusBarWindowStatePerDisplayRepository()
+ }
+}
+
+class FakeStatusBarWindowStatePerDisplayRepository : StatusBarWindowStatePerDisplayRepository {
+
+ private val _windowState = MutableStateFlow(StatusBarWindowState.Hidden)
+
+ override val windowState = _windowState.asStateFlow()
+
+ fun setWindowState(state: StatusBarWindowState) {
+ _windowState.value = state
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index e2b7f5f..2205a3b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@
import com.android.systemui.settings.displayTracker
import com.android.systemui.statusbar.commandQueue
+val Kosmos.fakeStatusBarWindowStateRepositoryStore by
+ Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
+
class KosmosStatusBarWindowStatePerDisplayRepositoryFactory(private val kosmos: Kosmos) :
StatusBarWindowStatePerDisplayRepositoryFactory {
override fun create(displayId: Int): StatusBarWindowStatePerDisplayRepositoryImpl {
@@ -32,7 +35,7 @@
}
}
-val Kosmos.statusBarWindowStateRepositoryStore by
+var Kosmos.statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore by
Kosmos.Fixture {
StatusBarWindowStateRepositoryStoreImpl(
displayId = displayTracker.defaultDisplayId,