[Status Bar Refactor] Move status bar window state tracking into a
centralized class and add some listeners for it.

Bug: 212424936
Test: manual (verified callbacks trigger)
Test: new StatusBarWindowStateControllerTest
Change-Id: Ib75206c05551ddf6512dd608295b9043dabd7c88
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 434671c..08b5cf5 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.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 @@
             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 @@
                 mQs.animateHeaderSlidingOut();
             }
         });
+        statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
         mThemeResId = mView.getContext().getThemeResId();
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -5006,4 +5011,14 @@
     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 8fe03e4..4ecbb7c 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.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 @@
     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 @@
     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 @@
             LightBarController lightBarController,
             AutoHideController autoHideController,
             StatusBarWindowController statusBarWindowController,
+            StatusBarWindowStateController statusBarWindowStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarSignalPolicy statusBarSignalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
@@ -872,6 +876,7 @@
         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 b391de3..51f2e67 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.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;
@@ -515,30 +512,6 @@
     }
 
     @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 f93a8dc..4830b37 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.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 @@
             LightBarController lightBarController,
             AutoHideController autoHideController,
             StatusBarWindowController statusBarWindowController,
+            StatusBarWindowStateController statusBarWindowStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarSignalPolicy statusBarSignalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
@@ -235,6 +237,7 @@
                 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 0000000..facbe8a
--- /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<StatusBarWindowStateListener> = 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 0000000..5586c1b
--- /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 1cd9b9e..df11bf6 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.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 @@
     private NotificationsQSContainerController mNotificationsQSContainerController;
     @Mock
     private QsFrameTranslateController mQsFrameTranslateController;
+    @Mock
+    private StatusBarWindowStateController mStatusBarWindowStateController;
     private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
     private SysuiStatusBarStateController mStatusBarStateController;
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -491,7 +494,9 @@
                 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 9d5b17e..a4b6480 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.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 @@
     @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 @@
                 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 0000000..8576d4f
--- /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