| /* |
| * Copyright (C) 2017 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 com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyFloat; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.verifyZeroInteractions; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.res.ColorStateList; |
| import android.graphics.Color; |
| import android.os.Handler; |
| import android.testing.AndroidTestingRunner; |
| import android.testing.TestableLooper; |
| import android.view.ViewGroup; |
| import android.view.ViewTreeObserver; |
| import android.widget.FrameLayout; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.keyguard.KeyguardHostView; |
| import com.android.keyguard.KeyguardSecurityModel; |
| import com.android.keyguard.KeyguardUpdateMonitor; |
| import com.android.keyguard.ViewMediatorCallback; |
| import com.android.systemui.DejankUtils; |
| import com.android.systemui.SysuiTestCase; |
| import com.android.systemui.keyguard.DismissCallbackRegistry; |
| import com.android.systemui.plugins.ActivityStarter.OnDismissAction; |
| import com.android.systemui.plugins.FalsingManager; |
| import com.android.systemui.statusbar.policy.KeyguardStateController; |
| |
| import org.junit.Assert; |
| 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; |
| |
| @SmallTest |
| @RunWith(AndroidTestingRunner.class) |
| @TestableLooper.RunWithLooper |
| public class KeyguardBouncerTest extends SysuiTestCase { |
| |
| @Mock |
| private FalsingManager mFalsingManager; |
| @Mock |
| private ViewMediatorCallback mViewMediatorCallback; |
| @Mock |
| private LockPatternUtils mLockPatternUtils; |
| @Mock |
| private DismissCallbackRegistry mDismissCallbackRegistry; |
| @Mock |
| private KeyguardHostView mKeyguardHostView; |
| @Mock |
| private ViewTreeObserver mViewTreeObserver; |
| @Mock |
| private KeyguardBouncer.BouncerExpansionCallback mExpansionCallback; |
| @Mock |
| private KeyguardUpdateMonitor mKeyguardUpdateMonitor; |
| @Mock |
| private KeyguardStateController mKeyguardStateController; |
| @Mock |
| private KeyguardBypassController mKeyguardBypassController; |
| @Mock |
| private Handler mHandler; |
| @Mock |
| private KeyguardSecurityModel mKeyguardSecurityModel; |
| |
| private KeyguardBouncer mBouncer; |
| |
| @Before |
| public void setup() { |
| com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); |
| MockitoAnnotations.initMocks(this); |
| mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); |
| mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel); |
| when(mKeyguardSecurityModel.getSecurityMode(anyInt())) |
| .thenReturn(KeyguardSecurityModel.SecurityMode.None); |
| DejankUtils.setImmediate(true); |
| final ViewGroup container = new FrameLayout(getContext()); |
| when(mKeyguardHostView.getViewTreeObserver()).thenReturn(mViewTreeObserver); |
| when(mKeyguardHostView.getHeight()).thenReturn(500); |
| mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback, |
| mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager, |
| mExpansionCallback, mKeyguardStateController, mKeyguardUpdateMonitor, |
| mKeyguardBypassController, mHandler) { |
| @Override |
| protected void inflateView() { |
| super.inflateView(); |
| mKeyguardView = mKeyguardHostView; |
| } |
| }; |
| } |
| |
| @Test |
| public void testInflateView_doesntCrash() { |
| mBouncer.inflateView(); |
| } |
| |
| @Test |
| public void testShow_notifiesFalsingManager() { |
| mBouncer.show(true); |
| verify(mFalsingManager).onBouncerShown(); |
| |
| mBouncer.show(true, false); |
| verifyNoMoreInteractions(mFalsingManager); |
| } |
| |
| /** |
| * Regression test: Invisible bouncer when occluded. |
| */ |
| @Test |
| public void testShow_bouncerIsVisible() { |
| // Expand notification panel as if we were in the keyguard. |
| mBouncer.ensureView(); |
| mBouncer.setExpansion(1); |
| |
| reset(mKeyguardHostView); |
| when(mKeyguardHostView.getHeight()).thenReturn(500); |
| |
| mBouncer.show(true); |
| verify(mKeyguardHostView).setAlpha(eq(1f)); |
| verify(mKeyguardHostView).setTranslationY(eq(0f)); |
| } |
| |
| @Test |
| public void testShow_notifiesVisibility() { |
| mBouncer.show(true); |
| verify(mViewMediatorCallback).onBouncerVisiblityChanged(eq(true)); |
| verify(mExpansionCallback).onStartingToShow(); |
| |
| // Not called again when visible |
| reset(mViewMediatorCallback); |
| mBouncer.show(true); |
| verifyNoMoreInteractions(mViewMediatorCallback); |
| } |
| |
| @Test |
| public void testShow_triesToDismissKeyguard() { |
| mBouncer.show(true); |
| verify(mKeyguardHostView).dismiss(anyInt()); |
| } |
| |
| @Test |
| public void testShow_resetsSecuritySelection() { |
| mBouncer.show(false); |
| verify(mKeyguardHostView, never()).showPrimarySecurityScreen(); |
| |
| mBouncer.hide(false); |
| mBouncer.show(true); |
| verify(mKeyguardHostView).showPrimarySecurityScreen(); |
| } |
| |
| @Test |
| public void testShow_animatesKeyguardView() { |
| mBouncer.show(true); |
| verify(mKeyguardHostView).startAppearAnimation(); |
| } |
| |
| @Test |
| public void testShow_showsErrorMessage() { |
| final String errorMessage = "an error message"; |
| when(mViewMediatorCallback.consumeCustomMessage()).thenReturn(errorMessage); |
| mBouncer.show(true); |
| verify(mKeyguardHostView).showErrorMessage(eq(errorMessage)); |
| } |
| |
| @Test |
| public void testSetExpansion_notifiesFalsingManager() { |
| mBouncer.ensureView(); |
| mBouncer.setExpansion(0.5f); |
| |
| mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); |
| verify(mFalsingManager).onBouncerHidden(); |
| verify(mExpansionCallback).onFullyHidden(); |
| |
| mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); |
| verify(mFalsingManager).onBouncerShown(); |
| verify(mExpansionCallback).onFullyShown(); |
| |
| verify(mExpansionCallback, never()).onStartingToHide(); |
| mBouncer.setExpansion(0.9f); |
| verify(mExpansionCallback).onStartingToHide(); |
| } |
| |
| @Test |
| public void testSetExpansion_notifiesKeyguardView() { |
| mBouncer.ensureView(); |
| mBouncer.setExpansion(0.1f); |
| |
| mBouncer.setExpansion(0); |
| verify(mKeyguardHostView).onResume(); |
| } |
| |
| @Test |
| public void testHide_notifiesFalsingManager() { |
| mBouncer.hide(false); |
| verify(mFalsingManager).onBouncerHidden(); |
| } |
| |
| @Test |
| public void testHide_notifiesVisibility() { |
| mBouncer.hide(false); |
| verify(mViewMediatorCallback).onBouncerVisiblityChanged(eq(false)); |
| } |
| |
| @Test |
| public void testHide_notifiesDismissCallbackIfVisible() { |
| mBouncer.hide(false); |
| verifyZeroInteractions(mDismissCallbackRegistry); |
| mBouncer.show(false); |
| mBouncer.hide(false); |
| verify(mDismissCallbackRegistry).notifyDismissCancelled(); |
| } |
| |
| @Test |
| public void testHide_notShowingAnymore() { |
| mBouncer.ensureView(); |
| mBouncer.show(false /* resetSecuritySelection */); |
| mBouncer.hide(false /* destroyViews */); |
| Assert.assertFalse("Not showing", mBouncer.isShowing()); |
| } |
| |
| @Test |
| public void testShowPromptReason_propagates() { |
| mBouncer.ensureView(); |
| mBouncer.showPromptReason(1); |
| verify(mKeyguardHostView).showPromptReason(eq(1)); |
| } |
| |
| @Test |
| public void testShowMessage_propagates() { |
| final String message = "a message"; |
| mBouncer.ensureView(); |
| mBouncer.showMessage(message, ColorStateList.valueOf(Color.GREEN)); |
| verify(mKeyguardHostView).showMessage(eq(message), eq(ColorStateList.valueOf(Color.GREEN))); |
| } |
| |
| @Test |
| public void testShowOnDismissAction_showsBouncer() { |
| final OnDismissAction dismissAction = () -> false; |
| final Runnable cancelAction = () -> {}; |
| mBouncer.showWithDismissAction(dismissAction, cancelAction); |
| verify(mKeyguardHostView).setOnDismissAction(dismissAction, cancelAction); |
| Assert.assertTrue("Should be showing", mBouncer.isShowing()); |
| } |
| |
| @Test |
| public void testStartPreHideAnimation_notifiesView() { |
| final boolean[] ran = {false}; |
| final Runnable r = () -> ran[0] = true; |
| mBouncer.startPreHideAnimation(r); |
| Assert.assertTrue("Callback should have been invoked", ran[0]); |
| |
| ran[0] = false; |
| mBouncer.ensureView(); |
| mBouncer.startPreHideAnimation(r); |
| verify(mKeyguardHostView).startDisappearAnimation(r); |
| Assert.assertFalse("Callback should have been deferred", ran[0]); |
| } |
| |
| @Test |
| public void testIsShowing_animated() { |
| Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing()); |
| mBouncer.show(true /* reset */); |
| Assert.assertTrue("Should be showing", mBouncer.isShowing()); |
| } |
| |
| @Test |
| public void testIsShowing_forSwipeUp() { |
| mBouncer.setExpansion(1f); |
| mBouncer.show(true /* reset */, false /* animated */); |
| Assert.assertFalse("Should only be showing after collapsing notification panel", |
| mBouncer.isShowing()); |
| mBouncer.setExpansion(0f); |
| Assert.assertTrue("Should be showing", mBouncer.isShowing()); |
| } |
| |
| @Test |
| public void testSetExpansion() { |
| mBouncer.ensureView(); |
| mBouncer.setExpansion(0.5f); |
| verify(mKeyguardHostView).setAlpha(anyFloat()); |
| verify(mKeyguardHostView).setTranslationY(anyFloat()); |
| } |
| |
| @Test |
| public void testIsFullscreenBouncer_asksKeyguardView() { |
| mBouncer.ensureView(); |
| mBouncer.isFullscreenBouncer(); |
| verify(mKeyguardHostView).getCurrentSecurityMode(); |
| verify(mKeyguardHostView, never()).getSecurityMode(); |
| } |
| |
| @Test |
| public void testIsHiding_preHideOrHide() { |
| Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway()); |
| mBouncer.startPreHideAnimation(null /* runnable */); |
| Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway()); |
| mBouncer.hide(false /* destroyView */); |
| Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway()); |
| } |
| |
| @Test |
| public void testIsHiding_skipsTranslation() { |
| mBouncer.show(false /* reset */); |
| reset(mKeyguardHostView); |
| mBouncer.startPreHideAnimation(null /* runnable */); |
| mBouncer.setExpansion(0.5f); |
| verify(mKeyguardHostView, never()).setTranslationY(anyFloat()); |
| verify(mKeyguardHostView, never()).setAlpha(anyFloat()); |
| } |
| |
| @Test |
| public void testIsSecure() { |
| Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure()); |
| |
| mBouncer.ensureView(); |
| for (KeyguardSecurityModel.SecurityMode mode : KeyguardSecurityModel.SecurityMode.values()){ |
| reset(mKeyguardHostView); |
| when(mKeyguardHostView.getSecurityMode()).thenReturn(mode); |
| Assert.assertEquals("Security doesn't match for mode: " + mode, |
| mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None); |
| } |
| } |
| |
| @Test |
| public void testIsShowingScrimmed_true() { |
| doAnswer(invocation -> { |
| assertThat(mBouncer.isScrimmed()).isTrue(); |
| return null; |
| }).when(mExpansionCallback).onFullyShown(); |
| mBouncer.show(false /* resetSecuritySelection */, true /* animate */); |
| assertThat(mBouncer.isScrimmed()).isTrue(); |
| mBouncer.hide(false /* destroyView */); |
| assertThat(mBouncer.isScrimmed()).isFalse(); |
| } |
| |
| @Test |
| public void testIsShowingScrimmed_false() { |
| doAnswer(invocation -> { |
| assertThat(mBouncer.isScrimmed()).isFalse(); |
| return null; |
| }).when(mExpansionCallback).onFullyShown(); |
| mBouncer.show(false /* resetSecuritySelection */, false /* animate */); |
| assertThat(mBouncer.isScrimmed()).isFalse(); |
| } |
| |
| @Test |
| public void testWillDismissWithAction() { |
| mBouncer.ensureView(); |
| Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction()); |
| when(mKeyguardHostView.hasDismissActions()).thenReturn(true); |
| Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction()); |
| } |
| |
| @Test |
| public void testShow_delaysIfFaceAuthIsRunning() { |
| when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); |
| mBouncer.show(true /* reset */); |
| |
| ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class); |
| verify(mHandler).postDelayed(showRunnable.capture(), |
| eq(KeyguardBouncer.BOUNCER_FACE_DELAY)); |
| |
| mBouncer.hide(false /* destroyView */); |
| verify(mHandler).removeCallbacks(eq(showRunnable.getValue())); |
| } |
| |
| @Test |
| public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() { |
| when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); |
| when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); |
| mBouncer.show(true /* reset */); |
| |
| verify(mHandler, never()).postDelayed(any(), anyLong()); |
| } |
| |
| @Test |
| public void testRegisterUpdateMonitorCallback() { |
| verify(mKeyguardUpdateMonitor).registerCallback(any()); |
| } |
| |
| @Test |
| public void testInTransit_whenTranslation() { |
| mBouncer.show(true); |
| mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); |
| assertThat(mBouncer.inTransit()).isFalse(); |
| mBouncer.setExpansion(0.5f); |
| assertThat(mBouncer.inTransit()).isTrue(); |
| mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); |
| assertThat(mBouncer.inTransit()).isFalse(); |
| } |
| } |