Do not allow bouncer translation when scrimmed

We had special cases when the bouncer shouldn't be translated but that
doesn't really scale. It's much simpler to just check if it's being
scrimmed - because otherwse it mus be translated.

Change-Id: Ide0af6718f1792ef1a1e16fa39512a2b3a0ba8be
Bug: 78222122
Fixes: 78205990
Fixes: 78140990
Test: atest packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
Test: atest packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
Test: go/sysui-bouncer-tests
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 48eb3e8..c74d09d 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 @@
 
     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 @@
     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 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 @@
         // 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 @@
         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 347a4b0..5b3b59b 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 @@
         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 9eeb4d2..38a1c9e 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 @@
     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 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 @@
     @Override
     public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
         mLeaveOpenOnKeyguardHide = true;
-        showBouncer(true /* animated */);
+        showBouncer(true /* scrimmed */);
         mPendingRemoteInputView = clicked;
     }
 
@@ -4705,7 +4705,7 @@
             // 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 b517d11..6a6a7dd 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.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 @@
         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 @@
         // • 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 @@
         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 @@
         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 73f05c4..12b14c8 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;
@@ -326,6 +328,14 @@
     }
 
     @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();
         Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
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 0000000..e2e5b32
--- /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