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