DO NOT MERGE Move clock and top padding while dismissing Keyguard.

Such translation. Much alpha. Very parallax. Wow.

Bug: 15163546
Change-Id: Id419acced94083cf6cd7abebbcf1d5a08439b0a7
(cherry picked from commit c42bdc3ba9493efe68b9ed39fbd55c84ba175185)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
new file mode 100644
index 0000000..6a83a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 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 android.content.res.Resources;
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.R;
+
+/**
+ * Utility class to calculate the clock position and top padding of notifications on Keyguard.
+ */
+public class KeyguardClockPositionAlgorithm {
+
+    private static final float SLOW_DOWN_FACTOR = 0.4f;
+
+    private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
+    private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
+
+    private int mClockNotificationsMarginMin;
+    private int mClockNotificationsMarginMax;
+    private float mClockYFractionMin;
+    private float mClockYFractionMax;
+    private int mMaxKeyguardNotifications;
+    private int mMaxPanelHeight;
+    private float mExpandedHeight;
+    private int mNotificationCount;
+    private int mHeight;
+    private int mKeyguardStatusHeight;
+
+    /**
+     * The number (fractional) of notifications the "more" card counts when calculating how many
+     * notifications are currently visible for the y positioning of the clock.
+     */
+    private float mMoreCardNotificationAmount;
+
+    private static final PathInterpolator sSlowDownInterpolator;
+
+    static {
+        Path path = new Path();
+        path.moveTo(0, 0);
+        path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
+        sSlowDownInterpolator = new PathInterpolator(path);
+    }
+
+    /**
+     * Refreshes the dimension values.
+     */
+    public void loadDimens(Resources res) {
+        mClockNotificationsMarginMin = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_min);
+        mClockNotificationsMarginMax = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_max);
+        mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
+        mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
+        mMoreCardNotificationAmount =
+                (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) /
+                        res.getDimensionPixelSize(R.dimen.notification_min_height);
+    }
+
+    public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
+            int notificationCount, int height, int keyguardStatusHeight) {
+        mMaxKeyguardNotifications = maxKeyguardNotifications;
+        mMaxPanelHeight = maxPanelHeight;
+        mExpandedHeight = expandedHeight;
+        mNotificationCount = notificationCount;
+        mHeight = height;
+        mKeyguardStatusHeight = keyguardStatusHeight;
+    }
+
+    public void run(Result result) {
+        int y = getClockY() - mKeyguardStatusHeight/2;
+        float clockAdjustment = getClockYExpansionAdjustment();
+        float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
+        result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+        int clockNotificationsPadding = getClockNotificationsPadding()
+                + result.stackScrollerPaddingAdjustment;
+        int padding = y + clockNotificationsPadding;
+        y += clockAdjustment;
+        result.clockY = y;
+        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
+        result.clockAlpha = getClockAlpha(result.stackScrollerPadding
+                - (y + mKeyguardStatusHeight));
+    }
+
+    private int getClockNotificationsPadding() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
+    }
+
+    private float getClockYFraction() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+    }
+
+    private int getClockY() {
+        return (int) (getClockYFraction() * mHeight);
+    }
+
+    private float getClockYExpansionAdjustment() {
+        float rubberbandFactor = getClockYExpansionRubberbandFactor();
+        float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
+        float t = value / mMaxPanelHeight;
+        float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
+                * mMaxPanelHeight;
+        if (mNotificationCount == 0) {
+            return (-2*value + slowedDownValue)/3;
+        } else {
+            return slowedDownValue;
+        }
+    }
+
+    private float getClockYExpansionRubberbandFactor() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        t = (float) Math.pow(t, 0.3f);
+        return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
+    }
+
+    private float getTopPaddingAdjMultiplier() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
+                + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
+    }
+
+    private float getClockAlpha(int clockNotificationPadding) {
+        float t = getNotificationAmountT();
+        t = (float) Math.pow(t, 0.3f);
+        float multiplier = 1 + 2 * (1 - t);
+        float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+        return Math.max(0, Math.min(1, alpha));
+    }
+
+    /**
+     * @return a value from 0 to 1 depending on how many notification there are
+     */
+    private float getNotificationAmountT() {
+        return mNotificationCount
+                / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
+    }
+
+    public static class Result {
+
+        /**
+         * The y translation of the clock.
+         */
+        public int clockY;
+
+        /**
+         * The alpha value of the clock.
+         */
+        public float clockAlpha;
+
+        /**
+         * The top padding of the stack scroller, in pixels.
+         */
+        public int stackScrollerPadding;
+
+        /**
+         * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
+         * the padding, but not the overall panel size.
+         */
+        public int stackScrollerPaddingAdjustment;
+    }
+}
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 123a4f0..fe7546d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,10 +19,11 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Path;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -31,6 +32,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
@@ -82,19 +84,14 @@
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
 
-    private int mClockNotificationsMarginMin;
-    private int mClockNotificationsMarginMax;
-    private float mClockYFractionMin;
-    private float mClockYFractionMax;
     private Interpolator mFastOutSlowInInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
-
-    /**
-     * The number (fractional) of notifications the "more" card counts when calculating how many
-     * notifications are currently visible for the y positioning of the clock.
-     */
-    private float mMoreCardNotificationAmount;
+    private int mTopPaddingAdjustment;
+    private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
+            new KeyguardClockPositionAlgorithm();
+    private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
+            new KeyguardClockPositionAlgorithm.Result();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -139,20 +136,10 @@
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
         mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
-        mClockNotificationsMarginMin = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_min);
-        mClockNotificationsMarginMax = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_max);
-        mClockYFractionMin =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
-        mClockYFractionMax =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
-        mMoreCardNotificationAmount =
-                (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) /
-                        getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
+        mClockPositionAlgorithm.loadDimens(getResources());
     }
 
     @Override
@@ -160,6 +147,7 @@
         super.onLayout(changed, left, top, right, bottom);
         if (!mQsExpanded) {
             positionClockAndNotifications();
+            mNotificationStackScroller.setStackHeight(getExpandedHeight());
         }
 
         // Calculate quick setting heights.
@@ -178,16 +166,24 @@
         boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding;
+            mTopPaddingAdjustment = 0;
         } else {
-            int notificationCount = mNotificationStackScroller.getNotGoneChildCount();
-            int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2;
-            int padding = getClockNotificationsPadding(notificationCount);
+            mClockPositionAlgorithm.setup(
+                    mStatusBar.getMaxKeyguardNotifications(),
+                    getMaxPanelHeight(),
+                    getExpandedHeight(),
+                    mNotificationStackScroller.getNotGoneChildCount(),
+                    getHeight(),
+                    mKeyguardStatusView.getHeight());
+            mClockPositionAlgorithm.run(mClockPositionResult);
             if (animateClock || mClockAnimator != null) {
-                startClockAnimation(y);
+                startClockAnimation(mClockPositionResult.clockY);
             } else {
-                mKeyguardStatusView.setY(y);
+                mKeyguardStatusView.setY(mClockPositionResult.clockY);
             }
-            mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding;
+            applyClockAlpha(mClockPositionResult.clockAlpha);
+            mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+            mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
                 mAnimateNextTopPaddingChange || animateClock);
@@ -224,22 +220,13 @@
         });
     }
 
-    private int getClockNotificationsPadding(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
-    }
-
-    private float getClockYFraction(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
-    }
-
-    private int getClockY(int notificationCount) {
-        return (int) (getClockYFraction(notificationCount) * getHeight());
+    private void applyClockAlpha(float alpha) {
+        if (alpha != 1.0f) {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        } else {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
+        }
+        mKeyguardStatusView.setAlpha(alpha);
     }
 
     public void animateToFullShade() {
@@ -626,7 +613,7 @@
         int emptyBottomMargin = mStackScrollerContainer.getHeight()
                 - mNotificationStackScroller.getHeight()
                 + mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = maxPanelHeight - emptyBottomMargin;
+        int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
         maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
         return maxHeight;
     }
@@ -637,6 +624,9 @@
 
     @Override
     protected void onHeightUpdated(float expandedHeight) {
+        if (!mQsExpanded) {
+            positionClockAndNotifications();
+        }
         mNotificationStackScroller.setStackHeight(expandedHeight);
     }
 
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 b6a43a7..8631e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -402,7 +402,7 @@
 
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
-        mExpandedHeight = h;
+        mExpandedHeight = Math.min(fh, h);
 
         if (DEBUG) {
             logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");