Merge changes from topic "ccassidy-sbwsc"

* changes:
  [Status Bar Refactor] Have SBHideIconsForBouncerManager listen to window state directly instead of getting it from StatusBar.java
  [Status Bar Refactor] Have NotificationShadeWindowViewController query the window state directly instead of going through StatusBar.java.
  [Status Bar Refactor] Move status bar window state tracking into a centralized class and add some listeners for it.
  [Status Bar Refactor] Pass status bar view's controller to NotificationShadeWindowViewController instead of the view itself.
This commit is contained in:
Caitlin Cassidy
2022-01-05 20:42:12 +00:00
committed by Android (Google) Code Review
17 changed files with 630 additions and 85 deletions

View File

@@ -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;
@@ -5009,4 +5014,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 */);
}
}
}

View File

@@ -16,10 +16,7 @@
package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import android.app.StatusBarManager;
import android.graphics.RectF;
import android.hardware.display.AmbientDisplayConfiguration;
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
@@ -47,6 +44,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -68,6 +66,7 @@ public class NotificationShadeWindowViewController {
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final LockIconViewController mLockIconViewController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private GestureDetector mGestureDetector;
private View mBrightnessMirror;
@@ -75,7 +74,7 @@ public class NotificationShadeWindowViewController {
private boolean mTouchCancelled;
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarView mStatusBarView;
private PhoneStatusBarViewController mStatusBarViewController;
private StatusBar mService;
private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
@@ -86,8 +85,6 @@ public class NotificationShadeWindowViewController {
private final NotificationPanelViewController mNotificationPanelViewController;
private final PanelExpansionStateManager mPanelExpansionStateManager;
// Used for determining view / touch intersection
private final RectF mTempRect = new RectF();
private boolean mIsTrackingBarGesture = false;
@Inject
@@ -103,6 +100,7 @@ public class NotificationShadeWindowViewController {
PanelExpansionStateManager panelExpansionStateManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -115,6 +113,7 @@ public class NotificationShadeWindowViewController {
mDepthController = depthController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
// This view is not part of the newly inflated expanded status bar.
@@ -175,7 +174,7 @@ public class NotificationShadeWindowViewController {
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
@Override
public Boolean handleDispatchTouchEvent(MotionEvent ev) {
if (mStatusBarView == null) {
if (mStatusBarViewController == null) { // Fix for b/192490822
Log.w(TAG, "Ignoring touch while statusBarView not yet set.");
return false;
}
@@ -243,27 +242,27 @@ public class NotificationShadeWindowViewController {
expandingBelowNotch = true;
}
if (expandingBelowNotch) {
return mStatusBarView.dispatchTouchEvent(ev);
return mStatusBarViewController.sendTouchToView(ev);
}
if (!mIsTrackingBarGesture && isDown
&& mNotificationPanelViewController.isFullyCollapsed()) {
float x = ev.getRawX();
float y = ev.getRawY();
if (isIntersecting(mStatusBarView, x, y)) {
if (mService.isSameStatusBarState(WINDOW_STATE_SHOWING)) {
if (mStatusBarViewController.touchIsWithinView(x, y)) {
if (mStatusBarWindowStateController.windowIsShowing()) {
mIsTrackingBarGesture = true;
return mStatusBarView.dispatchTouchEvent(ev);
return mStatusBarViewController.sendTouchToView(ev);
} else { // it's hidden or hiding, don't send to notification shade.
return true;
}
}
} else if (mIsTrackingBarGesture) {
final boolean sendToNotification = mStatusBarView.dispatchTouchEvent(ev);
final boolean sendToStatusBar = mStatusBarViewController.sendTouchToView(ev);
if (isUp || isCancel) {
mIsTrackingBarGesture = false;
}
return sendToNotification;
return sendToStatusBar;
}
return null;
@@ -442,8 +441,8 @@ public class NotificationShadeWindowViewController {
}
}
public void setStatusBarView(PhoneStatusBarView statusBarView) {
mStatusBarView = statusBarView;
public void setStatusBarViewController(PhoneStatusBarViewController statusBarViewController) {
mStatusBarViewController = statusBarViewController;
}
public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
@@ -455,11 +454,4 @@ public class NotificationShadeWindowViewController {
void setDragDownHelper(DragDownHelper dragDownHelper) {
mDragDownHelper = dragDownHelper;
}
private boolean isIntersecting(View view, float x, float y) {
int[] mTempLocation = view.getLocationOnScreen();
mTempRect.set(mTempLocation[0], mTempLocation[1], mTempLocation[0] + view.getWidth(),
mTempLocation[1] + view.getHeight());
return mTempRect.contains(x, y);
}
}

View File

@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone
import android.content.res.Configuration
import android.graphics.Point
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
@@ -92,6 +93,30 @@ class PhoneStatusBarViewController private constructor(
mView.importantForAccessibility = mode
}
/**
* Sends a touch event to the status bar view.
*
* This is required in certain cases because the status bar view is in a separate window from
* the rest of SystemUI, and other windows may decide that their touch should instead be treated
* as a status bar window touch.
*/
fun sendTouchToView(ev: MotionEvent): Boolean {
return mView.dispatchTouchEvent(ev)
}
/**
* Returns true if the given (x, y) point (in screen coordinates) is within the status bar
* view's range and false otherwise.
*/
fun touchIsWithinView(x: Float, y: Float): Boolean {
val left = mView.locationOnScreen[0]
val top = mView.locationOnScreen[1]
return left <= x &&
x <= left + mView.width &&
top <= y &&
y <= top + mView.height
}
class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {

View File

@@ -233,6 +233,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;
@@ -338,15 +339,9 @@ public class StatusBar extends CoreStartable implements
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
void setWindowState(int state) {
mStatusBarWindowState = state;
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
if (mStatusBarView != null) {
// Should #updateHideIconsForBouncer always be called, regardless of whether we have a
// status bar view? If so, we can make #updateHideIconsForBouncer private.
mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
}
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
updateBubblesVisibility();
mStatusBarWindowState = state;
}
void acquireGestureWakeLock(long time) {
@@ -465,7 +460,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;
@@ -695,6 +690,7 @@ public class StatusBar extends CoreStartable implements
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -877,6 +873,7 @@ public class StatusBar extends CoreStartable implements
mStartingSurfaceOptional = startingSurfaceOptional;
mNotifPipelineFlags = notifPipelineFlags;
lockscreenShadeTransitionController.setStatusbar(this);
statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
mScreenOffAnimationController = screenOffAnimationController;
@@ -1138,7 +1135,8 @@ public class StatusBar extends CoreStartable implements
mStatusBarView = statusBarView;
mPhoneStatusBarViewController = statusBarViewController;
mStatusBarTransitions = statusBarTransitions;
mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
mNotificationShadeWindowViewController
.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
@@ -2111,10 +2109,6 @@ public class StatusBar extends CoreStartable implements
}
}
boolean isSameStatusBarState(int state) {
return mStatusBarWindowState == state;
}
public GestureRecorder getGestureRecorder() {
return mGestureRec;
}

View File

@@ -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();

View File

@@ -1,10 +1,12 @@
package com.android.systemui.statusbar.phone
import android.app.StatusBarManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -25,6 +27,7 @@ import javax.inject.Inject
class StatusBarHideIconsForBouncerManager @Inject constructor(
private val commandQueue: CommandQueue,
@Main private val mainExecutor: DelayableExecutor,
statusBarWindowStateController: StatusBarWindowStateController,
dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
@@ -42,6 +45,9 @@ class StatusBarHideIconsForBouncerManager @Inject constructor(
init {
dumpManager.registerDumpable(this)
statusBarWindowStateController.addListener {
state -> setStatusBarStateAndTriggerUpdate(state)
}
}
/** Returns true if the status bar icons should be hidden in the bouncer. */
@@ -49,8 +55,9 @@ class StatusBarHideIconsForBouncerManager @Inject constructor(
return hideIconsForBouncer || wereIconsJustHidden
}
fun setStatusBarWindowHidden(statusBarWindowHidden: Boolean) {
this.statusBarWindowHidden = statusBarWindowHidden
private fun setStatusBarStateAndTriggerUpdate(@StatusBarManager.WindowVisibleState state: Int) {
statusBarWindowHidden = state == StatusBarManager.WINDOW_STATE_HIDDEN
updateHideIconsForBouncer(animate = false)
}
fun setDisplayId(displayId: Int) {
@@ -87,7 +94,7 @@ class StatusBarHideIconsForBouncerManager @Inject constructor(
* Updates whether the status bar icons should be hidden in the bouncer. May trigger
* [commandQueue.recomputeDisableFlags] if the icon visibility status changes.
*/
fun updateHideIconsForBouncer(animate: Boolean) {
private fun updateHideIconsForBouncer(animate: Boolean) {
val hideBecauseApp =
topAppHidesStatusBar &&
isOccluded &&

View File

@@ -106,6 +106,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;
@@ -141,6 +142,7 @@ public interface StatusBarPhoneModule {
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarWindowController statusBarWindowController,
StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarSignalPolicy statusBarSignalPolicy,
PulseExpansionHandler pulseExpansionHandler,
@@ -237,6 +239,7 @@ public interface StatusBarPhoneModule {
lightBarController,
autoHideController,
statusBarWindowController,
statusBarWindowStateController,
keyguardUpdateMonitor,
statusBarSignalPolicy,
pulseExpansionHandler,

View File

@@ -0,0 +1,88 @@
/*
* 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)
}
/** Returns true if the window is currently showing. */
fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING
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) }
}
}

View File

@@ -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. */
fun interface StatusBarWindowStateListener {
fun onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState state: Int)
}

View File

@@ -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;
@@ -359,6 +360,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private NotificationsQSContainerController mNotificationsQSContainerController;
@Mock
private QsFrameTranslateController mQsFrameTranslateController;
@Mock
private StatusBarWindowStateController mStatusBarWindowStateController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -495,7 +498,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,

View File

@@ -0,0 +1,241 @@
/*
* 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.phone
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.keyguard.LockIconViewController
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.tuner.TunerService
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
private lateinit var mController: NotificationShadeWindowViewController
@Mock
private lateinit var mView: NotificationShadeWindowView
@Mock
private lateinit var mTunerService: TunerService
@Mock
private lateinit var mStatusBarStateController: SysuiStatusBarStateController
@Mock
private lateinit var mStatusBar: StatusBar
@Mock
private lateinit var mDockManager: DockManager
@Mock
private lateinit var mNotificationPanelViewController: NotificationPanelViewController
@Mock
private lateinit var mNotificationShadeDepthController: NotificationShadeDepthController
@Mock
private lateinit var mNotificationShadeWindowController: NotificationShadeWindowController
@Mock
private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock
private lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock
private lateinit var mStatusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var mLockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock
private lateinit var mLockIconViewController: LockIconViewController
@Mock
private lateinit var mPhoneStatusBarViewController: PhoneStatusBarViewController
private lateinit var mInteractionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var mInteractionEventHandler: InteractionEventHandler
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mView.bottom).thenReturn(VIEW_BOTTOM)
mController = NotificationShadeWindowViewController(
mLockscreenShadeTransitionController,
FalsingCollectorFake(),
mTunerService,
mStatusBarStateController,
mDockManager,
mNotificationShadeDepthController,
mView,
mNotificationPanelViewController,
PanelExpansionStateManager(),
stackScrollLayoutController,
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController
)
mController.setupExpandedStatusBar()
mController.setService(mStatusBar, mNotificationShadeWindowController)
mInteractionEventHandlerCaptor =
ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture())
mInteractionEventHandler = mInteractionEventHandlerCaptor.value
}
// Note: So far, these tests only cover interactions with the status bar view controller. More
// tests need to be added to test the rest of handleDispatchTouchEvent.
@Test
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
mController.setStatusBarViewController(null)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
assertThat(returnVal).isFalse()
}
@Test
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
whenever(mPhoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(ev)
verify(mPhoneStatusBarViewController).sendTouchToView(ev)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
val downEvBelow = MotionEvent.obtain(
0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
)
mInteractionEventHandler.handleDispatchTouchEvent(downEvBelow)
val nextEvent = MotionEvent.obtain(
0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
)
whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
whenever(mPhoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
verify(mPhoneStatusBarViewController).sendTouchToView(downEv)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Item we're testing
whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(false)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isNull()
}
@Test
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
// Item we're testing
whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(false)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isNull()
}
@Test
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Item we're testing
whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(false)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
mController.setStatusBarViewController(mPhoneStatusBarViewController)
whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Down event first
mInteractionEventHandler.handleDispatchTouchEvent(downEv)
// Then another event
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
private const val VIEW_BOTTOM = 100

View File

@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
@@ -75,6 +76,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private LockIconViewController mLockIconViewController;
@@ -107,6 +109,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
new PanelExpansionStateManager(),
mNotificationStackScrollLayoutController,
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar, mNotificationShadeWindowController);

View File

@@ -77,9 +77,10 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
val parent = FrameLayout(mContext) // add parent to keep layout params
view = LayoutInflater.from(mContext)
.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
view.setLeftTopRightBottom(VIEW_LEFT, VIEW_TOP, VIEW_RIGHT, VIEW_BOTTOM)
}
controller = createController(view)
controller = createAndInitController(view)
}
@Test
@@ -99,8 +100,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
val view = createViewMock()
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
unfoldConfig.isEnabled = true
controller = createController(view)
controller.init()
controller = createAndInitController(view)
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
@@ -108,6 +108,64 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
verify(moveFromCenterAnimation).onViewsReady(any())
}
@Test
fun touchIsWithinView_inBounds_returnsTrue() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP + 1f)).isTrue()
}
@Test
fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())).isTrue()
}
@Test
fun touchIsWithinView_onBottomRightCorner_returnsTrue() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(
VIEW_RIGHT.toFloat(), VIEW_BOTTOM.toFloat())
).isTrue()
}
@Test
fun touchIsWithinView_xTooSmall_returnsFalse() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_LEFT - 1f, VIEW_TOP + 1f)).isFalse()
}
@Test
fun touchIsWithinView_xTooLarge_returnsFalse() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_RIGHT + 1f, VIEW_TOP + 1f)).isFalse()
}
@Test
fun touchIsWithinView_yTooSmall_returnsFalse() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP - 1f)).isFalse()
}
@Test
fun touchIsWithinView_yTooLarge_returnsFalse() {
val view = createViewMockWithScreenLocation()
controller = createAndInitController(view)
assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
}
private fun createViewMock(): PhoneStatusBarView {
val view = spy(view)
val viewTreeObserver = mock(ViewTreeObserver::class.java)
@@ -116,12 +174,23 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
return view
}
private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
private fun createViewMockWithScreenLocation(): PhoneStatusBarView {
val view = spy(view)
val location = IntArray(2)
location[0] = VIEW_LEFT
location[1] = VIEW_TOP
`when`(view.locationOnScreen).thenReturn(location)
return view
}
private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
configurationController
).create(view, touchEventHandler)
).create(view, touchEventHandler).also {
it.init()
}
}
private class UnfoldConfig : UnfoldTransitionConfig {
@@ -142,3 +211,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
}
}
private const val VIEW_LEFT = 30
private const val VIEW_RIGHT = 100
private const val VIEW_TOP = 40
private const val VIEW_BOTTOM = 100

View File

@@ -36,7 +36,6 @@ import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
@@ -46,8 +45,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import org.junit.Before;
@@ -84,6 +81,7 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private VibratorHelper mVibratorHelper;
@Mock private Vibrator mVibrator;
@Mock private LightBarController mLightBarController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
StatusBarCommandQueueCallbacks mSbcqCallbacks;
@@ -112,8 +110,7 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
mStatusBarStateController,
mNotificationShadeWindowView,
mNotificationStackScrollLayoutController,
new StatusBarHideIconsForBouncerManager(
mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mStatusBarHideIconsForBouncerManager,
mPowerManager,
mVibratorHelper,
Optional.of(mVibrator),

View File

@@ -148,6 +148,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;
@@ -229,6 +230,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;
@@ -264,6 +266,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private BrightnessSliderController.Factory mBrightnessSliderFactory;
@Mock private WallpaperController mWallpaperController;
@Mock private OngoingCallController mOngoingCallController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -376,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase {
mLightBarController,
mAutoHideController,
mStatusBarWindowController,
mStatusBarWindowStateController,
mKeyguardUpdateMonitor,
mStatusBarSignalPolicy,
mPulseExpansionHandler,
@@ -451,7 +455,7 @@ public class StatusBarTest extends SysuiTestCase {
mScreenOffAnimationController,
mWallpaperController,
mOngoingCallController,
new StatusBarHideIconsForBouncerManager(mCommandQueue, mMainExecutor, mDumpManager),
mStatusBarHideIconsForBouncerManager,
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,

View File

@@ -37,7 +37,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
@@ -57,8 +56,6 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -96,6 +93,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@Mock
private NotificationPanelViewController mNotificationPanelViewController;
@Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -325,8 +324,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
new PanelExpansionStateManager(),
mock(FeatureFlags.class),
mStatusBarIconController,
new StatusBarHideIconsForBouncerManager(
mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mStatusBarHideIconsForBouncerManager,
mKeyguardStateController,
mNotificationPanelViewController,
mNetworkController,

View File

@@ -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