Merge "resolved conflicts for merge of 4f342e87 to master"
diff --git a/api/current.txt b/api/current.txt
index 759b8d0..a543cbf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -865,6 +865,7 @@
     field public static final int permissionFlags = 16843719; // 0x10103c7
     field public static final int permissionGroup = 16842762; // 0x101000a
     field public static final int permissionGroupFlags = 16843717; // 0x10103c5
+    field public static final int persistable = 16843832; // 0x1010438
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -3262,6 +3263,7 @@
     method public void reportFullyDrawn();
     method public final boolean requestWindowFeature(int);
     method public final void runOnUiThread(java.lang.Runnable);
+    method public void setActivityLabelAndIcon(java.lang.CharSequence, android.graphics.Bitmap);
     method public void setContentTransitionManager(android.transition.TransitionManager);
     method public void setContentView(int);
     method public void setContentView(android.view.View);
@@ -3278,8 +3280,6 @@
     method public final void setProgressBarIndeterminate(boolean);
     method public final void setProgressBarIndeterminateVisibility(boolean);
     method public final void setProgressBarVisibility(boolean);
-    method public void setRecentsIcon(android.graphics.Bitmap);
-    method public void setRecentsLabel(java.lang.CharSequence);
     method public void setRequestedOrientation(int);
     method public final void setResult(int);
     method public final void setResult(int, android.content.Intent);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e38bbb3..23caccc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4701,42 +4701,33 @@
     }
 
     /**
-     * Set a label to be used in the Recents task display. The activities of a task are traversed
-     * in order from the topmost activity to the bottommost. As soon as one activity returns a
-     * non-null Recents label the traversal is ended and that value will be used in
-     * {@link ActivityManager.RecentTaskInfo#activityLabel}
+     * Set a label and icon to be used in the Recents task display. When {@link
+     * ActivityManager#getRecentTasks} is called, the activities of each task are
+     * traversed in order from the topmost activity to the bottommost. As soon as one activity is
+     * found with either a non-null label or a non-null icon set by this call the traversal is
+     * ended. For each task those values will be returned in {@link
+     * ActivityManager.RecentTaskInfo#activityLabel} and {@link
+     * ActivityManager.RecentTaskInfo#activityIcon}. The {link Intent} for the activity that set
+     * activityLabel and activityIcon will be returned in {@link
+     * ActivityManager.RecentTaskInfo#activityIntent}
      *
      * @see ActivityManager#getRecentTasks
+     * @see ActivityManager.RecentTaskInfo
      *
-     * @param recentsLabel The label to use in the RecentTaskInfo.
+     * @param activityLabel The label to use in the RecentTaskInfo.
+     * @param activityIcon The Bitmap to use in the RecentTaskInfo.
      */
-    public void setRecentsLabel(CharSequence recentsLabel) {
-        try {
-            ActivityManagerNative.getDefault().setRecentsLabel(mToken, recentsLabel);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Set an icon to be used in the Recents task display. The activities of a task are traversed
-     * in order from the topmost activity to the bottommost. As soon as one activity returns a
-     * non-null Recents icon the traversal is ended and that value will be used in
-     * {@link ActivityManager.RecentTaskInfo#activityIcon}.
-     *
-     * @see ActivityManager#getRecentTasks
-     *
-     * @param recentsIcon The Bitmap to use in the RecentTaskInfo.
-     */
-    public void setRecentsIcon(Bitmap recentsIcon) {
+    public void setActivityLabelAndIcon(CharSequence activityLabel, Bitmap activityIcon) {
         final Bitmap scaledIcon;
-        if (recentsIcon != null) {
+        if (activityIcon != null) {
             final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
-            scaledIcon = Bitmap.createScaledBitmap(recentsIcon, size, size, true);
+            scaledIcon = Bitmap.createScaledBitmap(activityIcon, size, size, true);
         } else {
             scaledIcon = null;
         }
         try {
-            ActivityManagerNative.getDefault().setRecentsIcon(mToken, scaledIcon);
+            ActivityManagerNative.getDefault().setActivityLabelAndIcon(mToken, activityLabel,
+                    scaledIcon);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9d6a340..c027e99 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -516,14 +516,14 @@
         public int userId;
 
         /**
-         * The label of the highest activity in the task stack to have set a label
-         * {@link Activity#setRecentsLabel}.
+         * The label of the highest activity in the task stack to have set a label using
+         * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
          */
         public CharSequence activityLabel;
 
         /**
          * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
-         * {@link Activity#setRecentsIcon}.
+         * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
          */
         public Bitmap activityIcon;
 
@@ -563,11 +563,7 @@
         public void readFromParcel(Parcel source) {
             id = source.readInt();
             persistentId = source.readInt();
-            if (source.readInt() != 0) {
-                baseIntent = Intent.CREATOR.createFromParcel(source);
-            } else {
-                baseIntent = null;
-            }
+            baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null;
             origActivity = ComponentName.readFromParcel(source);
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 707a038..a37a35a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2129,21 +2129,13 @@
             return true;
         }
 
-        case SET_RECENTS_LABEL_TRANSACTION: {
+        case SET_ACTIVITY_LABEL_ICON_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            CharSequence recentsLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
-            setRecentsLabel(token, recentsLabel);
-            reply.writeNoException();
-            return true;
-        }
-
-        case SET_RECENTS_ICON_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            IBinder token = data.readStrongBinder();
-            Bitmap recentsIcon = data.readInt() != 0
+            CharSequence activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+            Bitmap activityIcon = data.readInt() > 0
                     ? Bitmap.CREATOR.createFromParcel(data) : null;
-            setRecentsIcon(token, recentsIcon);
+            setActivityLabelAndIcon(token, activityLabel, activityIcon);
             reply.writeNoException();
             return true;
         }
@@ -4918,32 +4910,22 @@
         return isInLockTaskMode;
     }
 
-    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException
+    @Override
+    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
+            Bitmap activityIcon) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        TextUtils.writeToParcel(recentsLabel, data, 0);
-        mRemote.transact(SET_RECENTS_LABEL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
-        reply.readException();
-        data.recycle();
-        reply.recycle();
-    }
-
-    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(token);
-        if (recentsBitmap != null) {
+        TextUtils.writeToParcel(activityLabel, data, 0);
+        if (activityIcon != null) {
             data.writeInt(1);
-            recentsBitmap.writeToParcel(data, 0);
+            activityIcon.writeToParcel(data, 0);
         } else {
             data.writeInt(0);
         }
-        mRemote.transact(SET_RECENTS_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_ACTIVITY_LABEL_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3b56839..f7416d6 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -437,10 +437,8 @@
     public boolean isInLockTaskMode() throws RemoteException;
 
     /** @hide */
-    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException;
-
-    /** @hide */
-    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException;
+    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
+            Bitmap activityBitmap) throws RemoteException;
 
     /*
      * Private non-Binder interfaces
@@ -741,6 +739,5 @@
     int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
     int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
-    int SET_RECENTS_LABEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
-    int SET_RECENTS_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
+    int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
 }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 40275d8..9916476 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -189,6 +189,13 @@
      */
     public static final int FLAG_IMMERSIVE = 0x0800;
     /**
+     * Bit in {@link #flags} indicating that this activity is to be persisted across
+     * reboots for display in the Recents list.
+     * {@link android.R.attr#persistable}
+     * @hide
+     */
+    public static final int FLAG_PERSISTABLE = 0x1000;
+    /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the primary user.  Only works with broadcast receivers.  Set from the
      * {@link android.R.attr#primaryUserOnly} attribute.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a89c507..4b5616f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -57,7 +57,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.jar.JarEntry;
 import java.util.jar.StrictJarFile;
 import java.util.zip.ZipEntry;
 
@@ -2448,6 +2447,11 @@
             a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
         }
 
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
+            a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
+        }
+
         if (!receiver) {
             if (sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 39636fe..4c11fa9 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -574,6 +574,7 @@
             mUptime = in.readLong();
             mPastUptime = in.readLong();
             mUptimeStart = in.readLong();
+            mRealtime = in.readLong();
             mPastRealtime = in.readLong();
             mRealtimeStart = in.readLong();
             mUnpluggedUptime = in.readLong();
@@ -586,6 +587,7 @@
             out.writeLong(mUptime);
             out.writeLong(runningUptime);
             out.writeLong(mUptimeStart);
+            out.writeLong(mRealtime);
             out.writeLong(runningRealtime);
             out.writeLong(mRealtimeStart);
             out.writeLong(mUnpluggedUptime);
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 39b8a6b..b14453a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -866,6 +866,18 @@
     <!-- The name of the logical parent of the activity as it appears in the manifest. -->
     <attr name="parentActivityName" format="string" />
 
+    <!-- Define an activity that will persist across reboots. If such an activity is in the
+         Recents list when the device is shut off it will appear in the Recents list when
+         the device is next powered on. To be persisted all activities in the task from the
+         root activity up to the last activity before a <em>break</em> must be declared with
+         the persistable attribute. A <em>break</em> is the first activity after the root
+         started with Intent.FLAG_CLEAR_TASK_WHEN_RESET.
+
+         <p>Activities that are declared with the persistable attribute will be provided with a
+         forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
+         be passed a persistable Bundle in their Intent.extras. -->
+    <attr name="persistable" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1528,6 +1540,7 @@
         <!-- @hide This broacast receiver will only receive broadcasts for the
              primary user.  Can only be used with receivers. -->
         <attr name="primaryUserOnly" format="boolean" />
+        <attr name="persistable" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8300f38..667adde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2142,6 +2142,7 @@
   <public type="attr" name="colorButtonPressed" />
   <public type="attr" name="colorButtonNormalColored" />
   <public type="attr" name="colorButtonPressedColored" />
+  <public type="attr" name="persistable" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index f5ce222..96da21f 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -28,21 +28,29 @@
         android:layout_gravity="top|center_horizontal"
         android:background="#e6444444">
         <ImageView
-            android:id="@+id/activity_icon"
-            android:layout_width="@dimen/recents_task_view_icon_size"
-            android:layout_height="@dimen/recents_task_view_icon_size"
-            android:layout_gravity="top|left"
+            android:id="@+id/application_icon"
+            android:layout_width="@dimen/recents_task_view_application_icon_size"
+            android:layout_height="@dimen/recents_task_view_application_icon_size"
+            android:layout_gravity="center_vertical|left"
             android:padding="8dp" />
         <TextView
             android:id="@+id/activity_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical|left"
-            android:layout_marginLeft="@dimen/recents_task_view_icon_size"
+            android:layout_marginLeft="@dimen/recents_task_view_application_icon_size"
+            android:layout_marginRight="@dimen/recents_task_view_activity_icon_size"
             android:textSize="24sp"
             android:textColor="#ffffffff"
             android:text="@string/recents_empty_message"
             android:fontFamily="sans-serif-thin" />
+        <ImageView
+            android:id="@+id/activity_icon"
+            android:layout_width="@dimen/recents_task_view_activity_icon_size"
+            android:layout_height="@dimen/recents_task_view_activity_icon_size"
+            android:layout_gravity="center_vertical|right"
+            android:padding="12dp"
+            android:visibility="invisible" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1da66bb..a7ec064 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -24,19 +24,10 @@
     android:id="@+id/notification_panel"
     android:layout_width="0dp"
     android:layout_height="wrap_content"
-    android:background="@drawable/notification_panel_bg"
     android:paddingTop="@dimen/notification_panel_padding_top"
     android:layout_marginStart="@dimen/notification_panel_margin_left"
     >
 
-    <View
-        android:id="@+id/handle"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/close_handle_height"
-        android:background="@drawable/status_bar_close"
-        android:visibility="invisible"
-        />
-
     <include
         layout="@layout/carrier_label"
         android:layout_height="@dimen/carrier_label_height"
@@ -69,6 +60,7 @@
             />
 
         <FrameLayout
+            android:id="@+id/notification_container_parent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             >
@@ -77,21 +69,6 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 />
-    
-            <ScrollView
-                android:id="@+id/scroll"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:fadingEdge="none"
-                android:overScrollMode="ifContentScrolls"
-                >
-                <com.android.systemui.statusbar.policy.NotificationRowLayout
-                    android:id="@+id/latestItems"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    systemui:rowHeight="@dimen/notification_row_min_height"
-                    />
-            </ScrollView>
 
             <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
                     android:id="@+id/notification_stack_scroller"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 38e1083..1c6d5ad 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -230,8 +230,11 @@
     <!-- Default distance from each snap target that GlowPadView considers a "hit" -->
     <dimen name="glowpadview_inner_radius">15dip</dimen>
 
-    <!-- The size of the icon in the recents task view. -->
-    <dimen name="recents_task_view_icon_size">60dp</dimen>
+    <!-- The size of the application icon in the recents task view. -->
+    <dimen name="recents_task_view_application_icon_size">60dp</dimen>
+
+    <!-- The size of the activity icon in the recents task view. -->
+    <dimen name="recents_task_view_activity_icon_size">60dp</dimen>
 
     <!-- Space below the notification stack -->
     <dimen name="notification_stack_margin_bottom">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 90b0c49..1832d37 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -34,6 +34,8 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 
+import com.android.systemui.statusbar.policy.ScrollAdapter;
+
 public class ExpandHelper implements Gefingerpoken, OnClickListener {
     public interface Callback {
         View getChildAtRawPosition(float x, float y);
@@ -609,19 +611,5 @@
         }
         mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
     }
-
-    public interface ScrollAdapter {
-
-        /**
-         * @return Whether the view returned by {@link #getHostView()} is scrolled to the top
-         * and can therefore be expanded by a single finger drag
-         */
-        public boolean isScrolledToTop();
-
-        /**
-         * @return The view in which the scrolling is performed
-         */
-        public View getHostView();
-    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 2436aee..dd75921 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.os.SystemService;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index e375f08..e193a95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -110,17 +110,18 @@
 
     SystemServicesProxy mSystemServicesProxy;
     TaskResourceLoadQueue mLoadQueue;
-    DrawableLruCache mIconCache;
+    DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
-    public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
+    public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
+                              DrawableLruCache applicationIconCache,
                               BitmapLruCache thumbnailCache) {
         mLoadQueue = loadQueue;
-        mIconCache = iconCache;
+        mApplicationIconCache = applicationIconCache;
         mThumbnailCache = thumbnailCache;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
@@ -184,13 +185,13 @@
                 final Task t = nextTaskData.first;
                 final boolean forceLoadTask = nextTaskData.second;
                 if (t != null) {
-                    Drawable loadIcon = mIconCache.get(t.key);
+                    Drawable loadIcon = mApplicationIconCache.get(t.key);
                     Bitmap loadThumbnail = mThumbnailCache.get(t.key);
                     Console.log(Constants.DebugFlags.App.TaskDataLoader,
                             "  [TaskResourceLoader|load]",
                             t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
                                     " forceLoad: " + forceLoadTask);
-                    // Load the icon
+                    // Load the application icon
                     if (loadIcon == null || forceLoadTask) {
                         ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent());
                         Drawable icon = ssp.getActivityIcon(info);
@@ -200,7 +201,7 @@
                                         "    [TaskResourceLoader|loadIcon]",
                                         icon);
                                 loadIcon = icon;
-                                mIconCache.put(t.key, icon);
+                                mApplicationIconCache.put(t.key, icon);
                             }
                         }
                     }
@@ -293,7 +294,7 @@
     static RecentsTaskLoader sInstance;
 
     SystemServicesProxy mSystemServicesProxy;
-    DrawableLruCache mIconCache;
+    DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
@@ -301,7 +302,7 @@
     int mMaxThumbnailCacheSize;
     int mMaxIconCacheSize;
 
-    BitmapDrawable mDefaultIcon;
+    BitmapDrawable mDefaultApplicationIcon;
     Bitmap mDefaultThumbnail;
 
     /** Private Constructor */
@@ -324,9 +325,9 @@
         // Initialize the proxy, cache and loaders
         mSystemServicesProxy = new SystemServicesProxy(context);
         mLoadQueue = new TaskResourceLoadQueue();
-        mIconCache = new DrawableLruCache(iconCacheSize);
+        mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
-        mLoader = new TaskResourceLoader(mLoadQueue, mIconCache, mThumbnailCache);
+        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache);
 
         // Create the default assets
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
@@ -337,10 +338,10 @@
         c.setBitmap(mDefaultThumbnail);
         c.drawColor(0x00000000);
         c.setBitmap(null);
-        mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
+        mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
         Console.log(Constants.DebugFlags.App.TaskDataLoader,
                 "[RecentsTaskLoader|defaultBitmaps]",
-                "icon: " + mDefaultIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+                "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
     }
 
     /** Initializes the recents task loader */
@@ -406,41 +407,36 @@
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = tasks.get(i);
             ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent());
-            String title = ssp.getActivityLabel(info);
+            String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) :
+                    t.activityLabel.toString());
+            Bitmap activityIcon = t.activityIcon;
             boolean isForemostTask = (i == (taskCount - 1));
 
+            // Create a new task
+            Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
+                    activityIcon);
+
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
                 Console.log(Constants.DebugFlags.App.TaskDataLoader,
                         "[RecentsTaskLoader|preloadTask]",
                         "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
 
-                String label = (t.activityLabel == null ? title : t.activityLabel.toString());
-                BitmapDrawable bd = null;
-                if (t.activityIcon != null) {
-                    bd = new BitmapDrawable(res, t.activityIcon);
-                }
-                Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, label, bd);
-
                 // Load the icon (if possible and not the foremost task, from the cache)
-                if (task.icon != null) {
-                    mIconCache.put(task.key, task.icon);
-                } else {
-                    if (!isForemostTask) {
-                        task.icon = mIconCache.get(task.key);
-                        if (task.icon != null) {
-                            // Even though we get things from the cache, we should update them
-                            // if they've changed in the bg
-                            tasksToForceLoad.add(task);
-                        }
+                if (!isForemostTask) {
+                    task.applicationIcon = mApplicationIconCache.get(task.key);
+                    if (task.applicationIcon != null) {
+                        // Even though we get things from the cache, we should update them
+                        // if they've changed in the bg
+                        tasksToForceLoad.add(task);
                     }
-                    if (task.icon == null) {
-                        task.icon = ssp.getActivityIcon(info);
-                        if (task.icon != null) {
-                            mIconCache.put(task.key, task.icon);
-                        } else {
-                            task.icon = mDefaultIcon;
-                        }
+                }
+                if (task.applicationIcon == null) {
+                    task.applicationIcon = ssp.getActivityIcon(info);
+                    if (task.applicationIcon != null) {
+                        mApplicationIconCache.put(task.key, task.applicationIcon);
+                    } else {
+                        task.applicationIcon = mDefaultApplicationIcon;
                     }
                 }
 
@@ -463,18 +459,12 @@
                         task.thumbnail = mDefaultThumbnail;
                     }
                 }
-
-                // Add the task to the stack
-                Console.log(Constants.DebugFlags.App.TaskDataLoader,
-                    "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
-                stack.addTask(task);
-            } else {
-                // Add the task to the stack
-                Console.log(Constants.DebugFlags.App.TaskDataLoader,
-                    "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
-                stack.addTask(new Task(t.persistentId, (t.id > -1), t.baseIntent, title,
-                        null, null));
             }
+
+            // Add the task to the stack
+            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
+            stack.addTask(task);
         }
         Console.log(Constants.DebugFlags.App.TimeSystemCalls,
                 "[RecentsTaskLoader|getAllTaskTopThumbnail]",
@@ -507,16 +497,16 @@
 
     /** Acquires the task resource data from the pool. */
     public void loadTaskData(Task t) {
-        Drawable icon = mIconCache.get(t.key);
+        Drawable applicationIcon = mApplicationIconCache.get(t.key);
         Bitmap thumbnail = mThumbnailCache.get(t.key);
 
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
-                t + " icon: " + icon + " thumbnail: " + thumbnail +
+                t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail +
                         " thumbnailCacheSize: " + mThumbnailCache.size());
 
         boolean requiresLoad = false;
-        if (icon == null) {
-            icon = mDefaultIcon;
+        if (applicationIcon == null) {
+            applicationIcon = mDefaultApplicationIcon;
             requiresLoad = true;
         }
         if (thumbnail == null) {
@@ -526,7 +516,7 @@
         if (requiresLoad) {
             mLoadQueue.addTask(t, false);
         }
-        t.notifyTaskDataLoaded(thumbnail, icon, false);
+        t.notifyTaskDataLoaded(thumbnail, applicationIcon, false);
     }
 
     /** Releases the task resource data back into the pool. */
@@ -536,7 +526,7 @@
                 " thumbnailCacheSize: " + mThumbnailCache.size());
 
         mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
+        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
     }
 
     /** Completely removes the resource data from the pool. */
@@ -546,8 +536,8 @@
 
         mLoadQueue.removeTask(t);
         mThumbnailCache.remove(t.key);
-        mIconCache.remove(t.key);
-        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
+        mApplicationIconCache.remove(t.key);
+        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
     }
 
     /** Stops the task loader and clears all pending tasks */
@@ -570,19 +560,19 @@
             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                 // We are leaving recents, so trim the data a bit
                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
-                mIconCache.trimToSize(mMaxIconCacheSize / 2);
+                mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                 // We are going to be low on memory
                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
-                mIconCache.trimToSize(mMaxIconCacheSize / 4);
+                mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                 // We are low on memory, so release everything
                 mThumbnailCache.evictAll();
-                mIconCache.evictAll();
+                mApplicationIconCache.evictAll();
                 break;
             default:
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 57d6524..f147fbc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,6 +30,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 
 /**
  * Acts as a shim around the real system services that we need to access data from, and provides
@@ -76,8 +77,11 @@
                 rti.id = rti.persistentId = i;
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
-                rti.description = rti.activityLabel = "Recent Task";
-                rti.activityIcon = Bitmap.createBitmap(mDummyIcon);
+                rti.description = rti.activityLabel =
+                        Long.toString(Math.abs(new Random().nextLong()), 36);
+                if (i % 2 == 0) {
+                    rti.activityIcon = Bitmap.createBitmap(mDummyIcon);
+                }
                 tasks.add(rti);
             }
             return tasks;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 25a9aeb..ed2ab2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -60,23 +60,19 @@
     }
 
     public TaskKey key;
-    public String title;
-    public Drawable icon;
+    public Drawable applicationIcon;
+    public String activityLabel;
+    public Bitmap activityIcon;
     public Bitmap thumbnail;
     public boolean isActive;
 
     TaskCallbacks mCb;
 
-    public Task(int id, boolean isActive, Intent intent, String activityTitle, Drawable icon) {
-        this(id, isActive, intent, activityTitle, icon, null);
-    }
-
-    public Task(int id, boolean isActive, Intent intent, String activityTitle, Drawable icon,
-                Bitmap thumbnail) {
+    public Task(int id, boolean isActive, Intent intent, String activityTitle,
+                Bitmap activityIcon) {
         this.key = new TaskKey(id, intent);
-        this.title = activityTitle;
-        this.icon = icon;
-        this.thumbnail = thumbnail;
+        this.activityLabel = activityTitle;
+        this.activityIcon = activityIcon;
         this.isActive = isActive;
     }
 
@@ -86,8 +82,9 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
-    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon, boolean reloadingTaskData) {
-        this.icon = icon;
+    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon,
+                                     boolean reloadingTaskData) {
+        this.applicationIcon = applicationIcon;
         this.thumbnail = thumbnail;
         if (mCb != null) {
             mCb.onTaskDataLoaded(reloadingTaskData);
@@ -95,8 +92,8 @@
     }
 
     /** Notifies the callback listeners that this task has been unloaded */
-    public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) {
-        icon = defaultIcon;
+    public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) {
+        applicationIcon = defaultApplicationIcon;
         thumbnail = defaultThumbnail;
         if (mCb != null) {
             mCb.onTaskDataUnloaded();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 235c6cc..c9a6d67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -17,21 +17,12 @@
 package com.android.systemui.recents.views;
 
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
 
 
@@ -39,6 +30,7 @@
 class TaskBarView extends FrameLayout {
     Task mTask;
 
+    ImageView mApplicationIcon;
     ImageView mActivityIcon;
     TextView mActivityDescription;
 
@@ -61,6 +53,7 @@
     @Override
     protected void onFinishInflate() {
         // Initialize the icon and description views
+        mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityIcon = (ImageView) findViewById(R.id.activity_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
     }
@@ -68,9 +61,13 @@
     /** Binds the bar view to the task */
     void rebindToTask(Task t, boolean animate) {
         mTask = t;
-        if (t.icon != null) {
-            mActivityIcon.setImageDrawable(t.icon);
-            mActivityDescription.setText(t.title);
+        if (t.applicationIcon != null) {
+            mApplicationIcon.setImageDrawable(t.applicationIcon);
+            mActivityDescription.setText(t.activityLabel);
+            if (t.activityIcon != null) {
+                mActivityIcon.setImageBitmap(t.activityIcon);
+                mActivityIcon.setVisibility(View.VISIBLE);
+            }
             if (animate) {
                 // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
             }
@@ -80,7 +77,9 @@
     /** Unbinds the bar view from the task */
     void unbindFromTask() {
         mTask = null;
-        mActivityIcon.setImageDrawable(null);
+        mApplicationIcon.setImageDrawable(null);
+        mActivityIcon.setImageBitmap(null);
+        mActivityIcon.setVisibility(View.INVISIBLE);
         mActivityDescription.setText("");
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index e04a1b2..2c27d44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -75,7 +75,7 @@
         // Bind the views
         mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
         mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
-        mBarView.mActivityIcon.setOnClickListener(this);
+        mBarView.mApplicationIcon.setOnClickListener(this);
         if (mTaskDataLoaded) {
             onTaskDataLoaded(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e5e287d..f349036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -41,7 +41,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -78,6 +77,7 @@
 import com.android.systemui.SearchPanelView;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
 import java.util.Locale;
@@ -98,8 +98,6 @@
     protected static final int MSG_HIDE_HEADS_UP = 1027;
     protected static final int MSG_ESCALATE_HEADS_UP = 1028;
 
-    public static final boolean ENABLE_NOTIFICATION_STACK = SystemProperties
-            .getBoolean("persist.notifications.use_stack", false);
     protected static final boolean ENABLE_HEADS_UP = true;
     // scores above this threshold should be displayed in heads up mode.
     protected static final int INTERRUPTION_THRESHOLD = 10;
@@ -120,7 +118,7 @@
 
     // all notifications
     protected NotificationData mNotificationData = new NotificationData();
-    protected ViewGroup mPile;
+    protected NotificationStackScrollLayout mStackScroller;
 
     protected NotificationData.Entry mInterruptingNotificationEntry;
     protected long mInterruptingNotificationTime;
@@ -1033,7 +1031,7 @@
         }
         // Construct the expanded view.
         NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
-        if (!inflateViews(entry, mPile)) {
+        if (!inflateViews(entry, mStackScroller)) {
             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
                     + notification);
             return null;
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 6be6d4d..2d2f2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -17,45 +17,51 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.util.EventLog;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 public class NotificationPanelView extends PanelView {
     public static final boolean DEBUG_GESTURES = true;
 
-    Drawable mHandleBar;
-    int mHandleBarHeight;
-    View mHandleView;
-    int mFingers;
     PhoneStatusBar mStatusBar;
-    boolean mOkToFlip;
+    private NotificationStackScrollLayout mNotificationStackScroller;
+    private int[] mTempLocation = new int[2];
+    private int[] mTempChildLocation = new int[2];
+    private View mNotificationParent;
+
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     public void setStatusBar(PhoneStatusBar bar) {
+        if (mStatusBar != null) {
+            mStatusBar.setOnFlipRunnable(null);
+        }
         mStatusBar = bar;
+        if (bar != null) {
+            mStatusBar.setOnFlipRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    requestPanelHeightUpdate();
+                }
+            });
+        }
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        Resources resources = getContext().getResources();
-        mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
-        mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height);
-        mHandleView = findViewById(R.id.handle);
+        mNotificationStackScroller = (NotificationStackScrollLayout)
+                findViewById(R.id.notification_stack_scroller);
+        mNotificationParent = findViewById(R.id.notification_container_parent);
     }
 
     @Override
@@ -80,61 +86,86 @@
         return super.dispatchPopulateAccessibilityEvent(event);
     }
 
-    // We draw the handle ourselves so that it's always glued to the bottom of the window.
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        if (changed) {
-            final int pl = getPaddingLeft();
-            final int pr = getPaddingRight();
-            mHandleBar.setBounds(pl, 0, getWidth() - pr, (int) mHandleBarHeight);
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        final int off = (int) (getHeight() - mHandleBarHeight - getPaddingBottom());
-        canvas.translate(0, off);
-        mHandleBar.setState(mHandleView.getDrawableState());
-        mHandleBar.draw(canvas);
-        canvas.translate(0, -off);
+    /**
+     * 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 onTouchEvent(MotionEvent event) {
-        if (DEBUG_GESTURES) {
-            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
-                EventLog.writeEvent(EventLogTags.SYSUI_NOTIFICATIONPANEL_TOUCH,
-                       event.getActionMasked(), (int) event.getX(), (int) event.getY());
-            }
+        // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
+        // implementation.
+        return super.onTouchEvent(event);
+    }
+
+    @Override
+    protected boolean isScrolledToBottom() {
+        if (!isInSettings()) {
+            return mNotificationStackScroller.isScrolledToBottom();
         }
-        if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mOkToFlip = getExpandedHeight() == 0;
-                    break;
-                case MotionEvent.ACTION_POINTER_DOWN:
-                    if (mOkToFlip) {
-                        float miny = event.getY(0);
-                        float maxy = miny;
-                        for (int i=1; i<event.getPointerCount(); i++) {
-                            final float y = event.getY(i);
-                            if (y < miny) miny = y;
-                            if (y > maxy) maxy = y;
-                        }
-                        if (maxy - miny < mHandleBarHeight) {
-                            if (getMeasuredHeight() < mHandleBarHeight) {
-                                mStatusBar.switchToSettings();
-                            } else {
-                                mStatusBar.flipToSettings();
-                            }
-                            mOkToFlip = false;
-                        }
-                    }
-                    break;
-            }
+        return super.isScrolledToBottom();
+    }
+
+    @Override
+    protected int getMaxPanelHeight() {
+        if (!isInSettings()) {
+            int maxPanelHeight = super.getMaxPanelHeight();
+            int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+            return maxPanelHeight - emptyBottomMargin;
         }
-        return mHandleView.dispatchTouchEvent(event);
+        return super.getMaxPanelHeight();
+    }
+
+    private boolean isInSettings() {
+        return mStatusBar != null && mStatusBar.isFlippedToSettings();
+    }
+
+    @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) {
+        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)));
+        }
+    }
+
+    @Override
+    protected int getDesiredMeasureHeight() {
+        return mMaxPanelHeight;
     }
 }
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 4b2c3e1..3c8af30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
@@ -69,7 +70,7 @@
 
     private View mHandleView;
     private float mPeekHeight;
-    private float mTouchOffset;
+    private float mInitialOffsetOnTouch;
     private float mExpandedFraction = 0;
     private float mExpandedHeight = 0;
     private boolean mJustPeeked;
@@ -77,6 +78,7 @@
     private boolean mRubberbanding;
     private boolean mTracking;
     private int mTrackingPointer;
+    private int mTouchSlop;
 
     private TimeAnimator mTimeAnimator;
     private ObjectAnimator mPeekAnimator;
@@ -198,7 +200,6 @@
         }
     }
 
-    private int[] mAbsPos = new int[2];
     PanelBar mBar;
 
     private final TimeListener mAnimationCallback = new TimeListener() {
@@ -220,7 +221,7 @@
     };
 
     private float mVel, mAccel;
-    private int mFullHeight = 0;
+    protected int mMaxPanelHeight = 0;
     private String mViewName;
     protected float mInitialTouchY;
     protected float mFinalTouchY;
@@ -253,13 +254,13 @@
             mTimeAnimator.start();
 
             mRubberbanding = mRubberbandingEnabled // is it enabled at all?
-                    && mExpandedHeight > getFullHeight() // are we past the end?
+                    && mExpandedHeight > getMaxPanelHeight() // are we past the end?
                     && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
             if (mRubberbanding) {
                 mClosing = true;
             } else if (mVel == 0) {
                 // if the panel is less than halfway open, close it
-                mClosing = (mFinalTouchY / getFullHeight()) < 0.5f;
+                mClosing = (mFinalTouchY / getMaxPanelHeight()) < 0.5f;
             } else {
                 mClosing = mExpandedHeight > 0 && mVel < 0;
             }
@@ -268,7 +269,7 @@
             if (DEBUG) logf("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
             if (DEBUG) logf("tick: before: h=%d", (int) mExpandedHeight);
 
-            final float fh = getFullHeight();
+            final float fh = getMaxPanelHeight();
             boolean braking = false;
             if (BRAKES) {
                 if (mClosing) {
@@ -351,6 +352,9 @@
         mPeekHeight = res.getDimension(R.dimen.peek_height)
             + getPaddingBottom() // our window might have a dropshadow
             - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
+
+        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
     }
 
     private void trackMovement(MotionEvent event) {
@@ -363,10 +367,221 @@
         event.offsetLocation(-deltaX, -deltaY);
     }
 
-    // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        return mHandleView.dispatchTouchEvent(event);
+
+        /*
+         * We capture touch events here and update the expand height here in case according to
+         * the users fingers. This also handles multi-touch.
+         *
+         * If the user just clicks shortly, we give him a quick peek of the shade.
+         *
+         * Flinging is also enabled in order to open or close the shade.
+         */
+
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mTracking = true;
+                if (mHandleView != null) {
+                    mHandleView.setPressed(true);
+                    postInvalidate(); // catch the press state change
+                }
+
+                mInitialTouchY = y;
+                initVelocityTracker();
+                trackMovement(event);
+                mTimeAnimator.cancel(); // end any outstanding animations
+                mBar.onTrackingStarted(PanelView.this);
+                mInitialOffsetOnTouch = mExpandedHeight;
+                if (mExpandedHeight == 0) {
+                    mJustPeeked = true;
+                    runPeekAnimation();
+                }
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialOffsetOnTouch = mExpandedHeight;
+                    mInitialTouchY = newY;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
+                if (h > mPeekHeight) {
+                    if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
+                        mPeekAnimator.cancel();
+                    }
+                    mJustPeeked = false;
+                }
+                if (!mJustPeeked) {
+                    setExpandedHeightInternal(h);
+                    mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
+                }
+
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mFinalTouchY = y;
+                mTracking = false;
+                mTrackingPointer = -1;
+                if (mHandleView != null) {
+                    mHandleView.setPressed(false);
+                    postInvalidate(); // catch the press state change
+                }
+                mBar.onTrackingStopped(PanelView.this);
+                trackMovement(event);
+
+                float vel = getCurrentVelocity();
+                fling(vel, true);
+
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
+    }
+
+    private float getCurrentVelocity() {
+        float vel = 0;
+        float yVel = 0, xVel = 0;
+        boolean negative = false;
+
+        // the velocitytracker might be null if we got a bad input stream
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+
+        mVelocityTracker.computeCurrentVelocity(1000);
+
+        yVel = mVelocityTracker.getYVelocity();
+        negative = yVel < 0;
+
+        xVel = mVelocityTracker.getXVelocity();
+        if (xVel < 0) {
+            xVel = -xVel;
+        }
+        if (xVel > mFlingGestureMaxXVelocityPx) {
+            xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
+        }
+
+        vel = (float) Math.hypot(yVel, xVel);
+        if (vel > mFlingGestureMaxOutputVelocityPx) {
+            vel = mFlingGestureMaxOutputVelocityPx;
+        }
+
+        // if you've barely moved your finger, we treat the velocity as 0
+        // preventing spurious flings due to touch screen jitter
+        final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
+        if (deltaY < mFlingGestureMinDistPx
+                || vel < mFlingExpandMinVelocityPx
+                ) {
+            vel = 0;
+        }
+
+        if (negative) {
+            vel = -vel;
+        }
+
+        if (DEBUG) {
+            logf("gesture: dy=%f vel=(%f,%f) vlinear=%f",
+                    deltaY,
+                    xVel, yVel,
+                    vel);
+        }
+        return vel;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+
+        /*
+         * If the user drags anywhere inside the panel we intercept it if he moves his finger
+         * upwards. This allows closing the shade from anywhere inside the panel.
+         *
+         * We only do this if the current content is scrolled to the bottom,
+         * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
+         * possible.
+         */
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        boolean scrolledToBottom = isScrolledToBottom();
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mTracking = true;
+                if (mHandleView != null) {
+                    mHandleView.setPressed(true);
+                    // catch the press state change
+                    postInvalidate();
+                }
+                mInitialTouchY = y;
+                initVelocityTracker();
+                trackMovement(event);
+                mTimeAnimator.cancel(); // end any outstanding animations
+                if (mExpandedHeight == 0 || y > getContentHeight()) {
+                    return true;
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    final float newY = event.getY(newIndex);
+                    mInitialTouchY = newY;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                trackMovement(event);
+                if (scrolledToBottom) {
+                    if (h < -mTouchSlop) {
+                        mInitialOffsetOnTouch = mExpandedHeight;
+                        mInitialTouchY = y;
+                        return true;
+                    }
+                }
+                break;
+        }
+        return false;
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = FlingTracker.obtain();
+    }
+
+    protected boolean isScrolledToBottom() {
+        return false;
+    }
+
+    protected float getContentHeight() {
+        return mExpandedHeight;
     }
 
     @Override
@@ -375,134 +590,6 @@
         mHandleView = findViewById(R.id.handle);
 
         loadDimens();
-
-        if (DEBUG) logf("handle view: " + mHandleView);
-        if (mHandleView != null) {
-            mHandleView.setOnTouchListener(new View.OnTouchListener() {
-                @Override
-                public boolean onTouch(View v, MotionEvent event) {
-                    int pointerIndex = event.findPointerIndex(mTrackingPointer);
-                    if (pointerIndex < 0) {
-                        pointerIndex = 0;
-                        mTrackingPointer = event.getPointerId(pointerIndex);
-                    }
-                    final float y = event.getY(pointerIndex);
-                    final float rawDelta = event.getRawY() - event.getY();
-                    final float rawY = y + rawDelta;
-                    if (DEBUG) logf("handle.onTouch: a=%s p=[%d,%d] y=%.1f rawY=%.1f off=%.1f",
-                            MotionEvent.actionToString(event.getAction()),
-                            mTrackingPointer, pointerIndex,
-                            y, rawY, mTouchOffset);
-                    PanelView.this.getLocationOnScreen(mAbsPos);
-
-                    switch (event.getActionMasked()) {
-                        case MotionEvent.ACTION_DOWN:
-                            mTracking = true;
-                            mHandleView.setPressed(true);
-                            postInvalidate(); // catch the press state change
-                            mInitialTouchY = y;
-                            mVelocityTracker = FlingTracker.obtain();
-                            trackMovement(event);
-                            mTimeAnimator.cancel(); // end any outstanding animations
-                            mBar.onTrackingStarted(PanelView.this);
-                            mTouchOffset = (rawY - mAbsPos[1]) - mExpandedHeight;
-                            if (mExpandedHeight == 0) {
-                                mJustPeeked = true;
-                                runPeekAnimation();
-                            }
-                            break;
-
-                        case MotionEvent.ACTION_POINTER_UP:
-                            final int upPointer = event.getPointerId(event.getActionIndex());
-                            if (mTrackingPointer == upPointer) {
-                                // gesture is ongoing, find a new pointer to track
-                                final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                                final float newY = event.getY(newIndex);
-                                final float newRawY = newY + rawDelta;
-                                mTrackingPointer = event.getPointerId(newIndex);
-                                mTouchOffset = (newRawY - mAbsPos[1]) - mExpandedHeight;
-                                mInitialTouchY = newY;
-                            }
-                            break;
-
-                        case MotionEvent.ACTION_MOVE:
-                            final float h = rawY - mAbsPos[1] - mTouchOffset;
-                            if (h > mPeekHeight) {
-                                if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
-                                    mPeekAnimator.cancel();
-                                }
-                                mJustPeeked = false;
-                            }
-                            if (!mJustPeeked) {
-                                PanelView.this.setExpandedHeightInternal(h);
-                                mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
-                            }
-
-                            trackMovement(event);
-                            break;
-
-                        case MotionEvent.ACTION_UP:
-                        case MotionEvent.ACTION_CANCEL:
-                            mFinalTouchY = y;
-                            mTracking = false;
-                            mTrackingPointer = -1;
-                            mHandleView.setPressed(false);
-                            postInvalidate(); // catch the press state change
-                            mBar.onTrackingStopped(PanelView.this);
-                            trackMovement(event);
-
-                            float vel = 0, yVel = 0, xVel = 0;
-                            boolean negative = false;
-
-                            if (mVelocityTracker != null) {
-                                // the velocitytracker might be null if we got a bad input stream
-                                mVelocityTracker.computeCurrentVelocity(1000);
-
-                                yVel = mVelocityTracker.getYVelocity();
-                                negative = yVel < 0;
-
-                                xVel = mVelocityTracker.getXVelocity();
-                                if (xVel < 0) {
-                                    xVel = -xVel;
-                                }
-                                if (xVel > mFlingGestureMaxXVelocityPx) {
-                                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
-                                }
-
-                                vel = (float)Math.hypot(yVel, xVel);
-                                if (vel > mFlingGestureMaxOutputVelocityPx) {
-                                    vel = mFlingGestureMaxOutputVelocityPx;
-                                }
-
-                                mVelocityTracker.recycle();
-                                mVelocityTracker = null;
-                            }
-
-                            // if you've barely moved your finger, we treat the velocity as 0
-                            // preventing spurious flings due to touch screen jitter
-                            final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
-                            if (deltaY < mFlingGestureMinDistPx
-                                    || vel < mFlingExpandMinVelocityPx
-                                    ) {
-                                vel = 0;
-                            }
-
-                            if (negative) {
-                                vel = -vel;
-                            }
-
-                            if (DEBUG) logf("gesture: dy=%f vel=(%f,%f) vlinear=%f",
-                                    deltaY,
-                                    xVel, yVel,
-                                    vel);
-
-                            fling(vel, true);
-
-                            break;
-                    }
-                    return true;
-                }});
-        }
     }
 
     public void fling(float vel, boolean always) {
@@ -543,19 +630,18 @@
 
         // Did one of our children change size?
         int newHeight = getMeasuredHeight();
-        if (newHeight != mFullHeight) {
-            mFullHeight = newHeight;
-            // If the user isn't actively poking us, let's rubberband to the content
-            if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
-                    && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
-                mExpandedHeight = mFullHeight;
-            }
+        if (newHeight != mMaxPanelHeight) {
+            mMaxPanelHeight = newHeight;
         }
         heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                    (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec));
+                    getDesiredMeasureHeight(), MeasureSpec.AT_MOST);
         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
     }
 
+    protected int getDesiredMeasureHeight() {
+        return (int) mExpandedHeight;
+    }
+
 
     public void setExpandedHeight(float height) {
         if (DEBUG) logf("setExpandedHeight(%.1f)", height);
@@ -569,8 +655,20 @@
 
     @Override
     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
-        if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, mFullHeight);
+        if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom,
+                (int)mExpandedHeight, mMaxPanelHeight);
         super.onLayout(changed, left, top, right, bottom);
+        requestPanelHeightUpdate();
+    }
+
+    protected void requestPanelHeightUpdate() {
+        float currentMaxPanelHeight = getMaxPanelHeight();
+
+        // If the user isn't actively poking us, let's update the height
+        if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
+                && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) {
+            setExpandedHeightInternal(currentMaxPanelHeight);
+        }
     }
 
     public void setExpandedHeightInternal(float h) {
@@ -583,7 +681,7 @@
             h = 0;
         }
 
-        float fh = getFullHeight();
+        float fh = getMaxPanelHeight();
         if (fh == 0) {
             // Hmm, full height hasn't been computed yet
         }
@@ -593,9 +691,13 @@
 
         mExpandedHeight = h;
 
-        if (DEBUG) logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
+        if (DEBUG) {
+            logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh,
+                    mTracking ? "T" : "f", mRubberbanding ? "T" : "f");
+        }
 
-        requestLayout();
+        onHeightUpdated(mExpandedHeight);
+
 //        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
 //        lp.height = (int) mExpandedHeight;
 //        setLayoutParams(lp);
@@ -603,13 +705,23 @@
         mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
     }
 
-    private float getFullHeight() {
-        if (mFullHeight <= 0) {
-            if (DEBUG) logf("Forcing measure() since fullHeight=" + mFullHeight);
+    protected void onHeightUpdated(float expandedHeight) {
+        requestLayout();
+    }
+
+    /**
+     * This returns the maximum height of the panel. Children should override this if their
+     * desired height is not the full height.
+     *
+     * @return the default implementation simply returns the maximum height.
+     */
+    protected int getMaxPanelHeight() {
+        if (mMaxPanelHeight <= 0) {
+            if (DEBUG) logf("Forcing measure() since mMaxPanelHeight=" + mMaxPanelHeight);
             measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
         }
-        return mFullHeight;
+        return mMaxPanelHeight;
     }
 
     public void setExpandedFraction(float frac) {
@@ -621,7 +733,7 @@
             }
             frac = 0;
         }
-        setExpandedHeight(getFullHeight() * frac);
+        setExpandedHeight(getMaxPanelHeight() * frac);
     }
 
     public float getExpandedHeight() {
@@ -633,7 +745,7 @@
     }
 
     public boolean isFullyExpanded() {
-        return mExpandedHeight >= getFullHeight();
+        return mExpandedHeight >= getMaxPanelHeight();
     }
 
     public boolean isFullyCollapsed() {
@@ -681,12 +793,12 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("[PanelView(%s): expandedHeight=%f fullHeight=%f closing=%s"
+        pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%f closing=%s"
                 + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
                 + "]",
                 this.getClass().getSimpleName(),
                 getExpandedHeight(),
-                getFullHeight(),
+                getMaxPanelHeight(),
                 mClosing?"T":"f",
                 mTracking?"T":"f",
                 mRubberbanding?"T":"f",
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 2257aaa..4730f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -86,7 +86,6 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -101,8 +100,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NotificationRowLayout;
-import com.android.systemui.statusbar.policy.OnSizeChangedListener;
 import com.android.systemui.statusbar.policy.RotationLockController;
 
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -172,7 +169,7 @@
     Display mDisplay;
     Point mCurrentDisplaySize = new Point();
     private float mHeadsUpVerticalOffset;
-    private int[] mPilePosition = new int[2];
+    private int[] mStackScrollerPosition = new int[2];
 
     StatusBarWindowView mStatusBarWindow;
     PhoneStatusBarView mStatusBarView;
@@ -198,7 +195,6 @@
 
     // expanded notifications
     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
-    View mNotificationScroller;
     View mExpandedContents;
     int mNotificationPanelGravity;
     int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
@@ -350,6 +346,12 @@
             }
         }};
 
+    private Runnable mOnFlipRunnable;
+
+    public void setOnFlipRunnable(Runnable onFlipRunnable) {
+        mOnFlipRunnable = onFlipRunnable;
+    }
+
     @Override
     public void setZenMode(int mode) {
         super.setZenMode(mode);
@@ -417,7 +419,8 @@
         PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
         mStatusBarView.setPanelHolder(holder);
 
-        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
+        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
+                R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
         mNotificationPanelIsFullScreenWidth =
             (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
@@ -443,7 +446,8 @@
             mHeadsUpNotificationView.setBar(this);
         }
         if (MULTIUSER_DEBUG) {
-            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
+            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
+                    R.id.header_debug_info);
             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
         }
 
@@ -482,33 +486,11 @@
         mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
         mTickerView = mStatusBarView.findViewById(R.id.ticker);
 
-        View legacyScrollView = mStatusBarWindow.findViewById(R.id.scroll);
-        NotificationStackScrollLayout notificationStack
-                = (NotificationStackScrollLayout) mStatusBarWindow
-                .findViewById(R.id.notification_stack_scroller);
-        if (ENABLE_NOTIFICATION_STACK) {
-            notificationStack.setLongPressListener(getNotificationLongClicker());
-            mPile = notificationStack;
-            legacyScrollView.setVisibility(View.GONE);
+        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
+                R.id.notification_stack_scroller);
+        mStackScroller.setLongPressListener(getNotificationLongClicker());
 
-            // The scrollview and the notification container are unified now!
-            // TODO: remove mNotificationScroller entirely once we fully switch to the new Layout
-            mNotificationScroller = notificationStack;
-        } else {
-            mNotificationScroller = legacyScrollView;
-            // less drawing during pulldowns
-            mNotificationScroller.setVerticalScrollBarEnabled(false);
-            NotificationRowLayout rowLayout
-                    = (NotificationRowLayout) mStatusBarWindow.findViewById(R.id.latestItems);
-            rowLayout.setLayoutTransitionsEnabled(false);
-            rowLayout.setLongPressListener(getNotificationLongClicker());
-            mPile = rowLayout;
-            notificationStack.setVisibility(View.GONE);
-        }
-
-        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
-
-
+        mExpandedContents = mStackScroller;
 
         mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
 
@@ -551,7 +533,8 @@
             }
         }
         if (mHasFlipSettings) {
-            mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button);
+            mNotificationButton = (ImageView) mStatusBarWindow.findViewById(
+                    R.id.notification_button);
             if (mNotificationButton != null) {
                 mNotificationButton.setOnClickListener(mNotificationButtonListener);
             }
@@ -593,17 +576,18 @@
         if (isAPhone) {
             mEmergencyCallLabel =
                     (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
-            if (mEmergencyCallLabel != null) {
-                mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
-                mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
-                    public void onClick(View v) { }});
-                mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-                    @Override
-                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                        updateCarrierLabelVisibility(false);
-                    }});
-            }
+            // TODO: Uncomment when correctly positioned
+//            if (mEmergencyCallLabel != null) {
+//                mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
+//                mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
+//                    public void onClick(View v) { }});
+//                mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+//                    @Override
+//                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+//                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+//                        updateCarrierLabelVisibility(false);
+//                    }});
+//            }
         }
 
         mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
@@ -621,13 +605,12 @@
             }
 
             // set up the dynamic hide/show of the label
-            if (!ENABLE_NOTIFICATION_STACK)
-                ((NotificationRowLayout) mPile).setOnSizeChangedListener(new OnSizeChangedListener() {
-                @Override
-                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
-                    updateCarrierLabelVisibility(false);
-                }
-            });
+            // TODO: uncomment, handle this for the Stack scroller aswell
+//                ((NotificationRowLayout) mStackScroller)
+// .setOnSizeChangedListener(new OnSizeChangedListener() {
+//                @Override
+//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
+//                    updateCarrierLabelVisibility(false);
         }
 
         // Quick Settings (where available, some restrictions apply)
@@ -1066,7 +1049,7 @@
     }
 
     private void loadNotificationShade() {
-        if (mPile == null) return;
+        if (mStackScroller == null) return;
 
         int N = mNotificationData.size();
 
@@ -1092,21 +1075,21 @@
         }
 
         ArrayList<View> toRemove = new ArrayList<View>();
-        for (int i=0; i<mPile.getChildCount(); i++) {
-            View child = mPile.getChildAt(i);
+        for (int i=0; i< mStackScroller.getChildCount(); i++) {
+            View child = mStackScroller.getChildAt(i);
             if (!toShow.contains(child)) {
                 toRemove.add(child);
             }
         }
 
         for (View remove : toRemove) {
-            mPile.removeView(remove);
+            mStackScroller.removeView(remove);
         }
 
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mPile.addView(v, i);
+                mStackScroller.addView(v, i);
             }
         }
 
@@ -1178,15 +1161,17 @@
         // The idea here is to only show the carrier label when there is enough room to see it,
         // i.e. when there aren't enough notifications to fill the panel.
         if (SPEW) {
-            Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
-                    mPile.getHeight(), mNotificationScroller.getHeight(), mCarrierLabelHeight));
+            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
+                    mStackScroller.getHeight(), mStackScroller.getHeight(),
+                    mCarrierLabelHeight));
         }
 
         final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
         final boolean makeVisible =
             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
-            && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight)
-            && mNotificationScroller.getVisibility() == View.VISIBLE;
+            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
+                    - mCarrierLabelHeight - mNotificationHeaderHeight)
+            && mStackScroller.getVisibility() == View.VISIBLE;
 
         if (force || mCarrierLabelVisible != makeVisible) {
             mCarrierLabelVisible = makeVisible;
@@ -1229,7 +1214,7 @@
         if (mHasFlipSettings
                 && mFlipSettingsView != null
                 && mFlipSettingsView.getVisibility() == View.VISIBLE
-                && mNotificationScroller.getVisibility() != View.VISIBLE) {
+                && mStackScroller.getVisibility() != View.VISIBLE) {
             // the flip settings panel is unequivocally showing; we should not be shown
             mClearButton.setVisibility(View.INVISIBLE);
         } else if (mClearButton.isShown()) {
@@ -1483,9 +1468,6 @@
         }
 
         mExpandedVisible = true;
-        if (!ENABLE_NOTIFICATION_STACK) {
-            ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(true);
-        }
         if (mNavigationBarView != null)
             mNavigationBarView.setSlippery(true);
 
@@ -1600,7 +1582,7 @@
         }
 
         mNotificationPanel.expand();
-        if (mHasFlipSettings && mNotificationScroller.getVisibility() != View.VISIBLE) {
+        if (mHasFlipSettings && mStackScroller.getVisibility() != View.VISIBLE) {
             flipToNotifications();
         }
 
@@ -1614,11 +1596,11 @@
         if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
         if (mClearButtonAnim != null) mClearButtonAnim.cancel();
 
-        mNotificationScroller.setVisibility(View.VISIBLE);
+        mStackScroller.setVisibility(View.VISIBLE);
         mScrollViewAnim = start(
             startDelay(FLIP_DURATION_OUT,
                 interpolator(mDecelerateInterpolator,
-                    ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 0f, 1f)
+                    ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 0f, 1f)
                         .setDuration(FLIP_DURATION_IN)
                     )));
         mFlipSettingsViewAnim = start(
@@ -1645,6 +1627,9 @@
                 updateCarrierLabelVisibility(false);
             }
         }, FLIP_DURATION - 150);
+        if (mOnFlipRunnable != null) {
+            mOnFlipRunnable.run();
+        }
     }
 
     @Override
@@ -1676,11 +1661,21 @@
         mFlipSettingsView.setScaleX(1f);
         mFlipSettingsView.setVisibility(View.VISIBLE);
         mSettingsButton.setVisibility(View.GONE);
-        mNotificationScroller.setVisibility(View.GONE);
-        mNotificationScroller.setScaleX(0f);
+        mStackScroller.setVisibility(View.GONE);
+        mStackScroller.setScaleX(0f);
         mNotificationButton.setVisibility(View.VISIBLE);
         mNotificationButton.setAlpha(1f);
         mClearButton.setVisibility(View.GONE);
+        if (mOnFlipRunnable != null) {
+            mOnFlipRunnable.run();
+        }
+    }
+
+    public boolean isFlippedToSettings() {
+        if (mFlipSettingsView != null) {
+            return mFlipSettingsView.getVisibility() == View.VISIBLE;
+        }
+        return false;
     }
 
     public void flipToSettings() {
@@ -1704,15 +1699,15 @@
         mScrollViewAnim = start(
             setVisibilityWhenDone(
                 interpolator(mAccelerateInterpolator,
-                        ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 1f, 0f)
+                        ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 1f, 0f)
                         )
                     .setDuration(FLIP_DURATION_OUT),
-                    mNotificationScroller, View.INVISIBLE));
+                    mStackScroller, View.INVISIBLE));
         mSettingsButtonAnim = start(
             setVisibilityWhenDone(
                 ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
                     .setDuration(FLIP_DURATION),
-                    mNotificationScroller, View.INVISIBLE));
+                    mStackScroller, View.INVISIBLE));
         mNotificationButton.setVisibility(View.VISIBLE);
         mNotificationButtonAnim = start(
             ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
@@ -1727,6 +1722,9 @@
                 updateCarrierLabelVisibility(false);
             }
         }, FLIP_DURATION - 150);
+        if (mOnFlipRunnable != null) {
+            mOnFlipRunnable.run();
+        }
     }
 
     public void flipPanels() {
@@ -1766,8 +1764,8 @@
             if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
             if (mClearButtonAnim != null) mClearButtonAnim.cancel();
 
-            mNotificationScroller.setScaleX(1f);
-            mNotificationScroller.setVisibility(View.VISIBLE);
+            mStackScroller.setScaleX(1f);
+            mStackScroller.setVisibility(View.VISIBLE);
             mSettingsButton.setAlpha(1f);
             mSettingsButton.setVisibility(View.VISIBLE);
             mNotificationPanel.setVisibility(View.GONE);
@@ -1777,9 +1775,6 @@
         }
 
         mExpandedVisible = false;
-        if (!ENABLE_NOTIFICATION_STACK) {
-            ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(false);
-        }
         if (mNavigationBarView != null)
             mNavigationBarView.setSlippery(false);
         visibilityChanged(false);
@@ -1806,53 +1801,6 @@
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
     }
 
-    /**
-     * Enables or disables layers on the children of the notifications pile.
-     *
-     * When layers are enabled, this method attempts to enable layers for the minimal
-     * number of children. Only children visible when the notification area is fully
-     * expanded will receive a layer. The technique used in this method might cause
-     * more children than necessary to get a layer (at most one extra child with the
-     * current UI.)
-     *
-     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
-     */
-    private void setPileLayers(int layerType) {
-        final int count = mPile.getChildCount();
-
-        switch (layerType) {
-            case View.LAYER_TYPE_NONE:
-                for (int i = 0; i < count; i++) {
-                    mPile.getChildAt(i).setLayerType(layerType, null);
-                }
-                break;
-            case View.LAYER_TYPE_HARDWARE:
-                final int[] location = new int[2];
-                mNotificationPanel.getLocationInWindow(location);
-
-                final int left = location[0];
-                final int top = location[1];
-                final int right = left + mNotificationPanel.getWidth();
-                final int bottom = top + getExpandedViewMaxHeight();
-
-                final Rect childBounds = new Rect();
-
-                for (int i = 0; i < count; i++) {
-                    final View view = mPile.getChildAt(i);
-                    view.getLocationInWindow(location);
-
-                    childBounds.set(location[0], location[1],
-                            location[0] + view.getWidth(), location[1] + view.getHeight());
-
-                    if (childBounds.intersects(left, top, right, bottom)) {
-                        view.setLayerType(layerType, null);
-                    }
-                }
-
-                break;
-        }
-    }
-
     public boolean interceptTouchEvent(MotionEvent event) {
         if (DEBUG_GESTURES) {
             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
@@ -2230,11 +2178,11 @@
             pw.println("  mTicking=" + mTicking);
             pw.println("  mTracking=" + mTracking);
             pw.println("  mDisplayMetrics=" + mDisplayMetrics);
-            pw.println("  mPile: " + viewInfo(mPile));
+            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
             pw.println("  mTickerView: " + viewInfo(mTickerView));
-            pw.println("  mNotificationScroller: " + viewInfo(mNotificationScroller)
-                    + " scroll " + mNotificationScroller.getScrollX()
-                    + "," + mNotificationScroller.getScrollY());
+            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
+                    + " scroll " + mStackScroller.getScrollX()
+                    + "," + mStackScroller.getScrollY());
         }
 
         pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
@@ -2409,8 +2357,8 @@
 
         if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
             mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
-            mPile.getLocationOnScreen(mPilePosition);
-            mHeadsUpVerticalOffset = mPilePosition[1] - mNaturalBarHeight;
+            mStackScroller.getLocationOnScreen(mStackScrollerPosition);
+            mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight;
         }
 
         updateCarrierLabelVisibility(false);
@@ -2428,7 +2376,6 @@
 
     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
         public void onClick(View v) {
-            // TODO: Handle this better with notification stack scroller
             synchronized (mNotificationData) {
                 mPostCollapseCleanup = new Runnable() {
                     @Override
@@ -2437,86 +2384,14 @@
                             Log.v(TAG, "running post-collapse cleanup");
                         }
                         try {
-                            if (!ENABLE_NOTIFICATION_STACK) {
-                                ((NotificationRowLayout) mPile).setViewRemoval(true);
-                            }
                             mBarService.onClearAllNotifications(mCurrentUserId);
                         } catch (Exception ex) { }
                     }
                 };
 
-                if(ENABLE_NOTIFICATION_STACK) {
-                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                    return;
-                }
-
-                // animate-swipe all dismissable notifications, then animate the shade closed
-                int numChildren = mPile.getChildCount();
-
-                int scrollTop = mNotificationScroller.getScrollY();
-                int scrollBottom = scrollTop + mNotificationScroller.getHeight();
-                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
-                for (int i=0; i<numChildren; i++) {
-                    final View child = mPile.getChildAt(i);
-                    if (((SwipeHelper.Callback) mPile).canChildBeDismissed(child)
-                            && child.getBottom() > scrollTop && child.getTop() < scrollBottom) {
-                        snapshot.add(child);
-                    }
-                }
-                if (snapshot.isEmpty()) {
-                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                    return;
-                }
-                new Thread(new Runnable() {
-                    @Override
-                    public void run() {
-                        // Decrease the delay for every row we animate to give the sense of
-                        // accelerating the swipes
-                        final int ROW_DELAY_DECREMENT = 10;
-                        int currentDelay = 140;
-                        int totalDelay = 0;
-
-
-                        if (!ENABLE_NOTIFICATION_STACK) {
-                            // Set the shade-animating state to avoid doing other work during
-                            // all of these animations. In particular, avoid layout and
-                            // redrawing when collapsing the shade.
-                            ((NotificationRowLayout) mPile).setViewRemoval(false);
-                        }
-
-                        View sampleView = snapshot.get(0);
-                        int width = sampleView.getWidth();
-                        final int dir = sampleView.isLayoutRtl() ? -1 : +1;
-                        final int velocity = dir * width * 8; // 1000/8 = 125 ms duration
-                        for (final View _v : snapshot) {
-                            mHandler.postDelayed(new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (!ENABLE_NOTIFICATION_STACK) {
-                                        ((NotificationRowLayout) mPile).dismissRowAnimated(
-                                                _v, velocity);
-                                    } else {
-                                        ((NotificationStackScrollLayout) mPile).dismissRowAnimated(
-                                                _v, velocity);
-                                    }
-                                }
-                            }, totalDelay);
-                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
-                            totalDelay += currentDelay;
-                        }
-                        // Delay the collapse animation until after all swipe animations have
-                        // finished. Provide some buffer because there may be some extra delay
-                        // before actually starting each swipe animation. Ideally, we'd
-                        // synchronize the end of those animations with the start of the collaps
-                        // exactly.
-                        mHandler.postDelayed(new Runnable() {
-                            @Override
-                            public void run() {
-                                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                            }
-                        }, totalDelay + 225);
-                    }
-                }).start();
+                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                return;
+                // TODO: Handle this better with notification stack scroller
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index eeae081..a7121c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -24,13 +24,13 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.widget.FrameLayout;
 
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 
@@ -40,9 +40,8 @@
     public static final boolean DEBUG = BaseStatusBar.DEBUG;
 
     private ExpandHelper mExpandHelper;
-    private ViewGroup latestItems;
+    private NotificationStackScrollLayout mStackScrollLayout;
     private NotificationPanelView mNotificationPanel;
-    private View mNotificationScroller;
 
     PhoneStatusBar mService;
 
@@ -56,37 +55,15 @@
     protected void onAttachedToWindow () {
         super.onAttachedToWindow();
 
-        ExpandHelper.ScrollAdapter scrollAdapter;
-        if (BaseStatusBar.ENABLE_NOTIFICATION_STACK) {
-            NotificationStackScrollLayout stackScrollLayout =
-                    (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller);
-
-            // ScrollView and notification container are unified in a single view now.
-            latestItems = stackScrollLayout;
-            scrollAdapter = stackScrollLayout;
-            mNotificationScroller = stackScrollLayout;
-        } else {
-            latestItems = (ViewGroup) findViewById(R.id.latestItems);
-            mNotificationScroller = findViewById(R.id.scroll);
-            scrollAdapter = new ExpandHelper.ScrollAdapter() {
-                @Override
-                public boolean isScrolledToTop() {
-                    return mNotificationScroller.getScrollY() == 0;
-                }
-
-                @Override
-                public View getHostView() {
-                    return mNotificationScroller;
-                }
-            };
-        }
+        mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
+                R.id.notification_stack_scroller);
         mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
         int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
         int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
-        mExpandHelper = new ExpandHelper(getContext(), (ExpandHelper.Callback) latestItems,
+        mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout,
                 minHeight, maxHeight);
         mExpandHelper.setEventSource(this);
-        mExpandHelper.setScrollAdapter(scrollAdapter);
+        mExpandHelper.setScrollAdapter(mStackScrollLayout);
 
         // We really need to be able to animate while window animations are going on
         // so that activities may be started asynchronously from panel animations
@@ -113,7 +90,7 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         boolean intercept = false;
         if (mNotificationPanel.isFullyExpanded()
-                && mNotificationScroller.getVisibility() == View.VISIBLE) {
+                && mStackScrollLayout.getVisibility() == View.VISIBLE) {
             intercept = mExpandHelper.onInterceptTouchEvent(ev);
         }
         if (!intercept) {
@@ -122,7 +99,7 @@
         if (intercept) {
             MotionEvent cancellation = MotionEvent.obtain(ev);
             cancellation.setAction(MotionEvent.ACTION_CANCEL);
-            latestItems.onInterceptTouchEvent(cancellation);
+            mStackScrollLayout.onInterceptTouchEvent(cancellation);
             cancellation.recycle();
         }
         return intercept;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java
new file mode 100644
index 0000000..f35e22d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.policy;
+
+import android.view.View;
+
+/**
+ * A scroll adapter which can be queried for meta information about the scroll state
+ */
+public interface ScrollAdapter {
+
+    /**
+     * @return Whether the view returned by {@link #getHostView()} is scrolled to the top
+     */
+    public boolean isScrolledToTop();
+
+    /**
+     * @return Whether the view returned by {@link #getHostView()} is scrolled to the bottom
+     */
+    public boolean isScrolledToBottom();
+
+    /**
+     * @return The view in which the scrolling is performed
+     */
+    public View getHostView();
+}
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 f6eeb6d..04b7f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -39,12 +39,13 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
+import com.android.systemui.statusbar.policy.ScrollAdapter;
 
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
 public class NotificationStackScrollLayout extends ViewGroup
-        implements SwipeHelper.Callback, ExpandHelper.Callback, ExpandHelper.ScrollAdapter {
+        implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter {
 
     private static final String TAG = "NotificationStackScrollLayout";
     private static final boolean DEBUG = false;
@@ -55,7 +56,7 @@
     private static final int INVALID_POINTER = -1;
 
     private SwipeHelper mSwipeHelper;
-    private boolean mAllowScrolling = true;
+    private boolean mSwipingInProgress = true;
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private int mOwnScrollY;
     private int mMaxLayoutHeight;
@@ -89,7 +90,7 @@
      * The current State this Layout is in
      */
     private final StackScrollState mCurrentStackScrollState = new StackScrollState(this);
-
+    
     private OnChildLocationsChangedListener mListener;
 
     public NotificationStackScrollLayout(Context context) {
@@ -279,7 +280,7 @@
     }
 
     /**
-     * Get the current height of the view. This is at most the size of the view given by a the
+     * Get the current height of the view. This is at most the msize of the view given by a the
      * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
      *
      * @return either the layout height or the externally defined height, whichever is smaller
@@ -288,6 +289,14 @@
         return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
     }
 
+    public int getItemHeight() {
+        return mCollapsedSize;
+    }
+
+    public int getBottomStackPeekSize() {
+        return mBottomStackPeekSize;
+    }
+
     public void setLongPressListener(View.OnLongClickListener listener) {
         mSwipeHelper.setLongPressListener(listener);
     }
@@ -298,15 +307,15 @@
         if (veto != null && veto.getVisibility() != View.GONE) {
             veto.performClick();
         }
-        allowScrolling(true);
+        setSwipingInProgress(false);
     }
 
     public void onBeginDrag(View v) {
-        allowScrolling(false);
+        setSwipingInProgress(true);
     }
 
     public void onDragCancelled(View v) {
-        allowScrolling(true);
+        setSwipingInProgress(false);
     }
 
     public View getChildAtPosition(MotionEvent ev) {
@@ -365,8 +374,11 @@
         return (veto != null && veto.getVisibility() != View.GONE);
     }
 
-    private void allowScrolling(boolean allow) {
-        mAllowScrolling = allow;
+    private void setSwipingInProgress(boolean isSwiped) {
+        mSwipingInProgress = isSwiped;
+        if(isSwiped) {
+            requestDisallowInterceptTouchEvent(true);
+        }
     }
 
     @Override
@@ -386,7 +398,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean scrollerWantsIt = false;
-        if (mAllowScrolling) {
+        if (!mSwipingInProgress) {
             scrollerWantsIt = onScrollTouch(ev);
         }
         boolean horizontalSwipeWantsIt = false;
@@ -409,12 +421,6 @@
                 }
                 boolean isBeingDragged = !mScroller.isFinished();
                 setIsBeingDragged(isBeingDragged);
-                if (isBeingDragged) {
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                }
 
                 /*
                  * If being flinged and user touches, stop the fling. isFinished
@@ -439,10 +445,6 @@
                 final int y = (int) ev.getY(activePointerIndex);
                 int deltaY = mLastMotionY - y;
                 if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
                     setIsBeingDragged(true);
                     if (deltaY > 0) {
                         deltaY -= mTouchSlop;
@@ -642,7 +644,7 @@
         if (getChildCount() > 0) {
             int contentHeight = getContentHeight();
             scrollRange = Math.max(0,
-                    contentHeight - mMaxLayoutHeight + mCollapsedSize);
+                    contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
         }
         return scrollRange;
     }
@@ -697,7 +699,7 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         boolean scrollWantsIt = false;
-        if (mAllowScrolling) {
+        if (!mSwipingInProgress) {
             scrollWantsIt = onInterceptTouchEventScroll(ev);
         }
         boolean swipeWantsIt = false;
@@ -763,10 +765,6 @@
                     mLastMotionY = y;
                     initVelocityTrackerIfNotExists();
                     mVelocityTracker.addMovement(ev);
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
                 }
                 break;
             }
@@ -823,6 +821,7 @@
     private void setIsBeingDragged(boolean isDragged) {
         mIsBeingDragged = isDragged;
         if (isDragged) {
+            requestDisallowInterceptTouchEvent(true);
             mSwipeHelper.removeLongPressCallback();
         }
     }
@@ -841,10 +840,19 @@
     }
 
     @Override
+    public boolean isScrolledToBottom() {
+        return mOwnScrollY >= getScrollRange();
+    }
+
+    @Override
     public View getHostView() {
         return this;
     }
 
+    public int getEmptyBottomMargin() {
+        return Math.max(getHeight() - mContentHeight, 0);
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
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 6d2ba6a..4745f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -86,7 +86,7 @@
         // First we reset the view states to their default values.
         resultState.resetViewStates();
 
-        // The first element is always in there so it's initialized with 1.0f.
+        // The first element is always in there so it's initialized with 1.0f;
         algorithmState.itemsInTopStack = 1.0f;
         algorithmState.partialInTop = 0.0f;
         algorithmState.lastTopStackIndex = 0;
@@ -102,7 +102,7 @@
         // Phase 3:
         updateZValuesForState(resultState, algorithmState);
 
-        // Write the algorithm state to the result.
+        // write the algorithm state to the result
         resultState.setScrollY(algorithmState.scrollY);
     }
 
@@ -151,7 +151,7 @@
                 // Case 2:
                 // First element of regular scrollview comes next, so the position is just the
                 // scrolling position
-                nextYPosition = scrollOffset;
+                nextYPosition = Math.min(scrollOffset, transitioningPositionStart);
                 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
             } else if (nextYPosition >= transitioningPositionStart) {
                 if (currentYPosition >= transitioningPositionStart) {
@@ -180,6 +180,7 @@
             if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
                 Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
             }
+            nextYPosition = Math.max(0, nextYPosition);
             currentYPosition = nextYPosition;
             yPositionInScrollView = yPositionInScrollViewAfterElement;
         }
@@ -253,6 +254,8 @@
             nextYPosition = mCollapsedSize + mPaddingBetweenElements -
                     mTopStackIndentationFunctor.getValue(
                             algorithmState.itemsInTopStack - i - 1);
+            nextYPosition = Math.min(nextYPosition, mLayoutHeight - mCollapsedSize
+                    - mBottomStackPeekSize);
             if (paddedIndex == 0) {
                 childViewState.alpha = 1.0f - algorithmState.partialInTop;
                 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index afb389e..e9be1d7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6899,18 +6899,27 @@
                     rti.stackId = tr.stack.mStackId;
                     rti.userId = tr.userId;
 
+                    // Traverse upwards looking for any break between main task activities and
+                    // utility activities.
                     final ArrayList<ActivityRecord> activities = tr.mActivities;
-                    int numSet = 0;
-                    for (int activityNdx = activities.size() - 1; activityNdx >= 0 && numSet < 2;
-                            --activityNdx) {
+                    int activityNdx;
+                    final int numActivities = activities.size();
+                    for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
+                            ++activityNdx) {
                         final ActivityRecord r = activities.get(activityNdx);
-                        if (rti.activityLabel == null && r.recentsLabel != null) {
-                            rti.activityLabel = r.recentsLabel;
-                            ++numSet;
+                        if (r.intent != null &&
+                                (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
+                                        != 0) {
+                            break;
                         }
-                        if (rti.activityIcon == null && r.recentsIcon != null) {
-                            rti.activityIcon = r.recentsIcon;
-                            ++numSet;
+                    }
+                    // Traverse downwards starting below break looking for set label and icon.
+                    for (--activityNdx; activityNdx >= 0; --activityNdx) {
+                        final ActivityRecord r = activities.get(activityNdx);
+                        if (r.activityLabel != null || r.activityIcon != null) {
+                            rti.activityLabel = r.activityLabel;
+                            rti.activityIcon = r.activityIcon;
+                            break;
                         }
                     }
 
@@ -6979,21 +6988,13 @@
     }
 
     @Override
-    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) {
+    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
+            Bitmap activityIcon) {
         synchronized (this) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
-                r.recentsLabel = recentsLabel.toString();
-            }
-        }
-    }
-
-    @Override
-    public void setRecentsIcon(IBinder token, Bitmap recentsIcon) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.recentsIcon = recentsIcon;
+                r.activityLabel = activityLabel.toString();
+                r.activityIcon = activityIcon;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 77f5c59..33b12c5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -148,8 +148,8 @@
     boolean mStartingWindowShown = false;
     ActivityContainer mInitialActivityContainer;
 
-    String recentsLabel;
-    Bitmap recentsIcon;
+    String activityLabel;
+    Bitmap activityIcon;
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dd22b2d..ff90cae 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9201,7 +9201,7 @@
         }
         
         // Successfully disabled the old package. Now proceed with re-installation
-        mLastScanError = PackageManager.INSTALL_SUCCEEDED;
+        res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
         newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
         if (newPackage == null) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a388318..80c3c8e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2541,6 +2541,9 @@
         @Override // Binder call
         public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
                 String packageName, int uid) {
+            if (uid < 0) {
+                uid = Binder.getCallingUid();
+            }
             acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null);
         }