Merge "Clock padding and motion adjustments" into pi-dev
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b220091..a1e5b0e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -138,7 +138,7 @@
 
     <!-- Vertical translation of the shelf during animation that happens after the
     notification panel collapses -->
-    <dimen name="shelf_appear_translation">9dp</dimen>
+    <dimen name="shelf_appear_translation">42dp</dimen>
 
     <!-- The amount the content shifts upwards when transforming into the icon -->
     <dimen name="notification_icon_transform_content_shift">32dp</dimen>
@@ -497,9 +497,6 @@
          device. -->
     <dimen name="unlock_move_distance">75dp</dimen>
 
-    <!-- Distance after which the scrim starts fading in when dragging down the quick settings -->
-    <dimen name="notification_scrim_wait_distance">100dp</dimen>
-
     <!-- Move distance for the unlock hint animation on the lockscreen -->
     <dimen name="hint_move_distance">75dp</dimen>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d45c427..458e133d7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -24,6 +24,8 @@
     <item type="id" name="scale_y_animator_tag"/>
     <item type="id" name="top_inset_animator_tag"/>
     <item type="id" name="height_animator_tag"/>
+    <item type="id" name="x_animator_tag"/>
+    <item type="id" name="y_animator_tag"/>
     <item type="id" name="shadow_alpha_animator_tag"/>
     <item type="id" name="translation_x_animator_end_value_tag"/>
     <item type="id" name="translation_y_animator_end_value_tag"/>
@@ -34,6 +36,8 @@
     <item type="id" name="top_inset_animator_end_value_tag"/>
     <item type="id" name="height_animator_end_value_tag"/>
     <item type="id" name="shadow_alpha_animator_end_value_tag"/>
+    <item type="id" name="x_animator_tag_end_value"/>
+    <item type="id" name="y_animator_tag_end_value"/>
     <item type="id" name="translation_x_animator_start_value_tag"/>
     <item type="id" name="translation_y_animator_start_value_tag"/>
     <item type="id" name="translation_z_animator_start_value_tag"/>
@@ -43,6 +47,8 @@
     <item type="id" name="top_inset_animator_start_value_tag"/>
     <item type="id" name="height_animator_start_value_tag"/>
     <item type="id" name="shadow_alpha_animator_start_value_tag"/>
+    <item type="id" name="x_animator_tag_start_value"/>
+    <item type="id" name="y_animator_tag_start_value"/>
     <item type="id" name="doze_saved_filter_tag"/>
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="qs_slash_tag"/>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 6364f5b..44136c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -58,7 +58,7 @@
             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
     private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private static final String TAG = "NotificationShelf";
-    private static final long SHELF_IN_TRANSLATION_DURATION = 220;
+    private static final long SHELF_IN_TRANSLATION_DURATION = 200;
 
     private ViewInvertHelper mViewInvertHelper;
     private boolean mDark;
@@ -157,14 +157,18 @@
 
     public void fadeInTranslating() {
         float translation = mShelfIcons.getTranslationY();
-        mShelfIcons.setTranslationY(translation + mShelfAppearTranslation);
+        mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
         mShelfIcons.setAlpha(0);
         mShelfIcons.animate()
-                .alpha(1)
-                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+                .setInterpolator(Interpolators.DECELERATE_QUINT)
                 .translationY(translation)
                 .setDuration(SHELF_IN_TRANSLATION_DURATION)
                 .start();
+        mShelfIcons.animate()
+                .alpha(1)
+                .setInterpolator(Interpolators.LINEAR)
+                .setDuration(SHELF_IN_TRANSLATION_DURATION)
+                .start();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index d7b211f..75b41ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -20,25 +20,30 @@
 import android.util.Property;
 import android.view.View;
 
-import com.android.systemui.statusbar.stack.AnimationProperties;
+import com.android.systemui.R;
 
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 /**
  * An animatable property of a view. Used with {@link PropertyAnimator}
  */
-public interface AnimatableProperty {
-    int getAnimationStartTag();
+public abstract class AnimatableProperty {
 
-    int getAnimationEndTag();
+    public static final AnimatableProperty X = AnimatableProperty.from(View.X,
+            R.id.x_animator_tag, R.id.x_animator_tag_start_value, R.id.x_animator_tag_end_value);
+    public static final AnimatableProperty Y = AnimatableProperty.from(View.Y,
+            R.id.y_animator_tag, R.id.y_animator_tag_start_value, R.id.y_animator_tag_end_value);
 
-    int getAnimatorTag();
+    public abstract int getAnimationStartTag();
 
-    Property getProperty();
+    public abstract int getAnimationEndTag();
 
-    static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
+    public abstract int getAnimatorTag();
+
+    public abstract Property getProperty();
+
+    public static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
             Function<T, Float> getter, int animatorTag, int startValueTag, int endValueTag) {
         Property<T, Float> property = new FloatProperty<T>(name) {
 
@@ -74,4 +79,29 @@
             }
         };
     }
+
+    public static <T extends View> AnimatableProperty from(Property<T, Float> property,
+            int animatorTag, int startValueTag, int endValueTag) {
+        return new AnimatableProperty() {
+            @Override
+            public int getAnimationStartTag() {
+                return startValueTag;
+            }
+
+            @Override
+            public int getAnimationEndTag() {
+                return endValueTag;
+            }
+
+            @Override
+            public int getAnimatorTag() {
+                return animatorTag;
+            }
+
+            @Override
+            public Property getProperty() {
+                return property;
+            }
+        };
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 92dcc9e..2efcd16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,6 +24,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -115,4 +116,7 @@
         view.setTag(animationEndTag, newEndValue);
     }
 
+    public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
+        return  view.getTag(property.getAnimatorTag()) != null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d9a55c5..042e4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -20,8 +20,8 @@
 
 import android.content.res.Resources;
 import android.util.MathUtils;
-import com.android.keyguard.KeyguardStatusView;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -47,12 +47,6 @@
     private int mClockNotificationsMargin;
 
     /**
-     * Current height of {@link NotificationPanelView}, considering how much the
-     * user collapsed it.
-     */
-    private float mExpandedHeight;
-
-    /**
      * Height of the parent view - display size in px.
      */
     private int mHeight;
@@ -84,9 +78,9 @@
     private int mContainerTopPadding;
 
     /**
-     * @see NotificationPanelView#getMaxPanelHeight()
+     * @see NotificationPanelView#getExpandedFraction()
      */
-    private float mMaxPanelHeight;
+    private float mPanelExpansion;
 
     /**
      * Burn-in prevention x translation.
@@ -140,13 +134,13 @@
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
-            float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
-            float dark, boolean secure, boolean pulsing, int bouncerTop) {
+            float panelExpansion, int parentHeight,
+            int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
+            int bouncerTop) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
-        mExpandedHeight = expandedHeight;
-        mMaxPanelHeight = maxPanelHeight;
+        mPanelExpansion = panelExpansion;
         mHeight = parentHeight;
         mKeyguardStatusHeight = keyguardStatusHeight;
         mDarkAmount = dark;
@@ -171,16 +165,12 @@
         return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
     }
 
-    public int getExpandedClockBottom() {
-        return getExpandedClockPosition() + mKeyguardStatusHeight;
-    }
-
     /**
      * Vertically align the clock and the shade in the available space considering only
      * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
      * @return Clock Y in pixels.
      */
-    private int getExpandedClockPosition() {
+    public int getExpandedClockPosition() {
         final int availableHeight = mMaxShadeBottom - mMinTopMargin;
         final int containerCenter = mMinTopMargin + availableHeight / 2;
 
@@ -212,8 +202,7 @@
                 mMinTopMargin : -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
-        float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
-        shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(shadeExpansion);
+        float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
         final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
 
         return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
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 ca459a5..a0a97c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -20,7 +20,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
@@ -44,7 +43,6 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
@@ -69,8 +67,11 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
@@ -101,6 +102,8 @@
 
     public static final long DOZE_ANIMATION_DURATION = 700;
 
+    private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
+            .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
     private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
             new FloatProperty<NotificationPanelView>("mDarkAmount") {
                 @Override
@@ -122,11 +125,10 @@
     private QS mQs;
     private FrameLayout mQsFrame;
     private KeyguardStatusView mKeyguardStatusView;
-    private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
-    private boolean mAnimateNextTopPaddingChange;
+    private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
     private VelocityTracker mQsVelocityTracker;
@@ -173,9 +175,6 @@
     private int mUnlockMoveDistance;
     private float mEmptyDragAmount;
 
-    private Animator mClockAnimator;
-    private int mClockAnimationTargetX = Integer.MIN_VALUE;
-    private int mClockAnimationTargetY = Integer.MIN_VALUE;
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
@@ -183,7 +182,6 @@
     private boolean mIsExpanding;
 
     private boolean mBlockTouches;
-    private int mNotificationScrimWaitDistance;
     // Used for two finger gesture as well as accessibility shortcut to QS.
     private boolean mQsExpandImmediate;
     private boolean mTwoFingerQsExpandPossible;
@@ -310,8 +308,6 @@
                 getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
         mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
         mClockPositionAlgorithm.loadDimens(getResources());
-        mNotificationScrimWaitDistance =
-                getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
         mQsFalsingThreshold = getResources().getDimensionPixelSize(
                 R.dimen.qs_falsing_threshold);
         mPositionMinSideMargin = getResources().getDimensionPixelSize(
@@ -461,19 +457,19 @@
      */
     private void positionClockAndNotifications() {
         boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+        boolean animateClock = animate || mAnimateNextPositionUpdate;
         int stackScrollerPadding;
         if (mStatusBarState != StatusBarState.KEYGUARD) {
             stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
             +  mQsNotificationTopPadding;
         } else {
-            final int totalHeight = getHeight();
-            final int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+            int totalHeight = getHeight();
+            int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             mClockPositionAlgorithm.setup(
                     mStatusBarMinHeight,
                     totalHeight - bottomPadding,
-                    calculatePanelHeightShade() - mNotificationStackScroller.getTopPadding(),
-                    getExpandedHeight(),
-                    getMaxPanelHeight(),
+                    mNotificationStackScroller.getIntrinsicContentHeight(),
+                    getExpandedFraction(),
                     totalHeight,
                     mKeyguardStatusView.getHeight(),
                     mDarkAmount,
@@ -481,12 +477,10 @@
                     mPulsing,
                     mBouncerTop);
             mClockPositionAlgorithm.run(mClockPositionResult);
-            if (animate || mClockAnimator != null) {
-                startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
-            } else {
-                mKeyguardStatusView.setX(mClockPositionResult.clockX);
-                mKeyguardStatusView.setY(mClockPositionResult.clockY);
-            }
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
+                    mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
+                    mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
@@ -497,6 +491,7 @@
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
         mStackScrollerMeasuringPass = 0;
+        mAnimateNextPositionUpdate = false;
     }
 
     /**
@@ -558,42 +553,6 @@
         positionClockAndNotifications();
     }
 
-    private void startClockAnimation(int x, int y) {
-        if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
-            return;
-        }
-        mClockAnimationTargetX = x;
-        mClockAnimationTargetY = y;
-        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                getViewTreeObserver().removeOnPreDrawListener(this);
-                if (mClockAnimator != null) {
-                    mClockAnimator.removeAllListeners();
-                    mClockAnimator.cancel();
-                }
-                AnimatorSet set = new AnimatorSet();
-                set.play(ObjectAnimator.ofFloat(
-                        mKeyguardStatusView, View.Y, mClockAnimationTargetY))
-                        .with(ObjectAnimator.ofFloat(
-                                mKeyguardStatusView, View.X, mClockAnimationTargetX));
-                mClockAnimator = set;
-                mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-                mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                mClockAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mClockAnimator = null;
-                        mClockAnimationTargetX = Integer.MIN_VALUE;
-                        mClockAnimationTargetY = Integer.MIN_VALUE;
-                    }
-                });
-                mClockAnimator.start();
-                return true;
-            }
-        });
-    }
-
     private void updateClock() {
         if (!mKeyguardStatusViewAnimating) {
             mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
@@ -601,9 +560,9 @@
     }
 
     public void animateToFullShade(long delay) {
-        mAnimateNextTopPaddingChange = true;
         mNotificationStackScroller.goToFullShade(delay);
         requestLayout();
+        mAnimateNextPositionUpdate = true;
     }
 
     public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
@@ -1411,10 +1370,8 @@
 
     protected void requestScrollerTopPaddingUpdate(boolean animate) {
         mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
-                mAnimateNextTopPaddingChange || animate,
-                mKeyguardShowing
+                animate, mKeyguardShowing
                         && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
-        mAnimateNextTopPaddingChange = false;
     }
 
     private void trackMovement(MotionEvent event) {
@@ -1535,7 +1492,7 @@
         if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
             maxHeight = calculatePanelHeightQsExpanded();
         } else {
-            maxHeight = Math.max(calculatePanelHeightShade(), calculatePanelHeightShadeExpanded());
+            maxHeight = calculatePanelHeightShade();
         }
         maxHeight = Math.max(maxHeight, min);
         return maxHeight;
@@ -1606,14 +1563,15 @@
         int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
         int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
         maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
-        return maxHeight;
-    }
 
-    private int calculatePanelHeightShadeExpanded() {
-        return mNotificationStackScroller.getHeight()
-                - mNotificationStackScroller.getEmptyBottomMargin()
-                - mNotificationStackScroller.getTopPadding()
-                + mClockPositionAlgorithm.getExpandedClockBottom();
+        if (mStatusBarState == StatusBarState.KEYGUARD) {
+            int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition()
+                    + mKeyguardStatusView.getHeight()
+                    + mNotificationStackScroller.getIntrinsicContentHeight();
+            return Math.max(maxHeight, minKeyguardPanelBottom);
+        } else {
+            return maxHeight;
+        }
     }
 
     private int calculatePanelHeightQsExpanded() {
@@ -1652,7 +1610,8 @@
 
     private void updateNotificationTranslucency() {
         float alpha = 1f;
-        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp &&
+                !mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = getFadeoutAlpha();
         }
         mNotificationStackScroller.setAlpha(alpha);
@@ -1907,13 +1866,16 @@
         if (view == null && mQsExpanded) {
             return;
         }
+        if (needsAnimation) {
+            mAnimateNextPositionUpdate = true;
+        }
         ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
         ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
                 ? (ExpandableNotificationRow) firstChildNotGone
                 : null;
         if (firstRow != null
                 && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
-            requestScrollerTopPaddingUpdate(false);
+            requestScrollerTopPaddingUpdate(false /* animate */);
         }
         requestPanelHeightUpdate();
     }
@@ -2337,15 +2299,15 @@
             p.setColor(Color.YELLOW);
             canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
                     calculatePanelHeightShade(), p);
-            p.setColor(Color.GRAY);
-            canvas.drawLine(0, calculatePanelHeightShadeExpanded(), getWidth(),
-                    calculatePanelHeightShadeExpanded(), p);
             p.setColor(Color.MAGENTA);
             canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
                     calculateQsTopPadding(), p);
             p.setColor(Color.CYAN);
-            canvas.drawLine(0, mNotificationStackScroller.getTopPadding(), getWidth(),
+            canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(),
                     mNotificationStackScroller.getTopPadding(), p);
+            p.setColor(Color.GRAY);
+            canvas.drawLine(0, mClockPositionResult.clockY, getWidth(),
+                    mClockPositionResult.clockY, p);
         }
     }
 
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 304a499..347a4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -828,7 +828,7 @@
     }
 
     @Override
-    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         mStatusBar.onPanelLaidOut();
         requestPanelHeightUpdate();
@@ -1088,13 +1088,10 @@
         }
         cancelPeek();
         notifyExpandingStarted();
-        startUnlockHintAnimationPhase1(new Runnable() {
-            @Override
-            public void run() {
-                notifyExpandingFinished();
-                onUnlockHintFinished();
-                mHintAnimationRunning = false;
-            }
+        startUnlockHintAnimationPhase1(() -> {
+            notifyExpandingFinished();
+            onUnlockHintFinished();
+            mHintAnimationRunning = false;
         });
         onUnlockHintStarted();
         mHintAnimationRunning = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 7c64811..b1c0a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -164,6 +164,7 @@
 
     private Paint mDebugPaint;
     private int mContentHeight;
+    private int mIntrinsicContentHeight;
     private int mCollapsedSize;
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
@@ -538,15 +539,16 @@
                 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
             }
         } else {
-            float animProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation(1f - mDarkAmount);
-            float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation((1f - mDarkAmount) * 2);
+            float inverseDark = 1 - mDarkAmount;
+            float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
+            float xProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(inverseDark * 2f);
+
             mBackgroundAnimationRect.set(
-                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
-                    (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
-                    (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
-                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
+                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
+                    (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
+                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
             if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
                 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
                         mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
@@ -628,8 +630,12 @@
     }
 
     private void notifyHeightChangeListener(ExpandableView view) {
+        notifyHeightChangeListener(view, false /* needsAnimation */);
+    }
+
+    private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
         if (mOnHeightChangedListener != null) {
-            mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
+            mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
         }
     }
 
@@ -850,7 +856,7 @@
                 mNeedsAnimation =  true;
             }
             requestChildrenUpdate();
-            notifyHeightChangeListener(null);
+            notifyHeightChangeListener(null, animate);
         }
     }
 
@@ -915,6 +921,13 @@
         updateClipping();
     }
 
+    /**
+     * Return the height of the content ignoring the footer.
+     */
+    public int getIntrinsicContentHeight() {
+        return mIntrinsicContentHeight;
+    }
+
     public void updateClipping() {
         boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
@@ -2130,8 +2143,9 @@
 
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
+            boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
             if (expandableView.getVisibility() != View.GONE
-                    && !expandableView.hasNoContentHeight()) {
+                    && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
                 boolean limitReached = maxDisplayedNotifications != -1
                         && numShownItems >= maxDisplayedNotifications;
                 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
@@ -2179,6 +2193,7 @@
                 }
             }
         }
+        mIntrinsicContentHeight = height;
         mContentHeight = height + mTopPadding + mBottomMargin;
         updateScrollability();
         clampScrollPosition();
@@ -3656,7 +3671,7 @@
         updateContentHeight();
         updateScrollPositionOnExpandInBottom(view);
         clampScrollPosition();
-        notifyHeightChangeListener(view);
+        notifyHeightChangeListener(view, needsAnimation);
         ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
                 ? (ExpandableNotificationRow) view
                 : null;
@@ -3970,6 +3985,7 @@
                 mShelf.fadeInTranslating();
             }
         }
+        updateAlgorithmHeightAndPadding();
         updateBackgroundDimming();
         updateAntiBurnInTranslation();
         requestChildrenUpdate();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index a153140..f0ca3ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.ViewState;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -227,4 +228,13 @@
         assertNotNull(animator);
         assertTrue(animator.getListeners().contains(mFinishListener));
     }
+
+    @Test
+    public void testIsAnimating() {
+        mAnimationFilter.reset();
+        mAnimationFilter.animate(mProperty.getProperty());
+        assertFalse(PropertyAnimator.isAnimating(mView, mProperty));
+        PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
+        assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
+    }
 }