blob: 98cd0804bfd2dffc845eb13fc73a35d0d64e532c [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.IdRes;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.List;
import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class NotificationPanelViewControllerTest extends SysuiTestCase {
private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
@Mock
private CentralSurfaces mCentralSurfaces;
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
@Mock
private KeyguardBottomAreaView mQsFrame;
private KeyguardStatusView mKeyguardStatusView;
@Mock
private NotificationIconAreaController mNotificationAreaController;
@Mock
private HeadsUpManagerPhone mHeadsUpManager;
@Mock
private NotificationShelfController mNotificationShelfController;
@Mock
private KeyguardStatusBarView mKeyguardStatusBar;
@Mock
private KeyguardUserSwitcherView mUserSwitcherView;
@Mock
private ViewStub mUserSwitcherStubView;
@Mock
private HeadsUpTouchHelper.Callback mHeadsUpCallback;
@Mock
private KeyguardUpdateMonitor mUpdateMonitor;
@Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private DozeParameters mDozeParameters;
@Mock
private ScreenOffAnimationController mScreenOffAnimationController;
@Mock
private NotificationPanelView mView;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private FeatureFlags mFeatureFlags;
@Mock
private DynamicPrivacyController mDynamicPrivacyController;
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
private DozeLog mDozeLog;
@Mock
private CommandQueue mCommandQueue;
@Mock
private VibratorHelper mVibratorHelper;
@Mock
private LatencyTracker mLatencyTracker;
@Mock
private PowerManager mPowerManager;
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
private MetricsLogger mMetricsLogger;
@Mock
private ActivityManager mActivityManager;
@Mock
private Resources mResources;
@Mock
private Configuration mConfiguration;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@Mock
private KeyguardClockSwitch mKeyguardClockSwitch;
private PanelViewController.TouchHandler mTouchHandler;
private ConfigurationController mConfigurationController;
@Mock
private MediaHierarchyManager mMediaHiearchyManager;
@Mock
private ConversationNotificationManager mConversationNotificationManager;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
@Mock
private KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent;
@Mock
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
@Mock
private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
@Mock
private KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent;
@Mock
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock
private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@Mock
private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock
private KeyguardStatusViewController mKeyguardStatusViewController;
@Mock
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@Mock
private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock
private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock
private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock
private AuthController mAuthController;
@Mock
private ScrimController mScrimController;
@Mock
private MediaDataManager mMediaDataManager;
@Mock
private AmbientState mAmbientState;
@Mock
private UserManager mUserManager;
@Mock
private UiEventLogger mUiEventLogger;
@Mock
private LockIconViewController mLockIconViewController;
@Mock
private KeyguardMediaController mKeyguardMediaController;
@Mock
private PrivacyDotViewController mPrivacyDotViewController;
@Mock
private NavigationModeController mNavigationModeController;
@Mock
private SecureSettings mSecureSettings;
@Mock
private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
@Mock
private ContentResolver mContentResolver;
@Mock
private TapAgainViewController mTapAgainViewController;
@Mock
private KeyguardIndicationController mKeyguardIndicationController;
@Mock
private FragmentService mFragmentService;
@Mock
private FragmentHostManager mFragmentHostManager;
@Mock
private QuickAccessWalletController mQuickAccessWalletController;
@Mock
private QRCodeScannerController mQrCodeScannerController;
@Mock
private NotificationRemoteInputManager mNotificationRemoteInputManager;
@Mock
private RecordingController mRecordingController;
@Mock
private ControlsComponent mControlsComponent;
@Mock
private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock
private DumpManager mDumpManager;
@Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private NotificationsQSContainerController mNotificationsQSContainerController;
@Mock
private QsFrameTranslateController mQsFrameTranslateController;
@Mock
private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock
private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock
private SysUiState mSysUiState;
@Mock
private NotificationListContainer mNotificationListContainer;
@Mock
private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
private ShadeTransitionController mShadeTransitionController;
@Mock
private QS mQs;
@Mock
private View mQsHeader;
@Mock
private ViewParent mViewParent;
@Mock
private ViewTreeObserver mViewTreeObserver;
private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private Handler mMainHandler;
private final PanelExpansionStateManager mPanelExpansionStateManager =
new PanelExpansionStateManager();
private SystemClock mSystemClock;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mSystemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
mInteractionJankMonitor);
mKeyguardStatusView = new KeyguardStatusView(mContext);
mKeyguardStatusView.setId(R.id.keyguard_status_view);
DejankUtils.setImmediate(true);
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
when(mResources.getConfiguration()).thenReturn(mConfiguration);
mConfiguration.orientation = ORIENTATION_PORTRAIT;
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
mDisplayMetrics.density = 100;
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade))
.thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal))
.thenReturn(10);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
mUserSwitcherStubView);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
.thenReturn(mHeadsUpCallback);
when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
mNotificationContainerParent.addView(mKeyguardStatusView);
mNotificationContainerParent.onFinishInflate();
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
when(mKeyguardQsUserSwitchComponentFactory.build(any()))
.thenReturn(mKeyguardQsUserSwitchComponent);
when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController())
.thenReturn(mKeyguardQsUserSwitchController);
when(mKeyguardUserSwitcherComponentFactory.build(any()))
.thenReturn(mKeyguardUserSwitcherComponent);
when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
.thenReturn(mKeyguardUserSwitcherController);
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
doAnswer((Answer<Void>) invocation -> {
mTouchHandler = invocation.getArgument(0);
return null;
}).when(mView).setOnTouchListener(any(PanelViewController.TouchHandler.class));
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController);
mConfigurationController = new ConfigurationControllerImpl(mContext);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
mContext,
coordinator,
mKeyguardBypassController, mHeadsUpManager,
mock(NotificationRoundnessManager.class),
mConfigurationController,
mStatusBarStateController,
mFalsingManager,
mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
mDumpManager);
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
when(mKeyguardStatusBarViewComponentFactory.build(any(), any()))
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
.thenReturn(mKeyguardStatusView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
.thenReturn(mUserSwitcherView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
.thenReturn(mKeyguardBottomArea);
when(mNotificationRemoteInputManager.isRemoteInputActive())
.thenReturn(false);
when(mInteractionJankMonitor.begin(any(), anyInt()))
.thenReturn(true);
when(mInteractionJankMonitor.end(anyInt()))
.thenReturn(true);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mView.getParent()).thenReturn(mViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
mMainHandler = new Handler(Looper.getMainLooper());
mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter();
mNotificationPanelViewController = new NotificationPanelViewController(
mView,
mMainHandler,
mLayoutInflater,
mFeatureFlags,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
mNotificationEntryManager,
mKeyguardStateController,
mStatusBarStateController,
mStatusBarWindowStateController,
mNotificationShadeWindowController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
mMetricsLogger, mActivityManager, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
mNotificationsQSContainerController,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
mKeyguardQsUserSwitchComponentFactory,
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
mLockscreenShadeTransitionController,
mNotificationAreaController,
mAuthController,
mScrimController,
mUserManager,
mMediaDataManager,
mNotificationShadeDepthController,
mAmbientState,
mLockIconViewController,
mKeyguardMediaController,
mPrivacyDotViewController,
mTapAgainViewController,
mNavigationModeController,
mFragmentService,
mContentResolver,
mQuickAccessWalletController,
mQrCodeScannerController,
mRecordingController,
mLargeScreenShadeHeaderController,
mScreenOffAnimationController,
mLockscreenGestureLogger,
mPanelExpansionStateManager,
mNotificationRemoteInputManager,
mSysUIUnfoldComponent,
mControlsComponent,
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
() -> mKeyguardBottomAreaViewController,
mKeyguardUnlockAnimationController,
mNotificationListContainer,
mPanelEventsEmitter,
mNotificationStackSizeCalculator,
mUnlockedScreenOffAnimationController,
mShadeTransitionController,
mSystemClock,
mock(CameraGestureHelper.class));
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
mNotificationPanelViewController.setKeyguardIndicationController(
mKeyguardIndicationController);
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
verify(mView, atLeast(1)).addOnAttachStateChangeListener(
onAttachStateChangeListenerArgumentCaptor.capture());
mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues();
ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor =
ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue();
mNotificationPanelViewController.mStatusBarStateController
.addCallback(mNotificationPanelViewController.mStatusBarStateListener);
mNotificationPanelViewController
.setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
}
@After
public void tearDown() {
mNotificationPanelViewController.cancelHeightAnimator();
mMainHandler.removeCallbacksAndMessages(null);
}
@Test
public void onNotificationHeightChangeWhileOnKeyguardWillComputeMaxKeyguardNotifications() {
mStatusBarStateController.setState(KEYGUARD);
ArgumentCaptor<OnHeightChangedListener> captor =
ArgumentCaptor.forClass(OnHeightChangedListener.class);
verify(mNotificationStackScrollLayoutController)
.setOnHeightChangedListener(captor.capture());
OnHeightChangedListener listener = captor.getValue();
clearInvocations(mNotificationStackSizeCalculator);
listener.onHeightChanged(mock(ExpandableView.class), false);
verify(mNotificationStackSizeCalculator)
.computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
public void onNotificationHeightChangeWhileInShadeWillNotComputeMaxKeyguardNotifications() {
mStatusBarStateController.setState(SHADE);
ArgumentCaptor<OnHeightChangedListener> captor =
ArgumentCaptor.forClass(OnHeightChangedListener.class);
verify(mNotificationStackScrollLayoutController)
.setOnHeightChangedListener(captor.capture());
OnHeightChangedListener listener = captor.getValue();
clearInvocations(mNotificationStackSizeCalculator);
listener.onHeightChanged(mock(ExpandableView.class), false);
verify(mNotificationStackSizeCalculator, never())
.computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
public void computeMaxKeyguardNotifications_lockscreenToShade_returnsExistingMax() {
when(mAmbientState.getFractionToShade()).thenReturn(0.5f);
mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
// computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications())
.isEqualTo(-1);
}
@Test
public void computeMaxKeyguardNotifications_noTransition_updatesMax() {
when(mAmbientState.getFractionToShade()).thenReturn(0f);
mNotificationPanelViewController.setMaxDisplayedNotifications(-1);
// computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value
assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications())
.isNotEqualTo(-1);
}
private void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
int ambientPadding) {
when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
.thenReturn(indicationPadding);
mNotificationPanelViewController.loadDimens();
mNotificationPanelViewController.setAmbientIndicationTop(
/* ambientIndicationTop= */ stackBottom - ambientPadding,
/* ambientTextVisible= */ true);
}
@Test
public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 20,
/* indicationPadding= */ 0,
/* ambientPadding= */ 0);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
.isEqualTo(80);
}
@Test
public void getVerticalSpaceForLockscreenNotifications_useIndicationBottomPadding_returnsSpaceAvailable() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 30,
/* ambientPadding= */ 0);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
.isEqualTo(70);
}
@Test
public void getVerticalSpaceForLockscreenNotifications_useAmbientBottomPadding_returnsSpaceAvailable() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 0,
/* ambientPadding= */ 40);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
.isEqualTo(60);
}
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 20,
/* indicationPadding= */ 0,
/* ambientPadding= */ 0);
when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
}
@Test
public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 30,
/* ambientPadding= */ 0);
when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@Test
public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 0,
/* indicationPadding= */ 0,
/* ambientPadding= */ 40);
when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@Test
public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
setBottomPadding(/* stackScrollLayoutBottom= */ 100,
/* lockIconPadding= */ 10,
/* indicationPadding= */ 8,
/* ambientPadding= */ 0);
when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
}
@Test
public void testSetPanelScrimMinFraction() {
mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
}
@Test
public void testSetDozing_notifiesNsslAndStateController() {
mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */,
null /* touch */);
verify(mNotificationStackScrollLayoutController)
.setDozing(eq(true), eq(false), eq(null));
assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f);
}
@Test
public void testSetExpandedHeight() {
mNotificationPanelViewController.setExpandedHeight(200);
assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
}
@Test
public void testOnTouchEvent_expansionCanBeBlocked() {
onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
0 /* metaState */));
onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
0 /* metaState */));
assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
mNotificationPanelViewController.blockExpansionForCurrentTouch();
onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
0 /* metaState */));
// Expansion should not have changed because it was blocked
assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue();
onTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
0 /* metaState */));
assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
}
@Test
public void handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(false);
boolean returnVal = mNotificationPanelViewController
.getStatusBarTouchEventHandler()
.handleTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
assertThat(returnVal).isFalse();
verify(mView, never()).dispatchTouchEvent(any());
}
@Test
public void handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(false);
boolean returnVal = mNotificationPanelViewController
.getStatusBarTouchEventHandler()
.handleTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
assertThat(returnVal).isTrue();
verify(mView, never()).dispatchTouchEvent(any());
}
@Test
public void handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(false);
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0);
mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
verify(mView).dispatchTouchEvent(event);
}
@Test
public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(true);
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
verify(mView).dispatchTouchEvent(event);
}
@Test
public void testA11y_initializeNode() {
AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList();
assertThat(actionList).containsAtLeastElementsIn(
new AccessibilityNodeInfo.AccessibilityAction[] {
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD,
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP}
);
}
@Test
public void testA11y_scrollForward() {
mAccessibiltyDelegate.performAccessibilityAction(
mView,
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(),
null);
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
@Test
public void testA11y_scrollUp() {
mAccessibiltyDelegate.performAccessibilityAction(
mView,
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(),
null);
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
@Test
public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
mNotificationPanelViewController.updateResources();
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
.isEqualTo(R.id.qs_edge_guideline);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mNotificationPanelViewController.updateResources();
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
.isEqualTo(ConstraintSet.PARENT_ID);
}
@Test
public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
givenViewAttached();
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
updateMultiUserSetting(true);
clearInvocations(mView);
updateMultiUserSetting(false);
ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
verify(mView, atLeastOnce()).addView(captor.capture(), anyInt());
final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(),
view -> view.getId() == R.id.keyguard_user_switcher_stub);
assertThat(userSwitcherStub).isNotNull();
assertThat(userSwitcherStub).isInstanceOf(ViewStub.class);
}
@Test
public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() {
givenViewAttached();
when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null);
updateSmallestScreenWidth(300);
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true);
updateSmallestScreenWidth(800);
verify(mUserSwitcherStubView).inflate();
}
@Test
public void testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView() {
givenViewAttached();
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
.thenReturn(false);
mNotificationPanelViewController.onFinishInflate();
verify(mUserSwitcherStubView, never()).inflate();
}
@Test
public void testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() {
givenViewAttached();
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
.thenReturn(false);
mNotificationPanelViewController.reInflateViews();
verify(mUserSwitcherStubView, never()).inflate();
}
@Test
public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
mStatusBarStateController.setState(KEYGUARD);
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
}
@Test
public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() {
mStatusBarStateController.setState(SHADE);
when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true);
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
}
@Test
public void testCanCollapsePanelOnTouch_trueWhenInSettings() {
mStatusBarStateController.setState(SHADE);
mNotificationPanelViewController.setQsExpanded(true);
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
}
@Test
public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
mStatusBarStateController.setState(SHADE);
enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.setQsExpanded(true);
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
}
@Test
public void testSwipeWhileLocked_notifiesKeyguardState() {
mStatusBarStateController.setState(KEYGUARD);
// Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
// the fling occurred and did not dismiss the keyguard.
mNotificationPanelViewController.flingToHeight(
0f, true /* expand */, 1000f, 1f, false);
verify(mKeyguardStateController).notifyPanelFlingStart(false /* dismissKeyguard */);
// Fling un-expanded, which is a keyguard exit fling when we're in KEYGUARD state.
mNotificationPanelViewController.flingToHeight(
0f, false /* expand */, 1000f, 1f, false);
verify(mKeyguardStateController).notifyPanelFlingStart(true /* dismissKeyguard */);
}
@Test
public void testCancelSwipeWhileLocked_notifiesKeyguardState() {
mStatusBarStateController.setState(KEYGUARD);
// Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
// the fling occurred and did not dismiss the keyguard.
mNotificationPanelViewController.flingToHeight(
0f, true /* expand */, 1000f, 1f, false);
mNotificationPanelViewController.cancelHeightAnimator();
verify(mKeyguardStateController).notifyPanelFlingEnd();
}
@Test
public void testSwipe_exactlyToTarget_notifiesNssl() {
// No over-expansion
mNotificationPanelViewController.setOverExpansion(0f);
// Fling to a target that is equal to the current position (i.e. a no-op fling).
mNotificationPanelViewController.flingToHeight(
0f,
true,
mNotificationPanelViewController.mExpandedHeight,
1f,
false);
// Verify that the NSSL is notified that the panel is *not* flinging.
verify(mNotificationStackScrollLayoutController).setPanelFlinging(false);
}
@Test
public void testDoubleTapRequired_Keyguard() {
FalsingManager.FalsingTapListener listener = getFalsingTapListener();
mStatusBarStateController.setState(KEYGUARD);
listener.onDoubleTapRequired();
verify(mKeyguardIndicationController).showTransientIndication(anyInt());
}
@Test
public void testDoubleTapRequired_ShadeLocked() {
FalsingManager.FalsingTapListener listener = getFalsingTapListener();
mStatusBarStateController.setState(SHADE_LOCKED);
listener.onDoubleTapRequired();
verify(mTapAgainViewController).show();
}
@Test
public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() {
mStatusBarStateController.setState(KEYGUARD);
mNotificationPanelViewController.setQsExpanded(true);
enableSplitShade(true);
assertThat(mStatusBarStateController.getState()).isEqualTo(SHADE_LOCKED);
}
@Test
public void testSwitchesToCorrectClockInSinglePaneShade() {
mStatusBarStateController.setState(KEYGUARD);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
mNotificationPanelViewController.closeQs();
verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
}
@Test
public void testSwitchesToCorrectClockInSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
clearInvocations(mKeyguardStatusViewController);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
verify(mKeyguardStatusViewController, times(2))
.displayClock(LARGE, /* animate */ true);
verify(mKeyguardStatusViewController, never())
.displayClock(SMALL, /* animate */ true);
}
@Test
public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
enableSplitShade(/* enabled= */ true);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
}
@Test
public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
enableSplitShade(/* enabled= */ true);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
}
@Test
public void testHasNotifications_switchesToSmallClockWhenExitingSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
clearInvocations(mKeyguardStatusViewController);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
enableSplitShade(/* enabled= */ false);
verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
}
@Test
public void testNoNotifications_switchesToLargeClockWhenExitingSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
clearInvocations(mKeyguardStatusViewController);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
enableSplitShade(/* enabled= */ false);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
}
@Test
public void testSwitchesToBigClockInSplitShadeOnAod() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
clearInvocations(mKeyguardStatusViewController);
mNotificationPanelViewController.setDozing(true, false, null);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
}
@Test
public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
clearInvocations(mKeyguardStatusViewController);
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
mNotificationPanelViewController.setDozing(true, false, null);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
}
@Test
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
clearInvocations(mKeyguardStatusViewController);
when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
// one notification + media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
// only media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
@Test
public void testLargeScreenHeaderMadeActiveForLargeScreen() {
mStatusBarStateController.setState(SHADE);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
mNotificationPanelViewController.updateResources();
verify(mLargeScreenShadeHeaderController).setActive(true);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
mNotificationPanelViewController.updateResources();
verify(mLargeScreenShadeHeaderController).setActive(false);
}
@Test
public void testExpandWithQsMethodIsUsingLockscreenTransitionController() {
enableSplitShade(/* enabled= */ true);
mStatusBarStateController.setState(KEYGUARD);
mNotificationPanelViewController.expandWithQs();
verify(mLockscreenShadeTransitionController).goToLockedShade(
/* expandedView= */null, /* needsQSAnimation= */false);
}
@Test
public void testUnlockAnimationDoesNotAffectScrim() {
mNotificationPanelViewController.onUnlockHintStarted();
verify(mScrimController).setExpansionAffectsAlpha(false);
mNotificationPanelViewController.onUnlockHintFinished();
verify(mScrimController).setExpansionAffectsAlpha(true);
}
@Test
public void testUnlockHintAnimation_runs_whenNotInPowerSaveMode_andDozeAmountIsZero() {
when(mPowerManager.isPowerSaveMode()).thenReturn(false);
when(mAmbientState.getDozeAmount()).thenReturn(0f);
mNotificationPanelViewController.startUnlockHintAnimation();
assertThat(mNotificationPanelViewController.mHintAnimationRunning).isTrue();
}
@Test
public void testUnlockHintAnimation_doesNotRun_inPowerSaveMode() {
when(mPowerManager.isPowerSaveMode()).thenReturn(true);
mNotificationPanelViewController.startUnlockHintAnimation();
assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse();
}
@Test
public void testUnlockHintAnimation_doesNotRun_whenDozeAmountNotZero() {
when(mPowerManager.isPowerSaveMode()).thenReturn(false);
when(mAmbientState.getDozeAmount()).thenReturn(0.5f);
mNotificationPanelViewController.startUnlockHintAnimation();
assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse();
}
@Test
public void setKeyguardStatusBarAlpha_setsAlphaOnKeyguardStatusBarController() {
float statusBarAlpha = 0.5f;
mNotificationPanelViewController.setKeyguardStatusBarAlpha(statusBarAlpha);
verify(mKeyguardStatusBarViewController).setAlpha(statusBarAlpha);
}
@Test
public void testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade() {
enableSplitShade(/* enabled= */ true);
// set panel state to CLOSED
mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0,
/* expanded= */ false, /* tracking= */ false, /* dragDownPxAmount= */ 0);
assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse();
// change panel state to OPENING
mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0.5f,
/* expanded= */ true, /* tracking= */ true, /* dragDownPxAmount= */ 100);
assertThat(mNotificationPanelViewController.mQsExpandImmediate).isTrue();
}
@Test
public void testQsNotToBeImmediatelyExpandedWhenGoingFromUnlockedToLocked() {
enableSplitShade(/* enabled= */ true);
// set panel state to CLOSED
mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0,
/* expanded= */ false, /* tracking= */ false, /* dragDownPxAmount= */ 0);
// go to lockscreen, which also sets fraction to 1.0f and makes shade "expanded"
mStatusBarStateController.setState(KEYGUARD);
mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1,
/* expanded= */ true, /* tracking= */ true, /* dragDownPxAmount= */ 0);
assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse();
}
@Test
public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
// to make sure shade is in expanded state
mNotificationPanelViewController.startWaitingForOpenPanelGesture();
assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
// switch to split shade from portrait (default state)
enableSplitShade(/* enabled= */ true);
assertThat(mNotificationPanelViewController.isQsExpanded()).isTrue();
// switch to portrait from split shade
enableSplitShade(/* enabled= */ false);
assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
}
@Test
public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() {
mNotificationPanelViewController.mQs = mQs;
when(mQsFrame.getX()).thenReturn(0f);
when(mQsFrame.getWidth()).thenReturn(1000);
when(mQsHeader.getTop()).thenReturn(0);
when(mQsHeader.getBottom()).thenReturn(1000);
PanelViewController.TouchHandler touchHandler =
mNotificationPanelViewController.createTouchHandler();
mNotificationPanelViewController.setExpandedFraction(1f);
touchHandler.onInterceptTouchEvent(
createMotionEvent(/* x= */ 0, /* y= */ 0, MotionEvent.ACTION_DOWN));
touchHandler.onInterceptTouchEvent(
createMotionEvent(/* x= */ 0, /* y= */ 500, MotionEvent.ACTION_MOVE));
assertThat(mNotificationPanelViewController.isQsTracking()).isTrue();
}
@Test
public void interceptTouchEvent_withinQs_shadeExpanded_inSplitShade_doesNotStartQsTracking() {
enableSplitShade(true);
mNotificationPanelViewController.mQs = mQs;
when(mQsFrame.getX()).thenReturn(0f);
when(mQsFrame.getWidth()).thenReturn(1000);
when(mQsHeader.getTop()).thenReturn(0);
when(mQsHeader.getBottom()).thenReturn(1000);
PanelViewController.TouchHandler touchHandler =
mNotificationPanelViewController.createTouchHandler();
mNotificationPanelViewController.setExpandedFraction(1f);
touchHandler.onInterceptTouchEvent(
createMotionEvent(/* x= */ 0, /* y= */ 0, MotionEvent.ACTION_DOWN));
touchHandler.onInterceptTouchEvent(
createMotionEvent(/* x= */ 0, /* y= */ 500, MotionEvent.ACTION_MOVE));
assertThat(mNotificationPanelViewController.isQsTracking()).isFalse();
}
@Test
public void testOnAttachRefreshStatusBarState() {
mStatusBarStateController.setState(KEYGUARD);
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false);
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
}
verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility(
KEYGUARD/*statusBarState*/,
false/*keyguardFadingAway*/,
false/*goingToFullShade*/, SHADE/*oldStatusBarState*/);
}
@Test
public void getMaxPanelHeight_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
int splitShadeFullTransitionDistance = 123456;
enableSplitShade(true);
setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
assertThat(maxPanelHeight).isEqualTo(splitShadeFullTransitionDistance);
}
@Test
public void getMaxPanelHeight_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
mStatusBarStateController.setState(KEYGUARD);
int splitShadeFullTransitionDistance = 123456;
enableSplitShade(true);
setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
}
@Test
public void getMaxPanelHeight_expanding_notSplitShade_returnsNonSplitShadeValue() {
int splitShadeFullTransitionDistance = 123456;
enableSplitShade(false);
setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
}
private static MotionEvent createMotionEvent(int x, int y, int action) {
return MotionEvent.obtain(
/* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
}
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
}
assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1);
return mFalsingManager.getTapListeners().get(0);
}
private void givenViewAttached() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
}
}
private void givenViewDetached() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewDetachedFromWindow(mView);
}
}
private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mNotificationContainerParent);
return constraintSet.getConstraint(id).layout;
}
private void enableSplitShade(boolean enabled) {
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
mNotificationPanelViewController.updateResources();
}
private void updateMultiUserSetting(boolean enabled) {
when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled);
final ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
verify(mContentResolver)
.registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
observerCaptor.getValue().onChange(/* selfChange */ false);
}
private void updateSmallestScreenWidth(int smallestScreenWidthDp) {
Configuration configuration = new Configuration();
configuration.smallestScreenWidthDp = smallestScreenWidthDp;
mConfigurationController.onConfigurationChanged(configuration);
}
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
private void setSplitShadeFullTransitionDistance(int splitShadeFullTransitionDistance) {
when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
.thenReturn(splitShadeFullTransitionDistance);
mNotificationPanelViewController.updateResources();
}
}