1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.shade; 18 19 import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; 20 import static com.android.keyguard.KeyguardClockSwitch.LARGE; 21 import static com.android.keyguard.KeyguardClockSwitch.SMALL; 22 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; 23 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN; 24 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING; 25 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 26 import static com.android.systemui.statusbar.StatusBarState.SHADE; 27 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; 28 29 import static com.google.common.truth.Truth.assertThat; 30 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyBoolean; 33 import static org.mockito.ArgumentMatchers.anyFloat; 34 import static org.mockito.ArgumentMatchers.anyInt; 35 import static org.mockito.ArgumentMatchers.anyString; 36 import static org.mockito.ArgumentMatchers.eq; 37 import static org.mockito.Mockito.atLeastOnce; 38 import static org.mockito.Mockito.clearInvocations; 39 import static org.mockito.Mockito.inOrder; 40 import static org.mockito.Mockito.mock; 41 import static org.mockito.Mockito.never; 42 import static org.mockito.Mockito.reset; 43 import static org.mockito.Mockito.times; 44 import static org.mockito.Mockito.verify; 45 import static org.mockito.Mockito.when; 46 47 import android.animation.Animator; 48 import android.animation.ValueAnimator; 49 import android.graphics.Point; 50 import android.testing.AndroidTestingRunner; 51 import android.testing.TestableLooper; 52 import android.view.MotionEvent; 53 import android.view.View; 54 import android.view.accessibility.AccessibilityNodeInfo; 55 56 import androidx.constraintlayout.widget.ConstraintSet; 57 import androidx.test.filters.SmallTest; 58 59 import com.android.keyguard.FaceAuthApiRequestReason; 60 import com.android.systemui.DejankUtils; 61 import com.android.systemui.R; 62 import com.android.systemui.keyguard.shared.model.WakeSleepReason; 63 import com.android.systemui.keyguard.shared.model.WakefulnessModel; 64 import com.android.systemui.keyguard.shared.model.WakefulnessState; 65 import com.android.systemui.plugins.statusbar.StatusBarStateController; 66 import com.android.systemui.statusbar.notification.row.ExpandableView; 67 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; 68 import com.android.systemui.statusbar.notification.stack.AmbientState; 69 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 70 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm; 71 72 import org.junit.Before; 73 import org.junit.Ignore; 74 import org.junit.Test; 75 import org.junit.runner.RunWith; 76 import org.mockito.ArgumentCaptor; 77 import org.mockito.InOrder; 78 79 import java.util.List; 80 81 @SmallTest 82 @RunWith(AndroidTestingRunner.class) 83 @TestableLooper.RunWithLooper(setAsMainLooper = true) 84 public class NotificationPanelViewControllerTest extends NotificationPanelViewControllerBaseTest { 85 86 @Before before()87 public void before() { 88 DejankUtils.setImmediate(true); 89 } 90 91 /** 92 * When the Back gesture starts (progress 0%), the scrim will stay at 100% scale (1.0f). 93 */ 94 @Test testBackGesture_min_scrimAtMaxScale()95 public void testBackGesture_min_scrimAtMaxScale() { 96 mNotificationPanelViewController.onBackProgressed(0.0f); 97 verify(mScrimController).applyBackScaling(1.0f); 98 } 99 100 /** 101 * When the Back gesture is at max (progress 100%), the scrim will be scaled to its minimum. 102 */ 103 @Test testBackGesture_max_scrimAtMinScale()104 public void testBackGesture_max_scrimAtMinScale() { 105 mNotificationPanelViewController.onBackProgressed(1.0f); 106 verify(mScrimController).applyBackScaling( 107 NotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE); 108 } 109 110 @Test onNotificationHeightChangeWhileOnKeyguardWillComputeMaxKeyguardNotifications()111 public void onNotificationHeightChangeWhileOnKeyguardWillComputeMaxKeyguardNotifications() { 112 mStatusBarStateController.setState(KEYGUARD); 113 ArgumentCaptor<OnHeightChangedListener> captor = 114 ArgumentCaptor.forClass(OnHeightChangedListener.class); 115 verify(mNotificationStackScrollLayoutController) 116 .setOnHeightChangedListener(captor.capture()); 117 OnHeightChangedListener listener = captor.getValue(); 118 119 clearInvocations(mNotificationStackSizeCalculator); 120 listener.onHeightChanged(mock(ExpandableView.class), false); 121 122 verify(mNotificationStackSizeCalculator) 123 .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat()); 124 } 125 126 @Test onNotificationHeightChangeWhileInShadeWillNotComputeMaxKeyguardNotifications()127 public void onNotificationHeightChangeWhileInShadeWillNotComputeMaxKeyguardNotifications() { 128 mStatusBarStateController.setState(SHADE); 129 ArgumentCaptor<OnHeightChangedListener> captor = 130 ArgumentCaptor.forClass(OnHeightChangedListener.class); 131 verify(mNotificationStackScrollLayoutController) 132 .setOnHeightChangedListener(captor.capture()); 133 OnHeightChangedListener listener = captor.getValue(); 134 135 clearInvocations(mNotificationStackSizeCalculator); 136 listener.onHeightChanged(mock(ExpandableView.class), false); 137 138 verify(mNotificationStackSizeCalculator, never()) 139 .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat()); 140 } 141 142 @Test computeMaxKeyguardNotifications_lockscreenToShade_returnsExistingMax()143 public void computeMaxKeyguardNotifications_lockscreenToShade_returnsExistingMax() { 144 when(mAmbientState.getFractionToShade()).thenReturn(0.5f); 145 mNotificationPanelViewController.setMaxDisplayedNotifications(-1); 146 147 // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value 148 assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) 149 .isEqualTo(-1); 150 } 151 152 @Test computeMaxKeyguardNotifications_noTransition_updatesMax()153 public void computeMaxKeyguardNotifications_noTransition_updatesMax() { 154 when(mAmbientState.getFractionToShade()).thenReturn(0f); 155 mNotificationPanelViewController.setMaxDisplayedNotifications(-1); 156 157 // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value 158 assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) 159 .isNotEqualTo(-1); 160 } 161 162 @Test 163 @Ignore("b/261472011 - Test appears inconsistent across environments") getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable()164 public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() { 165 setBottomPadding(/* stackScrollLayoutBottom= */ 180, 166 /* lockIconPadding= */ 20, 167 /* indicationPadding= */ 0, 168 /* ambientPadding= */ 0); 169 170 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications()) 171 .isEqualTo(80); 172 } 173 174 @Test 175 @Ignore("b/261472011 - Test appears inconsistent across environments") getVerticalSpaceForLockscreenNotifications_useIndicationBottomPadding_returnsSpaceAvailable()176 public void getVerticalSpaceForLockscreenNotifications_useIndicationBottomPadding_returnsSpaceAvailable() { 177 setBottomPadding(/* stackScrollLayoutBottom= */ 180, 178 /* lockIconPadding= */ 0, 179 /* indicationPadding= */ 30, 180 /* ambientPadding= */ 0); 181 182 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications()) 183 .isEqualTo(70); 184 } 185 186 @Test 187 @Ignore("b/261472011 - Test appears inconsistent across environments") getVerticalSpaceForLockscreenNotifications_useAmbientBottomPadding_returnsSpaceAvailable()188 public void getVerticalSpaceForLockscreenNotifications_useAmbientBottomPadding_returnsSpaceAvailable() { 189 setBottomPadding(/* stackScrollLayoutBottom= */ 180, 190 /* lockIconPadding= */ 0, 191 /* indicationPadding= */ 0, 192 /* ambientPadding= */ 40); 193 194 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications()) 195 .isEqualTo(60); 196 } 197 198 @Test getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight()199 public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() { 200 setBottomPadding(/* stackScrollLayoutBottom= */ 100, 201 /* lockIconPadding= */ 20, 202 /* indicationPadding= */ 0, 203 /* ambientPadding= */ 0); 204 205 when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5); 206 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) 207 .isEqualTo(5); 208 } 209 210 @Test getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero()211 public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() { 212 setBottomPadding(/* stackScrollLayoutBottom= */ 100, 213 /* lockIconPadding= */ 0, 214 /* indicationPadding= */ 30, 215 /* ambientPadding= */ 0); 216 217 when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5); 218 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) 219 .isEqualTo(0); 220 } 221 222 @Test getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero()223 public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() { 224 setBottomPadding(/* stackScrollLayoutBottom= */ 100, 225 /* lockIconPadding= */ 0, 226 /* indicationPadding= */ 0, 227 /* ambientPadding= */ 40); 228 229 when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5); 230 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) 231 .isEqualTo(0); 232 } 233 234 @Test getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight()235 public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() { 236 setBottomPadding(/* stackScrollLayoutBottom= */ 100, 237 /* lockIconPadding= */ 10, 238 /* indicationPadding= */ 8, 239 /* ambientPadding= */ 0); 240 241 when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5); 242 assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) 243 .isEqualTo(2); 244 } 245 246 @Test testSetPanelScrimMinFractionWhenHeadsUpIsDragged()247 public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() { 248 mNotificationPanelViewController.setHeadsUpDraggingStartingHeight( 249 mNotificationPanelViewController.getMaxPanelHeight() / 2); 250 verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f)); 251 } 252 253 @Test testSetDozing_notifiesNsslAndStateController()254 public void testSetDozing_notifiesNsslAndStateController() { 255 mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */); 256 verify(mNotificationStackScrollLayoutController).setDozing(eq(true), eq(false)); 257 assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f); 258 } 259 260 @Test testOnDozeAmountChanged_positionClockAndNotificationsUsesUdfpsLocation()261 public void testOnDozeAmountChanged_positionClockAndNotificationsUsesUdfpsLocation() { 262 // GIVEN UDFPS is enrolled and we're on the keyguard 263 final Point udfpsLocationCenter = new Point(0, 100); 264 final float udfpsRadius = 10f; 265 when(mUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); 266 when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocationCenter); 267 when(mAuthController.getUdfpsRadius()).thenReturn(udfpsRadius); 268 mNotificationPanelViewController.getStatusBarStateListener().onStateChanged(KEYGUARD); 269 270 // WHEN the doze amount changes 271 mNotificationPanelViewController.mClockPositionAlgorithm = mock( 272 KeyguardClockPositionAlgorithm.class); 273 mNotificationPanelViewController.getStatusBarStateListener().onDozeAmountChanged(1f, 1f); 274 275 // THEN the clock positions accounts for the UDFPS location & its worst case burn in 276 final float udfpsTop = udfpsLocationCenter.y - udfpsRadius - mMaxUdfpsBurnInOffsetY; 277 verify(mNotificationPanelViewController.mClockPositionAlgorithm).setup( 278 anyInt(), 279 anyFloat(), 280 anyInt(), 281 anyInt(), 282 anyInt(), 283 /* darkAmount */ eq(1f), 284 anyFloat(), 285 anyBoolean(), 286 anyInt(), 287 anyFloat(), 288 anyInt(), 289 anyBoolean(), 290 /* udfpsTop */ eq(udfpsTop), 291 anyFloat(), 292 anyBoolean() 293 ); 294 } 295 296 297 @Test testSetExpandedHeight()298 public void testSetExpandedHeight() { 299 mNotificationPanelViewController.setExpandedHeight(200); 300 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 301 } 302 303 @Test testOnTouchEvent_expansionCanBeBlocked()304 public void testOnTouchEvent_expansionCanBeBlocked() { 305 onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); 306 onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 200f, 0)); 307 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 308 309 mNotificationPanelViewController.blockExpansionForCurrentTouch(); 310 onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 300f, 0)); 311 // Expansion should not have changed because it was blocked 312 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 313 } 314 315 @Test test_pulsing_onTouchEvent_noTracking()316 public void test_pulsing_onTouchEvent_noTracking() { 317 // GIVEN device is pulsing 318 mNotificationPanelViewController.setPulsing(true); 319 320 // WHEN touch DOWN & MOVE events received 321 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 322 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 323 0 /* metaState */)); 324 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 325 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */, 326 0 /* metaState */)); 327 328 // THEN touch is NOT tracked (since the device is pulsing) 329 assertThat(mNotificationPanelViewController.isTracking()).isFalse(); 330 } 331 332 @Test test_onTouchEvent_startTracking()333 public void test_onTouchEvent_startTracking() { 334 // GIVEN device is NOT pulsing 335 mNotificationPanelViewController.setPulsing(false); 336 337 // WHEN touch DOWN & MOVE events received 338 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 339 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 340 0 /* metaState */)); 341 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 342 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */, 343 0 /* metaState */)); 344 345 // THEN touch is tracked 346 assertThat(mNotificationPanelViewController.isTracking()).isTrue(); 347 } 348 349 @Test testOnTouchEvent_expansionResumesAfterBriefTouch()350 public void testOnTouchEvent_expansionResumesAfterBriefTouch() { 351 mFalsingManager.setIsClassifierEnabled(true); 352 mFalsingManager.setIsFalseTouch(false); 353 // Start shade collapse with swipe up 354 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 355 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 356 0 /* metaState */)); 357 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 358 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */, 359 0 /* metaState */)); 360 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 361 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */, 362 0 /* metaState */)); 363 364 assertThat(mNotificationPanelViewController.isClosing()).isTrue(); 365 assertThat(mNotificationPanelViewController.isFlinging()).isTrue(); 366 367 // simulate touch that does not exceed touch slop 368 onTouchEvent(MotionEvent.obtain(2L /* downTime */, 369 2L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 300f /* y */, 370 0 /* metaState */)); 371 372 mNotificationPanelViewController.setTouchSlopExceeded(false); 373 374 onTouchEvent(MotionEvent.obtain(2L /* downTime */, 375 2L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */, 376 0 /* metaState */)); 377 378 // fling should still be called after a touch that does not exceed touch slop 379 assertThat(mNotificationPanelViewController.isClosing()).isTrue(); 380 assertThat(mNotificationPanelViewController.isFlinging()).isTrue(); 381 } 382 383 @Test testA11y_initializeNode()384 public void testA11y_initializeNode() { 385 AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo(); 386 mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo); 387 388 List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList(); 389 assertThat(actionList).containsAtLeastElementsIn( 390 new AccessibilityNodeInfo.AccessibilityAction[] { 391 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD, 392 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP} 393 ); 394 } 395 396 @Test testA11y_scrollForward()397 public void testA11y_scrollForward() { 398 mAccessibilityDelegate.performAccessibilityAction( 399 mView, 400 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(), 401 null); 402 403 verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true); 404 } 405 406 @Test testA11y_scrollUp()407 public void testA11y_scrollUp() { 408 mAccessibilityDelegate.performAccessibilityAction( 409 mView, 410 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(), 411 null); 412 413 verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true); 414 } 415 416 @Test testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications()417 public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() { 418 mStatusBarStateController.setState(KEYGUARD); 419 enableSplitShade(/* enabled= */ true); 420 421 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 422 mNotificationPanelViewController.updateResources(); 423 assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) 424 .isEqualTo(R.id.qs_edge_guideline); 425 426 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 427 mNotificationPanelViewController.updateResources(); 428 assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) 429 .isEqualTo(ConstraintSet.PARENT_ID); 430 } 431 432 @Test keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered()433 public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() { 434 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 435 mStatusBarStateController.setState(KEYGUARD); 436 enableSplitShade(/* enabled= */ true); 437 438 setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true); 439 440 assertKeyguardStatusViewCentered(); 441 } 442 443 @Test keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered()444 public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() { 445 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 446 mStatusBarStateController.setState(KEYGUARD); 447 enableSplitShade(/* enabled= */ true); 448 449 setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false); 450 451 assertKeyguardStatusViewNotCentered(); 452 } 453 454 @Test keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered()455 public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() { 456 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 457 mStatusBarStateController.setState(KEYGUARD); 458 enableSplitShade(/* enabled= */ true); 459 460 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true); 461 462 assertKeyguardStatusViewNotCentered(); 463 } 464 465 @Test keyguardStatusView_splitShade_pulsing_isNotCentered()466 public void keyguardStatusView_splitShade_pulsing_isNotCentered() { 467 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 468 when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true); 469 mStatusBarStateController.setState(KEYGUARD); 470 enableSplitShade(/* enabled= */ true); 471 472 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false); 473 474 assertKeyguardStatusViewNotCentered(); 475 } 476 477 @Test keyguardStatusView_splitShade_notPulsing_isNotCentered()478 public void keyguardStatusView_splitShade_notPulsing_isNotCentered() { 479 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 480 when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false); 481 mStatusBarStateController.setState(KEYGUARD); 482 enableSplitShade(/* enabled= */ true); 483 484 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false); 485 486 assertKeyguardStatusViewNotCentered(); 487 } 488 489 @Test keyguardStatusView_singleShade_isCentered()490 public void keyguardStatusView_singleShade_isCentered() { 491 enableSplitShade(/* enabled= */ false); 492 // The conditions below would make the clock NOT be centered on split shade. 493 // On single shade it should always be centered though. 494 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 495 when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false); 496 mStatusBarStateController.setState(KEYGUARD); 497 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false); 498 499 assertKeyguardStatusViewCentered(); 500 } 501 502 @Test keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot()503 public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot() { 504 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 505 mStatusBarStateController.setState(KEYGUARD); 506 enableSplitShade(/* enabled= */ true); 507 508 mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true); 509 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false); 510 assertKeyguardStatusViewCentered(); 511 512 mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false); 513 assertKeyguardStatusViewNotCentered(); 514 } 515 516 @Test keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController()517 public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() { 518 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 519 mStatusBarStateController.setState(KEYGUARD); 520 enableSplitShade(/* enabled= */ true); 521 522 mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true); 523 524 verify(mKeyguardMediaController).setDozeWakeUpAnimationWaiting(true); 525 } 526 527 @Test keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs()528 public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() { 529 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 530 mStatusBarStateController.setState(KEYGUARD); 531 enableSplitShade(/* enabled= */ true); 532 533 mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true); 534 setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false); 535 assertKeyguardStatusViewCentered(); 536 537 mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false); 538 assertKeyguardStatusViewCentered(); 539 } 540 541 @Test onKeyguardStatusViewHeightChange_animatesNextTopPaddingChangeForNSSL()542 public void onKeyguardStatusViewHeightChange_animatesNextTopPaddingChangeForNSSL() { 543 ArgumentCaptor<View.OnLayoutChangeListener> captor = 544 ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); 545 verify(mKeyguardStatusView).addOnLayoutChangeListener(captor.capture()); 546 View.OnLayoutChangeListener listener = captor.getValue(); 547 548 clearInvocations(mNotificationStackScrollLayoutController); 549 550 when(mKeyguardStatusView.getHeight()).thenReturn(0); 551 listener.onLayoutChange(mKeyguardStatusView, /* left= */ 0, /* top= */ 0, /* right= */ 552 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ 553 0, /* oldBottom = */ 200); 554 555 verify(mNotificationStackScrollLayoutController).animateNextTopPaddingChange(); 556 } 557 558 @Test testCanCollapsePanelOnTouch_trueForKeyGuard()559 public void testCanCollapsePanelOnTouch_trueForKeyGuard() { 560 mStatusBarStateController.setState(KEYGUARD); 561 562 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 563 } 564 565 @Test testCanCollapsePanelOnTouch_trueWhenScrolledToBottom()566 public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() { 567 mStatusBarStateController.setState(SHADE); 568 when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true); 569 570 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 571 } 572 573 @Test testCanCollapsePanelOnTouch_trueWhenInSettings()574 public void testCanCollapsePanelOnTouch_trueWhenInSettings() { 575 mStatusBarStateController.setState(SHADE); 576 when(mQsController.getExpanded()).thenReturn(true); 577 578 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 579 } 580 581 @Test testCanCollapsePanelOnTouch_falseInDualPaneShade()582 public void testCanCollapsePanelOnTouch_falseInDualPaneShade() { 583 mStatusBarStateController.setState(SHADE); 584 enableSplitShade(/* enabled= */ true); 585 when(mQsController.getExpanded()).thenReturn(true); 586 587 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse(); 588 } 589 590 @Test testSwipeWhileLocked_notifiesKeyguardState()591 public void testSwipeWhileLocked_notifiesKeyguardState() { 592 mStatusBarStateController.setState(KEYGUARD); 593 594 // Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that 595 // the fling occurred and did not dismiss the keyguard. 596 mNotificationPanelViewController.flingToHeight( 597 0f, true /* expand */, 1000f, 1f, false); 598 verify(mKeyguardStateController).notifyPanelFlingStart(false /* dismissKeyguard */); 599 600 // Fling un-expanded, which is a keyguard exit fling when we're in KEYGUARD state. 601 mNotificationPanelViewController.flingToHeight( 602 0f, false /* expand */, 1000f, 1f, false); 603 verify(mKeyguardStateController).notifyPanelFlingStart(true /* dismissKeyguard */); 604 } 605 606 @Test testCancelSwipeWhileLocked_notifiesKeyguardState()607 public void testCancelSwipeWhileLocked_notifiesKeyguardState() { 608 mStatusBarStateController.setState(KEYGUARD); 609 610 // Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that 611 // the fling occurred and did not dismiss the keyguard. 612 mNotificationPanelViewController.flingToHeight( 613 0f, true /* expand */, 1000f, 1f, false); 614 mNotificationPanelViewController.cancelHeightAnimator(); 615 verify(mKeyguardStateController).notifyPanelFlingEnd(); 616 } 617 618 @Test testSwipe_exactlyToTarget_notifiesNssl()619 public void testSwipe_exactlyToTarget_notifiesNssl() { 620 // No over-expansion 621 mNotificationPanelViewController.setOverExpansion(0f); 622 // Fling to a target that is equal to the current position (i.e. a no-op fling). 623 mNotificationPanelViewController.flingToHeight( 624 0f, 625 true, 626 mNotificationPanelViewController.getExpandedHeight(), 627 1f, 628 false); 629 // Verify that the NSSL is notified that the panel is *not* flinging. 630 verify(mNotificationStackScrollLayoutController).setPanelFlinging(false); 631 } 632 633 @Test testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked()634 public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() { 635 mStatusBarStateController.setState(KEYGUARD); 636 when(mQsController.getExpanded()).thenReturn(true); 637 638 enableSplitShade(true); 639 640 assertThat(mStatusBarStateController.getState()).isEqualTo(SHADE_LOCKED); 641 } 642 643 @Test testUnlockedSplitShadeTransitioningToKeyguard_closesQS()644 public void testUnlockedSplitShadeTransitioningToKeyguard_closesQS() { 645 enableSplitShade(true); 646 mStatusBarStateController.setState(SHADE); 647 mStatusBarStateController.setState(KEYGUARD); 648 649 verify(mQsController).closeQs(); 650 } 651 652 @Test testLockedSplitShadeTransitioningToKeyguard_closesQS()653 public void testLockedSplitShadeTransitioningToKeyguard_closesQS() { 654 enableSplitShade(true); 655 mStatusBarStateController.setState(SHADE_LOCKED); 656 mStatusBarStateController.setState(KEYGUARD); 657 658 verify(mQsController).closeQs(); 659 } 660 661 @Test testSwitchesToCorrectClockInSinglePaneShade()662 public void testSwitchesToCorrectClockInSinglePaneShade() { 663 mStatusBarStateController.setState(KEYGUARD); 664 665 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 666 triggerPositionClockAndNotifications(); 667 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); 668 669 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 670 triggerPositionClockAndNotifications(); 671 verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true); 672 } 673 674 @Test testSwitchesToCorrectClockInSplitShade()675 public void testSwitchesToCorrectClockInSplitShade() { 676 mStatusBarStateController.setState(KEYGUARD); 677 enableSplitShade(/* enabled= */ true); 678 clearInvocations(mKeyguardStatusViewController); 679 680 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 681 triggerPositionClockAndNotifications(); 682 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); 683 684 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 685 triggerPositionClockAndNotifications(); 686 verify(mKeyguardStatusViewController, times(2)) 687 .displayClock(LARGE, /* animate */ true); 688 verify(mKeyguardStatusViewController, never()) 689 .displayClock(SMALL, /* animate */ true); 690 } 691 692 @Test testHasNotifications_switchesToLargeClockWhenEnteringSplitShade()693 public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() { 694 mStatusBarStateController.setState(KEYGUARD); 695 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 696 697 enableSplitShade(/* enabled= */ true); 698 699 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); 700 } 701 702 @Test testNoNotifications_switchesToLargeClockWhenEnteringSplitShade()703 public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() { 704 mStatusBarStateController.setState(KEYGUARD); 705 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 706 707 enableSplitShade(/* enabled= */ true); 708 709 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); 710 } 711 712 @Test testHasNotifications_switchesToSmallClockWhenExitingSplitShade()713 public void testHasNotifications_switchesToSmallClockWhenExitingSplitShade() { 714 mStatusBarStateController.setState(KEYGUARD); 715 enableSplitShade(/* enabled= */ true); 716 clearInvocations(mKeyguardStatusViewController); 717 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 718 719 enableSplitShade(/* enabled= */ false); 720 721 verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true); 722 } 723 724 @Test testNoNotifications_switchesToLargeClockWhenExitingSplitShade()725 public void testNoNotifications_switchesToLargeClockWhenExitingSplitShade() { 726 mStatusBarStateController.setState(KEYGUARD); 727 enableSplitShade(/* enabled= */ true); 728 clearInvocations(mKeyguardStatusViewController); 729 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 730 731 enableSplitShade(/* enabled= */ false); 732 733 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); 734 } 735 736 @Test clockSize_mediaShowing_inSplitShade_onAod_isLarge()737 public void clockSize_mediaShowing_inSplitShade_onAod_isLarge() { 738 when(mDozeParameters.getAlwaysOn()).thenReturn(true); 739 mStatusBarStateController.setState(KEYGUARD); 740 enableSplitShade(/* enabled= */ true); 741 when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true); 742 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 743 clearInvocations(mKeyguardStatusViewController); 744 745 mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false); 746 747 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate= */ true); 748 } 749 750 @Test clockSize_mediaShowing_inSplitShade_screenOff_notAod_isSmall()751 public void clockSize_mediaShowing_inSplitShade_screenOff_notAod_isSmall() { 752 when(mDozeParameters.getAlwaysOn()).thenReturn(false); 753 mStatusBarStateController.setState(KEYGUARD); 754 enableSplitShade(/* enabled= */ true); 755 when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true); 756 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 757 clearInvocations(mKeyguardStatusViewController); 758 759 mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false); 760 761 verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate= */ true); 762 } 763 764 @Test onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_showNSSL()765 public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_showNSSL() { 766 // GIVEN 767 mStatusBarStateController.setState(KEYGUARD); 768 when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false); 769 when(mQsController.getFullyExpanded()).thenReturn(true); 770 when(mQsController.getExpanded()).thenReturn(true); 771 772 // WHEN 773 int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 774 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 775 776 // THEN 777 // We are interested in the last value of the stack alpha. 778 ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class); 779 verify(mNotificationStackScrollLayoutController, atLeastOnce()) 780 .setAlpha(alphaCaptor.capture()); 781 assertThat(alphaCaptor.getValue()).isEqualTo(1.0f); 782 } 783 784 @Test onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_hideNSSL()785 public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_hideNSSL() { 786 // GIVEN 787 mStatusBarStateController.setState(KEYGUARD); 788 when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false); 789 when(mQsController.getFullyExpanded()).thenReturn(false); 790 when(mQsController.getExpanded()).thenReturn(true); 791 792 // WHEN 793 int transitionDistance = mNotificationPanelViewController 794 .getMaxPanelTransitionDistance() / 2; 795 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 796 797 // THEN 798 // We are interested in the last value of the stack alpha. 799 ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class); 800 verify(mNotificationStackScrollLayoutController, atLeastOnce()) 801 .setAlpha(alphaCaptor.capture()); 802 assertThat(alphaCaptor.getValue()).isEqualTo(0.0f); 803 } 804 805 @Test testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled()806 public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() { 807 when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false); 808 mStatusBarStateController.setState(KEYGUARD); 809 enableSplitShade(/* enabled= */ true); 810 clearInvocations(mKeyguardStatusViewController); 811 when(mMediaDataManager.hasActiveMedia()).thenReturn(true); 812 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 813 814 mNotificationPanelViewController.setDozing(true, false); 815 816 verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false); 817 } 818 819 @Test testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying()820 public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() { 821 mStatusBarStateController.setState(KEYGUARD); 822 enableSplitShade(/* enabled= */ true); 823 clearInvocations(mKeyguardStatusViewController); 824 when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true); 825 826 // one notification + media player visible 827 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 828 triggerPositionClockAndNotifications(); 829 verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true); 830 831 // only media player visible 832 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 833 triggerPositionClockAndNotifications(); 834 verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true); 835 verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true); 836 } 837 838 @Test testFoldToAodAnimationCleansupInAnimationEnd()839 public void testFoldToAodAnimationCleansupInAnimationEnd() { 840 ArgumentCaptor<Animator.AnimatorListener> animCaptor = 841 ArgumentCaptor.forClass(Animator.AnimatorListener.class); 842 ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> updateCaptor = 843 ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); 844 845 // Start fold animation & Capture Listeners 846 mNotificationPanelViewController.getShadeFoldAnimator() 847 .startFoldToAodAnimation(() -> {}, () -> {}, () -> {}); 848 verify(mViewPropertyAnimator).setListener(animCaptor.capture()); 849 verify(mViewPropertyAnimator).setUpdateListener(updateCaptor.capture()); 850 851 // End animation and validate listeners were unset 852 animCaptor.getValue().onAnimationEnd(null); 853 verify(mViewPropertyAnimator).setListener(null); 854 verify(mViewPropertyAnimator).setUpdateListener(null); 855 } 856 857 @Test testExpandWithQsMethodIsUsingLockscreenTransitionController()858 public void testExpandWithQsMethodIsUsingLockscreenTransitionController() { 859 enableSplitShade(/* enabled= */ true); 860 mStatusBarStateController.setState(KEYGUARD); 861 862 mNotificationPanelViewController.expandToQs(); 863 864 verify(mLockscreenShadeTransitionController).goToLockedShade( 865 /* expandedView= */null, /* needsQSAnimation= */true); 866 } 867 868 @Test testUnlockAnimationDoesNotAffectScrim()869 public void testUnlockAnimationDoesNotAffectScrim() { 870 mNotificationPanelViewController.onUnlockHintStarted(); 871 verify(mScrimController).setExpansionAffectsAlpha(false); 872 mNotificationPanelViewController.onUnlockHintFinished(); 873 verify(mScrimController).setExpansionAffectsAlpha(true); 874 } 875 876 @Test testUnlockHintAnimation_runs_whenNotInPowerSaveMode_andDozeAmountIsZero()877 public void testUnlockHintAnimation_runs_whenNotInPowerSaveMode_andDozeAmountIsZero() { 878 when(mPowerManager.isPowerSaveMode()).thenReturn(false); 879 when(mAmbientState.getDozeAmount()).thenReturn(0f); 880 mNotificationPanelViewController.startUnlockHintAnimation(); 881 assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isTrue(); 882 } 883 884 @Test testUnlockHintAnimation_doesNotRun_inPowerSaveMode()885 public void testUnlockHintAnimation_doesNotRun_inPowerSaveMode() { 886 when(mPowerManager.isPowerSaveMode()).thenReturn(true); 887 mNotificationPanelViewController.startUnlockHintAnimation(); 888 assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse(); 889 } 890 891 @Test testUnlockHintAnimation_doesNotRun_whenDozeAmountNotZero()892 public void testUnlockHintAnimation_doesNotRun_whenDozeAmountNotZero() { 893 when(mPowerManager.isPowerSaveMode()).thenReturn(false); 894 when(mAmbientState.getDozeAmount()).thenReturn(0.5f); 895 mNotificationPanelViewController.startUnlockHintAnimation(); 896 assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse(); 897 } 898 899 @Test setKeyguardStatusBarAlpha_setsAlphaOnKeyguardStatusBarController()900 public void setKeyguardStatusBarAlpha_setsAlphaOnKeyguardStatusBarController() { 901 float statusBarAlpha = 0.5f; 902 903 mNotificationPanelViewController.setKeyguardStatusBarAlpha(statusBarAlpha); 904 905 verify(mKeyguardStatusBarViewController).setAlpha(statusBarAlpha); 906 } 907 908 @Test testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade()909 public void testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade() { 910 enableSplitShade(/* enabled= */ true); 911 mShadeExpansionStateManager.updateState(STATE_OPEN); 912 verify(mQsController).setExpandImmediate(false); 913 914 mShadeExpansionStateManager.updateState(STATE_CLOSED); 915 verify(mQsController, times(2)).setExpandImmediate(false); 916 917 mShadeExpansionStateManager.updateState(STATE_OPENING); 918 verify(mQsController).setExpandImmediate(true); 919 } 920 921 @Test testQsNotToBeImmediatelyExpandedWhenGoingFromUnlockedToLocked()922 public void testQsNotToBeImmediatelyExpandedWhenGoingFromUnlockedToLocked() { 923 enableSplitShade(/* enabled= */ true); 924 mShadeExpansionStateManager.updateState(STATE_CLOSED); 925 926 mStatusBarStateController.setState(KEYGUARD); 927 // going to lockscreen would trigger STATE_OPENING 928 mShadeExpansionStateManager.updateState(STATE_OPENING); 929 930 verify(mQsController, never()).setExpandImmediate(true); 931 } 932 933 @Test testQsImmediateResetsWhenPanelOpensOrCloses()934 public void testQsImmediateResetsWhenPanelOpensOrCloses() { 935 mShadeExpansionStateManager.updateState(STATE_OPEN); 936 mShadeExpansionStateManager.updateState(STATE_CLOSED); 937 verify(mQsController, times(2)).setExpandImmediate(false); 938 } 939 940 @Test testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade()941 public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() { 942 // to make sure shade is in expanded state 943 mNotificationPanelViewController.startWaitingForExpandGesture(); 944 945 // switch to split shade from portrait (default state) 946 enableSplitShade(/* enabled= */ true); 947 verify(mQsController).setExpanded(true); 948 949 // switch to portrait from split shade 950 enableSplitShade(/* enabled= */ false); 951 verify(mQsController).setExpanded(false); 952 } 953 954 @Test testPanelClosedWhenClosingQsInSplitShade()955 public void testPanelClosedWhenClosingQsInSplitShade() { 956 mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1, 957 /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0); 958 enableSplitShade(/* enabled= */ true); 959 mNotificationPanelViewController.setExpandedFraction(1f); 960 961 assertThat(mNotificationPanelViewController.isClosing()).isFalse(); 962 mNotificationPanelViewController.animateCollapseQs(false); 963 964 assertThat(mNotificationPanelViewController.isClosing()).isTrue(); 965 } 966 967 @Test getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance()968 public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() { 969 enableSplitShade(true); 970 mNotificationPanelViewController.expandToQs(); 971 972 int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 973 974 assertThat(maxDistance).isEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); 975 } 976 977 @Test isExpandingOrCollapsing_returnsTrue_whenQsLockscreenDragInProgress()978 public void isExpandingOrCollapsing_returnsTrue_whenQsLockscreenDragInProgress() { 979 when(mQsController.getLockscreenShadeDragProgress()).thenReturn(0.5f); 980 assertThat(mNotificationPanelViewController.isExpandingOrCollapsing()).isTrue(); 981 } 982 983 984 @Test getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue()985 public void getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue() { 986 enableSplitShade(true); 987 mNotificationPanelViewController.expandToQs(); 988 when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true); 989 when(mQsController.calculatePanelHeightExpanded(anyInt())).thenReturn(10000); 990 mNotificationPanelViewController.setHeadsUpDraggingStartingHeight( 991 SPLIT_SHADE_FULL_TRANSITION_DISTANCE); 992 993 int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 994 995 // make sure we're ignoring the placeholder value for Qs max height 996 assertThat(maxDistance).isLessThan(10000); 997 assertThat(maxDistance).isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); 998 } 999 1000 @Test getMaxPanelTransitionDistance_expandingSplitShade_keyguard_returnsNonSplitShadeValue()1001 public void getMaxPanelTransitionDistance_expandingSplitShade_keyguard_returnsNonSplitShadeValue() { 1002 mStatusBarStateController.setState(KEYGUARD); 1003 enableSplitShade(true); 1004 mNotificationPanelViewController.expandToQs(); 1005 1006 int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1007 1008 assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); 1009 } 1010 1011 @Test getMaxPanelTransitionDistance_expanding_notSplitShade_returnsNonSplitShadeValue()1012 public void getMaxPanelTransitionDistance_expanding_notSplitShade_returnsNonSplitShadeValue() { 1013 enableSplitShade(false); 1014 mNotificationPanelViewController.expandToQs(); 1015 1016 int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1017 1018 assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); 1019 } 1020 1021 @Test onLayoutChange_fullWidth_updatesQSWithFullWithTrue()1022 public void onLayoutChange_fullWidth_updatesQSWithFullWithTrue() { 1023 setIsFullWidth(true); 1024 1025 verify(mQsController).setNotificationPanelFullWidth(true); 1026 } 1027 1028 @Test onLayoutChange_notFullWidth_updatesQSWithFullWithFalse()1029 public void onLayoutChange_notFullWidth_updatesQSWithFullWithFalse() { 1030 setIsFullWidth(false); 1031 1032 verify(mQsController).setNotificationPanelFullWidth(false); 1033 } 1034 1035 @Test onLayoutChange_qsNotSet_doesNotCrash()1036 public void onLayoutChange_qsNotSet_doesNotCrash() { 1037 mQuickSettingsController.setQs(null); 1038 1039 triggerLayoutChange(); 1040 } 1041 1042 @Test onEmptySpaceClicked_notDozingAndOnKeyguard_requestsFaceAuth()1043 public void onEmptySpaceClicked_notDozingAndOnKeyguard_requestsFaceAuth() { 1044 StatusBarStateController.StateListener statusBarStateListener = 1045 mNotificationPanelViewController.getStatusBarStateListener(); 1046 statusBarStateListener.onStateChanged(KEYGUARD); 1047 mNotificationPanelViewController.setDozing(false, false); 1048 1049 // This sets the dozing state that is read when onMiddleClicked is eventually invoked. 1050 mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); 1051 mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); 1052 1053 verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked(); 1054 verify(mUpdateMonitor).requestFaceAuth( 1055 FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED); 1056 } 1057 1058 @Test onEmptySpaceClicked_notDozingAndFaceDetectionIsNotRunning_startsUnlockAnimation()1059 public void onEmptySpaceClicked_notDozingAndFaceDetectionIsNotRunning_startsUnlockAnimation() { 1060 StatusBarStateController.StateListener statusBarStateListener = 1061 mNotificationPanelViewController.getStatusBarStateListener(); 1062 statusBarStateListener.onStateChanged(KEYGUARD); 1063 mNotificationPanelViewController.setDozing(false, false); 1064 when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(false); 1065 1066 // This sets the dozing state that is read when onMiddleClicked is eventually invoked. 1067 mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); 1068 mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); 1069 1070 verify(mNotificationStackScrollLayoutController).setUnlockHintRunning(true); 1071 } 1072 1073 @Test onEmptySpaceClicked_notDozingAndFaceDetectionIsRunning_doesNotStartUnlockHint()1074 public void onEmptySpaceClicked_notDozingAndFaceDetectionIsRunning_doesNotStartUnlockHint() { 1075 StatusBarStateController.StateListener statusBarStateListener = 1076 mNotificationPanelViewController.getStatusBarStateListener(); 1077 statusBarStateListener.onStateChanged(KEYGUARD); 1078 mNotificationPanelViewController.setDozing(false, false); 1079 when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(true); 1080 1081 // This sets the dozing state that is read when onMiddleClicked is eventually invoked. 1082 mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); 1083 mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); 1084 1085 verify(mNotificationStackScrollLayoutController, never()).setUnlockHintRunning(true); 1086 } 1087 1088 @Test onEmptySpaceClicked_whenDozingAndOnKeyguard_doesNotRequestFaceAuth()1089 public void onEmptySpaceClicked_whenDozingAndOnKeyguard_doesNotRequestFaceAuth() { 1090 StatusBarStateController.StateListener statusBarStateListener = 1091 mNotificationPanelViewController.getStatusBarStateListener(); 1092 statusBarStateListener.onStateChanged(KEYGUARD); 1093 mNotificationPanelViewController.setDozing(true, false); 1094 1095 // This sets the dozing state that is read when onMiddleClicked is eventually invoked. 1096 mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); 1097 mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); 1098 1099 verify(mUpdateMonitor, never()).requestFaceAuth(anyString()); 1100 } 1101 1102 @Test onEmptySpaceClicked_whenStatusBarShadeLocked_doesNotRequestFaceAuth()1103 public void onEmptySpaceClicked_whenStatusBarShadeLocked_doesNotRequestFaceAuth() { 1104 StatusBarStateController.StateListener statusBarStateListener = 1105 mNotificationPanelViewController.getStatusBarStateListener(); 1106 statusBarStateListener.onStateChanged(SHADE_LOCKED); 1107 1108 mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); 1109 1110 verify(mUpdateMonitor, never()).requestFaceAuth(anyString()); 1111 1112 } 1113 1114 @Test onSplitShadeChanged_duringShadeExpansion_resetsOverScrollState()1115 public void onSplitShadeChanged_duringShadeExpansion_resetsOverScrollState() { 1116 // There was a bug where there was left-over overscroll state after going from split shade 1117 // to single shade. 1118 // Since on single shade we don't set overscroll values on QS nor Scrim, those values that 1119 // were there from split shade were never reset. 1120 // To prevent this, we will reset all overscroll state. 1121 enableSplitShade(true); 1122 reset(mQsController, mScrimController, mNotificationStackScrollLayoutController); 1123 1124 mNotificationPanelViewController.setOverExpansion(123); 1125 verify(mQsController).setOverScrollAmount(123); 1126 verify(mScrimController).setNotificationsOverScrollAmount(123); 1127 verify(mNotificationStackScrollLayoutController).setOverExpansion(123); 1128 1129 enableSplitShade(false); 1130 verify(mQsController).setOverScrollAmount(0); 1131 verify(mScrimController).setNotificationsOverScrollAmount(0); 1132 verify(mNotificationStackScrollLayoutController).setOverExpansion(0); 1133 } 1134 1135 @Test onSplitShadeChanged_alwaysResetsOverScrollState()1136 public void onSplitShadeChanged_alwaysResetsOverScrollState() { 1137 enableSplitShade(true); 1138 enableSplitShade(false); 1139 1140 verify(mQsController, times(2)).setOverScrollAmount(0); 1141 verify(mScrimController, times(2)).setNotificationsOverScrollAmount(0); 1142 verify(mNotificationStackScrollLayoutController, times(2)).setOverExpansion(0); 1143 verify(mNotificationStackScrollLayoutController, times(2)).setOverScrollAmount(0); 1144 } 1145 1146 /** 1147 * When shade is flinging to close and this fling is not intercepted, 1148 * {@link AmbientState#setIsClosing(boolean)} should be called before 1149 * {@link NotificationStackScrollLayoutController#onExpansionStopped()} 1150 * to ensure scrollY can be correctly set to be 0 1151 */ 1152 @Test onShadeFlingClosingEnd_mAmbientStateSetClose_thenOnExpansionStopped()1153 public void onShadeFlingClosingEnd_mAmbientStateSetClose_thenOnExpansionStopped() { 1154 // Given: Shade is expanded 1155 mNotificationPanelViewController.notifyExpandingFinished(); 1156 mNotificationPanelViewController.setClosing(false); 1157 1158 // When: Shade flings to close not canceled 1159 mNotificationPanelViewController.notifyExpandingStarted(); 1160 mNotificationPanelViewController.setClosing(true); 1161 mNotificationPanelViewController.onFlingEnd(false); 1162 1163 // Then: AmbientState's mIsClosing should be set to false 1164 // before mNotificationStackScrollLayoutController.onExpansionStopped() is called 1165 // to ensure NotificationStackScrollLayout.resetScrollPosition() -> resetScrollPosition 1166 // -> setOwnScrollY(0) can set scrollY to 0 when shade is closed 1167 InOrder inOrder = inOrder(mAmbientState, mNotificationStackScrollLayoutController); 1168 inOrder.verify(mAmbientState).setIsClosing(false); 1169 inOrder.verify(mNotificationStackScrollLayoutController).onExpansionStopped(); 1170 } 1171 1172 @Test onShadeFlingEnd_mExpandImmediateShouldBeReset()1173 public void onShadeFlingEnd_mExpandImmediateShouldBeReset() { 1174 mNotificationPanelViewController.onFlingEnd(false); 1175 1176 verify(mQsController).setExpandImmediate(false); 1177 } 1178 1179 @Test inUnlockedSplitShade_transitioningMaxTransitionDistance_makesShadeFullyExpanded()1180 public void inUnlockedSplitShade_transitioningMaxTransitionDistance_makesShadeFullyExpanded() { 1181 mStatusBarStateController.setState(SHADE); 1182 enableSplitShade(true); 1183 int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1184 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 1185 assertThat(mNotificationPanelViewController.isFullyExpanded()).isTrue(); 1186 } 1187 1188 @Test shadeFullyExpanded_inShadeState()1189 public void shadeFullyExpanded_inShadeState() { 1190 mStatusBarStateController.setState(SHADE); 1191 1192 mNotificationPanelViewController.setExpandedHeight(0); 1193 assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isFalse(); 1194 1195 int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1196 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 1197 assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isTrue(); 1198 } 1199 1200 @Test shadeFullyExpanded_onKeyguard()1201 public void shadeFullyExpanded_onKeyguard() { 1202 mStatusBarStateController.setState(KEYGUARD); 1203 1204 int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1205 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 1206 assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isFalse(); 1207 } 1208 1209 @Test shadeFullyExpanded_onShadeLocked()1210 public void shadeFullyExpanded_onShadeLocked() { 1211 mStatusBarStateController.setState(SHADE_LOCKED); 1212 assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isTrue(); 1213 } 1214 1215 @Test shadeExpanded_whenHasHeight()1216 public void shadeExpanded_whenHasHeight() { 1217 int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); 1218 mNotificationPanelViewController.setExpandedHeight(transitionDistance); 1219 assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); 1220 } 1221 1222 @Test shadeExpanded_whenInstantExpanding()1223 public void shadeExpanded_whenInstantExpanding() { 1224 mNotificationPanelViewController.expand(true); 1225 assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); 1226 } 1227 1228 @Test shadeExpanded_whenHunIsPresent()1229 public void shadeExpanded_whenHunIsPresent() { 1230 when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); 1231 assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); 1232 } 1233 1234 @Test shadeExpanded_whenWaitingForExpandGesture()1235 public void shadeExpanded_whenWaitingForExpandGesture() { 1236 mNotificationPanelViewController.startWaitingForExpandGesture(); 1237 assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); 1238 } 1239 1240 @Test shadeExpanded_whenUnlockedOffscreenAnimationRunning()1241 public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() { 1242 when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true); 1243 assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); 1244 } 1245 1246 @Test getFalsingThreshold_deviceNotInteractive_isQsThreshold()1247 public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() { 1248 mFakeKeyguardRepository.setWakefulnessModel( 1249 new WakefulnessModel( 1250 WakefulnessState.ASLEEP, 1251 /* lastWakeReason= */ WakeSleepReason.TAP, 1252 /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) 1253 ); 1254 when(mQsController.getFalsingThreshold()).thenReturn(14); 1255 1256 assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14); 1257 } 1258 1259 @Test getFalsingThreshold_lastWakeNotDueToTouch_isQsThreshold()1260 public void getFalsingThreshold_lastWakeNotDueToTouch_isQsThreshold() { 1261 mFakeKeyguardRepository.setWakefulnessModel( 1262 new WakefulnessModel( 1263 WakefulnessState.AWAKE, 1264 /* lastWakeReason= */ WakeSleepReason.POWER_BUTTON, 1265 /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) 1266 ); 1267 when(mQsController.getFalsingThreshold()).thenReturn(14); 1268 1269 assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14); 1270 } 1271 1272 @Test getFalsingThreshold_lastWakeDueToTouch_greaterThanQsThreshold()1273 public void getFalsingThreshold_lastWakeDueToTouch_greaterThanQsThreshold() { 1274 mFakeKeyguardRepository.setWakefulnessModel( 1275 new WakefulnessModel( 1276 WakefulnessState.AWAKE, 1277 /* lastWakeReason= */ WakeSleepReason.TAP, 1278 /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) 1279 ); 1280 when(mQsController.getFalsingThreshold()).thenReturn(14); 1281 1282 assertThat(mNotificationPanelViewController.getFalsingThreshold()).isGreaterThan(14); 1283 } 1284 } 1285