Initial changes to support lock-to-app on the foremost task. (Bug 16221876)	
diff --git a/packages/SystemUI/res/drawable/ic_lock_to_app_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_to_app_24dp.xml
new file mode 100644
index 0000000..e5737ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_to_app_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24.0dp"
+        android:height="24.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="@color/recents_task_view_lock_to_app_button_color"
+        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml b/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml
new file mode 100644
index 0000000..d38c8a4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+     android:color="#ffdadada">
+    <item android:drawable="@color/recents_task_view_lock_to_app_button_background_color" />
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 2b50a95..7e8bfd32 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -62,6 +62,27 @@
             android:visibility="invisible"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
+    <FrameLayout
+        android:id="@+id/lock_to_app"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/recents_task_view_lock_to_app_button_height"
+        android:layout_gravity="center_horizontal|bottom"
+        android:background="@drawable/recents_lock_to_task_button_bg"
+        android:visibility="invisible">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center_horizontal"
+            android:gravity="center"
+            android:drawableLeft="@drawable/ic_lock_to_app_24dp"
+            android:drawablePadding="8dp"
+            android:textSize="16sp"
+            android:textColor="@color/recents_task_view_lock_to_app_button_color"
+            android:text="@string/recents_lock_to_app_button_label"
+            android:fontFamily="sans-serif-medium"
+            android:singleLine="true"
+            android:textAllCaps="true" />
+    </FrameLayout>
 </com.android.systemui.recents.views.TaskView>
 
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ef8302d..4cc0bb5 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -53,13 +53,17 @@
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
-    <color name="recents_task_bar_dark_text_color">#ff333333</color>
+    <color name="recents_task_bar_dark_text_color">#cc000000</color>
     <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
     <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
-    <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
+    <color name="recents_task_bar_dark_dismiss_color">#cc000000</color>
     <!-- The recents task bar highlight color. -->
     <color name="recents_task_bar_highlight_color">#28ffffff</color>
+    <!-- The lock to task button background color. -->
+    <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
+    <!-- The lock to task button foreground color. -->
+    <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
 
     <color name="keyguard_affordance">#ffffffff</color>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f8b04ae..94fcc23 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -127,6 +127,10 @@
     <integer name="recents_animate_task_enter_from_home_duration">275</integer>
     <!-- The animation stagger to apply to each task animation when transitioning from home. -->
     <integer name="recents_animate_task_enter_from_home_delay">10</integer>
+    <!-- The short duration when animating in/out the lock to app button. -->
+    <integer name="recents_animate_lock_to_app_button_short_duration">150</integer>
+    <!-- The long duration when animating in/out the lock to app button. -->
+    <integer name="recents_animate_lock_to_app_button_long_duration">300</integer>
     <!-- The min animation duration for animating the nav bar scrim in. -->
     <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
     <!-- The animation duration for animating the removal of a task view. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e20947f..e86aa0a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -210,6 +210,9 @@
     <!-- The amount of highlight to make on each task view. -->
     <dimen name="recents_task_view_highlight">1dp</dimen>
 
+    <!-- The height of the lock-to-app button. -->
+    <dimen name="recents_task_view_lock_to_app_button_height">48dp</dimen>
+
     <!-- The height of a task view bar. -->
     <dimen name="recents_task_bar_height">56dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 848fdf8..48670fb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -563,6 +563,8 @@
     <string name="recents_empty_message">No recent apps</string>
     <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
     <string name="recents_app_info_button_label">Application Info</string>
+    <!-- Recents: The lock-to-app button. [CHAR LIMIT=NONE] -->
+    <string name="recents_lock_to_app_button_label">lock to app</string>
     <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
     <string name="recents_search_bar_label">search</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index e375433..a9a606f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -68,6 +68,7 @@
     // Recents service binding
     Handler mHandler;
     boolean mBootCompleted = false;
+    boolean mStartAnimationTriggered = false;
 
     // Task launching
     RecentsConfiguration mConfig;
@@ -252,6 +253,7 @@
      * Creates the activity options for a unknown state->recents transition.
      */
     ActivityOptions getUnknownTransitionActivityOptions() {
+        mStartAnimationTriggered = false;
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_unknown_enter,
                 R.anim.recents_from_unknown_exit, mHandler, this);
@@ -261,6 +263,7 @@
      * Creates the activity options for a home->recents transition.
      */
     ActivityOptions getHomeTransitionActivityOptions() {
+        mStartAnimationTriggered = false;
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_launcher_enter,
                 R.anim.recents_from_launcher_exit, mHandler, this);
@@ -279,6 +282,7 @@
             // Take the full screenshot
             sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
             if (sLastScreenshot != null) {
+                mStartAnimationTriggered = false;
                 return ActivityOptions.makeCustomAnimation(mContext,
                         R.anim.recents_from_app_enter,
                         R.anim.recents_from_app_exit, mHandler, this);
@@ -302,6 +306,7 @@
                 c.setBitmap(null);
                 // Recycle the old thumbnail
                 firstThumbnail.recycle();
+                mStartAnimationTriggered = false;
                 return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
                         thumbnail, toTaskRect.left, toTaskRect.top, this);
             }
@@ -449,9 +454,12 @@
     @Override
     public void onAnimationStarted() {
         // Notify recents to start the enter animation
-        Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
-        intent.setPackage(mContext.getPackageName());
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcast(intent);
+        if (!mStartAnimationTriggered) {
+            Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+            intent.setPackage(mContext.getPackageName());
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendBroadcast(intent);
+            mStartAnimationTriggered = true;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index b039485..e62d989 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -92,6 +92,11 @@
     public int taskBarExitAnimDuration;
     public int taskBarDismissDozeDelaySeconds;
 
+    /** Lock to app */
+    public int taskViewLockToAppButtonHeight;
+    public int taskViewLockToAppShortAnimDuration;
+    public int taskViewLockToAppLongAnimDuration;
+
     /** Nav bar scrim */
     public int navBarScrimEnterDuration;
 
@@ -226,6 +231,14 @@
         taskBarDismissDozeDelaySeconds =
                 res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
 
+        // Lock to app
+        taskViewLockToAppButtonHeight =
+                res.getDimensionPixelSize(R.dimen.recents_task_view_lock_to_app_button_height);
+        taskViewLockToAppShortAnimDuration =
+                res.getInteger(R.integer.recents_animate_lock_to_app_button_short_duration);
+        taskViewLockToAppLongAnimDuration =
+                res.getInteger(R.integer.recents_animate_lock_to_app_button_long_duration);
+
         // Nav bar scrim
         navBarScrimEnterDuration =
                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 05c0f58..b8beda6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -17,8 +17,10 @@
 package com.android.systemui.recents.misc;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.IActivityManager;
 import android.app.SearchManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
@@ -66,6 +68,7 @@
     final static String TAG = "SystemServicesProxy";
 
     ActivityManager mAm;
+    IActivityManager mIam;
     AppWidgetManager mAwm;
     PackageManager mPm;
     IPackageManager mIpm;
@@ -83,6 +86,7 @@
     /** Private constructor */
     public SystemServicesProxy(Context context) {
         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        mIam = ActivityManagerNative.getDefault();
         mAwm = AppWidgetManager.getInstance(context);
         mPm = context.getPackageManager();
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -407,6 +411,17 @@
     }
 
     /**
+     * Locks the current task.
+     */
+    public void lockCurrentTask() {
+        if (mIam == null) return;
+
+        try {
+            mIam.startLockTaskModeOnCurrent();
+        } catch (RemoteException e) {}
+    }
+
+    /**
      * Takes a screenshot of the current surface.
      */
     public Bitmap takeScreenshot() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 94474e9..86e8981 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -429,7 +429,8 @@
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, 0, activityLabel,
-                    activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
+                    activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime,
+                    (i == (taskCount - 1)));
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
@@ -523,7 +524,7 @@
             if (info == null) continue;
 
             stack.addTask(new Task(t.persistentId, true, t.baseIntent, 0, null, null, 0, 0,
-                    t.firstActiveTime, t.lastActiveTime));
+                    t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1))));
         }
         stack.createSimulatedAffiliatedGroupings();
         return stack;
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 0667e4c..88e9f40 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -87,6 +87,7 @@
     public int colorPrimaryGreyscale;
     public Bitmap thumbnail;
     public boolean isActive;
+    public boolean canLockToTask;
     public int userId;
 
     TaskCallbacks mCb;
@@ -97,7 +98,7 @@
 
     public Task(int id, boolean isActive, Intent intent, int taskAffiliation, String activityTitle,
                 Drawable activityIcon, int colorPrimary, int userId,
-                long firstActiveTime, long lastActiveTime) {
+                long firstActiveTime, long lastActiveTime, boolean canLockToTask) {
         this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime);
         this.taskAffiliation = taskAffiliation;
         this.activityLabel = activityTitle;
@@ -105,6 +106,7 @@
         this.colorPrimary = colorPrimary;
         this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary);
         this.isActive = isActive;
+        this.canLockToTask = canLockToTask;
         this.userId = userId;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 1ed0edd..7dd15a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -150,7 +150,7 @@
         /* Notifies when a task has been added to the stack */
         public void onStackTaskAdded(TaskStack stack, Task t);
         /* Notifies when a task has been removed from the stack */
-        public void onStackTaskRemoved(TaskStack stack, Task t);
+        public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask);
         /** Notifies when the stack was filtered */
         public void onStackFiltered(TaskStack newStack, ArrayList<Task> curTasks, Task t);
         /** Notifies when the stack was un-filtered */
@@ -203,9 +203,15 @@
             if (group.getTaskCount() == 0) {
                 removeGroup(group);
             }
+            // Update the lock-to-app state
+            Task newFrontMostTask = getFrontMostTask();
+            t.canLockToTask = false;
+            if (newFrontMostTask != null) {
+                newFrontMostTask.canLockToTask = true;
+            }
             if (mCb != null) {
                 // Notify that a task has been removed
-                mCb.onStackTaskRemoved(this, t);
+                mCb.onStackTaskRemoved(this, t, newFrontMostTask);
             }
         }
     }
@@ -226,7 +232,7 @@
             }
             if (mCb != null) {
                 // Notify that a task has been removed
-                mCb.onStackTaskRemoved(this, t);
+                mCb.onStackTaskRemoved(this, t, null);
             }
         }
         mTaskList.set(tasks);
@@ -239,6 +245,7 @@
 
     /** Gets the front task */
     public Task getFrontMostTask() {
+        if (mTaskList.size() == 0) return null;
         return mTaskList.getTasks().get(mTaskList.size() - 1);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 85afb32..99b012e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,9 +34,10 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
@@ -144,7 +145,7 @@
                             Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
                                     "Found focused Task");
                         }
-                        onTaskViewClicked(stackView, tv, stack, task);
+                        onTaskViewClicked(stackView, tv, stack, task, false);
                         return true;
                     }
                 }
@@ -180,7 +181,7 @@
                             tv = stv;
                         }
                     }
-                    onTaskViewClicked(stackView, tv, stack, task);
+                    onTaskViewClicked(stackView, tv, stack, task, false);
                     return true;
                 }
             }
@@ -431,7 +432,7 @@
 
     @Override
     public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
-                                  final TaskStack stack, final Task task) {
+                                  final TaskStack stack, final Task task, final boolean lockToTask) {
         // Notify any callbacks of the launching of a new task
         if (mCb != null) {
             mCb.onTaskViewClicked();
@@ -456,6 +457,8 @@
         }
 
         // Compute the thumbnail to scale up from
+        final SystemServicesProxy ssp =
+                RecentsTaskLoader.getInstance().getSystemServicesProxy();
         ActivityOptions opts = null;
         int thumbnailWidth = transform.rect.width();
         int thumbnailHeight = transform.rect.height();
@@ -469,8 +472,26 @@
                     new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
                     new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
             c.setBitmap(null);
+            ActivityOptions.OnAnimationStartedListener animStartedListener = null;
+            if (lockToTask) {
+                animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+                    boolean mTriggered = false;
+                    @Override
+                    public void onAnimationStarted() {
+                        if (!mTriggered) {
+                            postDelayed(new Runnable() {
+                                @Override
+                                public void run() {
+                                    ssp.lockCurrentTask();
+                                }
+                            }, 350);
+                            mTriggered = true;
+                        }
+                    }
+                };
+            }
             opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
-                    b, offsetX, offsetY);
+                    b, offsetX, offsetY, animStartedListener);
         }
 
         final ActivityOptions launchOpts = opts;
@@ -496,8 +517,12 @@
                         UserHandle taskUser = new UserHandle(task.userId);
                         if (launchOpts != null) {
                             getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
+
                         } else {
                             getContext().startActivityAsUser(i, taskUser);
+                            if (lockToTask) {
+                                ssp.lockCurrentTask();
+                            }
                         }
                     } catch (ActivityNotFoundException anfe) {
                         Console.logError(getContext(), "Could not start Activity");
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 35cf8ab..599c590 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -51,11 +51,12 @@
 /* The visual representation of a task stack view */
 public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
         TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
-        View.OnClickListener, RecentsPackageMonitor.PackageCallbacks {
+        RecentsPackageMonitor.PackageCallbacks {
 
     /** The TaskView callbacks */
     interface TaskStackViewCallbacks {
-        public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
+        public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
+                                      boolean lockToTask);
         public void onTaskViewAppInfoClicked(Task t);
         public void onTaskViewDismissed(Task t);
         public void onAllTaskViewsDismissed();
@@ -734,7 +735,8 @@
         for (int i = 0; i < childCount; i++) {
             TaskView t = (TaskView) getChildAt(i);
             t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height(), MeasureSpec.EXACTLY));
+                    MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
+                            mConfig.taskViewLockToAppButtonHeight, MeasureSpec.EXACTLY));
         }
 
         setMeasuredDimension(width, height);
@@ -766,7 +768,7 @@
             TaskView t = (TaskView) getChildAt(i);
             t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
                     mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
-                    mStackAlgorithm.mTaskRect.height());
+                    mStackAlgorithm.mTaskRect.height() + mConfig.taskViewLockToAppButtonHeight);
         }
 
         if (mAwaitingFirstLayout) {
@@ -903,11 +905,6 @@
         mUIDozeTrigger.poke();
     }
 
-    /** Disables handling touch on this task view. */
-    void setTouchOnTaskView(TaskView tv, boolean enabled) {
-        tv.setOnClickListener(enabled ? this : null);
-    }
-
     /**** TaskStackCallbacks Implementation ****/
 
     @Override
@@ -919,25 +916,33 @@
     }
 
     @Override
-    public void onStackTaskRemoved(TaskStack stack, Task t) {
+    public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
         // Update the task offsets
         mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
 
         // Remove the view associated with this task, we can't rely on updateTransforms
         // to work here because the task is no longer in the list
-        TaskView tv = getChildViewForTask(t);
+        TaskView tv = getChildViewForTask(removedTask);
         if (tv != null) {
             mViewPool.returnViewToPool(tv);
         }
 
         // Notify the callback that we've removed the task and it can clean up after it
-        mCb.onTaskViewDismissed(t);
+        mCb.onTaskViewDismissed(removedTask);
 
         // Update the min/max scroll and animate other task views into their new positions
         updateMinMaxScroll(true);
         int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
         requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
 
+        // Update the new front most task
+        if (newFrontMostTask != null) {
+            TaskView frontTv = getChildViewForTask(newFrontMostTask);
+            if (frontTv != null) {
+                frontTv.onTaskBound(newFrontMostTask);
+            }
+        }
+
         // If there are no remaining tasks, then either unfilter the current stack, or just close
         // the activity if there are no filtered stacks
         if (mStack.getTaskCount() == 0) {
@@ -1086,7 +1091,7 @@
             addView(tv, insertIndex);
 
             // Set the callbacks and listeners for this new view
-            setTouchOnTaskView(tv, true);
+            tv.setTouchEnabled(true);
             tv.setCallbacks(this);
         } else {
             attachViewToParent(tv, insertIndex, tv.getLayoutParams());
@@ -1129,18 +1134,7 @@
     }
 
     @Override
-    public void onTaskViewDismissed(TaskView tv) {
-        Task task = tv.getTask();
-        // Remove the task from the view
-        mStack.removeTask(task);
-    }
-
-    /**** View.OnClickListener Implementation ****/
-
-    @Override
-    public void onClick(View v) {
-        TaskView tv = (TaskView) v;
-        Task task = tv.getTask();
+    public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
         if (Console.Enabled) {
             Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
                     task + " cb: " + mCb);
@@ -1150,10 +1144,17 @@
         mUIDozeTrigger.stopDozing();
 
         if (mCb != null) {
-            mCb.onTaskViewClicked(this, tv, mStack, task);
+            mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask);
         }
     }
 
+    @Override
+    public void onTaskViewDismissed(TaskView tv) {
+        Task task = tv.getTask();
+        // Remove the task from the view
+        mStack.removeTask(task);
+    }
+
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 789b4f7..e1e682b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -95,9 +95,11 @@
 
         if (numTasks <= 1) {
             // If there is only one task, then center the task in the stack rect (sans peek)
-            mMinScroll = mMaxScroll = -(stackHeight - taskHeight) / 2;
+            mMinScroll = mMaxScroll = -(stackHeight -
+                    (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
         } else {
-            int maxScrollHeight = taskHeight + getStackScrollForTaskIndex(tasks.get(tasks.size() - 1));
+            int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
+                    + taskHeight + mConfig.taskViewLockToAppButtonHeight;
             mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
             mMaxScroll = maxScrollHeight - stackHeight;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index e186e2e..15ace13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -377,7 +377,9 @@
         // Enable HW layers on that task
         tv.enableHwLayers();
         // Disallow touch events from this task view
-        mSv.setTouchOnTaskView(tv, false);
+        tv.setTouchEnabled(false);
+        // Hide the footer
+        tv.animateFooterVisibility(false, mSv.mConfig.taskViewLockToAppShortAnimDuration, 0);
         // Disallow parents from intercepting touch events
         final ViewParent parent = mSv.getParent();
         if (parent != null) {
@@ -413,7 +415,9 @@
         // Re-enable clipping with the stack
         tv.setClipViewInStack(true);
         // Re-enable touch events from this task view
-        mSv.setTouchOnTaskView(tv, true);
+        tv.setTouchEnabled(true);
+        // Restore the footer
+        tv.animateFooterVisibility(true, mSv.mConfig.taskViewLockToAppShortAnimDuration, 0);
     }
 
     @Override
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 33e3f58..125b018 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -35,6 +35,7 @@
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
 
 
 /* A task view */
@@ -44,11 +45,16 @@
     interface TaskViewCallbacks {
         public void onTaskViewAppIconClicked(TaskView tv);
         public void onTaskViewAppInfoClicked(TaskView tv);
+        public void onTaskViewClicked(TaskView tv, Task t, boolean lockToTask);
         public void onTaskViewDismissed(TaskView tv);
     }
 
     RecentsConfiguration mConfig;
 
+    int mFooterHeight;
+    int mMaxFooterHeight;
+    ObjectAnimator mFooterAnimator;
+
     int mDim;
     int mMaxDim;
     AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -60,9 +66,11 @@
     boolean mClipViewInStack;
     Rect mTmpRect = new Rect();
     Paint mLayerPaint = new Paint();
+    Outline mOutline = new Outline();
 
     TaskThumbnailView mThumbnailView;
     TaskBarView mBarView;
+    View mLockToAppButtonView;
     TaskViewCallbacks mCb;
 
     // Optimizations
@@ -102,9 +110,11 @@
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
+        mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
         setWillNotDraw(false);
         setClipToOutline(true);
         setDim(getDim());
+        setFooterHeight(getFooterHeight());
     }
 
     @Override
@@ -117,6 +127,7 @@
         // Bind the views
         mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
         mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
+        mLockToAppButtonView = findViewById(R.id.lock_to_app);
 
         if (mTaskDataLoaded) {
             onTaskDataLoaded();
@@ -125,13 +136,33 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
 
-        // Update the outline
-        Outline o = new Outline();
-        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+        // Measure the bar view, thumbnail, and lock-to-app buttons
+        mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
+        mLockToAppButtonView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
+                        MeasureSpec.EXACTLY));
+        // Measure the thumbnail height to be the same as the width
+        mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
+        setMeasuredDimension(width, height);
+        updateOutline();
+    }
+
+    /** Updates the outline to match whether the lock-to-app button is visible or not. */
+    void updateOutline() {
+        int height = getMeasuredHeight();
+        if (height == 0) return;
+
+        // Account for the current footer height
+        height = height - mMaxFooterHeight + mFooterHeight;
+
+        mOutline.setRoundRect(0, 0, getMeasuredWidth(), height,
                 mConfig.taskViewRoundedCornerRadiusPx);
-        setOutline(o);
+        setOutline(mOutline);
     }
 
     /** Set callback */
@@ -289,6 +320,8 @@
                         // Animate the task bar of the first task view
                         mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
                         setVisibility(View.VISIBLE);
+                        // Animate the footer into view
+                        animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration, 0);
                         // Decrement the post animation trigger
                         ctx.postAnimationTrigger.decrement();
                     }
@@ -335,6 +368,10 @@
                 });
                 anim.start();
                 ctx.postAnimationTrigger.increment();
+
+                // Animate the footer into view
+                animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration,
+                        mConfig.taskBarEnterAnimDelay);
             } else {
                 mEnableThumbnailClip.run();
             }
@@ -366,9 +403,16 @@
                     })
                     .start();
             ctx.postAnimationTrigger.increment();
+
+            // Animate the footer into view
+            animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration,
+                    mConfig.taskBarEnterAnimDelay);
         } else {
             // Otherwise, just enable the thumbnail clip
             mEnableThumbnailClip.run();
+
+            // Animate the footer into view
+            animateFooterVisibility(true, 0, 0);
         }
     }
 
@@ -457,12 +501,14 @@
     void enableHwLayers() {
         mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
         mBarView.enableHwLayers();
+        mLockToAppButtonView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
         mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
         mBarView.disableHwLayers();
+        mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 
     /** Sets the stubbed state of this task view. */
@@ -499,6 +545,57 @@
         }
     }
 
+    /** Sets the footer height. */
+    public void setFooterHeight(int height) {
+        mFooterHeight = height;
+        updateOutline();
+        invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
+                getMeasuredHeight());
+    }
+
+    /** Gets the footer height. */
+    public int getFooterHeight() {
+        return mFooterHeight;
+    }
+
+    /** Animates the footer into and out of view. */
+    public void animateFooterVisibility(boolean visible, int duration, int delay) {
+        if (!mTask.canLockToTask) return;
+        if (mMaxFooterHeight <= 0) return;
+
+        if (mFooterAnimator != null) {
+            mFooterAnimator.removeAllListeners();
+            mFooterAnimator.cancel();
+        }
+        int height = visible ? mMaxFooterHeight : 0;
+        if (visible && mLockToAppButtonView.getVisibility() != View.VISIBLE) {
+            if (duration > 0) {
+                setFooterHeight(0);
+            } else {
+                setFooterHeight(mMaxFooterHeight);
+            }
+            mLockToAppButtonView.setVisibility(View.VISIBLE);
+        }
+        if (duration > 0) {
+            mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", height);
+            mFooterAnimator.setDuration(duration);
+            mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+            if (!visible) {
+                mFooterAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mLockToAppButtonView.setVisibility(View.INVISIBLE);
+                    }
+                });
+            }
+            mFooterAnimator.start();
+        } else {
+            if (!visible) {
+                mLockToAppButtonView.setVisibility(View.INVISIBLE);
+            }
+        }
+    }
+
     /** Returns the current dim. */
     public void setDim(int dim) {
         mDim = dim;
@@ -584,6 +681,11 @@
     public void onTaskBound(Task t) {
         mTask = t;
         mTask.setCallbacks(this);
+        if (getMeasuredWidth() == 0) {
+            animateFooterVisibility(t.canLockToTask, 0, 0);
+        } else {
+            animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0);
+        }
     }
 
     @Override
@@ -597,6 +699,7 @@
                 mBarView.mApplicationIcon.setOnClickListener(this);
             }
             mBarView.mDismissButton.setOnClickListener(this);
+            mLockToAppButtonView.setOnClickListener(this);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 if (mConfig.developerOptionsEnabled) {
                     mBarView.mApplicationIcon.setOnLongClickListener(this);
@@ -618,6 +721,7 @@
                 mBarView.mApplicationIcon.setOnClickListener(null);
             }
             mBarView.mDismissButton.setOnClickListener(null);
+            mLockToAppButtonView.setOnClickListener(null);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 mBarView.mApplicationIcon.setOnLongClickListener(null);
             }
@@ -625,6 +729,11 @@
         mTaskDataLoaded = false;
     }
 
+    /** Enables/disables handling touch on this task view. */
+    void setTouchEnabled(boolean enabled) {
+        setOnClickListener(enabled ? this : null);
+    }
+
     @Override
     public void onClick(final View v) {
         // We purposely post the handler delayed to allow for the touch feedback to draw
@@ -642,6 +751,10 @@
                             mCb.onTaskViewDismissed(tv);
                         }
                     });
+                    // Hide the footer
+                    tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration, 0);
+                } else if (v == tv || v == mLockToAppButtonView) {
+                    mCb.onTaskViewClicked(tv, tv.getTask(), (v == mLockToAppButtonView));
                 }
             }
         }, 125);