Merge "Launch new tasks with Doc Centric flag."
diff --git a/api/current.txt b/api/current.txt
index 9a30c44..4534dd4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7646,6 +7646,7 @@
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+    field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
     field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
     field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location";
     field public static final java.lang.String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
@@ -7669,7 +7670,7 @@
     field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
     field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
     field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
-    field public static final java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
+    field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
     field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2facef6..e86833b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1233,6 +1233,26 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports leanback UI. This is
+     * typically used in a living room television experience, but is a software
+     * feature unlike {@link #FEATURE_TELEVISION}. Devices running with this
+     * feature will use resources associated with the "television" UI mode.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LEANBACK = "android.software.leanback";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports only leanback UI. Only
+     * applications designed for this experience should be run, though this is
+     * not enforced by the system.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports WiFi (802.11) networking.
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -1252,6 +1272,7 @@
      * room television experience: displayed on a big screen, where the user
      * is sitting far away from it, and the dominant form of input will be
      * something like a DPAD, not through touch or mouse.
+     * @deprecated use {@link #FEATURE_LEANBACK} instead.
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TELEVISION = "android.hardware.type.television";
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 875e8de..4a743a5 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -140,12 +140,33 @@
         mContext.registerReceiver(mExternalReceiver, sdFilter);
     }
 
+    private final void handlePackageEvent(Intent intent, int userId) {
+        // Don't regenerate the services map when the package is removed or its
+        // ASEC container unmounted as a step in replacement.  The subsequent
+        // _ADDED / _AVAILABLE call will regenerate the map in the final state.
+        final String action = intent.getAction();
+        // it's a new-component action if it isn't some sort of removal
+        final boolean isRemoval = Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action);
+        // if it's a removal, is it part of an update-in-place step?
+        final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+        if (isRemoval && replacing) {
+            // package is going away, but it's the middle of an upgrade: keep the current
+            // state and do nothing here.  This clause is intentionally empty.
+        } else {
+            // either we're adding/changing, or it's a removal without replacement, so
+            // we need to recalculate the set of available services
+            generateServicesMap(userId);
+        }
+    }
+
     private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
             if (uid != -1) {
-                generateServicesMap(UserHandle.getUserId(uid));
+                handlePackageEvent(intent, UserHandle.getUserId(uid));
             }
         }
     };
@@ -154,7 +175,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             // External apps can't coexist with multi-user, so scan owner
-            generateServicesMap(UserHandle.USER_OWNER);
+            handlePackageEvent(intent, UserHandle.USER_OWNER);
         }
     };
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 94c3f44..d8cf18e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -435,6 +435,14 @@
  * Various arguments, most determined by system properties, are passed in.
  * The "mOptions" vector is updated.
  *
+ * CAUTION: when adding options in here, be careful not to put the
+ * char buffer inside a nested scope.  Adding the buffer to the
+ * options using mOptions.add() does not copy the buffer, so if the
+ * buffer goes out of scope the option may be overwritten.  It's best
+ * to put the buffer at the top of the function so that it is more
+ * unlikely that someone will surround it in a scope at a later time
+ * and thus introduce a bug.
+ *
  * Returns 0 on success.
  */
 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
@@ -469,7 +477,15 @@
       kEMIntFast,
       kEMJitCompiler,
     } executionMode = kEMDefault;
-
+    char profile_period[sizeof("-Xprofile-period:") + PROPERTY_VALUE_MAX];
+    char profile_duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
+    char profile_interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
+    char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
+    char langOption[sizeof("-Duser.language=") + 3];
+    char regionOption[sizeof("-Duser.region=") + 3];
+    char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
+    char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
+    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
 
     property_get("dalvik.vm.checkjni", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
@@ -670,7 +686,6 @@
         //mOptions.add(opt);
     }
 
-    char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
     property_get("dalvik.vm.lockprof.threshold", propBuf, "");
     if (strlen(propBuf) > 0) {
       strcpy(lockProfThresholdBuf, "-Xlockprofthreshold:");
@@ -680,7 +695,6 @@
     }
 
     /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
-    char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.op", propBuf, "");
     if (strlen(propBuf) > 0) {
         strcpy(jitOpBuf, "-Xjitop:");
@@ -690,7 +704,6 @@
     }
 
     /* Force interpreter-only mode for selected methods */
-    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.method", propBuf, "");
     if (strlen(propBuf) > 0) {
         strcpy(jitMethodBuf, "-Xjitmethod:");
@@ -770,8 +783,6 @@
 
     /* Set the properties for locale */
     {
-        char langOption[sizeof("-Duser.language=") + 3];
-        char regionOption[sizeof("-Duser.region=") + 3];
         strcpy(langOption, "-Duser.language=");
         strcpy(regionOption, "-Duser.region=");
         readLocale(langOption, regionOption);
@@ -786,35 +797,30 @@
      * Set profiler options
      */
     if (libart) {
-      char period[sizeof("-Xprofile-period:") + PROPERTY_VALUE_MAX];
-      char duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
-      char interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
-      char backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
-
       // Number of seconds during profile runs.
-      strcpy(period, "-Xprofile-period:");
-      property_get("dalvik.vm.profile.period_secs", period+17, "10");
-      opt.optionString = period;
+      strcpy(profile_period, "-Xprofile-period:");
+      property_get("dalvik.vm.profile.period_secs", profile_period+17, "10");
+      opt.optionString = profile_period;
       mOptions.add(opt);
 
       // Length of each profile run (seconds).
-      strcpy(duration, "-Xprofile-duration:");
-      property_get("dalvik.vm.profile.duration_secs", duration+19, "30");
-      opt.optionString = duration;
+      strcpy(profile_duration, "-Xprofile-duration:");
+      property_get("dalvik.vm.profile.duration_secs", profile_duration+19, "30");
+      opt.optionString = profile_duration;
       mOptions.add(opt);
 
 
       // Polling interval during profile run (microseconds).
-      strcpy(interval, "-Xprofile-interval:");
-      property_get("dalvik.vm.profile.interval_us", interval+19, "10000");
-      opt.optionString = interval;
+      strcpy(profile_interval, "-Xprofile-interval:");
+      property_get("dalvik.vm.profile.interval_us", profile_interval+19, "10000");
+      opt.optionString = profile_interval;
       mOptions.add(opt);
 
       // Coefficient for period backoff.  The the period is multiplied
       // by this value after each profile run.
-      strcpy(backoff, "-Xprofile-backoff:");
-      property_get("dalvik.vm.profile.backoff_coeff", backoff+18, "2.0");
-      opt.optionString = backoff;
+      strcpy(profile_backoff, "-Xprofile-backoff:");
+      property_get("dalvik.vm.profile.backoff_coeff", profile_backoff+18, "2.0");
+      opt.optionString = profile_backoff;
       mOptions.add(opt);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/Console.java
index b3d9ccf..db95193 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Console.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Console.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -36,20 +37,20 @@
 
     /** Logs a key */
     public static void log(String key) {
-        Console.log(true, key, "", AnsiReset);
+        log(true, key, "", AnsiReset);
     }
 
     /** Logs a conditioned key */
     public static void log(boolean condition, String key) {
         if (condition) {
-            Console.log(condition, key, "", AnsiReset);
+            log(condition, key, "", AnsiReset);
         }
     }
 
     /** Logs a key in a specific color */
     public static void log(boolean condition, String key, Object data) {
         if (condition) {
-            Console.log(condition, key, data, AnsiReset);
+            log(condition, key, data, AnsiReset);
         }
     }
 
@@ -74,6 +75,50 @@
         }
     }
 
+    /** Logs a stack trace */
+    public static void logStackTrace() {
+        logStackTrace("", 99);
+    }
+
+    /** Logs a stack trace to a certain depth */
+    public static void logStackTrace(int depth) {
+        logStackTrace("", depth);
+    }
+
+    /** Logs a stack trace to a certain depth with a key */
+    public static void logStackTrace(String key, int depth) {
+        int offset = 0;
+        StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+        String tinyStackTrace = "";
+        // Skip over the known stack trace classes
+        for (int i = 0; i < callStack.length; i++) {
+            StackTraceElement el = callStack[i];
+            String className = el.getClassName();
+            if (className.indexOf("dalvik.system.VMStack") == -1 &&
+                className.indexOf("java.lang.Thread") == -1 &&
+                className.indexOf("recents.Console") == -1) {
+                break;
+            } else {
+                offset++;
+            }
+        }
+        // Build the pretty stack trace
+        int start = Math.min(offset + depth, callStack.length);
+        int end = offset;
+        String indent = "";
+        for (int i = start - 1; i >= end; i--) {
+            StackTraceElement el = callStack[i];
+            tinyStackTrace += indent + " -> " + el.getClassName() +
+                    "[" + el.getLineNumber() + "]." + el.getMethodName();
+            if (i > end) {
+                tinyStackTrace += "\n";
+                indent += "  ";
+            }
+        }
+        log(true, key, tinyStackTrace, AnsiRed);
+    }
+
+
     /** Returns the stringified MotionEvent action */
     public static String motionEventActionToString(int action) {
         switch (action) {
@@ -93,4 +138,25 @@
                 return "" + action;
         }
     }
+
+    public static String trimMemoryLevelToString(int level) {
+        switch (level) {
+            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
+                return "UI Hidden";
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
+                return "Running Moderate";
+            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
+                return "Background";
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
+                return "Running Low";
+            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                return "Moderate";
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
+                return "Critical";
+            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                return "Complete";
+            default:
+                return "" + level;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index aeae4ab..57ebbc2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -30,9 +30,11 @@
             public static final boolean EnableTaskStackClipping = false;
             public static final boolean EnableBackgroundTaskLoading = true;
             public static final boolean ForceDisableBackgroundCache = false;
+
             public static final boolean TaskDataLoader = false;
             public static final boolean SystemUIHandshake = false;
             public static final boolean TimeSystemCalls = false;
+            public static final boolean Memory = false;
         }
 
         public static class UI {
@@ -41,7 +43,7 @@
             public static final boolean TouchEvents = false;
             public static final boolean MeasureAndLayout = false;
             public static final boolean Clipping = false;
-            public static final boolean HwLayers = true;
+            public static final boolean HwLayers = false;
         }
 
         public static class TaskStack {
@@ -55,13 +57,16 @@
 
     public static class Values {
         public static class Window {
+            // The dark background dim is set behind the empty recents view
             public static final float DarkBackgroundDim = 0.5f;
+            // The background dim is set behind the card stack
             public static final float BackgroundDim = 0.35f;
         }
 
         public static class RecentsTaskLoader {
             // XXX: This should be calculated on the first load
             public static final int PreloadFirstTasksCount = 5;
+            // For debugging, this allows us to multiply the number of cards for each task
             public static final int TaskEntryMultiplier = 1;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d050847..fc4d819 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
+import com.android.systemui.recent.RecentTasksLoader;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.RecentsView;
@@ -168,6 +169,14 @@
     }
 
     @Override
+    public void onTrimMemory(int level) {
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        if (loader != null) {
+            loader.onTrimMemory(level);
+        }
+    }
+
+    @Override
     public void onBackPressed() {
         if (!mRecentsView.unfilterFilteredStacks()) {
             super.onBackPressed();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index f3881ae..ed981ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -30,7 +30,6 @@
 
     DisplayMetrics mDisplayMetrics;
 
-    public boolean layoutVerticalStack;
     public Rect systemInsets = new Rect();
 
     /** Private constructor */
@@ -56,7 +55,6 @@
 
         boolean isPortrait = context.getResources().getConfiguration().orientation ==
                 Configuration.ORIENTATION_PORTRAIT;
-        layoutVerticalStack = isPortrait || Constants.LANDSCAPE_LAYOUT_VERTICAL_STACK;
     }
 
     public void updateSystemInsets(Rect insets) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 522ab0f..13a3424 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -57,8 +57,9 @@
                     tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top);
                     tsv.boundScroll();
                     TaskViewTransform transform = tsv.getStackTransform(0);
+                    Rect taskRect = new Rect(transform.rect);
 
-                    data.putParcelable("taskRect", transform.rect);
+                    data.putParcelable("taskRect", taskRect);
                     Message reply = Message.obtain(null, MSG_UPDATE_RECENTS_FOR_CONFIGURATION, 0, 0);
                     reply.setData(data);
                     msg.replyTo.send(reply);
@@ -100,4 +101,12 @@
         Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onDestroy]");
         super.onDestroy();
     }
+
+    @Override
+    public void onTrimMemory(int level) {
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        if (loader != null) {
+            loader.onTrimMemory(level);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c303ca7..96efed4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -35,6 +36,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
@@ -233,7 +235,7 @@
     @Override
     protected int sizeOf(Task t, Bitmap bitmap) {
         // The cache size will be measured in kilobytes rather than number of items
-        return bitmap.getByteCount() / 1024;
+        return bitmap.getAllocationByteCount() / 1024;
     }
 }
 
@@ -247,17 +249,28 @@
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
 
+    int mMaxThumbnailCacheSize;
+    int mMaxIconCacheSize;
+
     BitmapDrawable mDefaultIcon;
     Bitmap mDefaultThumbnail;
 
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
+        // Calculate the cache sizes
         int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
-        int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 16;
-        int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 8;
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+        mMaxThumbnailCacheSize = maxMemory / 8;
+        mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
+        int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
+                mMaxIconCacheSize;
+        int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
+                mMaxThumbnailCacheSize;
+
+        Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
                 "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
                 " iconCache: " + iconCacheSize);
+
+        // Initialize the cache and loaders
         mLoadQueue = new TaskResourceLoadQueue();
         mIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
@@ -293,7 +306,7 @@
 
     /** Reload the set of recent tasks */
     SpaceNode reload(Context context, int preloadCount) {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|reload]");
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
         TaskStack stack = new TaskStack(context);
         SpaceNode root = new SpaceNode(context);
         root.setStack(stack);
@@ -310,7 +323,7 @@
             Console.log(Constants.DebugFlags.App.TimeSystemCalls,
                     "[RecentsTaskLoader|getRecentTasks]",
                     "" + (System.currentTimeMillis() - t1) + "ms");
-            Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+            Console.log(Constants.DebugFlags.App.TaskDataLoader,
                     "[RecentsTaskLoader|tasks]", "" + tasks.size());
 
             // Remove home/recents tasks
@@ -335,35 +348,51 @@
             int taskCount = tasks.size();
             for (int i = 0; i < taskCount; i++) {
                 ActivityManager.RecentTaskInfo t = tasks.get(i);
-
-                // Load the label, icon and thumbnail
                 ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
                         PackageManager.GET_META_DATA);
                 String title = info.loadLabel(pm).toString();
-                Drawable icon = null;
-                Bitmap thumbnail = null;
                 Task task;
-                if (i >= (taskCount - preloadCount) || !Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
-                    Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                // Preload the specified number of apps
+                if (i >= (taskCount - preloadCount) ||
+                        !Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
                             "[RecentsTaskLoader|preloadTask]",
                             "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
-                    icon = info.loadIcon(pm);
-                    thumbnail = am.getTaskTopThumbnail(t.id);
-                    for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
-                        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
-                                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
-                        task = new Task(t.persistentId, t.baseIntent, title, icon, thumbnail);
+
+                    task = new Task(t.persistentId, t.baseIntent, title, null, null);
+
+                    // Load the icon (if possible from the cache)
+                    if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                        task.icon = mIconCache.get(task);
+                    }
+                    if (task.icon == null) {
+                        task.icon = info.loadIcon(pm);
                         if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
-                            if (thumbnail != null) mThumbnailCache.put(task, thumbnail);
-                            if (icon != null) {
-                                mIconCache.put(task, icon);
-                            }
+                            mIconCache.put(task, task.icon);
                         }
+                    }
+
+                    // Load the thumbnail (if possible from the cache)
+                    if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                        task.thumbnail = mThumbnailCache.get(task);
+                    }
+                    if (task.thumbnail == null) {
+                        task.thumbnail = am.getTaskTopThumbnail(t.id);
+                        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                            mThumbnailCache.put(task, task.thumbnail);
+                        }
+                    }
+
+                    // Create as many tasks a we want to multiply by
+                    for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
+                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
                         stack.addTask(task);
                     }
                 } else {
+                    // Create as many tasks a we want to multiply by
                     for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
-                        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                 "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
                         task = new Task(t.persistentId, t.baseIntent, title, null, null);
                         stack.addTask(task);
@@ -388,9 +417,9 @@
             t1 = System.currentTimeMillis();
             List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
             Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
-            Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|stacks]", "" + tasks.size());
+            Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
             for (ActivityManager.StackInfo s : stackInfos) {
-                Console.log(Constants.DebugFlags.App.SystemUIHandshake, "  [RecentsTaskLoader|stack]", s.toString());
+                Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [RecentsTaskLoader|stack]", s.toString());
                 if (stacks.containsKey(s.stackId)) {
                     stacks.get(s.stackId).setRect(s.bounds);
                 }
@@ -403,45 +432,46 @@
         return root;
     }
 
-    /** Acquires the task resource data from the pool.
-     * XXX: Move this into Task? */
+    /** Acquires the task resource data from the pool. */
     public void loadTaskData(Task t) {
         if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
-            t.icon = mIconCache.get(t);
-            t.thumbnail = mThumbnailCache.get(t);
+            Drawable icon = mIconCache.get(t);
+            Bitmap thumbnail = mThumbnailCache.get(t);
 
             Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
-                    t + " icon: " + t.icon + " thumbnail: " + t.thumbnail);
+                    t + " icon: " + icon + " thumbnail: " + thumbnail +
+                            " thumbnailCacheSize: " + mThumbnailCache.size());
 
             boolean requiresLoad = false;
-            if (t.icon == null) {
-                t.icon = mDefaultIcon;
+            if (icon == null) {
+                icon = mDefaultIcon;
                 requiresLoad = true;
             }
-            if (t.thumbnail == null) {
-                t.thumbnail = mDefaultThumbnail;
+            if (thumbnail == null) {
+                thumbnail = mDefaultThumbnail;
                 requiresLoad = true;
             }
             if (requiresLoad) {
                 mLoadQueue.addTask(t);
             }
+            t.notifyTaskLoaded(thumbnail, icon);
         }
     }
 
-    /** Releases the task resource data back into the pool.
-     * XXX: Move this into Task? */
+    /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
         if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
             Console.log(Constants.DebugFlags.App.TaskDataLoader,
-                    "[RecentsTaskLoader|unloadTask]", t);
+                    "[RecentsTaskLoader|unloadTask]", t +
+                    " thumbnailCacheSize: " + mThumbnailCache.size());
             mLoadQueue.removeTask(t);
-            t.icon = mDefaultIcon;
-            t.thumbnail = mDefaultThumbnail;
+            t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
+        } else {
+            t.notifyTaskUnloaded(null, null);
         }
     }
 
-    /** Completely removes the resource data from the pool.
-     * XXX: Move this into Task? */
+    /** Completely removes the resource data from the pool. */
     public void deleteTaskData(Task t) {
         if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
             Console.log(Constants.DebugFlags.App.TaskDataLoader,
@@ -449,9 +479,10 @@
             mLoadQueue.removeTask(t);
             mThumbnailCache.remove(t);
             mIconCache.remove(t);
+            t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
+        } else {
+            t.notifyTaskUnloaded(null, null);
         }
-        t.icon = mDefaultIcon;
-        t.thumbnail = mDefaultThumbnail;
     }
 
     /** Stops the task loader */
@@ -460,4 +491,51 @@
         mLoader.stop();
         mLoadQueue.clearTasks();
     }
+
+    void onTrimMemory(int level) {
+        Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
+                Console.trimMemoryLevelToString(level));
+
+        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+            // If we are hidden, then we should unload each of the task keys
+            if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+                Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|unloadTasks]"
+                );
+                // Unload each of the keys in the thumbnail cache
+                Map<Task, Bitmap> thumbnailCache = mThumbnailCache.snapshot();
+                for (Task t : thumbnailCache.keySet()) {
+                    unloadTaskData(t);
+                }
+                // As well as the keys in the icon cache
+                Map<Task, Drawable> iconCache = mIconCache.snapshot();
+                for (Task t : iconCache.keySet()) {
+                    unloadTaskData(t);
+                }
+            }
+
+            switch (level) {
+                case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
+                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
+                case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
+                    // We are leaving recents, so trim the data a bit
+                    mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
+                    mIconCache.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);
+                    break;
+                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
+                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                    // We are low on memory, so release everything
+                    mThumbnailCache.evictAll();
+                    mIconCache.evictAll();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
 }
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 9b03c5d..378984c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -54,6 +54,24 @@
         }
     }
 
+    /** Notifies the callback listeners that this task has been loaded */
+    public void notifyTaskLoaded(Bitmap thumbnail, Drawable icon) {
+        this.icon = icon;
+        this.thumbnail = thumbnail;
+        if (mCb != null) {
+            mCb.onTaskBound();
+        }
+    }
+
+    /** Notifies the callback listeners that this task has been unloaded */
+    public void notifyTaskUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) {
+        icon = defaultIcon;
+        thumbnail = defaultThumbnail;
+        if (mCb != null) {
+            mCb.onTaskUnbound();
+        }
+    }
+
     @Override
     public boolean equals(Object o) {
         // If we have multiple task entries for the same task, then we do the simple object
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java
index 169f56c..712580d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java
@@ -20,4 +20,8 @@
 public interface TaskCallbacks {
     /* Notifies when a task's data has been updated */
     public void onTaskDataChanged(Task task);
+    /* Notifies when a task has been bound */
+    public void onTaskBound();
+    /* Notifies when a task has been unbound */
+    public void onTaskUnbound();
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index fe661bc..21ef9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,6 +28,8 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.animation.LinearInterpolator;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
 
 /**
  * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
@@ -176,6 +178,9 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        Console.log(Constants.DebugFlags.UI.TouchEvents,
+                "[SwipeHelper|interceptTouchEvent]",
+                Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
         final int action = ev.getAction();
 
         switch (action) {
@@ -200,7 +205,7 @@
                     if (Math.abs(delta) > mPagingTouchSlop) {
                         mCallback.onBeginDrag(mCurrView);
                         mDragging = true;
-                        mInitialTouchPos = getPos(ev) - getTranslation(mCurrView);
+                        mInitialTouchPos = pos - getTranslation(mCurrView);
                     }
                 }
                 break;
@@ -286,6 +291,10 @@
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
+        Console.log(Constants.DebugFlags.UI.TouchEvents,
+                "[SwipeHelper|touchEvent]",
+                Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+
         if (!mDragging) {
             if (!onInterceptTouchEvent(ev)) {
                 return mCanCurrViewBeDimissed;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 9dd6c0b..7753d69 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -435,19 +435,12 @@
         mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
 
         // Compute the task rect
-        if (RecentsConfiguration.getInstance().layoutVerticalStack) {
-            int minHeight = (int) (mStackRect.height() -
-                    (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
-            int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
-            int centerX = mStackRect.centerX();
-            mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
-                    centerX + size / 2, mStackRectSansPeek.top + size);
-        } else {
-            int size = Math.min(mStackRect.width(), mStackRect.height());
-            int centerY = mStackRect.centerY();
-            mTaskRect.set(mStackRectSansPeek.top, centerY - size / 2,
-                    mStackRectSansPeek.top + size, centerY + size / 2);
-        }
+        int minHeight = (int) (mStackRect.height() -
+                (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
+        int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
+        int centerX = mStackRect.centerX();
+        mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
+                centerX + size / 2, mStackRectSansPeek.top + size);
 
         // Update the scroll bounds
         updateMinMaxScroll(false);
@@ -589,7 +582,6 @@
         // Report that this tasks's data is no longer being used
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         loader.unloadTaskData(task);
-        tv.unbindFromTask();
 
         // Detach the view from the hierarchy
         detachViewFromParent(tv);
@@ -610,7 +602,6 @@
         // Request that this tasks's data be filled
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         loader.loadTaskData(task);
-        tv.syncToTask();
 
         // Find the index where this task should be placed in the children
         int insertIndex = -1;
@@ -678,14 +669,13 @@
 }
 
 /* Handles touch events */
-class TaskStackViewTouchHandler {
+class TaskStackViewTouchHandler implements SwipeHelper.Callback {
     static int INACTIVE_POINTER_ID = -1;
 
     TaskStackView mSv;
     VelocityTracker mVelocityTracker;
 
     boolean mIsScrolling;
-    boolean mIsSwiping;
 
     int mInitialMotionX, mInitialMotionY;
     int mLastMotionX, mLastMotionY;
@@ -697,21 +687,24 @@
     int mMaximumVelocity;
     // The scroll touch slop is used to calculate when we start scrolling
     int mScrollTouchSlop;
-    // The swipe touch slop is used to calculate when we start swiping left/right, this takes
-    // precendence over the scroll touch slop in case the user makes a gesture that starts scrolling
-    // but is intended to be a swipe
-    int mSwipeTouchSlop;
-    // After a certain amount of scrolling, we should start ignoring checks for swiping
-    int mMaxScrollMotionToRejectSwipe;
+    // The page touch slop is used to calculate when we start swiping
+    float mPagingTouchSlop;
+
+    SwipeHelper mSwipeHelper;
+    boolean mInterceptedBySwipeHelper;
 
     public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
         ViewConfiguration configuration = ViewConfiguration.get(context);
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mScrollTouchSlop = configuration.getScaledTouchSlop();
-        mSwipeTouchSlop = 2 * mScrollTouchSlop;
-        mMaxScrollMotionToRejectSwipe = 4 * mScrollTouchSlop;
+        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
         mSv = sv;
+
+
+        float densityScale = context.getResources().getDisplayMetrics().density;
+        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+        mSwipeHelper.setMinAlpha(1f);
     }
 
     /** Velocity tracker helpers */
@@ -754,11 +747,18 @@
                 "[TaskStackViewTouchHandler|interceptTouchEvent]",
                 Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
 
+        // Return early if we have no children
         boolean hasChildren = (mSv.getChildCount() > 0);
         if (!hasChildren) {
             return false;
         }
 
+        // Pass through to swipe helper if we are swiping
+        mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
+        if (mInterceptedBySwipeHelper) {
+            return true;
+        }
+
         boolean wasScrolling = !mSv.mScroller.isFinished() ||
                 (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
         int action = ev.getAction();
@@ -777,7 +777,8 @@
                 mVelocityTracker.addMovement(ev);
                 // Check if the scroller is finished yet
                 mIsScrolling = !mSv.mScroller.isFinished();
-                mIsSwiping = false;
+                // Enable HW layers
+                mSv.addHwLayersRefCount();
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
@@ -786,25 +787,7 @@
                 int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                 int y = (int) ev.getY(activePointerIndex);
                 int x = (int) ev.getX(activePointerIndex);
-                if (mActiveTaskView != null &&
-                        mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
-                        Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
-                        Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
-                    // Start swiping and stop scrolling
-                    mIsScrolling = false;
-                    mIsSwiping = true;
-                    System.out.println("SWIPING: " + mActiveTaskView);
-                    // Initialize the velocity tracker if necessary
-                    initOrResetVelocityTracker();
-                    mVelocityTracker.addMovement(ev);
-                    // Disallow parents from intercepting touch events
-                    final ViewParent parent = mSv.getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    // Enable HW layers
-                    mSv.addHwLayersRefCount();
-                } else if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
+                if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
                     // Save the touch move info
                     mIsScrolling = true;
                     // Initialize the velocity tracker if necessary
@@ -815,8 +798,6 @@
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
                     }
-                    // Enable HW layers
-                    mSv.addHwLayersRefCount();
                 }
 
                 mLastMotionX = x;
@@ -829,16 +810,17 @@
                 mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
                 // Reset the drag state and the velocity tracker
                 mIsScrolling = false;
-                mIsSwiping = false;
                 mActivePointerId = INACTIVE_POINTER_ID;
                 mActiveTaskView = null;
                 mTotalScrollMotion = 0;
                 recycleVelocityTracker();
+                // Disable HW layers
+                mSv.decHwLayersRefCount();
                 break;
             }
         }
 
-        return wasScrolling || mIsScrolling || mIsSwiping;
+        return wasScrolling || mIsScrolling;
     }
 
     /** Handles touch events once we have intercepted them */
@@ -853,6 +835,11 @@
             return false;
         }
 
+        // Pass through to swipe helper if we are swiping
+        if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
+            return true;
+        }
+
         // Update the velocity tracker
         initVelocityTrackerIfNotExists();
         mVelocityTracker.addMovement(ev);
@@ -871,7 +858,6 @@
                 // Initialize the velocity tracker
                 initOrResetVelocityTracker();
                 mVelocityTracker.addMovement(ev);
-                // XXX: Set mIsScrolling or mIsSwiping?
                 // Disallow parents from intercepting touch events
                 final ViewParent parent = mSv.getParent();
                 if (parent != null) {
@@ -886,28 +872,7 @@
                 int x = (int) ev.getX(activePointerIndex);
                 int y = (int) ev.getY(activePointerIndex);
                 int deltaY = mLastMotionY - y;
-                int deltaX = x - mLastMotionX;
-                if (!mIsSwiping) {
-                    if (mActiveTaskView != null &&
-                            mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
-                            Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
-                            Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
-                        mIsScrolling = false;
-                        mIsSwiping = true;
-                        System.out.println("SWIPING: " + mActiveTaskView);
-                        // Initialize the velocity tracker if necessary
-                        initOrResetVelocityTracker();
-                        mVelocityTracker.addMovement(ev);
-                        // Disallow parents from intercepting touch events
-                        final ViewParent parent = mSv.getParent();
-                        if (parent != null) {
-                            parent.requestDisallowInterceptTouchEvent(true);
-                        }
-                        // Enable HW layers
-                        mSv.addHwLayersRefCount();
-                    }
-                }
-                if (!mIsSwiping && !mIsScrolling) {
+                if (!mIsScrolling) {
                     if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
                         mIsScrolling = true;
                         // Initialize the velocity tracker
@@ -927,8 +892,6 @@
                     if (mSv.isScrollOutOfBounds()) {
                         mVelocityTracker.clear();
                     }
-                } else if (mIsSwiping) {
-                    mActiveTaskView.setTranslationX(mActiveTaskView.getTranslationX() + deltaX);
                 }
                 mLastMotionX = x;
                 mLastMotionY = y;
@@ -936,107 +899,33 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
-                if (mIsScrolling || mIsSwiping) {
-                    final TaskView activeTv = mActiveTaskView;
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
 
-                    if (mIsSwiping) {
-                        int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
-                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                            // Fling to dismiss
-                            int newScrollX = (int) (Math.signum(initialVelocity) *
-                                    activeTv.getMeasuredWidth());
-                            int duration = Math.min(Constants.Values.TaskStackView.Animation.SwipeDismissDuration,
-                                    (int) (Math.abs(newScrollX - activeTv.getScrollX()) *
-                                            1000f / Math.abs(initialVelocity)));
-                            activeTv.animate()
-                                    .translationX(newScrollX)
-                                    .alpha(0f)
-                                    .setDuration(duration)
-                                    .setListener(new AnimatorListenerAdapter() {
-                                        @Override
-                                        public void onAnimationEnd(Animator animation) {
-                                            Task task = activeTv.getTask();
-                                            Activity activity = (Activity) mSv.getContext();
-
-                                            // We have to disable the listener to ensure that we
-                                            // don't hit this again
-                                            activeTv.animate().setListener(null);
-
-                                            // Remove the task from the view
-                                            mSv.mStack.removeTask(task);
-
-                                            // Remove any stored data from the loader
-                                            RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-                                            loader.deleteTaskData(task);
-
-                                            // Remove the task from activity manager
-                                            final ActivityManager am = (ActivityManager)
-                                                activity.getSystemService(Context.ACTIVITY_SERVICE);
-                                            if (am != null) {
-                                                am.removeTask(activeTv.getTask().id,
-                                                        ActivityManager.REMOVE_TASK_KILL_PROCESS);
-                                            }
-
-                                            // If there are no remaining tasks, then just close the activity
-                                            if (mSv.mStack.getTaskCount() == 0) {
-                                                activity.finish();
-                                            }
-
-                                            // Disable HW layers
-                                            mSv.decHwLayersRefCount();
-                                        }
-                                    })
-                                    .start();
-                            // Enable HW layers
-                            mSv.addHwLayersRefCount();
-                        } else {
-                            // Animate it back into place
-                            // XXX: Make this animation a function of the velocity OR distance
-                            int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
-                            activeTv.animate()
-                                    .translationX(0)
-                                    .setDuration(duration)
-                                    .setListener(new AnimatorListenerAdapter() {
-                                        @Override
-                                        public void onAnimationEnd(Animator animation) {
-                                            // Disable HW layers
-                                            mSv.decHwLayersRefCount();
-                                        }
-                                    })
-                                    .start();
-                            // Enable HW layers
-                            mSv.addHwLayersRefCount();
-                        }
-                    } else {
-                        int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-                        if ((Math.abs(velocity) > mMinimumVelocity)) {
-                            Console.log(Constants.DebugFlags.UI.TouchEvents,
-                                "[TaskStackViewTouchHandler|fling]",
-                                "scroll: " + mSv.getStackScroll() + " velocity: " + velocity,
-                                    Console.AnsiGreen);
-                            // Enable HW layers on the stack
-                            mSv.addHwLayersRefCount();
-                            // Fling scroll
-                            mSv.mScroller.fling(0, mSv.getStackScroll(),
-                                    0, -velocity,
-                                    0, 0,
-                                    mSv.mMinScroll, mSv.mMaxScroll,
-                                    0, 0);
-                            // Invalidate to kick off computeScroll
-                            mSv.invalidate();
-                        } else if (mSv.isScrollOutOfBounds()) {
-                            // Animate the scroll back into bounds
-                            // XXX: Make this animation a function of the velocity OR distance
-                            mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
-                        }
-                    }
+                if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
+                    Console.log(Constants.DebugFlags.UI.TouchEvents,
+                        "[TaskStackViewTouchHandler|fling]",
+                        "scroll: " + mSv.getStackScroll() + " velocity: " + velocity,
+                            Console.AnsiGreen);
+                    // Enable HW layers on the stack
+                    mSv.addHwLayersRefCount();
+                    // Fling scroll
+                    mSv.mScroller.fling(0, mSv.getStackScroll(),
+                            0, -velocity,
+                            0, 0,
+                            mSv.mMinScroll, mSv.mMaxScroll,
+                            0, 0);
+                    // Invalidate to kick off computeScroll
+                    mSv.invalidate();
+                } else if (mSv.isScrollOutOfBounds()) {
+                    // Animate the scroll back into bounds
+                    // XXX: Make this animation a function of the velocity OR distance
+                    mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
                 }
 
                 mActivePointerId = INACTIVE_POINTER_ID;
                 mIsScrolling = false;
-                mIsSwiping = false;
                 mTotalScrollMotion = 0;
                 recycleVelocityTracker();
                 // Disable HW layers
@@ -1044,25 +933,14 @@
                 break;
             }
             case MotionEvent.ACTION_CANCEL: {
-                if (mIsScrolling || mIsSwiping) {
-                    if (mIsSwiping) {
-                        // Animate it back into place
-                        // XXX: Make this animation a function of the velocity OR distance
-                        int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
-                        mActiveTaskView.animate()
-                                .translationX(0)
-                                .setDuration(duration)
-                                .start();
-                    } else {
-                        // Animate the scroll back into bounds
-                        // XXX: Make this animation a function of the velocity OR distance
-                        mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
-                    }
+                if (mSv.isScrollOutOfBounds()) {
+                    // Animate the scroll back into bounds
+                    // XXX: Make this animation a function of the velocity OR distance
+                    mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
                 }
 
                 mActivePointerId = INACTIVE_POINTER_ID;
                 mIsScrolling = false;
-                mIsSwiping = false;
                 mTotalScrollMotion = 0;
                 recycleVelocityTracker();
                 // Disable HW layers
@@ -1072,4 +950,72 @@
         }
         return true;
     }
+
+    /**** SwipeHelper Implementation ****/
+
+    @Override
+    public View getChildAtPosition(MotionEvent ev) {
+        return findViewAtPoint((int) ev.getX(), (int) ev.getY());
+    }
+
+    @Override
+    public boolean canChildBeDismissed(View v) {
+        return true;
+    }
+
+    @Override
+    public void onBeginDrag(View v) {
+        // Enable HW layers
+        mSv.addHwLayersRefCount();
+        // Disallow parents from intercepting touch events
+        final ViewParent parent = mSv.getParent();
+        if (parent != null) {
+            parent.requestDisallowInterceptTouchEvent(true);
+        }
+    }
+
+    @Override
+    public void onChildDismissed(View v) {
+        TaskView tv = (TaskView) v;
+        Task task = tv.getTask();
+        Activity activity = (Activity) mSv.getContext();
+
+        // We have to disable the listener to ensure that we
+        // don't hit this again
+        tv.animate().setListener(null);
+
+        // Remove the task from the view
+        mSv.mStack.removeTask(task);
+
+        // Remove any stored data from the loader
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        loader.deleteTaskData(task);
+
+        // Remove the task from activity manager
+        final ActivityManager am = (ActivityManager)
+                activity.getSystemService(Context.ACTIVITY_SERVICE);
+        if (am != null) {
+            am.removeTask(tv.getTask().id,
+                    ActivityManager.REMOVE_TASK_KILL_PROCESS);
+        }
+
+        // If there are no remaining tasks, then just close the activity
+        if (mSv.mStack.getTaskCount() == 0) {
+            activity.finish();
+        }
+
+        // Disable HW layers
+        mSv.decHwLayersRefCount();
+    }
+
+    @Override
+    public void onSnapBackCompleted(View v) {
+        // Do Nothing
+    }
+
+    @Override
+    public void onDragCancelled(View v) {
+        // Disable HW layers
+        mSv.decHwLayersRefCount();
+    }
 }
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 b1d0d13..9ef74ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -255,13 +255,13 @@
     }
 
     /** Actually synchronizes the model data into the views */
-    void syncToTask() {
+    private void syncToTask() {
         mThumbnailView.rebindToTask(mTask, false);
         mIconView.rebindToTask(mTask, false);
     }
 
     /** Unset the task and callback */
-    void unbindFromTask() {
+    private void unbindFromTask() {
         mTask.setCallbacks(null);
         mThumbnailView.unbindFromTask();
         mIconView.unbindFromTask();
@@ -357,16 +357,16 @@
 
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
-        Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|enableHwLayers]");
         mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
-        Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|disableHwLayers]");
         mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
     }
 
+    /**** TaskCallbacks Implementation ****/
+
     @Override
     public void onTaskDataChanged(Task task) {
         Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
@@ -380,6 +380,16 @@
     }
 
     @Override
+    public void onTaskBound() {
+        syncToTask();
+    }
+
+    @Override
+    public void onTaskUnbound() {
+        unbindFromTask();
+    }
+
+    @Override
     public void onClick(View v) {
         mCb.onTaskIconClicked(this);
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 6666385..b235408 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -142,6 +142,13 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static boolean native_isConvex(long nPath) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.isConvex is not supported.", null, null);
+        return true;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static int native_getFillType(long nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 998b08b..b16b4aa 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.ULocale;
 
 import java.util.Locale;
@@ -117,6 +118,11 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static int getCurrencyNumericCode(String currencyCode) {
+        return Currency.getInstance(currencyCode).getNumericCode();
+    }
+
+    @LayoutlibDelegate
     /*package*/ static String getCurrencySymbol(String locale, String currencyCode) {
         return "";
     }