blob: d55875994468a174f163d64be8ced01384844fcb [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 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(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,
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 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();
}
}