RecentsImpl.java revision d8b1d63f96580fe961e1751e7b4f56c90c1e0a76
17137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang/*
27137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * Copyright (C) 2015 The Android Open Source Project
37137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang *
47137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * Licensed under the Apache License, Version 2.0 (the "License");
57137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * you may not use this file except in compliance with the License.
67137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * You may obtain a copy of the License at
77137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang *
87137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang *      http://www.apache.org/licenses/LICENSE-2.0
97137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang *
107137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * Unless required by applicable law or agreed to in writing, software
117137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * distributed under the License is distributed on an "AS IS" BASIS,
127137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * See the License for the specific language governing permissions and
147137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * limitations under the License.
157137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang */
167137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
177137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangpackage com.android.systemui.recents;
187137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
197137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.app.ActivityManager;
207137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.app.ActivityOptions;
217137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.app.ITaskStackListener;
2278acc89eae9789366b6384707861f4563addf2d3Chong Zhangimport android.appwidget.AppWidgetProviderInfo;
237137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.content.ActivityNotFoundException;
247137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.content.Context;
257137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.content.Intent;
267137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.content.res.Resources;
277137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.graphics.Bitmap;
287137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.graphics.Canvas;
297137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.graphics.Rect;
307137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.graphics.RectF;
317137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.os.Handler;
327137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.os.SystemClock;
333694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chungimport android.os.UserHandle;
343694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chungimport android.util.Log;
353694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chungimport android.util.MutableBoolean;
367137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.view.AppTransitionAnimationSpec;
377137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.view.LayoutInflater;
387137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport android.view.View;
397137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.internal.logging.MetricsLogger;
407137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.Prefs;
417137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.R;
427137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.SystemUIApplication;
437137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.EventBus;
447137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
457137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.activity.HideRecentsEvent;
467137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.activity.IterateRecentsEvent;
477137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.activity.ToggleRecentsEvent;
487137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
497137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
507137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
517137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
527137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.misc.DozeTrigger;
537137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.misc.ForegroundThread;
547137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.misc.SystemServicesProxy;
557137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.model.RecentsTaskLoadPlan;
567137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.model.RecentsTaskLoader;
576d339f1f764bbd32e3381dae7bfa7c6c575bb493Lajos Molnarimport com.android.systemui.recents.model.Task;
587137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.model.TaskGrouping;
597137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.model.TaskStack;
607137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
617137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.views.TaskStackView;
627137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.views.TaskViewHeader;
637137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.recents.views.TaskViewTransform;
647137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.statusbar.BaseStatusBar;
657137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport com.android.systemui.statusbar.phone.PhoneStatusBar;
667137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
677137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport java.util.ArrayList;
687137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
697137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangimport static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
707137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
717137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang/**
727137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * An implementation of the Recents component for the current user.  For secondary users, this can
737137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang * be called remotely from the system user.
747137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang */
757137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhangpublic class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
767137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ActivityOptions.OnAnimationFinishedListener {
777137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
787137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    private final static String TAG = "RecentsImpl";
797137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    private final static boolean DEBUG = false;
807137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
817137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    // The minimum amount of time between each recents button press that we will handle
827137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    private final static int MIN_TOGGLE_DELAY_MS = 350;
837137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    // The duration within which the user releasing the alt tab (from when they pressed alt tab)
847137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    // that the fast alt-tab animation will run.  If the user's alt-tab takes longer than this
857137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    // duration, then we will toggle recents after this duration.
867137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    private final static int FAST_ALT_TAB_DELAY_MS = 225;
877137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
887137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public final static String RECENTS_PACKAGE = "com.android.systemui";
897137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
907137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
917137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    /**
927137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * An implementation of ITaskStackListener, that allows us to listen for changes to the system
937137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * task stacks and update recents accordingly.
947137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     */
957137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
967137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        Handler mHandler;
977137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
987137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        public TaskStackListenerImpl(Handler handler) {
997137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            mHandler = handler;
1007137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
1017137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1027137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        @Override
1037137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        public void onTaskStackChanged() {
1047137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // Debounce any task stack changes
1057137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            mHandler.removeCallbacks(this);
1067137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            mHandler.post(this);
1077137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
1087137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1097137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        @Override
1107137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        public void onActivityPinned() {
1117137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
1127137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1137137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        /** Preloads the next task */
1147137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        public void run() {
1157137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // TODO: Temporarily skip this if multi stack is enabled
1167137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            /*
1177137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            RecentsConfiguration config = RecentsConfiguration.getInstance();
1187137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
1197137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                RecentsTaskLoader loader = Recents.getTaskLoader();
1207137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                SystemServicesProxy ssp = Recents.getSystemServices();
1217137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
1223694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
1233694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // Load the next task only if we aren't svelte
1243694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
1253694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                loader.preloadTasks(plan, true);
1263694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
1273694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // This callback is made when a new activity is launched and the old one is paused
1283694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // so ignore the current activity and try and preload the thumbnail for the
1293694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // previous one.
1303694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                if (runningTaskInfo != null) {
1317137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    launchOpts.runningTaskId = runningTaskInfo.id;
1327137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                }
1337137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                launchOpts.numVisibleTasks = 2;
1343694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                launchOpts.numVisibleTaskThumbnails = 2;
1357137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                launchOpts.onlyLoadForCache = true;
1367137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                launchOpts.onlyLoadPausedActivities = true;
1377137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                loader.loadTasks(mContext, plan, launchOpts);
1387137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            }
1397137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            */
1407137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
1417137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
1427137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1437137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    private static RecentsTaskLoadPlan sInstanceLoadPlan;
1443694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
1453694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    Context mContext;
1467137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    Handler mHandler;
1477137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    TaskStackListenerImpl mTaskStackListener;
1483694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    RecentsAppWidgetHost mAppWidgetHost;
1493694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    boolean mBootCompleted;
1503694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    boolean mCanReuseTaskStackViews = true;
1513694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    boolean mDraggingInRecents;
1523694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    boolean mReloadTasks;
1533694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
1543694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    // Task launching
1553694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    Rect mSearchBarBounds = new Rect();
1563694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    Rect mTaskStackBounds = new Rect();
1573694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    Rect mLastTaskViewBounds = new Rect();
1583694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    TaskViewTransform mTmpTransform = new TaskViewTransform();
1593694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    int mStatusBarHeight;
1603694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    int mNavBarHeight;
1613694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    int mNavBarWidth;
1623694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    int mTaskBarHeight;
1633694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
1643694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    // Header (for transition)
1653694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    TaskViewHeader mHeaderBar;
1663694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    final Object mHeaderBarLock = new Object();
1677137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    TaskStackView mDummyStackView;
1683694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
1697137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    // Variables to keep track of if we need to start recents after binding
1707137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    boolean mTriggeredFromAltTab;
1717137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    long mLastToggleTime;
1727137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
1737137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        @Override
1747137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        public void run() {
1757137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // When this fires, then the user has not released alt-tab for at least
1767137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // FAST_ALT_TAB_DELAY_MS milliseconds
1777137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
1787137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    false /* reloadTasks */);
1797137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
1807137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    });
1817137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1827137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    Bitmap mThumbnailTransitionBitmapCache;
1837137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    Task mThumbnailTransitionBitmapCacheKey;
1847137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1857137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public RecentsImpl(Context context) {
1867137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mContext = context;
1877137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mHandler = new Handler();
1887137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
1897137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        Resources res = mContext.getResources();
1907137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        LayoutInflater inflater = LayoutInflater.from(mContext);
1917137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1927137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Initialize the static foreground thread
1937137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ForegroundThread.get();
1947137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
1953694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Register the task stack listener
1963694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        mTaskStackListener = new TaskStackListenerImpl(mHandler);
1973694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        SystemServicesProxy ssp = Recents.getSystemServices();
1987137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ssp.registerTaskStackListener(mTaskStackListener);
1997137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
2007137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Initialize the static configuration resources
2017137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
2023694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
2037137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
2047137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
2057137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mDummyStackView = new TaskStackView(mContext, new TaskStack());
2067137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
2077137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                null, false);
2087137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
2097137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
2107137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // When we start, preload the data associated with the previous recent tasks.
2117137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // We can use a new plan since the caches will be the same.
2127137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        RecentsTaskLoader loader = Recents.getTaskLoader();
2137137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
2147137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        loader.preloadTasks(plan, true /* isTopTaskHome */);
2157137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
21678acc89eae9789366b6384707861f4563addf2d3Chong Zhang        launchOpts.numVisibleTasks = loader.getIconCacheSize();
21778acc89eae9789366b6384707861f4563addf2d3Chong Zhang        launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
21878acc89eae9789366b6384707861f4563addf2d3Chong Zhang        launchOpts.onlyLoadForCache = true;
2197137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        loader.loadTasks(mContext, plan, launchOpts);
2207137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
2213694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
22278acc89eae9789366b6384707861f4563addf2d3Chong Zhang    public void onBootCompleted() {
22378acc89eae9789366b6384707861f4563addf2d3Chong Zhang        mBootCompleted = true;
22478acc89eae9789366b6384707861f4563addf2d3Chong Zhang        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
22578acc89eae9789366b6384707861f4563addf2d3Chong Zhang    }
22678acc89eae9789366b6384707861f4563addf2d3Chong Zhang
22778acc89eae9789366b6384707861f4563addf2d3Chong Zhang    @Override
22878acc89eae9789366b6384707861f4563addf2d3Chong Zhang    public void onConfigurationChanged() {
22978acc89eae9789366b6384707861f4563addf2d3Chong Zhang        // Don't reuse task stack views if the configuration changes
23078acc89eae9789366b6384707861f4563addf2d3Chong Zhang        mCanReuseTaskStackViews = false;
23178acc89eae9789366b6384707861f4563addf2d3Chong Zhang        Recents.getConfiguration().updateOnConfigurationChange();
2323694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
2333694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
23478acc89eae9789366b6384707861f4563addf2d3Chong Zhang    /**
23578acc89eae9789366b6384707861f4563addf2d3Chong Zhang     * This is only called from the system user's Recents.  Secondary users will instead proxy their
23678acc89eae9789366b6384707861f4563addf2d3Chong Zhang     * visibility change events through to the system user via
23778acc89eae9789366b6384707861f4563addf2d3Chong Zhang     * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
23878acc89eae9789366b6384707861f4563addf2d3Chong Zhang     */
23978acc89eae9789366b6384707861f4563addf2d3Chong Zhang    public void onVisibilityChanged(Context context, boolean visible) {
24078acc89eae9789366b6384707861f4563addf2d3Chong Zhang        SystemUIApplication app = (SystemUIApplication) context;
2413694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
2423694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (statusBar != null) {
2437137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            statusBar.updateRecentsVisibility(visible);
2447137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
2457137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
2467137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
2477137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    /**
2487137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * This is only called from the system user's Recents.  Secondary users will instead proxy their
2497137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * visibility change events through to the system user via
2507137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
2517137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     */
2527137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public void onStartScreenPinning(Context context) {
2537137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        SystemUIApplication app = (SystemUIApplication) context;
2547137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
2557137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        if (statusBar != null) {
2567137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            statusBar.showScreenPinningRequest(false);
2577137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
2587137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
2597137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
2607137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    @Override
26178acc89eae9789366b6384707861f4563addf2d3Chong Zhang    public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
26278acc89eae9789366b6384707861f4563addf2d3Chong Zhang            boolean animate, boolean reloadTasks) {
26378acc89eae9789366b6384707861f4563addf2d3Chong Zhang        mTriggeredFromAltTab = triggeredFromAltTab;
26478acc89eae9789366b6384707861f4563addf2d3Chong Zhang        mDraggingInRecents = draggingInRecents;
26578acc89eae9789366b6384707861f4563addf2d3Chong Zhang        mReloadTasks = reloadTasks;
26678acc89eae9789366b6384707861f4563addf2d3Chong Zhang        if (mFastAltTabTrigger.hasTriggered()) {
26778acc89eae9789366b6384707861f4563addf2d3Chong Zhang            // We are calling this from the doze trigger, so just fall through to show Recents
26878acc89eae9789366b6384707861f4563addf2d3Chong Zhang            mFastAltTabTrigger.resetTrigger();
26978acc89eae9789366b6384707861f4563addf2d3Chong Zhang        } else if (mFastAltTabTrigger.isDozing()) {
27078acc89eae9789366b6384707861f4563addf2d3Chong Zhang            // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab,
27178acc89eae9789366b6384707861f4563addf2d3Chong Zhang            // otherwise, this is an additional tab (alt-tab*), which means that we should trigger
27278acc89eae9789366b6384707861f4563addf2d3Chong Zhang            // immediately (fall through and disable the pending trigger)
27378acc89eae9789366b6384707861f4563addf2d3Chong Zhang            // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started
27478acc89eae9789366b6384707861f4563addf2d3Chong Zhang            //       so we may actually additional signal to handle multiple quick tab cases.  The
27578acc89eae9789366b6384707861f4563addf2d3Chong Zhang            //       severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS
27678acc89eae9789366b6384707861f4563addf2d3Chong Zhang            //       duration though
27778acc89eae9789366b6384707861f4563addf2d3Chong Zhang            if (!triggeredFromAltTab) {
2787137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                return;
27978acc89eae9789366b6384707861f4563addf2d3Chong Zhang            }
2803694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            mFastAltTabTrigger.stopDozing();
2817137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        } else {
2827137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // Otherwise, the doze trigger is not running, and if this is an alt tab, we should
2837137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // start the trigger and then wait for the hide (or for it to elapse)
2847137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            if (triggeredFromAltTab) {
2857137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                mFastAltTabTrigger.startDozing();
2867137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                return;
2877137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            }
2887137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
2897137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
2907137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        try {
2917137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            // Check if the top task is in the home stack, and start the recents activity
2927137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            SystemServicesProxy ssp = Recents.getSystemServices();
2937137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
2947137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            MutableBoolean isTopTaskHome = new MutableBoolean(true);
2953694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (topTask == null || !ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
2963694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                startRecentsActivity(topTask, isTopTaskHome.value, animate);
2973694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
2983694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        } catch (ActivityNotFoundException e) {
2993694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            Log.e(TAG, "Failed to launch RecentsActivity", e);
3003694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
3013694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
3023694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3033694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
3043694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
3053694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (mBootCompleted) {
3063694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
3073694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // The user has released alt-tab before the trigger has run, so just show the next
3083694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // task immediately
3093694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                showNextTask();
3103694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3113694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // Cancel the fast alt-tab trigger
3123694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                mFastAltTabTrigger.stopDozing();
3133694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                mFastAltTabTrigger.resetTrigger();
3143694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                return;
3153694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
3163694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3173694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            // Defer to the activity to handle hiding recents, if it handles it, then it must still
3183694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            // be visible
3193694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
3203694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    triggeredFromHomeKey));
3213694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
3223694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
3233694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3243694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
3253694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void toggleRecents() {
3263694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Skip this toggle if we are already waiting to trigger recents via alt-tab
3273694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (mFastAltTabTrigger.isDozing()) {
3283694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            return;
3293694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
3303694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3313694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        mDraggingInRecents = false;
3323694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        mTriggeredFromAltTab = false;
3333694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3343694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        try {
3353694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            SystemServicesProxy ssp = Recents.getSystemServices();
3363694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
3373694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            MutableBoolean isTopTaskHome = new MutableBoolean(true);
3383694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
3393694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                RecentsConfiguration config = Recents.getConfiguration();
3403694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                RecentsActivityLaunchState launchState = config.getLaunchState();
3413694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                RecentsDebugFlags flags = Recents.getDebugFlags();
3423694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                if (!launchState.launchedWithAltTab) {
3433694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    // Notify recents to move onto the next task
3443694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    EventBus.getDefault().post(new IterateRecentsEvent());
3453694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                } else {
3463694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    // If the user has toggled it too quickly, then just eat up the event here (it's
3473694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    // better than showing a janky screenshot).
3483694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    // NOTE: Ideally, the screenshot mechanism would take the window transform into
3493694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    // account
3503694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
3513694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                        return;
3523694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    }
3533694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3543694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    EventBus.getDefault().post(new ToggleRecentsEvent());
3557137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    mLastToggleTime = SystemClock.elapsedRealtime();
3567137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                }
3573694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                return;
3583694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            } else {
3593694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // If the user has toggled it too quickly, then just eat up the event here (it's
3603694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // better than showing a janky screenshot).
3613694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // NOTE: Ideally, the screenshot mechanism would take the window transform into
3623694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // account
3633694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
3643694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    return;
3653694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                }
3663694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3673694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // Otherwise, start the recents activity
3683694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */);
3693694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3703694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // Only close the other system windows if we are actually showing recents
3713694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
3723694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                mLastToggleTime = SystemClock.elapsedRealtime();
3733694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
3743694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        } catch (ActivityNotFoundException e) {
3753694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            Log.e(TAG, "Failed to launch RecentsActivity", e);
3763694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
3773694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
3783694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
3793694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
3803694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void preloadRecents() {
3813694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Preload only the raw task list into a new load plan (which will be consumed by the
3823694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // RecentsActivity) only if there is a task to animate to.
3833694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        SystemServicesProxy ssp = Recents.getSystemServices();
3843694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
3853694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        MutableBoolean topTaskHome = new MutableBoolean(true);
3863694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoader loader = Recents.getTaskLoader();
3873694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        sInstanceLoadPlan = loader.createLoadPlan(mContext);
3883694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (topTask != null && !ssp.isRecentsTopMost(topTask, topTaskHome)) {
3893694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
3903694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
3913694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            TaskStack stack = sInstanceLoadPlan.getTaskStack();
3923694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (stack.getStackTaskCount() > 0) {
3933694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // We try and draw the thumbnail transition bitmap in parallel before
3943694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                // toggle/show recents is called
3953694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
3963694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
3973694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
3983694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
3993694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4003694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
4013694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void cancelPreloadingRecents() {
4023694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Do nothing
4033694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
4043694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4053694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
4063694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void onDraggingInRecents(float distanceFromTop) {
4073694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
4083694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
4093694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4103694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    @Override
4113694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void onDraggingInRecentsEnded(float velocity) {
4123694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
4133694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
4143694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4153694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    /**
4163694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     * Transitions to the next recent task in the stack.
4173694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     */
4183694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void showNextTask() {
4193694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        SystemServicesProxy ssp = Recents.getSystemServices();
4203694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoader loader = Recents.getTaskLoader();
4213694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
4223694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        loader.preloadTasks(plan, true /* isTopTaskHome */);
4233694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        TaskStack focusedStack = plan.getTaskStack();
4243694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4253694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there are no tasks in the focused stack
4263694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
4273694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4283694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
4293694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there is no running task
4303694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (runningTask == null) return;
4313694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4323694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Find the task in the recents list
4333694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        boolean isTopTaskHome = SystemServicesProxy.isHomeStack(runningTask.stackId);
4343694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ArrayList<Task> tasks = focusedStack.getStackTasks();
4353694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        Task toTask = null;
4363694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ActivityOptions launchOpts = null;
4373694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        int taskCount = tasks.size();
4383694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        for (int i = taskCount - 1; i >= 1; i--) {
4393694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            Task task = tasks.get(i);
4403694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (isTopTaskHome) {
4413694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                toTask = tasks.get(i - 1);
4423694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
4433694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                        R.anim.recents_launch_next_affiliated_task_target,
4443694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                        R.anim.recents_fast_toggle_app_home_exit);
4453694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                break;
4463694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            } else if (task.key.id == runningTask.id) {
4473694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                toTask = tasks.get(i - 1);
4483694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
4493694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                        R.anim.recents_launch_prev_affiliated_task_target,
4503694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                        R.anim.recents_launch_prev_affiliated_task_source);
4513694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                break;
4523694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
4533694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
4547137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
4553694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there is no next task
4563694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (toTask == null) {
4573694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            ssp.startInPlaceAnimationOnFrontMostApplication(
4583694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    ActivityOptions.makeCustomInPlaceAnimation(mContext,
4593694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                            R.anim.recents_launch_prev_affiliated_task_bounce));
4603694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            return;
4613694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
4623694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4637137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Launch the task
4643694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
4657137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
4667137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
4673694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    /**
4683694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     * Transitions to the next affiliated task.
4693694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     */
4703694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void showRelativeAffiliatedTask(boolean showNextTask) {
4713694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        SystemServicesProxy ssp = Recents.getSystemServices();
4723694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoader loader = Recents.getTaskLoader();
4733694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
4743694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        loader.preloadTasks(plan, true /* isTopTaskHome */);
4753694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        TaskStack focusedStack = plan.getTaskStack();
4763694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4773694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there are no tasks in the focused stack
4783694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
4793694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4803694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
4813694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there is no running task (can't determine affiliated tasks in this case)
4823694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (runningTask == null) return;
4833694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if the running task is in the home stack (optimization)
4843694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
4853694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
4863694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Find the task in the recents list
4877137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ArrayList<Task> tasks = focusedStack.getStackTasks();
4887137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        Task toTask = null;
4897137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ActivityOptions launchOpts = null;
4903694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        int taskCount = tasks.size();
4917137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        int numAffiliatedTasks = 0;
4927137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        for (int i = 0; i < taskCount; i++) {
4937137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            Task task = tasks.get(i);
4947137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            if (task.key.id == runningTask.id) {
4957137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                TaskGrouping group = task.group;
4967137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                Task.TaskKey toTaskKey;
4977137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                if (showNextTask) {
4987137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    toTaskKey = group.getNextTaskInGroup(task);
4993694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
5007137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                            R.anim.recents_launch_next_affiliated_task_target,
5017137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                            R.anim.recents_launch_next_affiliated_task_source);
5027137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                } else {
5037137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    toTaskKey = group.getPrevTaskInGroup(task);
5047137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
5057137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                            R.anim.recents_launch_prev_affiliated_task_target,
5067137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                            R.anim.recents_launch_prev_affiliated_task_source);
5077137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                }
5087137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang                if (toTaskKey != null) {
5093694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    toTask = focusedStack.findTaskWithId(toTaskKey.id);
5103694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                }
5113694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                numAffiliatedTasks = group.getTaskCount();
5123694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                break;
5133694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
5143694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
5153694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
5163694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        // Return early if there is no next task
5173694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (toTask == null) {
5183694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            if (numAffiliatedTasks > 1) {
5193694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                if (showNextTask) {
5203694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    ssp.startInPlaceAnimationOnFrontMostApplication(
5213694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
5223694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                                    R.anim.recents_launch_next_affiliated_task_bounce));
5233694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                } else {
5243694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    ssp.startInPlaceAnimationOnFrontMostApplication(
5253694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
5263694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                                    R.anim.recents_launch_prev_affiliated_task_bounce));
5273694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                }
5283694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            }
5297137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang            return;
5307137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        }
5317137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
5327137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Keep track of actually launched affiliated tasks
5337137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
5347137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
5357137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Launch the task
5367137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
5377137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
5387137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
5397137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public void showNextAffiliatedTask() {
5407137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Keep track of when the affiliated task is triggered
5417137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
5427137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        showRelativeAffiliatedTask(true);
5437137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
5447137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
5457137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    public void showPrevAffiliatedTask() {
5467137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        // Keep track of when the affiliated task is triggered
5477137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
5487137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang        showRelativeAffiliatedTask(false);
5497137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    }
5507137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang
5513694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
5523694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        SystemServicesProxy ssp = Recents.getSystemServices();
5533694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
5543694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
5553694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            ssp.moveTaskToDockedStack(topTask.id, stackCreateMode, initialBounds);
5563694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung            showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */,
5573694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung                    true /* reloadTasks*/);
5583694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        }
5593694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
5603694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
5613694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    /**
5623694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     * Returns the preloaded load plan and invalidates it.
5633694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung     */
5643694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
5653694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
5663694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        sInstanceLoadPlan = null;
5673694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung        return plan;
5683694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung    }
5693694d7cf40d1645bf05246cf38595eed606bb650Jaesung Chung
5707137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang    /**
5717137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * Prepares the header bar layout for the next transition, if the task view bounds has changed
5727137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
5737137ec7e005a5a6e3c0edb91cfacf16a31f4bf6aChong Zhang     *
574     * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
575     *                               is not already bound (can be expensive)
576     * @param stack the stack to initialize the stack layout with
577     */
578    private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
579        RecentsConfiguration config = Recents.getConfiguration();
580        SystemServicesProxy ssp = Recents.getSystemServices();
581        Rect windowRect = ssp.getWindowRect();
582
583        // Update the configuration for the current state
584        config.update(windowRect);
585
586        if (RecentsDebugFlags.Static.EnableSearchBar && tryAndBindSearchWidget) {
587            // Try and pre-emptively bind the search widget on startup to ensure that we
588            // have the right thumbnail bounds to animate to.
589            // Note: We have to reload the widget id before we get the task stack bounds below
590            if (ssp.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
591                config.getSearchBarBounds(windowRect, mStatusBarHeight, mSearchBarBounds);
592            }
593        }
594        Rect systemInsets = new Rect(0, mStatusBarHeight,
595                (config.hasTransposedNavBar ? mNavBarWidth : 0),
596                (config.hasTransposedNavBar ? 0 : mNavBarHeight));
597        config.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
598                mSearchBarBounds, mTaskStackBounds);
599
600        // Rebind the header bar and draw it for the transition
601        TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
602        Rect taskStackBounds = new Rect(mTaskStackBounds);
603        algo.setSystemInsets(systemInsets);
604        if (stack != null) {
605            algo.initialize(taskStackBounds,
606                    TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
607        }
608        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
609        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
610            mLastTaskViewBounds.set(taskViewBounds);
611
612            int taskViewWidth = taskViewBounds.width();
613            synchronized (mHeaderBarLock) {
614                mHeaderBar.measure(
615                    View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
616                    View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
617                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
618            }
619        }
620    }
621
622    /**
623     * Preloads the icon of a task.
624     */
625    private void preloadIcon(ActivityManager.RunningTaskInfo task) {
626        // Ensure that we load the running task's icon
627        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
628        launchOpts.runningTaskId = task.id;
629        launchOpts.loadThumbnails = false;
630        launchOpts.onlyLoadForCache = true;
631        Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
632    }
633
634    /**
635     * Caches the header thumbnail used for a window animation asynchronously into
636     * {@link #mThumbnailTransitionBitmapCache}.
637     */
638    private void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
639            TaskStack stack, TaskStackView stackView) {
640        preloadIcon(topTask);
641
642        // Update the header bar if necessary
643        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
644
645        // Update the destination rect
646        mDummyStackView.updateLayoutForStack(stack);
647        final Task toTask = new Task();
648        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
649                topTask.id, toTask);
650        ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
651            @Override
652            public void run() {
653                final Bitmap transitionBitmap = drawThumbnailTransitionBitmap(toTask, toTransform);
654                mHandler.post(new Runnable() {
655                    @Override
656                    public void run() {
657                        mThumbnailTransitionBitmapCache = transitionBitmap;
658                        mThumbnailTransitionBitmapCacheKey = toTask;
659                    }
660                });
661            }
662        });
663    }
664
665    /**
666     * Creates the activity options for a unknown state->recents transition.
667     */
668    private ActivityOptions getUnknownTransitionActivityOptions() {
669        return ActivityOptions.makeCustomAnimation(mContext,
670                R.anim.recents_from_unknown_enter,
671                R.anim.recents_from_unknown_exit,
672                mHandler, null);
673    }
674
675    /**
676     * Creates the activity options for a home->recents transition.
677     */
678    private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
679        if (fromSearchHome) {
680            return ActivityOptions.makeCustomAnimation(mContext,
681                    R.anim.recents_from_search_launcher_enter,
682                    R.anim.recents_from_search_launcher_exit,
683                    mHandler, null);
684        }
685        return ActivityOptions.makeCustomAnimation(mContext,
686                R.anim.recents_from_launcher_enter,
687                R.anim.recents_from_launcher_exit,
688                mHandler, null);
689    }
690
691    /**
692     * Creates the activity options for an app->recents transition.
693     */
694    private ActivityOptions getThumbnailTransitionActivityOptions(
695            ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
696        if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
697            ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
698            stackView.getScroller().setStackScrollToInitialState();
699            ArrayList<Task> tasks = stack.getStackTasks();
700            for (int i = tasks.size() - 1; i >= 0; i--) {
701                Task task = tasks.get(i);
702                if (task.isFreeformTask()) {
703                    mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
704                            stackView.getScroller().getStackScroll(), mTmpTransform, null);
705                    Rect toTaskRect = new Rect();
706                    mTmpTransform.rect.round(toTaskRect);
707                    Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
708                    specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
709                }
710            }
711            AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
712            specs.toArray(specsArray);
713            return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
714                    specsArray, mHandler, null, this);
715        } else {
716            // Update the destination rect
717            Task toTask = new Task();
718            TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
719                    topTask.id, toTask);
720            RectF toTaskRect = toTransform.rect;
721            Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
722            if (thumbnail != null) {
723                return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
724                        thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
725                        (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
726            }
727            // If both the screenshot and thumbnail fails, then just fall back to the default transition
728            return getUnknownTransitionActivityOptions();
729        }
730    }
731
732    private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
733            TaskViewTransform toTransform) {
734        Bitmap thumbnail;
735        if (mThumbnailTransitionBitmapCacheKey != null
736                && mThumbnailTransitionBitmapCacheKey.key != null
737                && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
738            thumbnail = mThumbnailTransitionBitmapCache;
739            mThumbnailTransitionBitmapCacheKey = null;
740            mThumbnailTransitionBitmapCache = null;
741        } else {
742            preloadIcon(topTask);
743            thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
744        }
745        return thumbnail;
746    }
747
748    /**
749     * Returns the transition rect for the given task id.
750     */
751    private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
752            TaskStackView stackView, int runningTaskId, Task runningTaskOut) {
753        // Find the running task in the TaskStack
754        Task task = null;
755        ArrayList<Task> tasks = stack.getStackTasks();
756        if (runningTaskId != -1) {
757            // Otherwise, try and find the task with the
758            int taskCount = tasks.size();
759            for (int i = taskCount - 1; i >= 0; i--) {
760                Task t = tasks.get(i);
761                if (t.key.id == runningTaskId) {
762                    task = t;
763                    runningTaskOut.copyFrom(t);
764                    break;
765                }
766            }
767        }
768        if (task == null) {
769            // If no task is specified or we can not find the task just use the front most one
770            task = tasks.get(tasks.size() - 1);
771            runningTaskOut.copyFrom(task);
772        }
773
774        // Get the transform for the running task
775        stackView.getScroller().setStackScrollToInitialState();
776        mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
777                stackView.getScroller().getStackScroll(), mTmpTransform, null);
778        return mTmpTransform;
779    }
780
781    /**
782     * Draws the header of a task used for the window animation into a bitmap.
783     */
784    private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
785        if (toTransform != null && toTask.key != null) {
786            Bitmap thumbnail;
787            synchronized (mHeaderBarLock) {
788                int toHeaderWidth = (int) toTransform.rect.width();
789                int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
790                mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
791                        (int) toTransform.rect.height());
792                thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
793                        Bitmap.Config.ARGB_8888);
794                if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
795                    thumbnail.eraseColor(0xFFff0000);
796                } else {
797                    Canvas c = new Canvas(thumbnail);
798                    c.scale(toTransform.scale, toTransform.scale);
799                    mHeaderBar.rebindToTask(toTask);
800                    mHeaderBar.draw(c);
801                    c.setBitmap(null);
802                }
803            }
804            return thumbnail.createAshmemBitmap();
805        }
806        return null;
807    }
808
809    /**
810     * Shows the recents activity
811     */
812    private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
813            boolean isTopTaskHome, boolean animate) {
814        RecentsTaskLoader loader = Recents.getTaskLoader();
815
816        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
817        // should always preload the tasks now. If we are dragging in recents, reload them as
818        // the stacks might have changed.
819        if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
820            // Create a new load plan if preloadRecents() was never triggered
821            sInstanceLoadPlan = loader.createLoadPlan(mContext);
822        }
823        if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
824            loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
825        }
826        TaskStack stack = sInstanceLoadPlan.getTaskStack();
827
828        // Update the header bar if necessary
829        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
830
831        // Prepare the dummy stack for the transition
832        mDummyStackView.updateLayoutForStack(stack);
833        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
834                mDummyStackView.computeStackVisibilityReport();
835
836        if (!animate) {
837            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
838            startRecentsActivity(topTask, opts, false /* fromHome */,
839                    false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
840            return;
841        }
842
843        boolean hasRecentTasks = stack.getStackTaskCount() > 0;
844        boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
845
846        if (useThumbnailTransition) {
847            // Try starting with a thumbnail transition
848            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
849                    mDummyStackView);
850            if (opts != null) {
851                startRecentsActivity(topTask, opts, false /* fromHome */,
852                        false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
853            } else {
854                // Fall through below to the non-thumbnail transition
855                useThumbnailTransition = false;
856            }
857        }
858
859        if (!useThumbnailTransition) {
860            // If there is no thumbnail transition, but is launching from home into recents, then
861            // use a quick home transition and do the animation from home
862            if (hasRecentTasks) {
863                SystemServicesProxy ssp = Recents.getSystemServices();
864                String homeActivityPackage = ssp.getHomeActivityPackageName();
865                String searchWidgetPackage = null;
866                if (RecentsDebugFlags.Static.EnableSearchBar) {
867                    searchWidgetPackage = Prefs.getString(mContext,
868                            Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
869                } else {
870                    AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
871                    if (searchWidgetInfo != null) {
872                        searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
873                    }
874                }
875
876                // Determine whether we are coming from a search owned home activity
877                boolean fromSearchHome = (homeActivityPackage != null) &&
878                        homeActivityPackage.equals(searchWidgetPackage);
879                ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
880                startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
881                        false /* fromThumbnail */, stackVr);
882            } else {
883                // Otherwise we do the normal fade from an unknown source
884                ActivityOptions opts = getUnknownTransitionActivityOptions();
885                startRecentsActivity(topTask, opts, true /* fromHome */,
886                        false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
887            }
888        }
889        mLastToggleTime = SystemClock.elapsedRealtime();
890    }
891
892    /**
893     * Starts the recents activity.
894     */
895    private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
896              ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
897              TaskStackLayoutAlgorithm.VisibilityReport vr) {
898        // Update the configuration based on the launch options
899        RecentsConfiguration config = Recents.getConfiguration();
900        RecentsActivityLaunchState launchState = config.getLaunchState();
901        launchState.launchedFromHome = fromSearchHome || fromHome;
902        launchState.launchedFromSearchHome = fromSearchHome;
903        launchState.launchedFromAppWithThumbnail = fromThumbnail;
904        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
905        launchState.launchedWithAltTab = mTriggeredFromAltTab;
906        launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
907        launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
908        launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
909        launchState.launchedHasConfigurationChanged = false;
910        launchState.launchedViaDragGesture = mDraggingInRecents;
911
912        Intent intent = new Intent();
913        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
914        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
915                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
916                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
917        if (opts != null) {
918            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
919        } else {
920            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
921        }
922        mCanReuseTaskStackViews = true;
923    }
924
925    /**** OnAnimationFinishedListener Implementation ****/
926
927    @Override
928    public void onAnimationFinished() {
929        EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
930    }
931}
932