Initial changes to enable keyboard support with alternate Recents. (Bug 14067913)

Change-Id: Icc5d2a784ed3c3c27143eb04cbb4305549ee223a
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 75feb5d..8fa662d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -36,9 +36,12 @@
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
     void setHardKeyboardStatus(boolean available, boolean enabled);
+    void setWindowState(int window, int state);
+
+    void showRecentApps(boolean triggeredFromAltTab);
+    void hideRecentApps();
     void toggleRecentApps();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
-    void setWindowState(int window, int state);
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index cf334c3..9ebfd78 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -52,8 +52,11 @@
             in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys);
     void setSystemUiVisibility(int vis, int mask);
     void setHardKeyboardEnabled(boolean enabled);
+    void setWindowState(int window, int state);
+
+    void showRecentApps(boolean triggeredFromAltTab);
+    void hideRecentApps();
     void toggleRecentApps();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
-    void setWindowState(int window, int state);
 }
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index bda6431..a68ad2f 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -16,7 +16,8 @@
 <com.android.systemui.recents.views.TaskView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent" 
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:focusable="true">
     <com.android.systemui.recents.views.TaskThumbnailView
         android:id="@+id/task_view_thumbnail"
         android:layout_width="match_parent"
@@ -69,7 +70,7 @@
             android:layout_height="@dimen/recents_task_view_application_icon_size"
             android:layout_gravity="center_vertical|end"
             android:padding="23dp"
-            android:src="@drawable/recents_dismiss_dark" />
+            android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
 
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 323905f..105f70e 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -20,11 +20,9 @@
 import android.view.View;
 
 public interface RecentsComponent {
+    void showRecents(boolean triggeredFromAltTab, View statusBarView);
+    void hideRecents();
     void toggleRecents(Display display, int layoutDirection, View statusBarView);
-
-    void preloadRecentTasksList();
-
-    void cancelPreloadingRecentTasksList();
-
-    void closeRecents();
+    void preloadRecents();
+    void cancelPreloadingRecents();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index ae18aa8..d3e949f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -27,7 +27,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -68,10 +67,30 @@
     }
 
     @Override
+    public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
+        if (mUseAlternateRecents) {
+            mAlternateRecents.onShowRecents(triggeredFromAltTab, statusBarView);
+        }
+    }
+
+    @Override
+    public void hideRecents() {
+        if (mUseAlternateRecents) {
+            mAlternateRecents.onHideRecents();
+        } else {
+            Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
+            intent.setPackage("com.android.systemui");
+            sendBroadcastSafely(intent);
+
+            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+        }
+    }
+
+    @Override
     public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
         if (mUseAlternateRecents) {
             // Launch the alternate recents if required
-            mAlternateRecents.onToggleRecents(display, layoutDirection, statusBarView);
+            mAlternateRecents.onToggleRecents(statusBarView);
             return;
         }
 
@@ -224,7 +243,7 @@
     }
 
     @Override
-    public void preloadRecentTasksList() {
+    public void preloadRecents() {
         if (mUseAlternateRecents) {
             mAlternateRecents.onPreloadRecents();
         } else {
@@ -238,7 +257,7 @@
     }
 
     @Override
-    public void cancelPreloadingRecentTasksList() {
+    public void cancelPreloadingRecents() {
         if (mUseAlternateRecents) {
             mAlternateRecents.onCancelPreloadingRecents();
         } else {
@@ -251,19 +270,6 @@
         }
     }
 
-    @Override
-    public void closeRecents() {
-        if (mUseAlternateRecents) {
-            mAlternateRecents.onCloseRecents();
-        } else {
-            Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
-            intent.setPackage("com.android.systemui");
-            sendBroadcastSafely(intent);
-
-            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
-        }
-    }
-
     /**
      * Send broadcast only if BOOT_COMPLETED
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 19a1b11..ec50bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -44,9 +44,9 @@
 import android.view.WindowManager;
 import com.android.systemui.R;
 
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /** A proxy implementation for the recents component */
 public class AlternateRecentsComponent {
@@ -72,7 +72,7 @@
                 // If we had the update the animation rects as a result of onServiceConnected, then
                 // we check for whether we need to toggle the recents here.
                 if (mToggleRecentsUponServiceBound) {
-                    startAlternateRecentsActivity();
+                    startRecentsActivity();
                     mToggleRecentsUponServiceBound = false;
                 }
             }
@@ -90,9 +90,9 @@
             mServiceIsBound = true;
 
             if (hasValidTaskRects()) {
-                // Toggle recents if this new service connection was triggered by hitting recents
+                // Start recents if this new service connection was triggered by hitting recents
                 if (mToggleRecentsUponServiceBound) {
-                    startAlternateRecentsActivity();
+                    startRecentsActivity();
                     mToggleRecentsUponServiceBound = false;
                 }
             } else {
@@ -114,10 +114,12 @@
     final public static int MSG_UPDATE_TASK_THUMBNAIL = 1;
     final public static int MSG_PRELOAD_TASKS = 2;
     final public static int MSG_CANCEL_PRELOAD_TASKS = 3;
-    final public static int MSG_CLOSE_RECENTS = 4;
-    final public static int MSG_TOGGLE_RECENTS = 5;
+    final public static int MSG_SHOW_RECENTS = 4;
+    final public static int MSG_HIDE_RECENTS = 5;
+    final public static int MSG_TOGGLE_RECENTS = 6;
 
     final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
+    final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
     final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
     final public static String KEY_WINDOW_RECT = "recents.windowRect";
     final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
@@ -142,7 +144,10 @@
     boolean mToggleRecentsUponServiceBound;
     RecentsServiceConnection mConnection = new RecentsServiceConnection();
 
+    // Variables to keep track of if we need to start recents after binding
     View mStatusBarView;
+    boolean mTriggeredFromAltTab;
+
     Rect mSingleCountFirstTaskRect = new Rect();
     Rect mMultipleCountFirstTaskRect = new Rect();
     long mLastToggleTime;
@@ -160,15 +165,11 @@
         bindToRecentsService(false);
     }
 
-    /** Toggles the alternate recents activity */
-    public void onToggleRecents(Display display, int layoutDirection, View statusBarView) {
-        Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup,
-                Constants.Log.App.TimeRecentsStartupKey);
-        Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
-                Constants.Log.App.TimeRecentsLaunchKey);
-        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
-                "serviceIsBound: " + mServiceIsBound);
+    /** Shows the recents */
+    public void onShowRecents(boolean triggeredFromAltTab, View statusBarView) {
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|showRecents]");
         mStatusBarView = statusBarView;
+        mTriggeredFromAltTab = triggeredFromAltTab;
         if (!mServiceIsBound) {
             // Try to create a long-running connection to the recents service before toggling
             // recents
@@ -177,7 +178,47 @@
         }
 
         try {
-            startAlternateRecentsActivity();
+            startRecentsActivity();
+        } catch (ActivityNotFoundException e) {
+            Console.logRawError("Failed to launch RecentAppsIntent", e);
+        }
+    }
+
+    /** Hides the recents */
+    public void onHideRecents() {
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
+        if (mServiceIsBound) {
+            // Notify recents to close it
+            try {
+                Bundle data = new Bundle();
+                Message msg = Message.obtain(null, MSG_HIDE_RECENTS, 0, 0);
+                msg.setData(data);
+                mService.send(msg);
+            } catch (RemoteException re) {
+                re.printStackTrace();
+            }
+        }
+    }
+
+    /** Toggles the alternate recents activity */
+    public void onToggleRecents(View statusBarView) {
+        Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey);
+        Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
+                Constants.Log.App.TimeRecentsLaunchKey);
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
+                "serviceIsBound: " + mServiceIsBound);
+        mStatusBarView = statusBarView;
+        mTriggeredFromAltTab = false;
+        if (!mServiceIsBound) {
+            // Try to create a long-running connection to the recents service before toggling
+            // recents
+            bindToRecentsService(true);
+            return;
+        }
+
+        try {
+            toggleRecentsActivity();
         } catch (ActivityNotFoundException e) {
             Console.logRawError("Failed to launch RecentAppsIntent", e);
         }
@@ -191,21 +232,6 @@
         // Do nothing
     }
 
-    public void onCloseRecents() {
-        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|closeRecents]");
-        if (mServiceIsBound) {
-            // Try and update the recents configuration
-            try {
-                Bundle data = new Bundle();
-                Message msg = Message.obtain(null, MSG_CLOSE_RECENTS, 0, 0);
-                msg.setData(data);
-                mService.send(msg);
-            } catch (RemoteException re) {
-                re.printStackTrace();
-            }
-        }
-    }
-
     public void onConfigurationChanged(Configuration newConfig) {
         updateAnimationRects();
     }
@@ -355,8 +381,32 @@
                 taskRect.left, taskRect.top, null);
     }
 
-    /** Starts the recents activity */
-    void startAlternateRecentsActivity() {
+    /** Returns whether the recents is currently running */
+    boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
+        SystemServicesProxy ssp = mSystemServicesProxy;
+        List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
+        if (!tasks.isEmpty()) {
+            ActivityManager.RunningTaskInfo topTask = tasks.get(0);
+            ComponentName topActivity = topTask.topActivity;
+
+            // Check if the front most activity is recents
+            if (topActivity.getPackageName().equals(sRecentsPackage) &&
+                    topActivity.getClassName().equals(sRecentsActivity)) {
+                if (isHomeTopMost != null) {
+                    isHomeTopMost.set(false);
+                }
+                return true;
+            }
+
+            if (isHomeTopMost != null) {
+                isHomeTopMost.set(ssp.isInHomeStack(topTask.id));
+            }
+        }
+        return false;
+    }
+
+    /** Toggles the recents activity */
+    void toggleRecentsActivity() {
         // If the user has toggled it too quickly, then just eat up the event here (it's better than
         // showing a janky screenshot).
         // NOTE: Ideally, the screenshot mechanism would take the window transform into account
@@ -366,43 +416,47 @@
 
         // If Recents is the front most activity, then we should just communicate with it directly
         // to launch the first task or dismiss itself
-        SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
-        boolean isTopTaskHome = false;
-        if (!tasks.isEmpty()) {
-            ActivityManager.RunningTaskInfo topTask = tasks.get(0);
-            ComponentName topActivity = topTask.topActivity;
+        AtomicBoolean isTopTaskHome = new AtomicBoolean();
+        if (isRecentsTopMost(isTopTaskHome)) {
+            // Notify recents to close itself
+            try {
+                Bundle data = new Bundle();
+                Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
+                msg.setData(data);
+                mService.send(msg);
 
-            // Check if the front most activity is recents
-            if (topActivity.getPackageName().equals(sRecentsPackage) &&
-                    topActivity.getClassName().equals(sRecentsActivity)) {
-                // Notify Recents to toggle itself
-                try {
-                    Bundle data = new Bundle();
-                    Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
-                    msg.setData(data);
-                    mService.send(msg);
-
-                    // Time this path
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                            Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                            Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
-                } catch (RemoteException re) {
-                    re.printStackTrace();
-                }
-                mLastToggleTime = System.currentTimeMillis();
-                return;
+                // Time this path
+                Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                        Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
+                Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                        Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+            } catch (RemoteException re) {
+                re.printStackTrace();
             }
-
-            // Determine whether the top task is currently home
-            isTopTaskHome = ssp.isInHomeStack(topTask.id);
+            mLastToggleTime = System.currentTimeMillis();
+            return;
+        } else {
+            // Otherwise, start the recents activity
+            startRecentsActivity(isTopTaskHome.get());
         }
+    }
 
-        // Otherwise, Recents is not the front-most activity and we should animate into it.  If
+    /** Starts the recents activity if it is not already running */
+    void startRecentsActivity() {
+        // Check if the top task is in the home stack, and start the recents activity
+        AtomicBoolean isTopTaskHome = new AtomicBoolean();
+        if (!isRecentsTopMost(isTopTaskHome)) {
+            startRecentsActivity(isTopTaskHome.get());
+        }
+    }
+
+    /** Starts the recents activity */
+    void startRecentsActivity(boolean isTopTaskHome) {
+        // If Recents is not the front-most activity and we should animate into it.  If
         // the activity at the root of the top task stack in the home stack, then we just do a
         // simple transition.  Otherwise, we animate to the rects defined by the Recents service,
         // which can differ depending on the number of items in the list.
+        SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> recentTasks =
                 ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier());
         Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
@@ -422,9 +476,6 @@
         }
 
         // If there is no thumbnail transition, then just use a generic transition
-        // XXX: This should be different between home and from a recents-excluded app, perhaps the
-        //      recents-excluded app should still show up in recents, when the app is in the
-        //      foreground
         if (!useThumbnailTransition) {
             ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
                     R.anim.recents_from_launcher_enter,
@@ -444,6 +495,7 @@
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         intent.putExtra(EXTRA_ANIMATING_WITH_THUMBNAIL, animatingWithThumbnail);
+        intent.putExtra(EXTRA_FROM_ALT_TAB, mTriggeredFromAltTab);
         if (opts != null) {
             mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
                     UserHandle.USER_CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 90998da..9390b0d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -69,6 +69,7 @@
             public static final boolean TouchEvents = false;
             public static final boolean MeasureAndLayout = false;
             public static final boolean HwLayers = false;
+            public static final boolean Focus = false;
         }
 
         public static class TaskStack {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b74f6ac..325e4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.util.Pair;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
@@ -81,7 +82,10 @@
             String action = intent.getAction();
             Console.log(Constants.Log.App.SystemUIHandshake,
                     "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
-            if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+            if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
+                // Dismiss recents, launching the focused task
+                dismissRecentsIfVisible();
+            } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // Try and unfilter and filtered stacks
                 if (!mRecentsView.unfilterFilteredStacks()) {
                     // If there are no filtered stacks, dismiss recents and launch the first task
@@ -105,6 +109,8 @@
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra(
                 AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false);
+        config.launchedFromAltTab = launchIntent.getBooleanExtra(
+                AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false);
 
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
@@ -184,7 +190,7 @@
             int appWidgetId = config.searchBarAppWidgetId;
             if (appWidgetId >= 0) {
                 Console.log(Constants.Log.App.SystemUIHandshake,
-                        "[RecentsActivity|onCreate|addSearchAppWidgetView]",
+                "[RecentsActivity|onCreate|addSearchAppWidgetView]",
                         "Id: " + appWidgetId,
                         Console.AnsiBlue);
                 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
@@ -205,8 +211,10 @@
     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
     boolean dismissRecentsIfVisible() {
         if (mVisible) {
-            if (!mRecentsView.launchFirstTask()) {
-                finish();
+            if (!mRecentsView.launchFocusedTask()) {
+                if (!mRecentsView.launchFirstTask()) {
+                    finish();
+                }
             }
             return true;
         }
@@ -303,6 +311,7 @@
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
+        filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
@@ -362,6 +371,18 @@
     }
 
     @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_TAB) {
+            // Focus the next task in the stack
+            final boolean backward = event.isShiftPressed();
+            mRecentsView.focusNextTask(!backward);
+            return true;
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
     public void onBackPressed() {
         boolean interceptedByInfoPanelClose = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 9afc1cb..8399551 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -65,6 +65,7 @@
     public int taskBarViewLightTextColor;
     public int taskBarViewDarkTextColor;
 
+    public boolean launchedFromAltTab;
     public boolean launchedWithThumbnailAnimation;
 
     /** Private constructor */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 837cb34..601b382 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -106,8 +106,11 @@
             } catch (RemoteException re) {
                 re.printStackTrace();
             }
-        } else if (msg.what == AlternateRecentsComponent.MSG_CLOSE_RECENTS) {
-            // Do nothing
+        } else if (msg.what == AlternateRecentsComponent.MSG_HIDE_RECENTS) {
+            // Send a broadcast to hide recents
+            Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
+            intent.setPackage(context.getPackageName());
+            context.sendBroadcast(intent);
         } else if (msg.what == AlternateRecentsComponent.MSG_TOGGLE_RECENTS) {
             // Send a broadcast to toggle recents
             Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
@@ -125,6 +128,7 @@
 
 /* Service */
 public class RecentsService extends Service {
+    final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
     final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
 
     Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
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 a6d7e67..2821052 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -94,6 +94,34 @@
         }
     }
 
+    /** Launches the focused task from the first stack if possible */
+    public boolean launchFocusedTask() {
+        // Get the first stack view
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                TaskStackView stackView = (TaskStackView) child;
+                TaskStack stack = stackView.mStack;
+                // Iterate the stack views and try and find the focused task
+                int taskCount = stackView.getChildCount();
+                for (int j = 0; j < taskCount; j++) {
+                    TaskView tv = (TaskView) stackView.getChildAt(j);
+                    Task task = tv.getTask();
+                    if (tv.isFocusedTask()) {
+                        Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
+                                "Found focused Task");
+                        onTaskLaunched(stackView, tv, stack, task);
+                        return true;
+                    }
+                }
+            }
+        }
+        Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
+                "No Tasks focused");
+        return false;
+    }
+
     /** Launches the first task from the first stack if possible */
     public boolean launchFirstTask() {
         // Get the first stack view
@@ -234,6 +262,24 @@
         }
     }
 
+    /** Focuses the next task in the first stack view */
+    public void focusNextTask(boolean forward) {
+        // Get the first stack view
+        TaskStackView stackView = null;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                stackView = (TaskStackView) child;
+                break;
+            }
+        }
+
+        if (stackView != null) {
+            stackView.focusNextTask(forward);
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         Console.log(Constants.Log.UI.Draw, "[RecentsView|dispatchDraw]", "",
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 b64225e..37c3c35 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -80,6 +80,7 @@
     int mMaxScroll;
     int mStashedScroll;
     int mLastInfoPaneStackScroll;
+    int mFocusedTaskIndex = -1;
     OverScroller mScroller;
     ObjectAnimator mScrollAnimator;
 
@@ -306,6 +307,15 @@
         mStackScroll = value;
     }
 
+    /**
+     * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
+     * is not bounded)
+     */
+    int getStackScrollForTaskIndex(int i) {
+        int taskHeight = mTaskRect.height();
+        return (int) (i * Constants.Values.TaskStackView.StackOverlapPct * taskHeight);
+    }
+
     /** Gets the current stack scroll */
     public int getStackScroll() {
         return mStackScroll;
@@ -460,6 +470,64 @@
         return false;
     }
 
+    /** Focuses the task at the specified index in the stack */
+    void focusTask(int taskIndex, boolean scrollToNewPosition) {
+        Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "" + taskIndex);
+        if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
+            mFocusedTaskIndex = taskIndex;
+
+            // Focus the view if possible, otherwise, focus the view after we scroll into position
+            Task t = mStack.getTasks().get(taskIndex);
+            TaskView tv = getChildViewForTask(t);
+            Runnable postScrollRunnable = null;
+            if (tv != null) {
+                tv.setFocusedTask();
+                Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "Requesting focus");
+            } else {
+                postScrollRunnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        Task t = mStack.getTasks().get(mFocusedTaskIndex);
+                        TaskView tv = getChildViewForTask(t);
+                        if (tv != null) {
+                            tv.setFocusedTask();
+                            Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]",
+                                    "Requesting focus after scroll animation");
+                        }
+                    }
+                };
+            }
+
+            if (scrollToNewPosition) {
+                // Scroll the view into position
+                int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll,
+                        getStackScrollForTaskIndex(taskIndex)));
+
+                animateScroll(getStackScroll(), newScroll, postScrollRunnable);
+            } else {
+                if (postScrollRunnable != null) {
+                    postScrollRunnable.run();
+                }
+            }
+        }
+    }
+
+    /** Focuses the next task in the stack */
+    void focusNextTask(boolean forward) {
+        Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusNextTask]", "" + mFocusedTaskIndex);
+
+        // Find the next index to focus
+        int numTasks = mStack.getTaskCount();
+        if (mFocusedTaskIndex < 0) {
+            mFocusedTaskIndex = numTasks - 1;
+        }
+        if (0 <= mFocusedTaskIndex && mFocusedTaskIndex < numTasks) {
+            mFocusedTaskIndex = Math.max(0, Math.min(numTasks - 1,
+                    mFocusedTaskIndex + (forward ? -1 : 1)));
+        }
+        focusTask(mFocusedTaskIndex, true);
+    }
+
     /** Enables the hw layers and increments the hw layer requirement ref count */
     void addHwLayersRefCount(String reason) {
         Console.log(Constants.Log.UI.HwLayers,
@@ -631,6 +699,11 @@
             requestSynchronizeStackViewsWithModel();
             synchronizeStackViewsWithModel();
 
+            // Update the focused task index to be the next item to the top task
+            if (config.launchedFromAltTab) {
+                focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
+            }
+
             // Animate the task bar of the first task view
             if (config.launchedWithThumbnailAnimation) {
                 TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
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 5fad629..ffcb82b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -18,6 +18,7 @@
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Outline;
@@ -57,6 +58,7 @@
     Task mTask;
     boolean mTaskDataLoaded;
     boolean mTaskInfoPaneVisible;
+    boolean mIsFocused;
     Point mLastTouchDown = new Point();
     Path mRoundedRectClipPath = new Path();
 
@@ -367,6 +369,34 @@
         }
     }
 
+    /**
+     * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
+     * if the view is not currently visible, or we are in touch state (where we still want to keep
+     * track of focus).
+     */
+    public void setFocusedTask() {
+        mIsFocused = true;
+        requestFocus();
+    }
+
+    /**
+     * Updates the explicitly focused state when the view focus changes.
+     */
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        if (!gainFocus) {
+            mIsFocused = false;
+        }
+    }
+
+    /**
+     * Returns whether we have explicitly been focused.
+     */
+    public boolean isFocusedTask() {
+        return mIsFocused || isFocused();
+    }
+
     /**** TaskCallbacks Implementation ****/
 
     /** Binds this task view to the task */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 898f06e..b079265 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -87,8 +87,9 @@
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
 
-    protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
-    protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
+    protected static final int MSG_SHOW_RECENT_APPS = 1019;
+    protected static final int MSG_HIDE_RECENT_APPS = 1020;
+    protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
     protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
@@ -494,8 +495,22 @@
     }
 
     @Override
+    public void showRecentApps(boolean triggeredFromAltTab) {
+        int msg = MSG_SHOW_RECENT_APPS;
+        mHandler.removeMessages(msg);
+        mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
+    }
+
+    @Override
+    public void hideRecentApps() {
+        int msg = MSG_HIDE_RECENT_APPS;
+        mHandler.removeMessages(msg);
+        mHandler.sendEmptyMessage(msg);
+    }
+
+    @Override
     public void toggleRecentApps() {
-        int msg = MSG_TOGGLE_RECENTS_PANEL;
+        int msg = MSG_TOGGLE_RECENTS_APPS;
         mHandler.removeMessages(msg);
         mHandler.sendEmptyMessage(msg);
     }
@@ -578,12 +593,12 @@
         public boolean onTouch(View v, MotionEvent event) {
             int action = event.getAction() & MotionEvent.ACTION_MASK;
             if (action == MotionEvent.ACTION_DOWN) {
-                preloadRecentTasksList();
+                preloadRecents();
             } else if (action == MotionEvent.ACTION_CANCEL) {
-                cancelPreloadingRecentTasksList();
+                cancelPreloadingRecents();
             } else if (action == MotionEvent.ACTION_UP) {
                 if (!v.isPressed()) {
-                    cancelPreloadingRecentTasksList();
+                    cancelPreloadingRecents();
                 }
 
             }
@@ -591,28 +606,38 @@
         }
     };
 
-    protected void toggleRecentsActivity() {
+    /** Proxy for RecentsComponent */
+
+    protected void showRecents(boolean triggeredFromAltTab) {
+        if (mRecents != null) {
+            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
+            mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
+        }
+    }
+
+    protected void hideRecents() {
+        if (mRecents != null) {
+            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
+            mRecents.hideRecents();
+        }
+    }
+
+    protected void toggleRecents() {
         if (mRecents != null) {
             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
         }
     }
 
-    protected void preloadRecentTasksList() {
+    protected void preloadRecents() {
         if (mRecents != null) {
-            mRecents.preloadRecentTasksList();
+            mRecents.preloadRecents();
         }
     }
 
-    protected void cancelPreloadingRecentTasksList() {
+    protected void cancelPreloadingRecents() {
         if (mRecents != null) {
-            mRecents.cancelPreloadingRecentTasksList();
-        }
-    }
-
-    protected void closeRecents() {
-        if (mRecents != null) {
-            mRecents.closeRecents();
+            mRecents.cancelPreloadingRecents();
         }
     }
 
@@ -653,17 +678,20 @@
         public void handleMessage(Message m) {
             Intent intent;
             switch (m.what) {
-             case MSG_TOGGLE_RECENTS_PANEL:
-                 toggleRecentsActivity();
+             case MSG_SHOW_RECENT_APPS:
+                 showRecents(m.arg1 > 0);
                  break;
-             case MSG_CLOSE_RECENTS_PANEL:
-                 closeRecents();
+             case MSG_HIDE_RECENT_APPS:
+                 hideRecents();
+                 break;
+             case MSG_TOGGLE_RECENTS_APPS:
+                 toggleRecents();
                  break;
              case MSG_PRELOAD_RECENT_APPS:
-                  preloadRecentTasksList();
+                  preloadRecents();
                   break;
              case MSG_CANCEL_PRELOAD_RECENT_APPS:
-                  cancelPreloadingRecentTasksList();
+                  cancelPreloadingRecents();
                   break;
              case MSG_OPEN_SEARCH_PANEL:
                  if (DEBUG) Log.d(TAG, "opening search panel");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 5362af5..ebab7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -57,6 +57,8 @@
     private static final int MSG_PRELOAD_RECENT_APPS        = 14 << MSG_SHIFT;
     private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 15 << MSG_SHIFT;
     private static final int MSG_SET_WINDOW_STATE           = 16 << MSG_SHIFT;
+    private static final int MSG_SHOW_RECENT_APPS           = 17 << MSG_SHIFT;
+    private static final int MSG_HIDE_RECENT_APPS           = 18 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -96,11 +98,13 @@
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
                 boolean showImeSwitcher);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
+        public void showRecentApps(boolean triggeredFromAltTab);
+        public void hideRecentApps();
         public void toggleRecentApps();
         public void preloadRecentApps();
+        public void cancelPreloadRecentApps();
         public void showSearchPanel();
         public void hideSearchPanel();
-        public void cancelPreloadRecentApps();
         public void setWindowState(int window, int state);
 
     }
@@ -211,6 +215,21 @@
         }
     }
 
+    public void showRecentApps(boolean triggeredFromAltTab) {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
+            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS,
+                    triggeredFromAltTab ? 1 : 0, 0, null).sendToTarget();
+        }
+    }
+
+    public void hideRecentApps() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
+            mHandler.obtainMessage(MSG_HIDE_RECENT_APPS, 0, 0, null).sendToTarget();
+        }
+    }
+
     public void toggleRecentApps() {
         synchronized (mList) {
             mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
@@ -309,6 +328,12 @@
                 case MSG_SET_HARD_KEYBOARD_STATUS:
                     mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
                     break;
+                case MSG_SHOW_RECENT_APPS:
+                    mCallbacks.showRecentApps(msg.arg1 != 0);
+                    break;
+                case MSG_HIDE_RECENT_APPS:
+                    mCallbacks.hideRecentApps();
+                    break;
                 case MSG_TOGGLE_RECENT_APPS:
                     mCallbacks.toggleRecentApps();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 23b0594..842627c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1390,8 +1390,8 @@
 
             if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
                 // close recents if it's visible
-                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
-                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
+                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
+                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
             }
         }
 
@@ -1542,8 +1542,8 @@
         }
 
         if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
-            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
-            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
+            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
+            mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
         }
 
         if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 4ee8103..43c7391 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -260,13 +260,7 @@
     WindowState mLastInputMethodWindow = null;
     WindowState mLastInputMethodTargetWindow = null;
 
-    static final int RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS = 0;
-    static final int RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW = 1;
-    static final int RECENT_APPS_BEHAVIOR_DISMISS = 2;
-    static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 3;
-
-    RecentApplicationsDialog mRecentAppsDialog;
-    int mRecentAppsDialogHeldModifiers;
+    int mRecentAppsHeldModifiers;
     boolean mLanguageSwitchKeyPressed;
 
     int mLidState = LID_ABSENT;
@@ -806,52 +800,6 @@
         }
     };
 
-    /**
-     * Create (if necessary) and show or dismiss the recent apps dialog according
-     * according to the requested behavior.
-     */
-    void showOrHideRecentAppsDialog(final int behavior) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mRecentAppsDialog == null) {
-                    mRecentAppsDialog = new RecentApplicationsDialog(mContext);
-                }
-                if (mRecentAppsDialog.isShowing()) {
-                    switch (behavior) {
-                        case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:
-                        case RECENT_APPS_BEHAVIOR_DISMISS:
-                            mRecentAppsDialog.dismiss();
-                            break;
-                        case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
-                            mRecentAppsDialog.dismissAndSwitch();
-                            break;
-                        case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:
-                        default:
-                            break;
-                    }
-                } else {
-                    switch (behavior) {
-                        case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:
-                            mRecentAppsDialog.show();
-                            break;
-                        case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:
-                            try {
-                                mWindowManager.setInTouchMode(false);
-                            } catch (RemoteException e) {
-                            }
-                            mRecentAppsDialog.show();
-                            break;
-                        case RECENT_APPS_BEHAVIOR_DISMISS:
-                        case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
-                        default:
-                            break;
-                    }
-                }
-            }
-        });
-    }
-
     /** {@inheritDoc} */
     @Override
     public void init(Context context, IWindowManager windowManager,
@@ -2253,21 +2201,20 @@
 
         // Display task switcher for ALT-TAB or Meta-TAB.
         if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
-            if (mRecentAppsDialogHeldModifiers == 0 && !keyguardOn) {
+            if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
                 if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
                         || KeyEvent.metaStateHasModifiers(
                                 shiftlessModifiers, KeyEvent.META_META_ON)) {
-                    mRecentAppsDialogHeldModifiers = shiftlessModifiers;
-                    showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW);
+                    mRecentAppsHeldModifiers = shiftlessModifiers;
+                    showRecentApps(true);
                     return -1;
                 }
             }
-        } else if (!down && mRecentAppsDialogHeldModifiers != 0
-                && (metaState & mRecentAppsDialogHeldModifiers) == 0) {
-            mRecentAppsDialogHeldModifiers = 0;
-            showOrHideRecentAppsDialog(keyguardOn ? RECENT_APPS_BEHAVIOR_DISMISS :
-                    RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH);
+        } else if (!down && mRecentAppsHeldModifiers != 0
+                && (metaState & mRecentAppsHeldModifiers) == 0) {
+            mRecentAppsHeldModifiers = 0;
+            hideRecentApps();
         }
 
         // Handle keyboard language switching.
@@ -2440,7 +2387,7 @@
                     statusbar.cancelPreloadRecentApps();
                 }
             } catch (RemoteException e) {
-                Slog.e(TAG, "RemoteException when showing recent apps", e);
+                Slog.e(TAG, "RemoteException when cancelling recent apps preload", e);
                 // re-acquire status bar service next time it is needed.
                 mStatusBarService = null;
             }
@@ -2449,19 +2396,46 @@
 
     private void toggleRecentApps() {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
-        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
         try {
             IStatusBarService statusbar = getStatusBarService();
             if (statusbar != null) {
                 statusbar.toggleRecentApps();
             }
         } catch (RemoteException e) {
+            Slog.e(TAG, "RemoteException when toggling recent apps", e);
+            // re-acquire status bar service next time it is needed.
+            mStatusBarService = null;
+        }
+    }
+
+    private void showRecentApps(boolean triggeredFromAltTab) {
+        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
+        try {
+            IStatusBarService statusbar = getStatusBarService();
+            if (statusbar != null) {
+                statusbar.showRecentApps(triggeredFromAltTab);
+            }
+        } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException when showing recent apps", e);
             // re-acquire status bar service next time it is needed.
             mStatusBarService = null;
         }
     }
 
+    private void hideRecentApps() {
+        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
+        try {
+            IStatusBarService statusbar = getStatusBarService();
+            if (statusbar != null) {
+                statusbar.hideRecentApps();
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "RemoteException when closing recent apps", e);
+            // re-acquire status bar service next time it is needed.
+            mStatusBarService = null;
+        }
+    }
+
     /**
      * A home key -> launch home action was detected.  Take the appropriate action
      * given the situation with the keyguard.
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index 2f0d7d6..bc55ed1 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -123,7 +123,7 @@
     }
 
     @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
+      public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_TAB) {
             // Ignore all meta keys other than SHIFT.  The app switch key could be a
             // fallback action chorded with ALT, META or even CTRL depending on the key map.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 738ad32..2c38d3c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -460,6 +460,24 @@
     }
 
     @Override
+    public void showRecentApps(boolean triggeredFromAltTab) {
+        if (mBar != null) {
+            try {
+                mBar.showRecentApps(triggeredFromAltTab);
+            } catch (RemoteException ex) {}
+        }
+    }
+
+    @Override
+    public void hideRecentApps() {
+        if (mBar != null) {
+            try {
+                mBar.hideRecentApps();
+            } catch (RemoteException ex) {}
+        }
+    }
+
+    @Override
     public void setCurrentUser(int newUserId) {
         if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
         mCurrentUserId = newUserId;