Layout notifications on top and introduce artifical margin

This allows for animating the notifications between the different
states. So we can nicely switch between Keyguard and full shade,
and make the stack smaller when going to quick settings.

Change-Id: I768dc7cd8f4dc0197365a5befebad24086816a8d
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 24ccb2b..761ad42 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -54,51 +54,45 @@
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <LinearLayout
+    <include layout="@layout/status_bar_expanded_header"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/notification_panel_header_height"
+        />
+
+    <include
+        layout="@layout/keyguard_status_view"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/emergency_calls_only"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:padding="4dp"
+        android:gravity="center"
+        android:visibility="gone"
+        />
+
+    <FrameLayout
+        android:id="@+id/notification_container_parent"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/close_handle_underlap"
-        android:orientation="vertical"
-        android:animateLayoutChanges="false"
         >
-
-        <include layout="@layout/status_bar_expanded_header"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_panel_header_height"
-            />
-
         <include
-            layout="@layout/keyguard_status_view"
-            android:visibility="gone" />
-
-        <TextView
-            android:id="@+id/emergency_calls_only"
-            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
-            android:layout_height="wrap_content"
+            layout="@layout/flip_settings"
+            android:layout_marginTop="@dimen/notification_panel_header_height"
             android:layout_width="match_parent"
-            android:padding="4dp"
-            android:gravity="center"
-            android:visibility="gone"
+            android:layout_height="wrap_content"
             />
 
-        <FrameLayout
-            android:id="@+id/notification_container_parent"
+        <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
+            android:id="@+id/notification_stack_scroller"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            >
-            <include
-                layout="@layout/flip_settings"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                />
-
-            <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
-                    android:id="@+id/notification_stack_scroller"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    />
-        </FrameLayout>
-    </LinearLayout>
+            />
+    </FrameLayout>
 
     <include
         layout="@layout/keyguard_bottom_area"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0fac8c0..9837d9b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -266,4 +266,6 @@
 
     <dimen name="quick_settings_tmp_scrim_stroke_width">8dp</dimen>
     <dimen name="quick_settings_tmp_scrim_text_size">30dp</dimen>
+
+    <dimen name="notifications_top_padding">8dp</dimen>
 </resources>
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 82fbb16..712eec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -36,11 +36,8 @@
     private View mKeyguardStatusView;
 
     private NotificationStackScrollLayout mNotificationStackScroller;
-    private int[] mTempLocation = new int[2];
-    private int[] mTempChildLocation = new int[2];
-    private View mNotificationParent;
     private boolean mTrackingSettings;
-    private float mExpandedHeight = -1;
+    private int mNotificationTopPadding;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -70,7 +67,18 @@
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
-        mNotificationParent = findViewById(R.id.notification_container_parent);
+        mNotificationTopPadding = getResources().getDimensionPixelSize(
+                R.dimen.notifications_top_padding);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        int keyguardBottomMargin =
+                ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin;
+        mNotificationStackScroller.setTopPadding(mStatusBar.isOnKeyguard()
+                ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
+                : mHeader.getBottom() + mNotificationTopPadding);
     }
 
     @Override
@@ -95,18 +103,6 @@
         return super.dispatchPopulateAccessibilityEvent(event);
     }
 
-    /**
-     * Gets the relative position of a view on the screen in regard to this view.
-     *
-     * @param requestedView the view we want to find the relative position for
-     * @return
-     */
-    private int getRelativeTop(View requestedView) {
-        getLocationOnScreen(mTempLocation);
-        requestedView.getLocationOnScreen(mTempChildLocation);
-        return mTempChildLocation[1] - mTempLocation[1];
-    }
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
         // intercept for quick settings
@@ -167,42 +163,7 @@
 
     @Override
     protected void onHeightUpdated(float expandedHeight) {
-        updateNotificationStackHeight(expandedHeight);
-    }
-
-    /**
-     * Update the height of the {@link #mNotificationStackScroller} to the new expanded height.
-     * This is much more efficient than doing it over the layout pass.
-     *
-     * @param expandedHeight the new expanded height
-     */
-    private void updateNotificationStackHeight(float expandedHeight) {
-        if (mExpandedHeight == expandedHeight) return;
-        mExpandedHeight = expandedHeight;
-        mNotificationStackScroller.setIsExpanded(expandedHeight > 0.0f);
-        float childOffset = getRelativeTop(mNotificationStackScroller)
-                - mNotificationParent.getTranslationY();
-        int newStackHeight = (int) (expandedHeight - childOffset);
-        int itemHeight = mNotificationStackScroller.getItemHeight();
-        int bottomStackPeekSize = mNotificationStackScroller.getBottomStackPeekSize();
-        int minStackHeight = itemHeight + bottomStackPeekSize;
-        if (newStackHeight >= minStackHeight) {
-            mNotificationParent.setTranslationY(0);
-            mNotificationStackScroller.setCurrentStackHeight(newStackHeight);
-        } else {
-
-            // We did not reach the position yet where we actually start growing,
-            // so we translate the stack upwards.
-            int translationY = (newStackHeight - minStackHeight);
-            // A slight parallax effect is introduced in order for the stack to catch up with
-            // the top card.
-            float partiallyThere = (float) newStackHeight / minStackHeight;
-            partiallyThere = Math.max(0, partiallyThere);
-            translationY += (1 - partiallyThere) * bottomStackPeekSize;
-            mNotificationParent.setTranslationY(translationY);
-            mNotificationStackScroller.setCurrentStackHeight(
-                    (int) (expandedHeight - (childOffset + translationY)));
-        }
+        mNotificationStackScroller.setStackHeight(expandedHeight);
     }
 
     @Override
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 2e30594..9a43e37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -83,6 +83,7 @@
     private int mBottomStackPeekSize;
     private int mEmptyMarginBottom;
     private int mPaddingBetweenElements;
+    private int mTopPadding;
     private boolean mListenForHeightChanges = true;
 
     /**
@@ -228,11 +229,12 @@
 
     private void setMaxLayoutHeight(int maxLayoutHeight) {
         mMaxLayoutHeight = maxLayoutHeight;
-        updateAlgorithmHeight();
+        updateAlgorithmHeightAndPadding();
     }
 
-    private void updateAlgorithmHeight() {
+    private void updateAlgorithmHeightAndPadding() {
         mStackScrollAlgorithm.setLayoutHeight(getLayoutHeight());
+        mStackScrollAlgorithm.setTopPadding(mTopPadding);
     }
 
     /**
@@ -263,10 +265,52 @@
         }
     }
 
-    public void setCurrentStackHeight(int currentStackHeight) {
-        this.mCurrentStackHeight = currentStackHeight;
-        updateAlgorithmHeight();
-        updateChildren();
+    public int getTopPadding() {
+        return mTopPadding;
+    }
+
+    public void setTopPadding(int topPadding) {
+        if (mTopPadding != topPadding) {
+            mTopPadding = topPadding;
+            updateAlgorithmHeightAndPadding();
+            updateContentHeight();
+            updateChildren();
+        }
+    }
+
+    /**
+     * Update the height of the stack to a new height.
+     *
+     * @param height the new height of the stack
+     */
+    public void setStackHeight(float height) {
+        setIsExpanded(height > 0.0f);
+        int newStackHeight = (int) height;
+        int itemHeight = getItemHeight();
+        int bottomStackPeekSize = mBottomStackPeekSize;
+        int minStackHeight = itemHeight + bottomStackPeekSize;
+        int stackHeight;
+        if (newStackHeight - mTopPadding >= minStackHeight) {
+            setTranslationY(0);
+            stackHeight = newStackHeight;
+        } else {
+
+            // We did not reach the position yet where we actually start growing,
+            // so we translate the stack upwards.
+            int translationY = (newStackHeight - minStackHeight);
+            // A slight parallax effect is introduced in order for the stack to catch up with
+            // the top card.
+            float partiallyThere = (float) (newStackHeight - mTopPadding) / minStackHeight;
+            partiallyThere = Math.max(0, partiallyThere);
+            translationY += (1 - partiallyThere) * bottomStackPeekSize;
+            setTranslationY(translationY - mTopPadding);
+            stackHeight = (int) (height - (translationY - mTopPadding));
+        }
+        if (stackHeight != mCurrentStackHeight) {
+            mCurrentStackHeight = stackHeight;
+            updateAlgorithmHeightAndPadding();
+            updateChildren();
+        }
     }
 
     /**
@@ -691,7 +735,7 @@
                 }
             }
         }
-        mContentHeight = height;
+        mContentHeight = height + mTopPadding;
     }
 
     /**
@@ -1007,7 +1051,7 @@
         mStackScrollAlgorithm.onExpansionStopped();
     }
 
-    public void setIsExpanded(boolean isExpanded) {
+    private void setIsExpanded(boolean isExpanded) {
         mIsExpanded = isExpanded;
         mStackScrollAlgorithm.setIsExpanded(isExpanded);
         if (!isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index acd1c6a..9bde673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -50,6 +50,10 @@
     private StackIndentationFunctor mBottomStackIndentationFunctor;
 
     private int mLayoutHeight;
+
+    /** mLayoutHeight - mTopPadding */
+    private int mInnerHeight;
+    private int mTopPadding;
     private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
     private boolean mIsExpansionChanging;
     private int mFirstChildMaxHeight;
@@ -144,7 +148,7 @@
             StackScrollAlgorithmState algorithmState) {
 
         // The starting position of the bottom stack peek
-        float bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
+        float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
 
         // The position where the bottom stack starts.
         float bottomStackStart = bottomPeekStart - mCollapsedSize;
@@ -226,6 +230,8 @@
             }
             currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
             yPositionInScrollView = yPositionInScrollViewAfterElement;
+
+            childViewState.yTranslation += mTopPadding;
         }
     }
 
@@ -250,7 +256,7 @@
     private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState,
             int childHeight) {
         childViewState.yTranslation = Math.min(childViewState.yTranslation,
-                mLayoutHeight - mBottomStackPeekSize - childHeight);
+                mInnerHeight - mBottomStackPeekSize - childHeight);
     }
 
     /**
@@ -318,7 +324,7 @@
                 childViewState.alpha = 1.0f - algorithmState.partialInBottom;
             }
             childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
-            currentYPosition = mLayoutHeight;
+            currentYPosition = mInnerHeight;
         }
         childViewState.yTranslation = currentYPosition - childHeight;
         clampPositionToTopStackEnd(childViewState, childHeight);
@@ -396,7 +402,7 @@
                 if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
 
                     // The starting position of the bottom stack peek
-                    int bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
+                    int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
                     // Collapse and expand the first child while the shade is being expanded
                     float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
                             ? mFirstChildMaxHeight
@@ -475,12 +481,18 @@
         }
     }
 
-    public int getLayoutHeight() {
-        return mLayoutHeight;
-    }
-
     public void setLayoutHeight(int layoutHeight) {
         this.mLayoutHeight = layoutHeight;
+        updateInnerHeight();
+    }
+
+    public void setTopPadding(int topPadding) {
+        mTopPadding = topPadding;
+        updateInnerHeight();
+    }
+
+    private void updateInnerHeight() {
+        mInnerHeight = mLayoutHeight - mTopPadding;
     }
 
     public void onExpansionStarted(StackScrollState currentState) {