diff options
29 files changed, 1906 insertions, 110 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java index 55ccaa68c855..92bc95af5931 100644 --- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java +++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java @@ -70,4 +70,12 @@ public interface CoreStartable extends Dumpable { * {@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 000000000000..57c8bc6133e6 --- /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 000000000000..8bd990b83a63 --- /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 3903ff3c2b9b..cf238d553225 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 @@ import dagger.multibindings.IntoMap */ @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 1d3f0e1f6dc3..5f4f72f293a6 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.bubbles.Bubbles; 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }; void onStatusBarWindowStateChanged(@WindowVisibleState int state) { + StatusBarSimpleFragment.assertInLegacyMode(); mStatusBarWindowState = state; updateBubblesVisibility(); } @@ -813,8 +814,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { /** * @deprecated use {@link - * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead. + * WindowRootViewVisibilityInteractor#isLockscreenOrShadeVisible()} instead. */ @VisibleForTesting @Deprecated void initShadeVisibilityListener() { @@ -1168,13 +1193,16 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { * @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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 13b651e8c0be..5b0319883b5f 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 @@ import dagger.multibindings.IntoMap @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 @@ interface StatusBarPhoneModule { 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 000000000000..2a196c6b979f --- /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 000000000000..580336539c37 --- /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 c710c56fd516..15ea811287b8 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.PulseExpansionHandler; 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 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @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 000000000000..cc0597bc3853 --- /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 000000000000..1304161e81e1 --- /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 000000000000..39384fdec396 --- /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 000000000000..13169e133c9b --- /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 3a59f6a8784f..601c14509107 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 @@ class FakeCommandQueue @Inject constructor() : 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 @@ class FakeCommandQueue @Inject constructor() : } 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 000000000000..9e2039eb6b54 --- /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 000000000000..f1388e9975bf --- /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 1ceab68604f3..a9f9c82be98b 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.kosmos.Kosmos 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 27f7f6823cc7..f571c1be9e6e 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 @@ package com.android.systemui.statusbar 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 554bdbe0c382..d436cd4f2ed2 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 @@ package com.android.systemui.statusbar 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 000000000000..cba4e8efe9fe --- /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 000000000000..edd660490e4d --- /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 000000000000..d10320004454 --- /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 000000000000..c53e44d514f7 --- /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 000000000000..090ce31bd43c --- /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 000000000000..603ee08b6b28 --- /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 000000000000..528c9d9ec64d --- /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 000000000000..c198b35be289 --- /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 000000000000..6532a7ecc85a --- /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 e2b7f5fa0717..2205a3b6e084 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.kosmos.applicationCoroutineScope 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 @@ class KosmosStatusBarWindowStatePerDisplayRepositoryFactory(private val kosmos: } } -val Kosmos.statusBarWindowStateRepositoryStore by +var Kosmos.statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore by Kosmos.Fixture { StatusBarWindowStateRepositoryStoreImpl( displayId = displayTracker.defaultDisplayId, |