1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.qs; 16 17 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 18 import static com.android.systemui.statusbar.StatusBarState.SHADE; 19 20 import static com.google.common.truth.Truth.assertThat; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyBoolean; 26 import static org.mockito.ArgumentMatchers.anyInt; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.ArgumentMatchers.nullable; 29 import static org.mockito.Mockito.clearInvocations; 30 import static org.mockito.Mockito.doAnswer; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.spy; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.annotation.Nullable; 37 import android.app.Fragment; 38 import android.content.Context; 39 import android.graphics.Rect; 40 import android.os.Bundle; 41 import android.testing.AndroidTestingRunner; 42 import android.testing.TestableLooper.RunWithLooper; 43 import android.view.LayoutInflater; 44 import android.view.View; 45 import android.view.ViewGroup; 46 47 import androidx.lifecycle.Lifecycle; 48 import androidx.test.filters.SmallTest; 49 50 import com.android.keyguard.BouncerPanelExpansionCalculator; 51 import com.android.systemui.R; 52 import com.android.systemui.SysuiBaseFragmentTest; 53 import com.android.systemui.dump.DumpManager; 54 import com.android.systemui.flags.FeatureFlags; 55 import com.android.systemui.media.controls.ui.MediaHost; 56 import com.android.systemui.qs.customize.QSCustomizerController; 57 import com.android.systemui.qs.dagger.QSFragmentComponent; 58 import com.android.systemui.qs.external.TileServiceRequestController; 59 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; 60 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; 61 import com.android.systemui.qs.logging.QSLogger; 62 import com.android.systemui.settings.FakeDisplayTracker; 63 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; 64 import com.android.systemui.statusbar.CommandQueue; 65 import com.android.systemui.statusbar.StatusBarState; 66 import com.android.systemui.statusbar.SysuiStatusBarStateController; 67 import com.android.systemui.statusbar.phone.KeyguardBypassController; 68 import com.android.systemui.statusbar.policy.ConfigurationController; 69 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; 70 import com.android.systemui.util.animation.UniqueObjectHostView; 71 72 import org.junit.Before; 73 import org.junit.Test; 74 import org.junit.runner.RunWith; 75 import org.mockito.Mock; 76 import org.mockito.MockitoAnnotations; 77 78 @RunWith(AndroidTestingRunner.class) 79 @RunWithLooper(setAsMainLooper = true) 80 @SmallTest 81 public class QSFragmentTest extends SysuiBaseFragmentTest { 82 83 @Mock private QSFragmentComponent.Factory mQsComponentFactory; 84 @Mock private QSFragmentComponent mQsFragmentComponent; 85 @Mock private QSPanelController mQSPanelController; 86 @Mock private MediaHost mQSMediaHost; 87 @Mock private MediaHost mQQSMediaHost; 88 @Mock private KeyguardBypassController mBypassController; 89 @Mock private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder; 90 @Mock private TileServiceRequestController mTileServiceRequestController; 91 @Mock private QSCustomizerController mQsCustomizerController; 92 @Mock private QuickQSPanelController mQuickQSPanelController; 93 @Mock private FooterActionsController mQSFooterActionController; 94 @Mock private QSContainerImplController mQSContainerImplController; 95 @Mock private QSContainerImpl mContainer; 96 @Mock private QSFooter mFooter; 97 @Mock private LayoutInflater mLayoutInflater; 98 @Mock private NonInterceptingScrollView mQSPanelScrollView; 99 @Mock private QuickStatusBarHeader mHeader; 100 @Mock private QSPanel.QSTileLayout mQsTileLayout; 101 @Mock private QSPanel.QSTileLayout mQQsTileLayout; 102 @Mock private QSAnimator mQSAnimator; 103 @Mock private SysuiStatusBarStateController mStatusBarStateController; 104 @Mock private QSSquishinessController mSquishinessController; 105 @Mock private FooterActionsViewModel mFooterActionsViewModel; 106 @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory; 107 @Mock private FooterActionsViewBinder mFooterActionsViewBinder; 108 @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; 109 @Mock private FeatureFlags mFeatureFlags; 110 private View mQsFragmentView; 111 QSFragmentTest()112 public QSFragmentTest() { 113 super(QSFragment.class); 114 } 115 116 @Before setup()117 public void setup() { 118 injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); 119 } 120 121 @Test testListening()122 public void testListening() { 123 QSFragment qs = (QSFragment) mFragment; 124 mFragments.dispatchResume(); 125 processAllMessages(); 126 127 qs.setListening(true); 128 processAllMessages(); 129 130 qs.setListening(false); 131 processAllMessages(); 132 } 133 134 @Test testSaveState()135 public void testSaveState() { 136 mFragments.dispatchResume(); 137 processAllMessages(); 138 139 QSFragment qs = (QSFragment) mFragment; 140 qs.setListening(true); 141 qs.setExpanded(true); 142 qs.setQsVisible(true); 143 processAllMessages(); 144 recreateFragment(); 145 processAllMessages(); 146 147 // Get the reference to the new fragment. 148 qs = (QSFragment) mFragment; 149 assertTrue(qs.isListening()); 150 assertTrue(qs.isExpanded()); 151 assertTrue(qs.isQsVisible()); 152 } 153 154 @Test transitionToFullShade_smallScreen_alphaAlways1()155 public void transitionToFullShade_smallScreen_alphaAlways1() { 156 QSFragment fragment = resumeAndGetFragment(); 157 setIsSmallScreen(); 158 setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE); 159 boolean isTransitioningToFullShade = true; 160 float transitionProgress = 0.5f; 161 float squishinessFraction = 0.5f; 162 163 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 164 squishinessFraction); 165 166 assertThat(mQsFragmentView.getAlpha()).isEqualTo(1f); 167 } 168 169 @Test transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator()170 public void transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator() { 171 QSFragment fragment = resumeAndGetFragment(); 172 setIsLargeScreen(); 173 setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE); 174 boolean isTransitioningToFullShade = true; 175 float transitionProgress = 0.5f; 176 float squishinessFraction = 0.5f; 177 when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f); 178 179 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 180 squishinessFraction); 181 182 assertThat(mQsFragmentView.getAlpha()) 183 .isEqualTo(123f); 184 } 185 186 @Test 187 public void transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator()188 transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() { 189 QSFragment fragment = resumeAndGetFragment(); 190 setStatusBarCurrentAndUpcomingState(KEYGUARD); 191 when(mQSPanelController.isBouncerInTransit()).thenReturn(false); 192 boolean isTransitioningToFullShade = true; 193 float transitionProgress = 0.5f; 194 float squishinessFraction = 0.5f; 195 196 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 197 squishinessFraction); 198 199 assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress); 200 } 201 202 @Test 203 public void transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator()204 transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator() { 205 QSFragment fragment = resumeAndGetFragment(); 206 setStatusBarCurrentAndUpcomingState(KEYGUARD); 207 when(mQSPanelController.isBouncerInTransit()).thenReturn(true); 208 boolean isTransitioningToFullShade = true; 209 float transitionProgress = 0.5f; 210 float squishinessFraction = 0.5f; 211 212 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 213 squishinessFraction); 214 215 assertThat(mQsFragmentView.getAlpha()) 216 .isEqualTo( 217 BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( 218 transitionProgress)); 219 } 220 221 @Test transitionToFullShade_inFullWidth_alwaysSetsAlphaTo1()222 public void transitionToFullShade_inFullWidth_alwaysSetsAlphaTo1() { 223 QSFragment fragment = resumeAndGetFragment(); 224 fragment.setIsNotificationPanelFullWidth(true); 225 226 boolean isTransitioningToFullShade = true; 227 float transitionProgress = 0.1f; 228 float squishinessFraction = 0.5f; 229 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 230 squishinessFraction); 231 assertThat(mQsFragmentView.getAlpha()).isEqualTo(1); 232 233 transitionProgress = 0.5f; 234 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 235 squishinessFraction); 236 assertThat(mQsFragmentView.getAlpha()).isEqualTo(1); 237 assertThat(mQsFragmentView.getAlpha()).isEqualTo(1); 238 239 transitionProgress = 0.7f; 240 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 241 squishinessFraction); 242 assertThat(mQsFragmentView.getAlpha()).isEqualTo(1); 243 } 244 245 @Test transitionToFullShade_setsSquishinessOnController()246 public void transitionToFullShade_setsSquishinessOnController() { 247 QSFragment fragment = resumeAndGetFragment(); 248 boolean isTransitioningToFullShade = true; 249 float transitionProgress = 0.123f; 250 float squishinessFraction = 0.456f; 251 252 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 253 squishinessFraction); 254 255 verify(mQsFragmentComponent.getQSSquishinessController()) 256 .setSquishiness(squishinessFraction); 257 } 258 259 @Test setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction()260 public void setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction() { 261 // Random test values without any meaning. They just have to be different from each other. 262 float expansion = 0.123f; 263 float panelExpansionFraction = 0.321f; 264 float proposedTranslation = 456f; 265 float squishinessFraction = 0.987f; 266 267 QSFragment fragment = resumeAndGetFragment(); 268 enableSplitShade(); 269 270 fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation, 271 squishinessFraction); 272 273 verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged( 274 panelExpansionFraction, /* isInSplitShade= */ true); 275 } 276 277 @Test setQsExpansion_notInSplitShade_setsFooterActionsExpansion_basedOnExpansion()278 public void setQsExpansion_notInSplitShade_setsFooterActionsExpansion_basedOnExpansion() { 279 // Random test values without any meaning. They just have to be different from each other. 280 float expansion = 0.123f; 281 float panelExpansionFraction = 0.321f; 282 float proposedTranslation = 456f; 283 float squishinessFraction = 0.987f; 284 285 QSFragment fragment = resumeAndGetFragment(); 286 disableSplitShade(); 287 288 fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation, 289 squishinessFraction); 290 291 verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged( 292 expansion, /* isInSplitShade= */ false); 293 } 294 295 @Test setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress()296 public void setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress() { 297 QSFragment fragment = resumeAndGetFragment(); 298 enableSplitShade(); 299 when(mStatusBarStateController.getState()).thenReturn(SHADE); 300 when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); 301 boolean isTransitioningToFullShade = false; 302 float transitionProgress = 0; 303 float squishinessFraction = 0f; 304 305 fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, 306 squishinessFraction); 307 308 // trigger alpha refresh with non-zero expansion and fraction values 309 fragment.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1, 310 /* proposedTranslation= */ 0, /* squishinessFraction= */ 1); 311 312 // alpha should follow lockscreen to shade progress, not panel expansion fraction 313 assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress); 314 } 315 316 @Test getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight()317 public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() { 318 QSFragment fragment = resumeAndGetFragment(); 319 disableSplitShade(); 320 when(mHeader.getHeight()).thenReturn(1234); 321 322 int height = fragment.getQsMinExpansionHeight(); 323 324 assertThat(height).isEqualTo(mHeader.getHeight()); 325 } 326 327 @Test getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomOfQSContainer()328 public void getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomOfQSContainer() { 329 int top = 1234; 330 int height = 9876; 331 QSFragment fragment = resumeAndGetFragment(); 332 enableSplitShade(); 333 setLocationOnScreen(mQsFragmentView, top); 334 when(mQsFragmentView.getHeight()).thenReturn(height); 335 336 int expectedHeight = top + height; 337 assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight); 338 } 339 340 @Test getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomExcludingTranslation()341 public void getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomExcludingTranslation() { 342 int top = 1234; 343 int height = 9876; 344 float translationY = -600f; 345 QSFragment fragment = resumeAndGetFragment(); 346 enableSplitShade(); 347 setLocationOnScreen(mQsFragmentView, (int) (top + translationY)); 348 when(mQsFragmentView.getHeight()).thenReturn(height); 349 when(mQsFragmentView.getTranslationY()).thenReturn(translationY); 350 351 int expectedHeight = top + height; 352 assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight); 353 } 354 355 @Test hideImmediately_notInSplitShade_movesViewUpByHeaderHeight()356 public void hideImmediately_notInSplitShade_movesViewUpByHeaderHeight() { 357 QSFragment fragment = resumeAndGetFragment(); 358 disableSplitShade(); 359 when(mHeader.getHeight()).thenReturn(555); 360 361 fragment.hideImmediately(); 362 363 assertThat(mQsFragmentView.getY()).isEqualTo(-mHeader.getHeight()); 364 } 365 366 @Test hideImmediately_inSplitShade_movesViewUpByQSAbsoluteBottom()367 public void hideImmediately_inSplitShade_movesViewUpByQSAbsoluteBottom() { 368 QSFragment fragment = resumeAndGetFragment(); 369 enableSplitShade(); 370 int top = 1234; 371 int height = 9876; 372 setLocationOnScreen(mQsFragmentView, top); 373 when(mQsFragmentView.getHeight()).thenReturn(height); 374 375 fragment.hideImmediately(); 376 377 int qsAbsoluteBottom = top + height; 378 assertThat(mQsFragmentView.getY()).isEqualTo(-qsAbsoluteBottom); 379 } 380 381 @Test setCollapseExpandAction_passedToControllers()382 public void setCollapseExpandAction_passedToControllers() { 383 Runnable action = () -> {}; 384 QSFragment fragment = resumeAndGetFragment(); 385 fragment.setCollapseExpandAction(action); 386 387 verify(mQSPanelController).setCollapseExpandAction(action); 388 verify(mQuickQSPanelController).setCollapseExpandAction(action); 389 } 390 391 @Test setOverScrollAmount_setsTranslationOnView()392 public void setOverScrollAmount_setsTranslationOnView() { 393 QSFragment fragment = resumeAndGetFragment(); 394 395 fragment.setOverScrollAmount(123); 396 397 assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123); 398 } 399 400 @Test setOverScrollAmount_beforeViewCreated_translationIsNotSet()401 public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() { 402 QSFragment fragment = getFragment(); 403 404 fragment.setOverScrollAmount(123); 405 406 assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0); 407 } 408 getListeningAndVisibilityLifecycleState()409 private Lifecycle.State getListeningAndVisibilityLifecycleState() { 410 return getFragment() 411 .getListeningAndVisibilityLifecycleOwner() 412 .getLifecycle() 413 .getCurrentState(); 414 } 415 416 @Test setListeningFalse_notVisible()417 public void setListeningFalse_notVisible() { 418 QSFragment fragment = resumeAndGetFragment(); 419 fragment.setQsVisible(false); 420 clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); 421 422 fragment.setListening(false); 423 verify(mQSContainerImplController).setListening(false); 424 assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED); 425 verify(mQSPanelController).setListening(eq(false), anyBoolean()); 426 } 427 428 @Test setListeningTrue_notVisible()429 public void setListeningTrue_notVisible() { 430 QSFragment fragment = resumeAndGetFragment(); 431 fragment.setQsVisible(false); 432 clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); 433 434 fragment.setListening(true); 435 verify(mQSContainerImplController).setListening(false); 436 assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED); 437 verify(mQSPanelController).setListening(eq(false), anyBoolean()); 438 } 439 440 @Test setListeningFalse_visible()441 public void setListeningFalse_visible() { 442 QSFragment fragment = resumeAndGetFragment(); 443 fragment.setQsVisible(true); 444 clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); 445 446 fragment.setListening(false); 447 verify(mQSContainerImplController).setListening(false); 448 assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED); 449 verify(mQSPanelController).setListening(eq(false), anyBoolean()); 450 } 451 452 @Test setListeningTrue_visible()453 public void setListeningTrue_visible() { 454 QSFragment fragment = resumeAndGetFragment(); 455 fragment.setQsVisible(true); 456 clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); 457 458 fragment.setListening(true); 459 verify(mQSContainerImplController).setListening(true); 460 assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED); 461 verify(mQSPanelController).setListening(eq(true), anyBoolean()); 462 } 463 464 @Test passCorrectExpansionState_inSplitShade()465 public void passCorrectExpansionState_inSplitShade() { 466 QSFragment fragment = resumeAndGetFragment(); 467 enableSplitShade(); 468 clearInvocations(mQSPanelController); 469 470 fragment.setExpanded(true); 471 verify(mQSPanelController).setExpanded(true); 472 473 fragment.setExpanded(false); 474 verify(mQSPanelController).setExpanded(false); 475 } 476 477 @Test startsListeningAfterStateChangeToExpanded_inSplitShade()478 public void startsListeningAfterStateChangeToExpanded_inSplitShade() { 479 QSFragment fragment = resumeAndGetFragment(); 480 enableSplitShade(); 481 fragment.setQsVisible(true); 482 clearInvocations(mQSPanelController); 483 484 fragment.setExpanded(true); 485 verify(mQSPanelController).setListening(true, true); 486 } 487 488 @Test testUpdateQSBounds_setMediaClipCorrectly()489 public void testUpdateQSBounds_setMediaClipCorrectly() { 490 QSFragment fragment = resumeAndGetFragment(); 491 disableSplitShade(); 492 493 Rect mediaHostClip = new Rect(); 494 when(mQSPanelController.getPaddingBottom()).thenReturn(50); 495 setLocationOnScreen(mQSPanelScrollView, 25); 496 when(mQSPanelScrollView.getMeasuredHeight()).thenReturn(200); 497 when(mQSMediaHost.getCurrentClipping()).thenReturn(mediaHostClip); 498 499 fragment.updateQsBounds(); 500 501 assertEquals(25, mediaHostClip.top); 502 assertEquals(175, mediaHostClip.bottom); 503 } 504 505 @Test testQsUpdatesQsAnimatorWithUpcomingState()506 public void testQsUpdatesQsAnimatorWithUpcomingState() { 507 QSFragment fragment = resumeAndGetFragment(); 508 setStatusBarCurrentAndUpcomingState(SHADE); 509 fragment.onUpcomingStateChanged(KEYGUARD); 510 511 verify(mQSAnimator).setOnKeyguard(true); 512 } 513 514 @Override instantiate(Context context, String className, Bundle arguments)515 protected Fragment instantiate(Context context, String className, Bundle arguments) { 516 MockitoAnnotations.initMocks(this); 517 CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context)); 518 519 setupQsComponent(); 520 setUpViews(); 521 setUpInflater(); 522 setUpMedia(); 523 setUpOther(); 524 525 return new QSFragment( 526 new RemoteInputQuickSettingsDisabler( 527 context, commandQueue, mock(ConfigurationController.class)), 528 mStatusBarStateController, 529 commandQueue, 530 mQSMediaHost, 531 mQQSMediaHost, 532 mBypassController, 533 mQsComponentFactory, 534 mock(QSFragmentDisableFlagsLogger.class), 535 mock(DumpManager.class), 536 mock(QSLogger.class), 537 mock(FooterActionsController.class), 538 mFooterActionsViewModelFactory, 539 mFooterActionsViewBinder, 540 mLargeScreenShadeInterpolator, 541 mFeatureFlags); 542 } 543 setUpOther()544 private void setUpOther() { 545 when(mTileServiceRequestControllerBuilder.create(any())) 546 .thenReturn(mTileServiceRequestController); 547 when(mQSContainerImplController.getView()).thenReturn(mContainer); 548 when(mQSPanelController.getTileLayout()).thenReturn(mQQsTileLayout); 549 when(mQuickQSPanelController.getTileLayout()).thenReturn(mQsTileLayout); 550 when(mFooterActionsViewModelFactory.create(any())).thenReturn(mFooterActionsViewModel); 551 } 552 setUpMedia()553 private void setUpMedia() { 554 when(mQSMediaHost.getCurrentClipping()).thenReturn(new Rect()); 555 when(mQSMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(mContext)); 556 when(mQQSMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(mContext)); 557 } 558 setUpViews()559 private void setUpViews() { 560 mQsFragmentView = spy(new View(mContext)); 561 when(mQsFragmentView.findViewById(R.id.expanded_qs_scroll_view)) 562 .thenReturn(mQSPanelScrollView); 563 when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader); 564 when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext)); 565 when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer( 566 invocation -> new FooterActionsViewBinder().create(mContext)); 567 } 568 setUpInflater()569 private void setUpInflater() { 570 LayoutInflater realInflater = LayoutInflater.from(mContext); 571 572 when(mLayoutInflater.cloneInContext(any(Context.class))).thenReturn(mLayoutInflater); 573 when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class), anyBoolean())) 574 .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), 575 (ViewGroup) invocation.getArgument(1), 576 (boolean) invocation.getArgument(2))); 577 when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class))) 578 .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), 579 (ViewGroup) invocation.getArgument(1))); 580 mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater); 581 } 582 inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root)583 private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root) { 584 return inflate(realInflater, layoutRes, root, root != null); 585 } 586 inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root, boolean attachToRoot)587 private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root, 588 boolean attachToRoot) { 589 if (layoutRes == R.layout.footer_actions 590 || layoutRes == R.layout.footer_actions_text_button 591 || layoutRes == R.layout.footer_actions_number_button 592 || layoutRes == R.layout.footer_actions_icon_button) { 593 return realInflater.inflate(layoutRes, root, attachToRoot); 594 } 595 596 return mQsFragmentView; 597 } 598 setupQsComponent()599 private void setupQsComponent() { 600 when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent); 601 when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController); 602 when(mQsFragmentComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController); 603 when(mQsFragmentComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController); 604 when(mQsFragmentComponent.getQSContainerImplController()) 605 .thenReturn(mQSContainerImplController); 606 when(mQsFragmentComponent.getQSFooter()).thenReturn(mFooter); 607 when(mQsFragmentComponent.getQSFooterActionController()) 608 .thenReturn(mQSFooterActionController); 609 when(mQsFragmentComponent.getQSAnimator()).thenReturn(mQSAnimator); 610 when(mQsFragmentComponent.getQSSquishinessController()).thenReturn(mSquishinessController); 611 } 612 getFragment()613 private QSFragment getFragment() { 614 return ((QSFragment) mFragment); 615 } 616 resumeAndGetFragment()617 private QSFragment resumeAndGetFragment() { 618 mFragments.dispatchResume(); 619 processAllMessages(); 620 return getFragment(); 621 } 622 setStatusBarCurrentAndUpcomingState(int statusBarState)623 private void setStatusBarCurrentAndUpcomingState(int statusBarState) { 624 when(mStatusBarStateController.getState()).thenReturn(statusBarState); 625 when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(statusBarState); 626 getFragment().onStateChanged(statusBarState); 627 } 628 enableSplitShade()629 private void enableSplitShade() { 630 setSplitShadeEnabled(true); 631 } 632 disableSplitShade()633 private void disableSplitShade() { 634 setSplitShadeEnabled(false); 635 } 636 setSplitShadeEnabled(boolean enabled)637 private void setSplitShadeEnabled(boolean enabled) { 638 getFragment().setInSplitShade(enabled); 639 } 640 setLocationOnScreen(View view, int top)641 private void setLocationOnScreen(View view, int top) { 642 doAnswer(invocation -> { 643 int[] locationOnScreen = invocation.getArgument(/* index= */ 0); 644 locationOnScreen[0] = 0; 645 locationOnScreen[1] = top; 646 return null; 647 }).when(view).getLocationOnScreen(any(int[].class)); 648 } 649 setIsLargeScreen()650 private void setIsLargeScreen() { 651 getFragment().setIsNotificationPanelFullWidth(false); 652 } 653 setIsSmallScreen()654 private void setIsSmallScreen() { 655 getFragment().setIsNotificationPanelFullWidth(true); 656 } 657 } 658