diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 434671c02035e..08b5cf5613f0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.view.View.GONE; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -197,6 +198,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.Utils; import com.android.systemui.util.settings.SecureSettings; @@ -732,7 +734,9 @@ public class NotificationPanelViewController extends PanelViewController { NotificationEntryManager notificationEntryManager, CommunalStateController communalStateController, KeyguardStateController keyguardStateController, - StatusBarStateController statusBarStateController, DozeLog dozeLog, + StatusBarStateController statusBarStateController, + StatusBarWindowStateController statusBarWindowStateController, + DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, @@ -862,6 +866,7 @@ public class NotificationPanelViewController extends PanelViewController { mQs.animateHeaderSlidingOut(); } }); + statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); mThemeResId = mView.getContext().getThemeResId(); mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; @@ -5006,4 +5011,14 @@ public class NotificationPanelViewController extends PanelViewController { public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() { return mStatusBarViewTouchEventHandler; } + + private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) { + if (state != WINDOW_STATE_SHOWING + && mStatusBarStateController.getState() == StatusBarState.SHADE) { + collapsePanel( + false /* animate */, + false /* delayed */, + 1.0f /* speedUpFactor */); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 8fe03e46e25dc..4ecbb7c4d1ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -232,6 +232,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.util.DumpUtilsKt; import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -337,9 +338,11 @@ public class StatusBar extends CoreStartable implements private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private StatusBarCommandQueueCallbacks mCommandQueueCallbacks; - void setWindowState(int state) { - mStatusBarWindowState = state; + void onStatusBarWindowStateChanged(@WindowVisibleState int state) { + updateBubblesVisibility(); + mStatusBarWindowState = state; mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN; + // TODO(b/212424936): Have this icon manager just implement its own listener. mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden); if (mStatusBarView != null) { // Should #updateHideIconsForBouncer always be called, regardless of whether we have a @@ -464,7 +467,7 @@ public class StatusBar extends CoreStartable implements private PhoneStatusBarViewController mPhoneStatusBarViewController; private PhoneStatusBarTransitions mStatusBarTransitions; private AuthRippleController mAuthRippleController; - private int mStatusBarWindowState = WINDOW_STATE_SHOWING; + @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING; protected NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarWindowController mStatusBarWindowController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -692,6 +695,7 @@ public class StatusBar extends CoreStartable implements LightBarController lightBarController, AutoHideController autoHideController, StatusBarWindowController statusBarWindowController, + StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarSignalPolicy statusBarSignalPolicy, PulseExpansionHandler pulseExpansionHandler, @@ -872,6 +876,7 @@ public class StatusBar extends CoreStartable implements mStartingSurfaceOptional = startingSurfaceOptional; mNotifPipelineFlags = notifPipelineFlags; lockscreenShadeTransitionController.setStatusbar(this); + statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); mScreenOffAnimationController = screenOffAnimationController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java index b391de3c5ab18..51f2e6728b805 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import static android.app.StatusBarManager.windowStateToString; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.containsType; @@ -58,7 +56,6 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.qs.QSPanelController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -514,30 +511,6 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { .setTopAppHidesStatusBarAndTriggerUpdate(topAppHidesStatusBar); } - @Override - public void setWindowState( - int displayId, @StatusBarManager.WindowType int window, - @StatusBarManager.WindowVisibleState int state) { - if (displayId != mDisplayId) { - return; - } - boolean showing = state == WINDOW_STATE_SHOWING; - if (mNotificationShadeWindowView != null - && window == StatusBarManager.WINDOW_STATUS_BAR - && !mStatusBar.isSameStatusBarState(state)) { - mStatusBar.setWindowState(state); - if (StatusBar.DEBUG_WINDOW_STATE) { - Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state)); - } - if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) { - mNotificationPanelViewController.collapsePanel( - false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */); - } - } - - mStatusBar.updateBubblesVisibility(); - } - @Override public void showAssistDisclosure() { mAssistManager.showDisclosure(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index f93a8dcad2234..4830b37dcc9a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -105,6 +105,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.MessageRouter; @@ -140,6 +141,7 @@ public interface StatusBarPhoneModule { LightBarController lightBarController, AutoHideController autoHideController, StatusBarWindowController statusBarWindowController, + StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarSignalPolicy statusBarSignalPolicy, PulseExpansionHandler pulseExpansionHandler, @@ -235,6 +237,7 @@ public interface StatusBarPhoneModule { lightBarController, autoHideController, statusBarWindowController, + statusBarWindowStateController, keyguardUpdateMonitor, statusBarSignalPolicy, pulseExpansionHandler, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt new file mode 100644 index 0000000000000..facbe8abfd138 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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.app.StatusBarManager +import android.app.StatusBarManager.WindowVisibleState +import android.app.StatusBarManager.WINDOW_STATE_SHOWING +import android.app.StatusBarManager.WINDOW_STATUS_BAR +import android.app.StatusBarManager.windowStateToString +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.phone.StatusBar +import javax.inject.Inject + +/** + * A centralized class maintaining the state of the status bar window. + * + * Classes that want to get updates about the status bar window state should subscribe to this class + * via [addListener] and should NOT add their own callback on [CommandQueue]. + */ +@SysUISingleton +class StatusBarWindowStateController @Inject constructor( + @DisplayId private val thisDisplayId: Int, + commandQueue: CommandQueue +) { + private val commandQueueCallback = object : CommandQueue.Callbacks { + override fun setWindowState( + displayId: Int, + @StatusBarManager.WindowType window: Int, + @WindowVisibleState state: Int + ) { + this@StatusBarWindowStateController.setWindowState(displayId, window, state) + } + } + private val listeners: MutableSet = HashSet() + + @WindowVisibleState private var windowState: Int = WINDOW_STATE_SHOWING + + init { + commandQueue.addCallback(commandQueueCallback) + } + + /** Adds a listener. */ + fun addListener(listener: StatusBarWindowStateListener) { + listeners.add(listener) + } + + private fun setWindowState( + displayId: Int, + @StatusBarManager.WindowType window: Int, + @WindowVisibleState state: Int + ) { + if (displayId != thisDisplayId) { + return + } + if (window != WINDOW_STATUS_BAR) { + return + } + if (windowState == state) { + return + } + + windowState = state + if (StatusBar.DEBUG_WINDOW_STATE) { + Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state)) + } + listeners.forEach { it.onStatusBarWindowStateChanged(state) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt new file mode 100644 index 0000000000000..5586c1b5d291d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateListener.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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.app.StatusBarManager + +/** Listener interface for changes in the status bar window state. */ +interface StatusBarWindowStateListener { + fun onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState state: Int) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 1cd9b9e774236..df11bf6178235 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -141,6 +141,7 @@ import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; @@ -358,6 +359,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { private NotificationsQSContainerController mNotificationsQSContainerController; @Mock private QsFrameTranslateController mQsFrameTranslateController; + @Mock + private StatusBarWindowStateController mStatusBarWindowStateController; private Optional mSysUIUnfoldComponent = Optional.empty(); private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; @@ -491,7 +494,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, mFalsingManager, new FalsingCollectorFake(), mNotificationLockscreenUserManager, mNotificationEntryManager, - mCommunalStateController, mKeyguardStateController, mStatusBarStateController, + mCommunalStateController, mKeyguardStateController, + mStatusBarStateController, + mStatusBarWindowStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 9d5b17ea67389..a4b64803a859f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -147,6 +147,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.MessageRouterImpl; @@ -228,6 +229,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private AutoHideController mAutoHideController; @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager; @Mock private UserSwitcherController mUserSwitcherController; @Mock private NetworkController mNetworkController; @@ -374,6 +376,7 @@ public class StatusBarTest extends SysuiTestCase { mLightBarController, mAutoHideController, mStatusBarWindowController, + mStatusBarWindowStateController, mKeyguardUpdateMonitor, mStatusBarSignalPolicy, mPulseExpansionHandler, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt new file mode 100644 index 0000000000000..8576d4f56906a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 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.app.StatusBarManager.WindowVisibleState +import android.app.StatusBarManager.WINDOW_NAVIGATION_BAR +import android.app.StatusBarManager.WINDOW_STATE_HIDDEN +import android.app.StatusBarManager.WINDOW_STATE_SHOWING +import android.app.StatusBarManager.WINDOW_STATUS_BAR +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.CommandQueue +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +class StatusBarWindowStateControllerTest : SysuiTestCase() { + private lateinit var controller: StatusBarWindowStateController + private lateinit var callback: CommandQueue.Callbacks + + @Mock + private lateinit var commandQueue: CommandQueue + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + controller = StatusBarWindowStateController(DISPLAY_ID, commandQueue) + + val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) + verify(commandQueue).addCallback(callbackCaptor.capture()) + callback = callbackCaptor.value!! + } + + @Test + fun setWindowState_notSameDisplayId_listenersNotNotified() { + val listener = TestListener() + controller.addListener(listener) + + callback.setWindowState(DISPLAY_ID + 1, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN) + + assertThat(listener.state).isNull() + } + + @Test + fun setWindowState_notStatusBarWindow_listenersNotNotified() { + val listener = TestListener() + controller.addListener(listener) + + callback.setWindowState(DISPLAY_ID, WINDOW_NAVIGATION_BAR, WINDOW_STATE_HIDDEN) + + assertThat(listener.state).isNull() + } + + @Test + fun setWindowState_sameState_listenersNotNotified() { + val listener = TestListener() + controller.addListener(listener) + + callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING) + + assertThat(listener.state).isNull() + } + + @Test + fun setWindowState_newState_listenersNotified() { + val listener = TestListener() + controller.addListener(listener) + val newState = WINDOW_STATE_HIDDEN + + callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, newState) + + assertThat(listener.state).isEqualTo(newState) + } + + private class TestListener : StatusBarWindowStateListener { + @WindowVisibleState var state: Int? = null + override fun onStatusBarWindowStateChanged(@WindowVisibleState state: Int) { + this.state = state + } + } +} + +private const val DISPLAY_ID = 10