Merge "DO NOT MERGE. Implement connection error dialogs (1/4)"
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index d21b442..6730e0a 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -21,6 +21,7 @@
 #include <utils/threads.h>
 #include <media/mediascanner.h>
 #include <media/stagefright/StagefrightMediaScanner.h>
+#include <private/media/VideoFrame.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -347,21 +348,21 @@
     }
 
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    char* data = mp->extractAlbumArt(fd);
-    if (!data) {
+    MediaAlbumArt* mediaAlbumArt =
+            reinterpret_cast<MediaAlbumArt*>(mp->extractAlbumArt(fd));
+    if (mediaAlbumArt != NULL) {
         return NULL;
     }
-    jsize len = *((uint32_t*)data);
 
-    jbyteArray array = env->NewByteArray(len);
+    jbyteArray array = env->NewByteArray(mediaAlbumArt->mSize);
     if (array != NULL) {
         jbyte* bytes = env->GetByteArrayElements(array, NULL);
-        memcpy(bytes, data + 4, len);
+        memcpy(bytes, &mediaAlbumArt->mData[0], mediaAlbumArt->mSize);
         env->ReleaseByteArrayElements(array, bytes, 0);
     }
 
 done:
-    free(data);
+    free(mediaAlbumArt);
     // if NewByteArray() returned NULL, an out-of-memory
     // exception will have been raised. I just want to
     // return null in that case.
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 7926d03..34179cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -48,7 +48,6 @@
         View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
         KeyguardPageSwipeHelper.Callback {
 
-    private static final float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;
     private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
 
     private KeyguardPageSwipeHelper mPageSwiper;
@@ -719,6 +718,16 @@
         updateUnlockIcon();
     }
 
+    @Override
+    protected float getOverExpansionAmount() {
+        return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
+    }
+
+    @Override
+    protected float getOverExpansionPixels() {
+        return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
+    }
+
     private void updateUnlockIcon() {
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -805,14 +814,17 @@
     }
 
     @Override
-    protected void onOverExpansionChanged(float overExpansion) {
+    protected void setOverExpansion(float overExpansion, boolean isPixels) {
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
-            float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
-            float expansionChange = overExpansion - mOverExpansion;
-            expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
-            mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
-                    true /* onTop */,
-                    false /* animate */);
+            mNotificationStackScroller.setOnHeightChangedListener(null);
+            if (isPixels) {
+                mNotificationStackScroller.setOverScrolledPixels(
+                        overExpansion, true /* onTop */, false /* animate */);
+            } else {
+                mNotificationStackScroller.setOverScrollAmount(
+                        overExpansion, true /* onTop */, false /* animate */);
+            }
+            mNotificationStackScroller.setOnHeightChangedListener(this);
         }
     }
 
@@ -828,7 +840,10 @@
     @Override
     protected void onTrackingStopped(boolean expand) {
         super.onTrackingStopped(expand);
-        mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, true /* animate */);
+        if (expand) {
+            mNotificationStackScroller.setOverScrolledPixels(
+                    0.0f, true /* onTop */, true /* animate */);
+        }
         if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
             mPageSwiper.showAllIcons(true);
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 89a1907..772d0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -41,7 +41,6 @@
 public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
-    protected float mOverExpansion;
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -61,6 +60,7 @@
     private int mTrackingPointer;
     protected int mTouchSlop;
     protected boolean mHintAnimationRunning;
+    private boolean mOverExpandedBeforeFling;
 
     private ValueAnimator mHeightAnimator;
     private ObjectAnimator mPeekAnimator;
@@ -370,13 +370,12 @@
     protected void fling(float vel, boolean expand) {
         cancelPeek();
         float target = expand ? getMaxPanelHeight() : 0.0f;
-        if (target == mExpandedHeight || mOverExpansion > 0) {
+        if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
             onExpandingFinished();
-            mExpandedHeight = target;
-            mOverExpansion = 0.0f;
             mBar.panelExpansionChanged(this, mExpandedFraction);
             return;
         }
+        mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
         ValueAnimator animator = createHeightAnimator(target);
         if (expand) {
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
@@ -396,8 +395,8 @@
                 onExpandingFinished();
             }
         });
-        animator.start();
         mHeightAnimator = animator;
+        animator.start();
     }
 
     @Override
@@ -433,7 +432,7 @@
 
     public void setExpandedHeight(float height) {
         if (DEBUG) logf("setExpandedHeight(%.1f)", height);
-        setExpandedHeightInternal(height);
+        setExpandedHeightInternal(height + getOverExpansionPixels());
         mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
     }
 
@@ -451,32 +450,39 @@
         // If the user isn't actively poking us, let's update the height
         if (!mTracking && mHeightAnimator == null
                 && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) {
-            setExpandedHeightInternal(currentMaxPanelHeight);
+            setExpandedHeight(currentMaxPanelHeight);
         }
     }
 
     public void setExpandedHeightInternal(float h) {
-        float fh = getMaxPanelHeight();
-        mExpandedHeight = Math.max(0, Math.min(fh, h));
-        float overExpansion = h - fh;
-        overExpansion = Math.max(0, overExpansion);
-        if (overExpansion != mOverExpansion) {
-            onOverExpansionChanged(overExpansion);
-            mOverExpansion = overExpansion;
-        }
-
-        if (DEBUG) {
-            logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
+        float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
+        if (mHeightAnimator == null) {
+            float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
+            if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
+                setOverExpansion(overExpansionPixels, true /* isPixels */);
+            }
+            mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
+        } else {
+            mExpandedHeight = h;
+            if (mOverExpandedBeforeFling) {
+                setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
+            }
         }
 
         onHeightUpdated(mExpandedHeight);
-        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
+        mExpandedFraction = Math.min(1f, fhWithoutOverExpansion == 0
+                ? 0
+                : mExpandedHeight / fhWithoutOverExpansion);
     }
 
-    protected abstract void onOverExpansionChanged(float overExpansion);
+    protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
 
     protected abstract void onHeightUpdated(float expandedHeight);
 
+    protected abstract float getOverExpansionAmount();
+
+    protected abstract float getOverExpansionPixels();
+
     /**
      * This returns the maximum height of the panel. Children should override this if their
      * desired height is not the full height.
@@ -624,7 +630,8 @@
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
-                setExpandedHeight((Float) animation.getAnimatedValue());
+                setExpandedHeightInternal((Float) animation.getAnimatedValue());
+                mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
             }
         });
         return animator;
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 0383706..4d86213 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -52,6 +52,7 @@
     private static final boolean DEBUG = false;
     private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
+    private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
 
     /**
      * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
@@ -1267,11 +1268,14 @@
     }
 
     private float getRubberBandFactor() {
-        return mExpandedInThisMotion
-                ? RUBBER_BAND_FACTOR_AFTER_EXPAND
-                : (mScrolledToTopOnFirstDown
-                    ? 1.0f
-                    : RUBBER_BAND_FACTOR_NORMAL);
+        if (mExpandedInThisMotion) {
+            return RUBBER_BAND_FACTOR_AFTER_EXPAND;
+        } else if (mIsExpansionChanging) {
+            return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
+        } else if (mScrolledToTopOnFirstDown) {
+            return 1.0f;
+        }
+        return RUBBER_BAND_FACTOR_NORMAL;
     }
 
     private void endDrag() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 2edd7d1..225398a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -726,11 +726,21 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float currentOverScroll = (float) animation.getAnimatedValue();
-                mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
-                        false /* cancelAnimators */);
+                mHostLayout.setOverScrollAmount(
+                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
             }
         });
         overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        overScrollAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onTop) {
+                    mTopOverScrollAnimator = null;
+                } else {
+                    mBottomOverScrollAnimator = null;
+                }
+            }
+        });
         overScrollAnimator.start();
         if (onTop) {
             mTopOverScrollAnimator = overScrollAnimator;