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.statusbar.phone; 18 19 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 20 21 import static com.android.keyguard.KeyguardClockSwitch.LARGE; 22 import static com.android.keyguard.KeyguardClockSwitch.SMALL; 23 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 24 import static com.android.systemui.statusbar.StatusBarState.SHADE; 25 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; 26 import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.reset; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.ArgumentMatchers.anyBoolean; 32 import static org.mockito.ArgumentMatchers.anyInt; 33 import static org.mockito.ArgumentMatchers.eq; 34 import static org.mockito.Mockito.atLeast; 35 import static org.mockito.Mockito.atLeastOnce; 36 import static org.mockito.Mockito.clearInvocations; 37 import static org.mockito.Mockito.doAnswer; 38 import static org.mockito.Mockito.mock; 39 import static org.mockito.Mockito.never; 40 import static org.mockito.Mockito.times; 41 import static org.mockito.Mockito.verify; 42 import static org.mockito.Mockito.when; 43 44 import android.annotation.IdRes; 45 import android.app.ActivityManager; 46 import android.content.ContentResolver; 47 import android.content.res.Configuration; 48 import android.content.res.Resources; 49 import android.database.ContentObserver; 50 import android.os.Handler; 51 import android.os.Looper; 52 import android.os.PowerManager; 53 import android.os.UserManager; 54 import android.testing.AndroidTestingRunner; 55 import android.testing.TestableLooper; 56 import android.util.DisplayMetrics; 57 import android.view.LayoutInflater; 58 import android.view.MotionEvent; 59 import android.view.View; 60 import android.view.ViewGroup; 61 import android.view.ViewPropertyAnimator; 62 import android.view.ViewStub; 63 import android.view.accessibility.AccessibilityManager; 64 import android.view.accessibility.AccessibilityNodeInfo; 65 66 import androidx.constraintlayout.widget.ConstraintLayout; 67 import androidx.constraintlayout.widget.ConstraintSet; 68 import androidx.test.filters.SmallTest; 69 70 import com.android.internal.logging.MetricsLogger; 71 import com.android.internal.logging.UiEventLogger; 72 import com.android.internal.logging.testing.UiEventLoggerFake; 73 import com.android.internal.util.CollectionUtils; 74 import com.android.internal.util.LatencyTracker; 75 import com.android.keyguard.KeyguardClockSwitch; 76 import com.android.keyguard.KeyguardClockSwitchController; 77 import com.android.keyguard.KeyguardStatusView; 78 import com.android.keyguard.KeyguardStatusViewController; 79 import com.android.keyguard.KeyguardUpdateMonitor; 80 import com.android.keyguard.LockIconViewController; 81 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; 82 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; 83 import com.android.keyguard.dagger.KeyguardStatusViewComponent; 84 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; 85 import com.android.systemui.R; 86 import com.android.systemui.SysuiTestCase; 87 import com.android.systemui.biometrics.AuthController; 88 import com.android.systemui.classifier.FalsingCollectorFake; 89 import com.android.systemui.classifier.FalsingManagerFake; 90 import com.android.systemui.controls.dagger.ControlsComponent; 91 import com.android.systemui.doze.DozeLog; 92 import com.android.systemui.dump.DumpManager; 93 import com.android.systemui.flags.FeatureFlags; 94 import com.android.systemui.fragments.FragmentHostManager; 95 import com.android.systemui.fragments.FragmentService; 96 import com.android.systemui.media.KeyguardMediaController; 97 import com.android.systemui.media.MediaDataManager; 98 import com.android.systemui.media.MediaHierarchyManager; 99 import com.android.systemui.navigationbar.NavigationModeController; 100 import com.android.systemui.plugins.FalsingManager; 101 import com.android.systemui.qs.QSDetailDisplayer; 102 import com.android.systemui.screenrecord.RecordingController; 103 import com.android.systemui.statusbar.CommandQueue; 104 import com.android.systemui.statusbar.KeyguardAffordanceView; 105 import com.android.systemui.statusbar.KeyguardIndicationController; 106 import com.android.systemui.statusbar.LockscreenShadeTransitionController; 107 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 108 import com.android.systemui.statusbar.NotificationRemoteInputManager; 109 import com.android.systemui.statusbar.NotificationShadeDepthController; 110 import com.android.systemui.statusbar.NotificationShelfController; 111 import com.android.systemui.statusbar.PulseExpansionHandler; 112 import com.android.systemui.statusbar.StatusBarStateControllerImpl; 113 import com.android.systemui.statusbar.SysuiStatusBarStateController; 114 import com.android.systemui.statusbar.VibratorHelper; 115 import com.android.systemui.statusbar.events.PrivacyDotViewController; 116 import com.android.systemui.statusbar.notification.ConversationNotificationManager; 117 import com.android.systemui.statusbar.notification.DynamicPrivacyController; 118 import com.android.systemui.statusbar.notification.NotificationEntryManager; 119 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 120 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; 121 import com.android.systemui.statusbar.notification.stack.AmbientState; 122 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; 123 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 124 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 125 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; 126 import com.android.systemui.statusbar.policy.ConfigurationController; 127 import com.android.systemui.statusbar.policy.KeyguardStateController; 128 import com.android.systemui.unfold.SysUIUnfoldComponent; 129 import com.android.systemui.util.concurrency.FakeExecutor; 130 import com.android.systemui.util.settings.SecureSettings; 131 import com.android.systemui.util.time.FakeSystemClock; 132 import com.android.systemui.wallet.controller.QuickAccessWalletController; 133 import com.android.wm.shell.animation.FlingAnimationUtils; 134 135 import org.junit.Before; 136 import org.junit.Test; 137 import org.junit.runner.RunWith; 138 import org.mockito.ArgumentCaptor; 139 import org.mockito.Mock; 140 import org.mockito.MockitoAnnotations; 141 import org.mockito.stubbing.Answer; 142 143 import java.util.List; 144 import java.util.Optional; 145 146 @SmallTest 147 @RunWith(AndroidTestingRunner.class) 148 @TestableLooper.RunWithLooper 149 public class NotificationPanelViewControllerTest extends SysuiTestCase { 150 151 private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; 152 153 @Mock 154 private StatusBar mStatusBar; 155 @Mock 156 private NotificationStackScrollLayout mNotificationStackScrollLayout; 157 @Mock 158 private KeyguardBottomAreaView mKeyguardBottomArea; 159 @Mock 160 private KeyguardBottomAreaView mQsFrame; 161 private KeyguardStatusView mKeyguardStatusView; 162 @Mock 163 private NotificationIconAreaController mNotificationAreaController; 164 @Mock 165 private HeadsUpManagerPhone mHeadsUpManager; 166 @Mock 167 private NotificationShelfController mNotificationShelfController; 168 @Mock 169 private NotificationGroupManagerLegacy mGroupManager; 170 @Mock 171 private KeyguardStatusBarView mKeyguardStatusBar; 172 @Mock 173 private View mUserSwitcherView; 174 @Mock 175 private ViewStub mUserSwitcherStubView; 176 @Mock 177 private HeadsUpTouchHelper.Callback mHeadsUpCallback; 178 @Mock 179 private KeyguardUpdateMonitor mUpdateMonitor; 180 @Mock 181 private KeyguardBypassController mKeyguardBypassController; 182 @Mock 183 private DozeParameters mDozeParameters; 184 @Mock 185 private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 186 @Mock 187 private NotificationPanelView mView; 188 @Mock 189 private LayoutInflater mLayoutInflater; 190 @Mock 191 private DynamicPrivacyController mDynamicPrivacyController; 192 @Mock 193 private ShadeController mShadeController; 194 @Mock 195 private NotificationLockscreenUserManager mNotificationLockscreenUserManager; 196 @Mock 197 private NotificationEntryManager mNotificationEntryManager; 198 @Mock 199 private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; 200 @Mock 201 private KeyguardStateController mKeyguardStateController; 202 @Mock 203 private DozeLog mDozeLog; 204 @Mock 205 private CommandQueue mCommandQueue; 206 @Mock 207 private VibratorHelper mVibratorHelper; 208 @Mock 209 private LatencyTracker mLatencyTracker; 210 @Mock 211 private PowerManager mPowerManager; 212 @Mock 213 private AccessibilityManager mAccessibilityManager; 214 @Mock 215 private MetricsLogger mMetricsLogger; 216 @Mock 217 private ActivityManager mActivityManager; 218 @Mock 219 private Resources mResources; 220 @Mock 221 private Configuration mConfiguration; 222 private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 223 @Mock 224 private KeyguardClockSwitch mKeyguardClockSwitch; 225 private PanelViewController.TouchHandler mTouchHandler; 226 private ConfigurationController mConfigurationController; 227 @Mock 228 private MediaHierarchyManager mMediaHiearchyManager; 229 @Mock 230 private ConversationNotificationManager mConversationNotificationManager; 231 @Mock 232 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 233 @Mock 234 private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; 235 @Mock 236 private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; 237 @Mock 238 private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; 239 @Mock 240 private QSDetailDisplayer mQSDetailDisplayer; 241 @Mock 242 private KeyguardStatusViewComponent mKeyguardStatusViewComponent; 243 @Mock 244 private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; 245 @Mock 246 private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; 247 @Mock 248 private KeyguardClockSwitchController mKeyguardClockSwitchController; 249 @Mock 250 private KeyguardStatusViewController mKeyguardStatusViewController; 251 @Mock 252 private KeyguardStatusBarViewController mKeyguardStatusBarViewController; 253 @Mock 254 private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; 255 @Mock 256 private NotificationShadeDepthController mNotificationShadeDepthController; 257 @Mock 258 private LockscreenShadeTransitionController mLockscreenShadeTransitionController; 259 @Mock 260 private AuthController mAuthController; 261 @Mock 262 private ScrimController mScrimController; 263 @Mock 264 private MediaDataManager mMediaDataManager; 265 @Mock 266 private AmbientState mAmbientState; 267 @Mock 268 private UserManager mUserManager; 269 @Mock 270 private UiEventLogger mUiEventLogger; 271 @Mock 272 private LockIconViewController mLockIconViewController; 273 @Mock 274 private KeyguardMediaController mKeyguardMediaController; 275 @Mock 276 private PrivacyDotViewController mPrivacyDotViewController; 277 @Mock 278 private NavigationModeController mNavigationModeController; 279 @Mock 280 private SecureSettings mSecureSettings; 281 @Mock 282 private SplitShadeHeaderController mSplitShadeHeaderController; 283 @Mock 284 private ContentResolver mContentResolver; 285 @Mock 286 private TapAgainViewController mTapAgainViewController; 287 @Mock 288 private KeyguardIndicationController mKeyguardIndicationController; 289 @Mock 290 private FragmentService mFragmentService; 291 @Mock 292 private FragmentHostManager mFragmentHostManager; 293 @Mock 294 private QuickAccessWalletController mQuickAccessWalletController; 295 @Mock 296 private NotificationRemoteInputManager mNotificationRemoteInputManager; 297 @Mock 298 private RecordingController mRecordingController; 299 @Mock 300 private ControlsComponent mControlsComponent; 301 @Mock 302 private LockscreenGestureLogger mLockscreenGestureLogger; 303 @Mock 304 private DumpManager mDumpManager; 305 @Mock 306 private NotificationsQSContainerController mNotificationsQSContainerController; 307 @Mock 308 private FeatureFlags mFeatureFlags; 309 private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); 310 private SysuiStatusBarStateController mStatusBarStateController; 311 private NotificationPanelViewController mNotificationPanelViewController; 312 private View.AccessibilityDelegate mAccessibiltyDelegate; 313 private NotificationsQuickSettingsContainer mNotificationContainerParent; 314 private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; 315 private FalsingManagerFake mFalsingManager = new FalsingManagerFake(); 316 317 @Before setup()318 public void setup() { 319 MockitoAnnotations.initMocks(this); 320 mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager); 321 322 mKeyguardStatusView = new KeyguardStatusView(mContext); 323 mKeyguardStatusView.setId(R.id.keyguard_status_view); 324 325 when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); 326 when(mHeadsUpCallback.getContext()).thenReturn(mContext); 327 when(mView.getResources()).thenReturn(mResources); 328 when(mResources.getConfiguration()).thenReturn(mConfiguration); 329 mConfiguration.orientation = ORIENTATION_PORTRAIT; 330 when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); 331 mDisplayMetrics.density = 100; 332 when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); 333 when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) 334 .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); 335 when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400); 336 when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400); 337 when(mView.getContext()).thenReturn(getContext()); 338 when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); 339 when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); 340 when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn( 341 mUserSwitcherStubView); 342 when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); 343 when(mView.findViewById(R.id.notification_stack_scroller)) 344 .thenReturn(mNotificationStackScrollLayout); 345 when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); 346 when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) 347 .thenReturn(mHeadsUpCallback); 348 when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); 349 when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class)); 350 when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); 351 when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); 352 when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); 353 when(mView.findViewById(R.id.keyguard_status_view)) 354 .thenReturn(mock(KeyguardStatusView.class)); 355 mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); 356 mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); 357 mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); 358 mNotificationContainerParent.addView(mKeyguardStatusView); 359 mNotificationContainerParent.onFinishInflate(); 360 when(mView.findViewById(R.id.notification_container_parent)) 361 .thenReturn(mNotificationContainerParent); 362 when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); 363 FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( 364 mDisplayMetrics); 365 366 doAnswer((Answer<Void>) invocation -> { 367 mTouchHandler = invocation.getArgument(0); 368 return null; 369 }).when(mView).setOnTouchListener(any(PanelViewController.TouchHandler.class)); 370 371 NotificationWakeUpCoordinator coordinator = 372 new NotificationWakeUpCoordinator( 373 mock(HeadsUpManagerPhone.class), 374 new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager), 375 mKeyguardBypassController, 376 mDozeParameters, 377 mUnlockedScreenOffAnimationController); 378 mConfigurationController = new ConfigurationControllerImpl(mContext); 379 PulseExpansionHandler expansionHandler = new PulseExpansionHandler( 380 mContext, 381 coordinator, 382 mKeyguardBypassController, mHeadsUpManager, 383 mock(NotificationRoundnessManager.class), 384 mConfigurationController, 385 mStatusBarStateController, 386 mFalsingManager, 387 mLockscreenShadeTransitionController, 388 new FalsingCollectorFake(), 389 mDumpManager); 390 when(mKeyguardStatusViewComponentFactory.build(any())) 391 .thenReturn(mKeyguardStatusViewComponent); 392 when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) 393 .thenReturn(mKeyguardClockSwitchController); 394 when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) 395 .thenReturn(mKeyguardStatusViewController); 396 when(mKeyguardStatusBarViewComponentFactory.build(any(), any())) 397 .thenReturn(mKeyguardStatusBarViewComponent); 398 when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) 399 .thenReturn(mKeyguardStatusBarViewController); 400 when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean())) 401 .thenReturn(mKeyguardStatusView); 402 when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) 403 .thenReturn(mKeyguardBottomArea); 404 when(mNotificationRemoteInputManager.isRemoteInputActive()).thenReturn(false); 405 406 reset(mView); 407 408 mNotificationPanelViewController = new NotificationPanelViewController(mView, 409 mResources, 410 new Handler(Looper.getMainLooper()), 411 mLayoutInflater, 412 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, 413 mFalsingManager, new FalsingCollectorFake(), 414 mNotificationLockscreenUserManager, mNotificationEntryManager, 415 mKeyguardStateController, mStatusBarStateController, mDozeLog, 416 mDozeParameters, mCommandQueue, mVibratorHelper, 417 mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, 418 mMetricsLogger, mActivityManager, mConfigurationController, 419 () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, 420 mConversationNotificationManager, mMediaHiearchyManager, 421 mStatusBarKeyguardViewManager, 422 mNotificationsQSContainerController, 423 mNotificationStackScrollLayoutController, 424 mKeyguardStatusViewComponentFactory, 425 mKeyguardQsUserSwitchComponentFactory, 426 mKeyguardUserSwitcherComponentFactory, 427 mKeyguardStatusBarViewComponentFactory, 428 mLockscreenShadeTransitionController, 429 mQSDetailDisplayer, 430 mGroupManager, 431 mNotificationAreaController, 432 mAuthController, 433 mScrimController, 434 mUserManager, 435 mMediaDataManager, 436 mNotificationShadeDepthController, 437 mAmbientState, 438 mLockIconViewController, 439 mKeyguardMediaController, 440 mPrivacyDotViewController, 441 mTapAgainViewController, 442 mNavigationModeController, 443 mFragmentService, 444 mContentResolver, 445 mQuickAccessWalletController, 446 mRecordingController, 447 new FakeExecutor(new FakeSystemClock()), 448 mSecureSettings, 449 mSplitShadeHeaderController, 450 mUnlockedScreenOffAnimationController, 451 mLockscreenGestureLogger, 452 new PanelExpansionStateManager(), 453 mNotificationRemoteInputManager, 454 mSysUIUnfoldComponent, 455 mControlsComponent, 456 mFeatureFlags); 457 mNotificationPanelViewController.initDependencies( 458 mStatusBar, 459 () -> {}, 460 mNotificationShelfController); 461 mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); 462 mNotificationPanelViewController.setKeyguardIndicationController( 463 mKeyguardIndicationController); 464 ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = 465 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); 466 verify(mView, atLeast(1)).addOnAttachStateChangeListener( 467 onAttachStateChangeListenerArgumentCaptor.capture()); 468 mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); 469 470 ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = 471 ArgumentCaptor.forClass(View.AccessibilityDelegate.class); 472 verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); 473 mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue(); 474 mNotificationPanelViewController.mStatusBarStateController 475 .addCallback(mNotificationPanelViewController.mStatusBarStateListener); 476 mNotificationPanelViewController 477 .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); 478 } 479 480 @Test testSetPanelScrimMinFraction()481 public void testSetPanelScrimMinFraction() { 482 mNotificationPanelViewController.setPanelScrimMinFraction(0.5f); 483 verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f)); 484 } 485 486 @Test testSetDozing_notifiesNsslAndStateController()487 public void testSetDozing_notifiesNsslAndStateController() { 488 mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */, 489 null /* touch */); 490 verify(mNotificationStackScrollLayoutController) 491 .setDozing(eq(true), eq(false), eq(null)); 492 assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f); 493 } 494 495 @Test testSetExpandedHeight()496 public void testSetExpandedHeight() { 497 mNotificationPanelViewController.setExpandedHeight(200); 498 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 499 } 500 501 @Test testOnTouchEvent_expansionCanBeBlocked()502 public void testOnTouchEvent_expansionCanBeBlocked() { 503 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 504 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 505 0 /* metaState */)); 506 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 507 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */, 508 0 /* metaState */)); 509 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 510 assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse(); 511 512 mNotificationPanelViewController.blockExpansionForCurrentTouch(); 513 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 514 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */, 515 0 /* metaState */)); 516 // Expansion should not have changed because it was blocked 517 assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); 518 assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue(); 519 520 onTouchEvent(MotionEvent.obtain(0L /* downTime */, 521 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */, 522 0 /* metaState */)); 523 assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse(); 524 } 525 526 @Test handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent()527 public void handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() { 528 when(mCommandQueue.panelsEnabled()).thenReturn(false); 529 530 boolean returnVal = mNotificationPanelViewController 531 .getStatusBarTouchEventHandler() 532 .handleTouchEvent( 533 MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); 534 535 assertThat(returnVal).isFalse(); 536 verify(mView, never()).dispatchTouchEvent(any()); 537 } 538 539 @Test handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent()540 public void handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() { 541 when(mCommandQueue.panelsEnabled()).thenReturn(true); 542 when(mView.isEnabled()).thenReturn(false); 543 544 boolean returnVal = mNotificationPanelViewController 545 .getStatusBarTouchEventHandler() 546 .handleTouchEvent( 547 MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); 548 549 assertThat(returnVal).isTrue(); 550 verify(mView, never()).dispatchTouchEvent(any()); 551 } 552 553 @Test handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent()554 public void handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() { 555 when(mCommandQueue.panelsEnabled()).thenReturn(true); 556 when(mView.isEnabled()).thenReturn(false); 557 MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0); 558 559 mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event); 560 561 verify(mView).dispatchTouchEvent(event); 562 } 563 564 @Test handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent()565 public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() { 566 when(mCommandQueue.panelsEnabled()).thenReturn(true); 567 when(mView.isEnabled()).thenReturn(true); 568 MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0); 569 570 mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event); 571 572 verify(mView).dispatchTouchEvent(event); 573 } 574 575 @Test testA11y_initializeNode()576 public void testA11y_initializeNode() { 577 AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo(); 578 mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo); 579 580 List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList(); 581 assertThat(actionList).containsAtLeastElementsIn( 582 new AccessibilityNodeInfo.AccessibilityAction[] { 583 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD, 584 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP} 585 ); 586 } 587 588 @Test testA11y_scrollForward()589 public void testA11y_scrollForward() { 590 mAccessibiltyDelegate.performAccessibilityAction( 591 mView, 592 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(), 593 null); 594 595 verify(mStatusBarKeyguardViewManager).showBouncer(true); 596 } 597 598 @Test testA11y_scrollUp()599 public void testA11y_scrollUp() { 600 mAccessibiltyDelegate.performAccessibilityAction( 601 mView, 602 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(), 603 null); 604 605 verify(mStatusBarKeyguardViewManager).showBouncer(true); 606 } 607 608 @Test testAllChildrenOfNotificationContainer_haveIds()609 public void testAllChildrenOfNotificationContainer_haveIds() { 610 enableSplitShade(/* enabled= */ true); 611 mNotificationContainerParent.removeAllViews(); 612 mNotificationContainerParent.addView(newViewWithId(1)); 613 mNotificationContainerParent.addView(newViewWithId(View.NO_ID)); 614 615 mNotificationPanelViewController.updateResources(); 616 617 assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1); 618 assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID); 619 } 620 621 @Test testSinglePaneShadeLayout_isAlignedToParent()622 public void testSinglePaneShadeLayout_isAlignedToParent() { 623 enableSplitShade(/* enabled= */ false); 624 625 mNotificationPanelViewController.updateResources(); 626 627 assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) 628 .isEqualTo(ConstraintSet.PARENT_ID); 629 assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) 630 .isEqualTo(ConstraintSet.PARENT_ID); 631 } 632 633 @Test testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications()634 public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() { 635 mStatusBarStateController.setState(KEYGUARD); 636 enableSplitShade(/* enabled= */ true); 637 638 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 639 mNotificationPanelViewController.updateResources(); 640 assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) 641 .isEqualTo(R.id.qs_edge_guideline); 642 643 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 644 mNotificationPanelViewController.updateResources(); 645 assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) 646 .isEqualTo(ConstraintSet.PARENT_ID); 647 } 648 649 @Test testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy()650 public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() { 651 givenViewAttached(); 652 when(mResources.getBoolean( 653 com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); 654 updateMultiUserSetting(true); 655 clearInvocations(mView); 656 657 updateMultiUserSetting(false); 658 659 ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class); 660 verify(mView, atLeastOnce()).addView(captor.capture(), anyInt()); 661 final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(), 662 view -> view.getId() == R.id.keyguard_user_switcher_stub); 663 assertThat(userSwitcherStub).isNotNull(); 664 assertThat(userSwitcherStub).isInstanceOf(ViewStub.class); 665 } 666 667 @Test testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView()668 public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() { 669 givenViewAttached(); 670 when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null); 671 updateSmallestScreenWidth(300); 672 when(mResources.getBoolean( 673 com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); 674 when(mUserManager.isUserSwitcherEnabled()).thenReturn(true); 675 676 updateSmallestScreenWidth(800); 677 678 verify(mUserSwitcherStubView).inflate(); 679 } 680 681 @Test testSplitShadeLayout_isAlignedToGuideline()682 public void testSplitShadeLayout_isAlignedToGuideline() { 683 enableSplitShade(/* enabled= */ true); 684 685 mNotificationPanelViewController.updateResources(); 686 687 assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) 688 .isEqualTo(R.id.qs_edge_guideline); 689 assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) 690 .isEqualTo(R.id.qs_edge_guideline); 691 } 692 693 @Test testSinglePaneShadeLayout_childrenHaveConstantWidth()694 public void testSinglePaneShadeLayout_childrenHaveConstantWidth() { 695 enableSplitShade(/* enabled= */ false); 696 697 mNotificationPanelViewController.updateResources(); 698 699 assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth) 700 .isEqualTo(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)); 701 assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth) 702 .isEqualTo(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)); 703 } 704 705 @Test testSplitShadeLayout_childrenHaveZeroWidth()706 public void testSplitShadeLayout_childrenHaveZeroWidth() { 707 enableSplitShade(/* enabled= */ true); 708 709 mNotificationPanelViewController.updateResources(); 710 711 assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0); 712 assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0); 713 } 714 715 @Test testCanCollapsePanelOnTouch_trueForKeyGuard()716 public void testCanCollapsePanelOnTouch_trueForKeyGuard() { 717 mStatusBarStateController.setState(KEYGUARD); 718 719 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 720 } 721 722 @Test testCanCollapsePanelOnTouch_trueWhenScrolledToBottom()723 public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() { 724 mStatusBarStateController.setState(SHADE); 725 when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true); 726 727 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 728 } 729 730 @Test testCanCollapsePanelOnTouch_trueWhenInSettings()731 public void testCanCollapsePanelOnTouch_trueWhenInSettings() { 732 mStatusBarStateController.setState(SHADE); 733 mNotificationPanelViewController.setQsExpanded(true); 734 735 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); 736 } 737 738 @Test testCanCollapsePanelOnTouch_falseInDualPaneShade()739 public void testCanCollapsePanelOnTouch_falseInDualPaneShade() { 740 mStatusBarStateController.setState(SHADE); 741 enableSplitShade(/* enabled= */ true); 742 mNotificationPanelViewController.setQsExpanded(true); 743 744 assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse(); 745 } 746 747 @Test testSwipeWhileLocked_notifiesKeyguardState()748 public void testSwipeWhileLocked_notifiesKeyguardState() { 749 mStatusBarStateController.setState(KEYGUARD); 750 751 // Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that 752 // the fling occurred and did not dismiss the keyguard. 753 mNotificationPanelViewController.flingToHeight( 754 0f, true /* expand */, 1000f, 1f, false); 755 verify(mKeyguardStateController).notifyPanelFlingStart(false /* dismissKeyguard */); 756 757 // Fling un-expanded, which is a keyguard exit fling when we're in KEYGUARD state. 758 mNotificationPanelViewController.flingToHeight( 759 0f, false /* expand */, 1000f, 1f, false); 760 verify(mKeyguardStateController).notifyPanelFlingStart(true /* dismissKeyguard */); 761 } 762 763 @Test testCancelSwipeWhileLocked_notifiesKeyguardState()764 public void testCancelSwipeWhileLocked_notifiesKeyguardState() { 765 mStatusBarStateController.setState(KEYGUARD); 766 767 // Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that 768 // the fling occurred and did not dismiss the keyguard. 769 mNotificationPanelViewController.flingToHeight( 770 0f, true /* expand */, 1000f, 1f, false); 771 mNotificationPanelViewController.cancelHeightAnimator(); 772 verify(mKeyguardStateController).notifyPanelFlingEnd(); 773 } 774 775 @Test testDoubleTapRequired_Keyguard()776 public void testDoubleTapRequired_Keyguard() { 777 FalsingManager.FalsingTapListener listener = getFalsingTapListener(); 778 mStatusBarStateController.setState(KEYGUARD); 779 780 listener.onDoubleTapRequired(); 781 782 verify(mKeyguardIndicationController).showTransientIndication(anyInt()); 783 } 784 785 @Test testDoubleTapRequired_ShadeLocked()786 public void testDoubleTapRequired_ShadeLocked() { 787 FalsingManager.FalsingTapListener listener = getFalsingTapListener(); 788 mStatusBarStateController.setState(SHADE_LOCKED); 789 790 listener.onDoubleTapRequired(); 791 792 verify(mTapAgainViewController).show(); 793 } 794 795 @Test testSwitchesToCorrectClockInSinglePaneShade()796 public void testSwitchesToCorrectClockInSinglePaneShade() { 797 mStatusBarStateController.setState(KEYGUARD); 798 799 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 800 triggerPositionClockAndNotifications(); 801 verify(mKeyguardStatusViewController).displayClock(LARGE); 802 803 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 804 mNotificationPanelViewController.closeQs(); 805 verify(mKeyguardStatusViewController).displayClock(SMALL); 806 } 807 808 @Test testSwitchesToCorrectClockInSplitShade()809 public void testSwitchesToCorrectClockInSplitShade() { 810 mStatusBarStateController.setState(KEYGUARD); 811 enableSplitShade(/* enabled= */ true); 812 813 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 814 triggerPositionClockAndNotifications(); 815 verify(mKeyguardStatusViewController).displayClock(LARGE); 816 817 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 818 triggerPositionClockAndNotifications(); 819 verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE); 820 verify(mKeyguardStatusViewController, never()).displayClock(SMALL); 821 } 822 823 @Test testSwitchesToBigClockInSplitShadeOnAod()824 public void testSwitchesToBigClockInSplitShadeOnAod() { 825 mStatusBarStateController.setState(KEYGUARD); 826 enableSplitShade(/* enabled= */ true); 827 when(mMediaDataManager.hasActiveMedia()).thenReturn(true); 828 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); 829 830 mNotificationPanelViewController.setDozing(true, false, null); 831 832 verify(mKeyguardStatusViewController).displayClock(LARGE); 833 } 834 835 @Test testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying()836 public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() { 837 mStatusBarStateController.setState(KEYGUARD); 838 enableSplitShade(/* enabled= */ true); 839 when(mMediaDataManager.hasActiveMedia()).thenReturn(true); 840 841 // one notification + media player visible 842 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); 843 triggerPositionClockAndNotifications(); 844 verify(mKeyguardStatusViewController).displayClock(SMALL); 845 846 // only media player visible 847 when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); 848 triggerPositionClockAndNotifications(); 849 verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL); 850 verify(mKeyguardStatusViewController, never()).displayClock(LARGE); 851 } 852 triggerPositionClockAndNotifications()853 private void triggerPositionClockAndNotifications() { 854 mNotificationPanelViewController.closeQs(); 855 } 856 getFalsingTapListener()857 private FalsingManager.FalsingTapListener getFalsingTapListener() { 858 for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { 859 listener.onViewAttachedToWindow(mView); 860 } 861 assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); 862 return mFalsingManager.getTapListeners().get(0); 863 } 864 givenViewAttached()865 private void givenViewAttached() { 866 for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { 867 listener.onViewAttachedToWindow(mView); 868 } 869 } 870 newViewWithId(int id)871 private View newViewWithId(int id) { 872 View view = new View(mContext); 873 view.setId(id); 874 ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams( 875 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 876 // required as cloning ConstraintSet fails if view doesn't have layout params 877 view.setLayoutParams(layoutParams); 878 return view; 879 } 880 getConstraintSetLayout(@dRes int id)881 private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { 882 ConstraintSet constraintSet = new ConstraintSet(); 883 constraintSet.clone(mNotificationContainerParent); 884 return constraintSet.getConstraint(id).layout; 885 } 886 enableSplitShade(boolean enabled)887 private void enableSplitShade(boolean enabled) { 888 when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled); 889 mNotificationPanelViewController.updateResources(); 890 } 891 updateMultiUserSetting(boolean enabled)892 private void updateMultiUserSetting(boolean enabled) { 893 when(mUserManager.isUserSwitcherEnabled()).thenReturn(enabled); 894 final ArgumentCaptor<ContentObserver> observerCaptor = 895 ArgumentCaptor.forClass(ContentObserver.class); 896 verify(mContentResolver) 897 .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); 898 observerCaptor.getValue().onChange(/* selfChange */ false); 899 } 900 updateSmallestScreenWidth(int smallestScreenWidthDp)901 private void updateSmallestScreenWidth(int smallestScreenWidthDp) { 902 Configuration configuration = new Configuration(); 903 configuration.smallestScreenWidthDp = smallestScreenWidthDp; 904 mConfigurationController.onConfigurationChanged(configuration); 905 } 906 onTouchEvent(MotionEvent ev)907 private void onTouchEvent(MotionEvent ev) { 908 mTouchHandler.onTouch(mView, ev); 909 } 910 } 911