Show status bar header in SHADE_LOCKED

Makes SHADE_LOCKED more consistent with SHADE in order to avoid
confusion about this special state. Add animations between these
states for smooth transitions.

Bug: 17446409
Change-Id: I410f0ac255abd0f453b77f42c948e814c5dfd838
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2ff64a..b12b90e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -112,6 +112,7 @@
     private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private boolean mDozing;
+    private boolean mDozingOnDown;
     private int mStatusBarState;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -162,7 +163,7 @@
     private Runnable mLaunchAnimationEndRunnable;
     private boolean mOnlyAffordanceInThisMotion;
     private boolean mKeyguardStatusViewAnimating;
-    private boolean mHeaderAnimatingIn;
+    private boolean mHeaderAnimating;
     private ObjectAnimator mQsContainerAnimator;
     private ValueAnimator mQsSizeChangeAnimator;
 
@@ -221,9 +222,8 @@
         // recompute internal state when qspanel height changes
         mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
-            public void onLayoutChange(View v, int left, int top, int right,
-                    int bottom, int oldLeft, int oldTop, int oldRight,
-                    int oldBottom) {
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 final int height = bottom - top;
                 final int oldHeight = oldBottom - oldTop;
                 if (height != oldHeight) {
@@ -500,7 +500,7 @@
         if (mBlockTouches) {
             return false;
         }
-        resetDownStates(event);
+        initDownStates(event);
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -586,10 +586,11 @@
                 && x < stackScrollerX + mNotificationStackScroller.getWidth();
     }
 
-    private void resetDownStates(MotionEvent event) {
+    private void initDownStates(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mOnlyAffordanceInThisMotion = false;
             mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
+            mDozingOnDown = isDozing();
         }
     }
 
@@ -634,7 +635,7 @@
         if (mBlockTouches) {
             return false;
         }
-        resetDownStates(event);
+        initDownStates(event);
         if ((!mIsExpanding || mHintAnimationRunning)
                 && !mQsExpanded
                 && mStatusBar.getBarState() != StatusBarState.SHADE) {
@@ -865,26 +866,28 @@
 
     public void setBarState(int statusBarState, boolean keyguardFadingAway,
             boolean goingToFullShade) {
-        boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD
-                || statusBarState == StatusBarState.SHADE_LOCKED;
-        if (!mKeyguardShowing && keyguardShowing) {
-            setQsTranslation(mQsExpansionHeight);
-            mHeader.setTranslationY(0f);
-        }
+        int oldState = mStatusBarState;
+        boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
         setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
         setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
-        if (goingToFullShade) {
+
+        mStatusBarState = statusBarState;
+        mKeyguardShowing = keyguardShowing;
+
+        if (goingToFullShade || (oldState == StatusBarState.KEYGUARD
+                && statusBarState == StatusBarState.SHADE_LOCKED)) {
             animateKeyguardStatusBarOut();
+            animateHeaderSlidingIn();
+        } else if (oldState == StatusBarState.SHADE_LOCKED
+                && statusBarState == StatusBarState.KEYGUARD) {
+            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            animateHeaderSlidingOut();
         } else {
             mKeyguardStatusBar.setAlpha(1f);
             mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
         }
-        mStatusBarState = statusBarState;
-        mKeyguardShowing = keyguardShowing;
+
         updateQsState();
-        if (goingToFullShade) {
-            animateHeaderSlidingIn();
-        }
     }
 
     private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
@@ -906,7 +909,7 @@
             = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            mHeaderAnimatingIn = false;
+            mHeaderAnimating = false;
             mQsContainerAnimator = null;
             mQsContainer.removeOnLayoutChangeListener(mQsContainerAnimatorUpdater);
         }
@@ -934,10 +937,13 @@
         @Override
         public boolean onPreDraw() {
             getViewTreeObserver().removeOnPreDrawListener(this);
+            long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
+                    ? 0
+                    : mStatusBar.calculateGoingToFullShadeDelay();
             mHeader.setTranslationY(-mHeader.getCollapsedHeight() - mQsPeekHeight);
             mHeader.animate()
                     .translationY(0f)
-                    .setStartDelay(mStatusBar.calculateGoingToFullShadeDelay())
+                    .setStartDelay(delay)
                     .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
                     .setInterpolator(mFastOutSlowInInterpolator)
                     .start();
@@ -946,7 +952,7 @@
                     mQsContainer.getTranslationY(),
                     mHeader.getCollapsedHeight() + mQsPeekHeight - mQsContainer.getHeight()
                             - mQsContainer.getTop());
-            mQsContainerAnimator.setStartDelay(mStatusBar.calculateGoingToFullShadeDelay());
+            mQsContainerAnimator.setStartDelay(delay);
             mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
             mQsContainerAnimator.setInterpolator(mFastOutSlowInInterpolator);
             mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
@@ -955,11 +961,33 @@
             return true;
         }
     };
-    
-    private void animateHeaderSlidingIn() {
-        mHeaderAnimatingIn = true;
-        getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
 
+    private void animateHeaderSlidingIn() {
+        mHeaderAnimating = true;
+        getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+    }
+
+    private void animateHeaderSlidingOut() {
+        mHeaderAnimating = true;
+        mHeader.animate().y(-mHeader.getHeight())
+                .setStartDelay(0)
+                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mHeader.animate().setListener(null);
+                        mHeaderAnimating = false;
+                        updateQsState();
+                    }
+                })
+                .start();
+        mQsContainer.animate()
+                .y(-mQsContainer.getHeight())
+                .setStartDelay(0)
+                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .start();
     }
 
     private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
@@ -974,8 +1002,12 @@
     private void animateKeyguardStatusBarOut() {
         mKeyguardStatusBar.animate()
                 .alpha(0f)
-                .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
-                .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
+                .setStartDelay(mStatusBar.isKeyguardFadingAway()
+                        ? mStatusBar.getKeyguardFadingAwayDelay()
+                        : 0)
+                .setDuration(mStatusBar.isKeyguardFadingAway()
+                        ? mStatusBar.getKeyguardFadingAwayDuration() / 2
+                        : StackStateAnimator.ANIMATION_DURATION_STANDARD)
                 .setInterpolator(PhoneStatusBar.ALPHA_OUT)
                 .setUpdateListener(mStatusBarAnimateAlphaListener)
                 .withEndAction(mAnimateKeyguardStatusBarInvisibleEndRunnable)
@@ -990,13 +1022,13 @@
         }
     };
 
-    private void animateKeyguardStatusBarIn() {
+    private void animateKeyguardStatusBarIn(long duration) {
         mKeyguardStatusBar.setVisibility(View.VISIBLE);
         mKeyguardStatusBar.setAlpha(0f);
         mKeyguardStatusBar.animate()
                 .alpha(1f)
                 .setStartDelay(0)
-                .setDuration(DOZE_ANIMATION_DURATION)
+                .setDuration(duration)
                 .setInterpolator(mDozeAnimationInterpolator)
                 .setUpdateListener(mStatusBarAnimateAlphaListener)
                 .start();
@@ -1076,9 +1108,12 @@
     }
 
     private void updateQsState() {
-        boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
-        mHeader.setVisibility((mQsExpanded || !mKeyguardShowing) ? View.VISIBLE : View.INVISIBLE);
-        mHeader.setExpanded(mKeyguardShowing || (mQsExpanded && !mStackScrollerOverscrolling));
+        boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
+        mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+                ? View.VISIBLE
+                : View.INVISIBLE);
+        mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
+                || (mQsExpanded && !mStackScrollerOverscrolling));
         mNotificationStackScroller.setScrollingEnabled(
                 mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
@@ -1116,6 +1151,10 @@
         if (mKeyguardShowing) {
             updateHeaderKeyguard();
         }
+        if (mStatusBarState == StatusBarState.SHADE_LOCKED
+                || mStatusBarState == StatusBarState.KEYGUARD) {
+            updateKeyguardBottomAreaAlpha();
+        }
         if (mStatusBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled) {
             mQsNavbarScrim.setAlpha(getQsExpansionFraction());
@@ -1156,10 +1195,10 @@
     }
 
     private void setQsTranslation(float height) {
-        if (!mHeaderAnimatingIn) {
+        if (!mHeaderAnimating) {
             mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation());
         }
-        if (mKeyguardShowing) {
+        if (mKeyguardShowing && !mHeaderAnimating) {
             mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0));
         }
     }
@@ -1471,8 +1510,7 @@
      * Hides the header when notifications are colliding with it.
      */
     private void updateHeader() {
-        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
             updateHeaderKeyguard();
         } else {
             updateHeaderShade();
@@ -1481,15 +1519,14 @@
     }
 
     private void updateHeaderShade() {
-        if (!mHeaderAnimatingIn) {
+        if (!mHeaderAnimating) {
             mHeader.setTranslationY(getHeaderTranslation());
         }
         setQsTranslation(mQsExpansionHeight);
     }
 
     private float getHeaderTranslation() {
-        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
             return 0;
         }
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
@@ -1502,30 +1539,42 @@
         return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
     }
 
-    private void updateHeaderKeyguard() {
-        float alphaNotifications;
+    /**
+     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+     *         during swiping up
+     */
+    private float getKeyguardContentsAlpha() {
+        float alpha;
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
 
             // When on Keyguard, we hide the header as soon as the top card of the notification
             // stack scroller is close enough (collision distance) to the bottom of the header.
-            alphaNotifications = getNotificationsTopY()
+            alpha = getNotificationsTopY()
                     /
                     (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
         } else {
 
             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
             // soon as we start translating the stack.
-            alphaNotifications = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
+            alpha = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
         }
-        alphaNotifications = MathUtils.constrain(alphaNotifications, 0, 1);
-        alphaNotifications = (float) Math.pow(alphaNotifications, 0.75);
+        alpha = MathUtils.constrain(alpha, 0, 1);
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
+    private void updateHeaderKeyguard() {
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
-        mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion)
+        mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
                 * mKeyguardStatusBarAnimateAlpha);
-        mKeyguardBottomArea.setAlpha(Math.min(1 - getQsExpansionFraction(), alphaNotifications));
         setQsTranslation(mQsExpansionHeight);
     }
 
+    private void updateKeyguardBottomAreaAlpha() {
+        mKeyguardBottomArea.setAlpha(
+                Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction()));
+    }
+
     private float getNotificationsTopY() {
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
             return getExpandedHeight();
@@ -1756,6 +1805,7 @@
         mSecureCameraLaunchManager.onSwipingStarted();
         requestDisallowInterceptTouchEvent(true);
         mOnlyAffordanceInThisMotion = true;
+        mQsTracking = false;
     }
 
     @Override
@@ -1897,7 +1947,7 @@
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardStatusBar.setVisibility(View.VISIBLE);
             if (animate) {
-                animateKeyguardStatusBarIn();
+                animateKeyguardStatusBarIn(DOZE_ANIMATION_DURATION);
                 mKeyguardBottomArea.startFinishDozeAnimation();
             }
         }
@@ -1947,6 +1997,32 @@
         onEmptySpaceClick(x);
     }
 
+    protected boolean onMiddleClicked() {
+        switch (mStatusBar.getBarState()) {
+            case StatusBarState.KEYGUARD:
+                if (!mDozingOnDown) {
+                    EventLogTags.writeSysuiLockscreenGesture(
+                            EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT,
+                            0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+                    startUnlockHintAnimation();
+                }
+                return true;
+            case StatusBarState.SHADE_LOCKED:
+                if (!mQsExpanded) {
+                    mStatusBar.goToKeyguard();
+                }
+                return true;
+            case StatusBarState.SHADE:
+
+                // This gets called in the middle of the touch handling, where the state is still
+                // that we are tracking the panel. Collapse the panel after this is done.
+                post(mPostCollapseRunnable);
+                return false;
+            default:
+                return true;
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);