Improve doze transitions

This change animates the status bar and the bottom area when exiting
doze mode from touch. We also prevent all animations when exiting
from other means, i.e. usually when the power button is pressed, so
we don't have a distracting animation there. In addition, this also
optimizes the scrim animations a bit in terms of interpolation and
duration to make the experience smoother and cleans up some logic.

Bug: 18146441
Bug: 17717584
Change-Id: I495ce480c25de24b6433adebdfe923b637d98f66
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index b566bbc..a956151 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -72,6 +72,10 @@
         return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
     }
 
+    public int getPulseInDelay() {
+        return getInt("doze.pulse.delay.in", R.integer.doze_pulse_delay_in);
+    }
+
     public int getPulseVisibleDuration() {
         return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 598a35c..fddbee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -39,6 +39,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -72,6 +74,8 @@
     private static final Intent INSECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
+    private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
+    private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
 
     private KeyguardAffordanceView mCameraImageView;
     private KeyguardAffordanceView mPhoneImageView;
@@ -92,7 +96,7 @@
     private PhoneStatusBar mPhoneStatusBar;
 
     private final TrustDrawable mTrustDrawable;
-
+    private final Interpolator mLinearOutSlowInInterpolator;
     private int mLastUnlockIconRes = 0;
 
     public KeyguardBottomAreaView(Context context) {
@@ -111,6 +115,8 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mTrustDrawable = new TrustDrawable(mContext);
+        mLinearOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
     }
 
     private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@@ -450,6 +456,35 @@
         }
     }
 
+    public void startFinishDozeAnimation() {
+        long delay = 0;
+        if (mPhoneImageView.getVisibility() == View.VISIBLE) {
+            startFinishDozeAnimationElement(mPhoneImageView, delay);
+            delay += DOZE_ANIMATION_STAGGER_DELAY;
+        }
+        startFinishDozeAnimationElement(mLockIcon, delay);
+        delay += DOZE_ANIMATION_STAGGER_DELAY;
+        if (mCameraImageView.getVisibility() == View.VISIBLE) {
+            startFinishDozeAnimationElement(mCameraImageView, delay);
+        }
+        mIndicationText.setAlpha(0f);
+        mIndicationText.animate()
+                .alpha(1f)
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+    }
+
+    private void startFinishDozeAnimationElement(View element, long delay) {
+        element.setAlpha(0f);
+        element.setTranslationY(element.getHeight() / 2);
+        element.animate()
+                .alpha(1f)
+                .translationY(0f)
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setStartDelay(delay)
+                .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
+    }
+
     private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             post(new Runnable() {
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 bc2196a..0bde7ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -64,7 +64,7 @@
 
     private static final int DOZE_BACKGROUND_COLOR = 0xff000000;
     private static final int TAG_KEY_ANIM = R.id.scrim;
-    private static final long DOZE_BACKGROUND_ANIM_DURATION = ScrimController.ANIMATION_DURATION;
+    public static final long DOZE_ANIMATION_DURATION = 700;
 
     private KeyguardAffordanceHelper mAfforanceHelper;
     private StatusBarHeaderView mHeader;
@@ -132,6 +132,7 @@
 
     private Interpolator mFastOutSlowInInterpolator;
     private Interpolator mFastOutLinearInterpolator;
+    private Interpolator mDozeAnimationInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
     private int mTopPaddingAdjustment;
@@ -167,6 +168,8 @@
     private boolean mQsTouchAboveFalsingThreshold;
     private int mQsFalsingThreshold;
 
+    private float mKeyguardStatusBarAnimateAlpha = 1f;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -199,6 +202,8 @@
                 android.R.interpolator.fast_out_slow_in);
         mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.fast_out_linear_in);
+        mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(getContext(),
+                android.R.interpolator.linear_out_slow_in);
         mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
@@ -909,6 +914,8 @@
         @Override
         public void run() {
             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
+            mKeyguardStatusBar.setAlpha(1f);
+            mKeyguardStatusBarAnimateAlpha = 1f;
         }
     };
 
@@ -918,10 +925,31 @@
                 .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
                 .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
                 .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .setUpdateListener(mStatusBarAnimateAlphaListener)
                 .withEndAction(mAnimateKeyguardStatusBarInvisibleEndRunnable)
                 .start();
     }
 
+    private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            mKeyguardStatusBarAnimateAlpha = mKeyguardStatusBar.getAlpha();
+        }
+    };
+
+    private void animateKeyguardStatusBarIn() {
+        mKeyguardStatusBar.setVisibility(View.VISIBLE);
+        mKeyguardStatusBar.setAlpha(0f);
+        mKeyguardStatusBar.animate()
+                .alpha(1f)
+                .setStartDelay(0)
+                .setDuration(DOZE_ANIMATION_DURATION)
+                .setInterpolator(mDozeAnimationInterpolator)
+                .setUpdateListener(mStatusBarAnimateAlphaListener)
+                .start();
+    }
+
     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1387,7 +1415,8 @@
         alphaNotifications = MathUtils.constrain(alphaNotifications, 0, 1);
         alphaNotifications = (float) Math.pow(alphaNotifications, 0.75);
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
-        mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion));
+        mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion)
+                * mKeyguardStatusBarAnimateAlpha);
         mKeyguardBottomArea.setAlpha(Math.min(1 - getQsExpansionFraction(), alphaNotifications));
         setQsTranslation(mQsExpansionHeight);
     }
@@ -1736,19 +1765,22 @@
         return (1 - t) * start + t * end;
     }
 
-    private void updateKeyguardStatusBarVisibility() {
-        mKeyguardStatusBar.setVisibility(mKeyguardShowing && !mDozing ? VISIBLE : INVISIBLE);
-    }
-
-    public void setDozing(boolean dozing) {
+    public void setDozing(boolean dozing, boolean animate) {
         if (dozing == mDozing) return;
         mDozing = dozing;
         if (mDozing) {
-            setBackgroundColorAlpha(this, DOZE_BACKGROUND_COLOR, 0xff, false /*animate*/);
+            setBackgroundColorAlpha(DOZE_BACKGROUND_COLOR, 0xff, false /*animate*/);
+            mKeyguardStatusBar.setVisibility(View.INVISIBLE);
+            mKeyguardBottomArea.setVisibility(View.INVISIBLE);
         } else {
-            setBackgroundColorAlpha(this, DOZE_BACKGROUND_COLOR, 0, true /*animate*/);
+            setBackgroundColorAlpha(DOZE_BACKGROUND_COLOR, 0, animate);
+            mKeyguardBottomArea.setVisibility(View.VISIBLE);
+            mKeyguardStatusBar.setVisibility(View.VISIBLE);
+            if (animate) {
+                animateKeyguardStatusBarIn();
+                mKeyguardBottomArea.startFinishDozeAnimation();
+            }
         }
-        updateKeyguardStatusBarVisibility();
     }
 
     @Override
@@ -1756,21 +1788,21 @@
         return mDozing;
     }
 
-    private static void setBackgroundColorAlpha(final View target, int rgb, int targetAlpha,
+    private void setBackgroundColorAlpha(int rgb, int targetAlpha,
             boolean animate) {
-        int currentAlpha = getBackgroundAlpha(target);
+        int currentAlpha = getBackgroundAlpha(this);
         if (currentAlpha == targetAlpha) {
             return;
         }
         final int r = Color.red(rgb);
         final int g = Color.green(rgb);
         final int b = Color.blue(rgb);
-        Object runningAnim = target.getTag(TAG_KEY_ANIM);
+        Object runningAnim = getTag(TAG_KEY_ANIM);
         if (runningAnim instanceof ValueAnimator) {
             ((ValueAnimator) runningAnim).cancel();
         }
         if (!animate) {
-            target.setBackgroundColor(Color.argb(targetAlpha, r, g, b));
+            setBackgroundColor(Color.argb(targetAlpha, r, g, b));
             return;
         }
         ValueAnimator anim = ValueAnimator.ofInt(currentAlpha, targetAlpha);
@@ -1778,18 +1810,19 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 int value = (int) animation.getAnimatedValue();
-                target.setBackgroundColor(Color.argb(value, r, g, b));
+                setBackgroundColor(Color.argb(value, r, g, b));
             }
         });
-        anim.setDuration(DOZE_BACKGROUND_ANIM_DURATION);
+        anim.setInterpolator(mDozeAnimationInterpolator);
+        anim.setDuration(DOZE_ANIMATION_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                target.setTag(TAG_KEY_ANIM, null);
+                setTag(TAG_KEY_ANIM, null);
             }
         });
         anim.start();
-        target.setTag(TAG_KEY_ANIM, anim);
+        setTag(TAG_KEY_ANIM, anim);
     }
 
     private static int getBackgroundAlpha(View view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9a33a36..eb48754 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3681,15 +3681,13 @@
         if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
             return;
         }
-        mNotificationPanel.setDozing(mDozing);
+        mNotificationPanel.setDozing(mDozing, mScrimController.isPulsing() /*animate*/);
         if (mDozing) {
-            mKeyguardBottomArea.setVisibility(View.INVISIBLE);
             mStackScroller.setDark(true, false /*animate*/);
         } else {
-            mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mStackScroller.setDark(false, false /*animate*/);
         }
-        mScrimController.setDozing(mDozing);
+        mScrimController.setDozing(mDozing, mScrimController.isPulsing() /*animate*/);
     }
 
     public void updateStackScrollerState(boolean goingToFullShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 54adbf4..10d6594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -28,6 +28,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeHost;
@@ -70,9 +71,12 @@
     private Runnable mOnAnimationFinished;
     private boolean mAnimationStarted;
     private boolean mDozing;
+    private boolean mPulsingOut;
     private DozeHost.PulseCallback mPulseCallback;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
     private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
+    private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
     private BackDropView mBackDropView;
     private boolean mScrimSrcEnabled;
 
@@ -130,14 +134,12 @@
         scheduleUpdate();
     }
 
-    public void setDozing(boolean dozing) {
+    public void setDozing(boolean dozing, boolean animate) {
         if (mDozing == dozing) return;
         mDozing = dozing;
         if (!mDozing) {
             cancelPulsing();
-            mAnimateChange = true;
-        } else {
-            mAnimateChange = false;
+            mAnimateChange = animate;
         }
         scheduleUpdate();
     }
@@ -181,6 +183,7 @@
     }
 
     private void pulseFinished() {
+        mPulsingOut = false;
         if (mPulseCallback != null) {
             mPulseCallback.onPulseFinished();
             mPulseCallback = null;
@@ -220,8 +223,12 @@
         } else if (mBouncerShowing) {
             setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
             setScrimBehindColor(0f);
+        } else if (mDozing && isPulsing() && !mPulsingOut) {
+            setScrimInFrontColor(0);
+            setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
         } else if (mDozing) {
             setScrimInFrontColor(1);
+            setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
         } else {
             float fraction = Math.max(0, Math.min(mFraction, 1));
             setScrimInFrontColor(0f);
@@ -276,7 +283,7 @@
     private void startScrimAnimation(final ScrimView scrim, int targetColor) {
         int current = Color.alpha(scrim.getScrimColor());
         int target = Color.alpha(targetColor);
-        if (current == targetColor) {
+        if (current == target) {
             return;
         }
         ValueAnimator anim = ValueAnimator.ofInt(current, target);
@@ -287,9 +294,7 @@
                 scrim.setScrimColor(Color.argb(value, 0, 0, 0));
             }
         });
-        anim.setInterpolator(mAnimateKeyguardFadingOut
-                ? mLinearOutSlowInInterpolator
-                : mInterpolator);
+        anim.setInterpolator(getInterpolator());
         anim.setStartDelay(mAnimationDelay);
         anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
@@ -307,6 +312,18 @@
         mAnimationStarted = true;
     }
 
+    private Interpolator getInterpolator() {
+       if (mAnimateKeyguardFadingOut) {
+           return mLinearOutSlowInInterpolator;
+       } else if (isPulsing() && !mPulsingOut) {
+           return mPulseInInterpolator;
+       } else if (isPulsing()) {
+           return mPulseOutInterpolator;
+       } else {
+           return mInterpolator;
+       }
+    }
+
     @Override
     public boolean onPreDraw() {
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
@@ -332,10 +349,10 @@
             if (!mDozing) return;
             DozeLog.tracePulseStart();
             mDurationOverride = mDozeParameters.getPulseInDuration();
-            mAnimationDelay = 0;
+            mAnimationDelay = mDozeParameters.getPulseInDelay();
             mAnimateChange = true;
             mOnAnimationFinished = mPulseInFinished;
-            setScrimColor(mScrimInFront, 0);
+            scheduleUpdate();
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
@@ -357,10 +374,10 @@
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
             mDurationOverride = mDozeParameters.getPulseOutDuration();
-            mAnimationDelay = 0;
             mAnimateChange = true;
             mOnAnimationFinished = mPulseOutFinished;
-            setScrimColor(mScrimInFront, 1);
+            mPulsingOut = true;
+            scheduleUpdate();
         }
     };