diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 48eb3e84a4f5e..c74d09d93fc41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -54,8 +54,8 @@ public class KeyguardBouncer { private static final String TAG = "KeyguardBouncer"; static final float ALPHA_EXPANSION_THRESHOLD = 0.95f; - private static final float EXPANSION_HIDDEN = 1f; - private static final float EXPANSION_VISIBLE = 0f; + static final float EXPANSION_HIDDEN = 1f; + static final float EXPANSION_VISIBLE = 0f; protected final Context mContext; protected final ViewMediatorCallback mCallback; @@ -86,6 +86,7 @@ public class KeyguardBouncer { private boolean mShowingSoon; private int mBouncerPromptReason; private boolean mIsAnimatingAway; + private boolean mIsScrimmed; public KeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, @@ -103,17 +104,17 @@ public class KeyguardBouncer { } public void show(boolean resetSecuritySelection) { - show(resetSecuritySelection, true /* animated */); + show(resetSecuritySelection, true /* scrimmed */); } /** * Shows the bouncer. * * @param resetSecuritySelection Cleans keyguard view - * @param animated true when the bouncer show show animated, false when the user will be - * dragging it and animation should be deferred. + * @param isScrimmed true when the bouncer show show scrimmed, false when the user will be + * dragging it and translation should be deferred. */ - public void show(boolean resetSecuritySelection, boolean animated) { + public void show(boolean resetSecuritySelection, boolean isScrimmed) { final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser(); if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) { // In split system user mode, we never unlock system user. @@ -126,9 +127,10 @@ public class KeyguardBouncer { // are valid. // Later, at the end of the animation, when the bouncer is at the top of the screen, // onFullyShown() will be called and FalsingManager will stop recording touches. - if (animated) { + if (isScrimmed) { setExpansion(EXPANSION_VISIBLE); } + mIsScrimmed = isScrimmed; if (resetSecuritySelection) { // showPrimarySecurityScreen() updates the current security method. This is needed in @@ -164,6 +166,10 @@ public class KeyguardBouncer { mCallback.onBouncerVisiblityChanged(true /* shown */); } + public boolean isShowingScrimmed() { + return isShowing() && mIsScrimmed; + } + /** * This method must be called at the end of the bouncer animation when * the translation is performed manually by the user, otherwise FalsingManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 347a4b04554b7..5b3b59b8b05c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -947,17 +947,6 @@ public abstract class PanelView extends FrameLayout { return mClosing || mLaunchingNotification; } - /** - * Bouncer might need a scrim when you double tap on notifications or edit QS. - * On other cases, when you drag up the bouncer with the finger or just fling, - * the scrim should be hidden to avoid occluding the clock. - * - * @return true when we need a scrim to show content on top of the notification panel. - */ - public boolean needsScrimming() { - return !isTracking() && !isCollapsing() && !isFullyCollapsed(); - } - public boolean isTracking() { return mTracking; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9eeb4d250d995..38a1c9ef1dd54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3977,12 +3977,12 @@ public class StatusBar extends SystemUI implements DemoMode, private void showBouncerIfKeyguard() { if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) && !mKeyguardViewMediator.isHiding()) { - showBouncer(true /* animated */); + showBouncer(true /* scrimmed */); } } - protected void showBouncer(boolean animated) { - mStatusBarKeyguardViewManager.showBouncer(animated); + protected void showBouncer(boolean scrimmed) { + mStatusBarKeyguardViewManager.showBouncer(scrimmed); } private void instantExpandNotificationsPanel() { @@ -4096,7 +4096,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onTrackingStopped(boolean expand) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { if (!expand && !mUnlockMethodCache.canSkipBouncer()) { - showBouncer(false /* animated */); + showBouncer(false /* scrimmed */); } } } @@ -4235,7 +4235,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) { mLeaveOpenOnKeyguardHide = true; - showBouncer(true /* animated */); + showBouncer(true /* scrimmed */); mPendingRemoteInputView = clicked; } @@ -4705,7 +4705,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. - ScrimState state = mIsOccluded || mNotificationPanel.needsScrimming() + ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() || mStatusBarKeyguardViewManager.willDismissWithAction() || mStatusBarKeyguardViewManager.isFullscreenBouncer() ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b517d11640375..6a6a7dd08120f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -31,6 +31,7 @@ import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; @@ -158,7 +159,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mNotificationPanelView.setBouncerTop(mBouncer.getTop()); } - private void onPanelExpansionChanged(float expansion, boolean tracking) { + @VisibleForTesting + void onPanelExpansionChanged(float expansion, boolean tracking) { // We don't want to translate the bounce when: // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to // conserve the original animation. @@ -166,13 +168,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY // • Full-screen user switcher is displayed. if (mNotificationPanelView.isUnlockHintRunning()) { - mBouncer.setExpansion(1); - } else if (mOccluded || mBouncer.willDismissWithAction() + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } else if (mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed() || mStatusBar.isFullScreenUserSwitcherState()) { - mBouncer.setExpansion(0); + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else if (mShowing && !mDozing) { mBouncer.setExpansion(expansion); - if (expansion != 1 && tracking && mStatusBar.isKeyguardCurrentlySecure() + if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking + && mStatusBar.isKeyguardCurrentlySecure() && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { mBouncer.show(false /* resetSecuritySelection */, false /* animated */); } @@ -215,9 +218,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb cancelPendingWakeupAction(); } - public void showBouncer(boolean animated) { - if (mShowing) { - mBouncer.show(false /* resetSecuritySelection */, animated); + public void showBouncer(boolean scrimmed) { + if (mShowing && !mBouncer.isShowing()) { + mBouncer.show(false /* resetSecuritySelection */, scrimmed); } updateStates(); } @@ -725,6 +728,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer.willDismissWithAction(); } + public boolean bouncerNeedsScrimming() { + return mBouncer.isShowingScrimmed(); + } + public void dump(PrintWriter pw) { pw.println("StatusBarKeyguardViewManager:"); pw.println(" mShowing: " + mShowing); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 73f05c44f734b..12b14c8079173 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -325,6 +327,14 @@ public class KeyguardBouncerTest extends SysuiTestCase { } } + @Test + public void testIsShowingScrimmed() { + mBouncer.show(false /* resetSecuritySelection */, true /* animate */); + assertThat(mBouncer.isShowingScrimmed()).isTrue(); + mBouncer.show(false /* resetSecuritySelection */, false /* animate */); + assertThat(mBouncer.isShowingScrimmed()).isFalse(); + } + @Test public void testWillDismissWithAction() { mBouncer.ensureView(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java new file mode 100644 index 0000000000000..e2e5b32798542 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -0,0 +1,183 @@ +/* + * 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 com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.ViewGroup; + +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardHostView; +import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.DismissCallbackRegistry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { + + @Mock + private ViewMediatorCallback mViewMediatorCallback; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private KeyguardBouncer mBouncer; + @Mock + private StatusBar mStatusBar; + @Mock + private ViewGroup mContainer; + @Mock + private NotificationPanelView mNotificationPanelView; + @Mock + private FingerprintUnlockController mFingerprintUnlockController; + @Mock + private DismissCallbackRegistry mDismissCallbackRegistry; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mDependency.injectMockDependency(StatusBarWindowManager.class); + mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(), + mViewMediatorCallback, mLockPatternUtils); + mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer, + mNotificationPanelView, mFingerprintUnlockController, mDismissCallbackRegistry); + mStatusBarKeyguardViewManager.show(null); + } + + @Test + public void dismissWithAction_AfterKeyguardGoneSetToFalse() { + KeyguardHostView.OnDismissAction action = () -> false; + Runnable cancelAction = () -> {}; + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + false /* afterKeyguardGone */); + verify(mBouncer).showWithDismissAction(eq(action), eq(cancelAction)); + } + + @Test + public void showBouncer_onlyWhenShowing() { + mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); + mStatusBar.showBouncer(true /* scrimmed */); + verify(mBouncer, never()).show(anyBoolean(), anyBoolean()); + verify(mBouncer, never()).show(anyBoolean()); + } + + @Test + public void showBouncer_notWhenBouncerAlreadyShowing() { + mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); + when(mBouncer.isSecure()).thenReturn(true); + mStatusBar.showBouncer(true /* scrimmed */); + verify(mBouncer, never()).show(anyBoolean(), anyBoolean()); + verify(mBouncer, never()).show(anyBoolean()); + } + + @Test + public void showBouncer_showsTheBouncer() { + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); + verify(mBouncer).show(anyBoolean(), eq(true)); + } + + @Test + public void onPanelExpansionChanged_neverHidesFullscreenBouncer() { + // TODO: StatusBar should not be here, mBouncer.isFullscreenBouncer() should do the same. + when(mStatusBar.isFullScreenUserSwitcherState()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE)); + } + + @Test + public void onPanelExpansionChanged_neverHidesScrimmedBouncer() { + when(mBouncer.isShowingScrimmed()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE)); + } + + @Test + public void onPanelExpansionChanged_neverShowsDuringHintAnimation() { + when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN)); + } + + @Test + public void onPanelExpansionChanged_propagatesToBouncer() { + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer).setExpansion(eq(0.5f)); + } + + @Test + public void onPanelExpansionChanged_showsBouncerWhenSwiping() { + when(mStatusBar.isKeyguardCurrentlySecure()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */); + verify(mBouncer).show(eq(false), eq(false)); + + // But not when it's already visible + reset(mBouncer); + when(mBouncer.isShowing()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, true /* tracking */); + verify(mBouncer, never()).show(eq(false), eq(false)); + + // Or animating away + reset(mBouncer); + when(mBouncer.isAnimatingAway()).thenReturn(true); + mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, true /* tracking */); + verify(mBouncer, never()).show(eq(false), eq(false)); + } + + private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { + + public TestableStatusBarKeyguardViewManager(Context context, + ViewMediatorCallback callback, + LockPatternUtils lockPatternUtils) { + super(context, callback, lockPatternUtils); + } + + @Override + public void registerStatusBar(StatusBar statusBar, ViewGroup container, + NotificationPanelView notificationPanelView, + FingerprintUnlockController fingerprintUnlockController, + DismissCallbackRegistry dismissCallbackRegistry) { + super.registerStatusBar(statusBar, container, notificationPanelView, + fingerprintUnlockController, dismissCallbackRegistry); + mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer; + } + } +} \ No newline at end of file