blob: e192da7bb547143e5cb0837cefffa511d0d858c5 [file] [log] [blame]
Winson190fe3bf2015-10-20 14:57:24 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents;
18
Winsone693aaf2016-03-01 12:05:59 -080019import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
Winson94bc4f22016-04-07 14:22:12 -070020import static android.view.View.MeasureSpec;
Winsone693aaf2016-03-01 12:05:59 -080021
Winson190fe3bf2015-10-20 14:57:24 -070022import android.app.ActivityManager;
23import android.app.ActivityOptions;
Winson190fe3bf2015-10-20 14:57:24 -070024import android.content.ActivityNotFoundException;
Winson190fe3bf2015-10-20 14:57:24 -070025import android.content.Context;
26import android.content.Intent;
27import android.content.res.Resources;
28import android.graphics.Bitmap;
29import android.graphics.Canvas;
30import android.graphics.Rect;
Winson3150e572015-10-23 15:07:24 -070031import android.graphics.RectF;
Winson22574af2016-03-23 19:00:28 -070032import android.graphics.drawable.Drawable;
Winson190fe3bf2015-10-20 14:57:24 -070033import android.os.Handler;
34import android.os.SystemClock;
35import android.os.UserHandle;
Winson1b585612015-11-06 09:16:26 -080036import android.util.Log;
Winson190fe3bf2015-10-20 14:57:24 -070037import android.util.MutableBoolean;
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -070038import android.view.AppTransitionAnimationSpec;
Winson190fe3bf2015-10-20 14:57:24 -070039import android.view.LayoutInflater;
Winsonb61e6542016-02-04 14:37:18 -080040import android.view.ViewConfiguration;
Winsonc0d70582016-01-29 10:24:39 -080041
Winson190fe3bf2015-10-20 14:57:24 -070042import com.android.internal.logging.MetricsLogger;
Winson190fe3bf2015-10-20 14:57:24 -070043import com.android.systemui.R;
44import com.android.systemui.SystemUIApplication;
Winson412e1802015-10-20 16:57:57 -070045import com.android.systemui.recents.events.EventBus;
Jorim Jaggi899327f2016-02-25 20:44:18 -050046import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
Winson1b585612015-11-06 09:16:26 -080047import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
Winson412e1802015-10-20 16:57:57 -070048import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070049import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Winsonb61e6542016-02-04 14:37:18 -080050import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080051import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
Winson412e1802015-10-20 16:57:57 -070052import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
Winson190fe3bf2015-10-20 14:57:24 -070053import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
54import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
Jorim Jaggidd98d412015-11-18 15:57:38 -080055import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
56import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
Winson6b92c6e2015-11-06 13:11:16 -080057import com.android.systemui.recents.misc.DozeTrigger;
Winsonab84fc52015-10-23 11:52:07 -070058import com.android.systemui.recents.misc.ForegroundThread;
Winson190fe3bf2015-10-20 14:57:24 -070059import com.android.systemui.recents.misc.SystemServicesProxy;
Jaewan Kim938a50b2016-03-14 17:35:43 +090060import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
Winson190fe3bf2015-10-20 14:57:24 -070061import com.android.systemui.recents.model.RecentsTaskLoadPlan;
62import com.android.systemui.recents.model.RecentsTaskLoader;
63import com.android.systemui.recents.model.Task;
64import com.android.systemui.recents.model.TaskGrouping;
65import com.android.systemui.recents.model.TaskStack;
Winson36a5a2c2015-10-29 18:04:39 -070066import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
Winson1b585612015-11-06 09:16:26 -080067import com.android.systemui.recents.views.TaskStackView;
Winsone693aaf2016-03-01 12:05:59 -080068import com.android.systemui.recents.views.TaskStackViewScroller;
Winson190fe3bf2015-10-20 14:57:24 -070069import com.android.systemui.recents.views.TaskViewHeader;
70import com.android.systemui.recents.views.TaskViewTransform;
Winsond8b1d632016-01-04 17:51:18 -080071import com.android.systemui.statusbar.BaseStatusBar;
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080072import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
Winson190fe3bf2015-10-20 14:57:24 -070073import com.android.systemui.statusbar.phone.PhoneStatusBar;
74
75import java.util.ArrayList;
76
77/**
78 * An implementation of the Recents component for the current user. For secondary users, this can
79 * be called remotely from the system user.
80 */
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080081public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
Winson190fe3bf2015-10-20 14:57:24 -070082
83 private final static String TAG = "RecentsImpl";
Winsonb61e6542016-02-04 14:37:18 -080084
Winson6b92c6e2015-11-06 13:11:16 -080085 // The minimum amount of time between each recents button press that we will handle
86 private final static int MIN_TOGGLE_DELAY_MS = 350;
Winsonb61e6542016-02-04 14:37:18 -080087
Winson6b92c6e2015-11-06 13:11:16 -080088 // The duration within which the user releasing the alt tab (from when they pressed alt tab)
89 // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
90 // duration, then we will toggle recents after this duration.
91 private final static int FAST_ALT_TAB_DELAY_MS = 225;
Winson190fe3bf2015-10-20 14:57:24 -070092
93 public final static String RECENTS_PACKAGE = "com.android.systemui";
94 public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
Winsone693aaf2016-03-01 12:05:59 -080095
Winson190fe3bf2015-10-20 14:57:24 -070096 /**
Jaewan Kim938a50b2016-03-14 17:35:43 +090097 * An implementation of TaskStackListener, that allows us to listen for changes to the system
Winson190fe3bf2015-10-20 14:57:24 -070098 * task stacks and update recents accordingly.
99 */
Jaewan Kim938a50b2016-03-14 17:35:43 +0900100 class TaskStackListenerImpl extends TaskStackListener {
Winson190fe3bf2015-10-20 14:57:24 -0700101 @Override
102 public void onTaskStackChanged() {
Jaewan Kim938a50b2016-03-14 17:35:43 +0900103 // Preloads the next task
Jorim Jaggia0fdeec2016-01-07 14:42:28 +0100104 RecentsConfiguration config = Recents.getConfiguration();
Winson190fe3bf2015-10-20 14:57:24 -0700105 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Winsone7f138c2015-10-22 16:15:21 -0700106 RecentsTaskLoader loader = Recents.getTaskLoader();
107 SystemServicesProxy ssp = Recents.getSystemServices();
Winson190fe3bf2015-10-20 14:57:24 -0700108 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
109
110 // Load the next task only if we aren't svelte
111 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winson65c851e2016-01-20 12:43:35 -0800112 loader.preloadTasks(plan, -1, true /* isTopTaskHome */);
Winson190fe3bf2015-10-20 14:57:24 -0700113 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
114 // This callback is made when a new activity is launched and the old one is paused
115 // so ignore the current activity and try and preload the thumbnail for the
116 // previous one.
117 if (runningTaskInfo != null) {
118 launchOpts.runningTaskId = runningTaskInfo.id;
119 }
120 launchOpts.numVisibleTasks = 2;
121 launchOpts.numVisibleTaskThumbnails = 2;
122 launchOpts.onlyLoadForCache = true;
123 launchOpts.onlyLoadPausedActivities = true;
124 loader.loadTasks(mContext, plan, launchOpts);
125 }
Winson190fe3bf2015-10-20 14:57:24 -0700126 }
127 }
128
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700129 protected static RecentsTaskLoadPlan sInstanceLoadPlan;
Winson190fe3bf2015-10-20 14:57:24 -0700130
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700131 protected Context mContext;
132 protected Handler mHandler;
Winson190fe3bf2015-10-20 14:57:24 -0700133 TaskStackListenerImpl mTaskStackListener;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800134 boolean mDraggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800135 boolean mLaunchedWhileDocking;
Winson190fe3bf2015-10-20 14:57:24 -0700136
137 // Task launching
Winson190fe3bf2015-10-20 14:57:24 -0700138 Rect mTaskStackBounds = new Rect();
Winson190fe3bf2015-10-20 14:57:24 -0700139 TaskViewTransform mTmpTransform = new TaskViewTransform();
140 int mStatusBarHeight;
141 int mNavBarHeight;
142 int mNavBarWidth;
143 int mTaskBarHeight;
144
145 // Header (for transition)
146 TaskViewHeader mHeaderBar;
147 final Object mHeaderBarLock = new Object();
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700148 protected TaskStackView mDummyStackView;
Winson190fe3bf2015-10-20 14:57:24 -0700149
150 // Variables to keep track of if we need to start recents after binding
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700151 protected boolean mTriggeredFromAltTab;
152 protected long mLastToggleTime;
Winson6b92c6e2015-11-06 13:11:16 -0800153 DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
154 @Override
155 public void run() {
156 // When this fires, then the user has not released alt-tab for at least
157 // FAST_ALT_TAB_DELAY_MS milliseconds
Jorim Jaggi435b2e42015-11-24 15:09:30 -0800158 showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700159 false /* reloadTasks */, false /* fromHome */);
Winson6b92c6e2015-11-06 13:11:16 -0800160 }
161 });
Winson190fe3bf2015-10-20 14:57:24 -0700162
Winsonaeb298c2016-04-05 13:08:11 -0700163 protected Bitmap mThumbTransitionBitmapCache;
Winson190fe3bf2015-10-20 14:57:24 -0700164
Winson190fe3bf2015-10-20 14:57:24 -0700165 public RecentsImpl(Context context) {
166 mContext = context;
Winson190fe3bf2015-10-20 14:57:24 -0700167 mHandler = new Handler();
Winson190fe3bf2015-10-20 14:57:24 -0700168
Winsonab84fc52015-10-23 11:52:07 -0700169 // Initialize the static foreground thread
170 ForegroundThread.get();
171
Winson190fe3bf2015-10-20 14:57:24 -0700172 // Register the task stack listener
Jaewan Kim938a50b2016-03-14 17:35:43 +0900173 mTaskStackListener = new TaskStackListenerImpl();
Winsone7f138c2015-10-22 16:15:21 -0700174 SystemServicesProxy ssp = Recents.getSystemServices();
175 ssp.registerTaskStackListener(mTaskStackListener);
Winson190fe3bf2015-10-20 14:57:24 -0700176
177 // Initialize the static configuration resources
Winson670ea712016-04-12 17:02:26 -0700178 LayoutInflater inflater = LayoutInflater.from(mContext);
179 mDummyStackView = new TaskStackView(mContext);
180 mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
181 null, false);
182 reloadResources();
Winson190fe3bf2015-10-20 14:57:24 -0700183
184 // When we start, preload the data associated with the previous recent tasks.
185 // We can use a new plan since the caches will be the same.
Winsone7f138c2015-10-22 16:15:21 -0700186 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700187 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winson65c851e2016-01-20 12:43:35 -0800188 loader.preloadTasks(plan, -1, true /* isTopTaskHome */);
Winson190fe3bf2015-10-20 14:57:24 -0700189 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chung296278a2015-12-17 12:09:02 -0500190 launchOpts.numVisibleTasks = loader.getIconCacheSize();
Winson190fe3bf2015-10-20 14:57:24 -0700191 launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
192 launchOpts.onlyLoadForCache = true;
193 loader.loadTasks(mContext, plan, launchOpts);
194 }
195
196 public void onBootCompleted() {
Winsonaeb298c2016-04-05 13:08:11 -0700197 // Do nothing
Winson190fe3bf2015-10-20 14:57:24 -0700198 }
199
Winson190fe3bf2015-10-20 14:57:24 -0700200 public void onConfigurationChanged() {
Winson670ea712016-04-12 17:02:26 -0700201 reloadResources();
202 mDummyStackView.reloadOnConfigurationChange();
203 mHeaderBar.onConfigurationChanged();
Winson190fe3bf2015-10-20 14:57:24 -0700204 }
205
206 /**
207 * This is only called from the system user's Recents. Secondary users will instead proxy their
208 * visibility change events through to the system user via
209 * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
210 */
211 public void onVisibilityChanged(Context context, boolean visible) {
212 SystemUIApplication app = (SystemUIApplication) context;
213 PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
214 if (statusBar != null) {
215 statusBar.updateRecentsVisibility(visible);
216 }
217 }
218
219 /**
220 * This is only called from the system user's Recents. Secondary users will instead proxy their
221 * visibility change events through to the system user via
222 * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
223 */
Andrii Kulian0f051f52016-04-14 00:41:51 -0700224 public void onStartScreenPinning(Context context, int taskId) {
Winson190fe3bf2015-10-20 14:57:24 -0700225 SystemUIApplication app = (SystemUIApplication) context;
226 PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
227 if (statusBar != null) {
Andrii Kulian0f051f52016-04-14 00:41:51 -0700228 statusBar.showScreenPinningRequest(taskId, false);
Winson190fe3bf2015-10-20 14:57:24 -0700229 }
230 }
231
Jorim Jaggibb42a462015-11-20 16:27:16 -0800232 public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700233 boolean animate, boolean launchedWhileDockingTask, boolean fromHome) {
Winson190fe3bf2015-10-20 14:57:24 -0700234 mTriggeredFromAltTab = triggeredFromAltTab;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800235 mDraggingInRecents = draggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800236 mLaunchedWhileDocking = launchedWhileDockingTask;
Winsone693aaf2016-03-01 12:05:59 -0800237 if (mFastAltTabTrigger.isAsleep()) {
238 // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
239 mFastAltTabTrigger.stopDozing();
Winson6b92c6e2015-11-06 13:11:16 -0800240 } else if (mFastAltTabTrigger.isDozing()) {
Winsone693aaf2016-03-01 12:05:59 -0800241 // Fast alt-tab duration has not elapsed. If this is triggered by a different
242 // showRecents() call, then ignore that call for now.
243 // TODO: We can not handle quick tabs that happen between the initial showRecents() call
244 // that started the activity and the activity starting up. The severity of this
245 // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
Winson6b92c6e2015-11-06 13:11:16 -0800246 if (!triggeredFromAltTab) {
247 return;
248 }
249 mFastAltTabTrigger.stopDozing();
Winsone693aaf2016-03-01 12:05:59 -0800250 } else if (triggeredFromAltTab) {
251 // The fast alt-tab detector is not yet running, so start the trigger and wait for the
252 // hideRecents() call, or for the fast alt-tab duration to elapse
253 mFastAltTabTrigger.startDozing();
254 return;
Winson6b92c6e2015-11-06 13:11:16 -0800255 }
Winson190fe3bf2015-10-20 14:57:24 -0700256
257 try {
258 // Check if the top task is in the home stack, and start the recents activity
Winsone7f138c2015-10-22 16:15:21 -0700259 SystemServicesProxy ssp = Recents.getSystemServices();
260 ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
Winson190fe3bf2015-10-20 14:57:24 -0700261 MutableBoolean isTopTaskHome = new MutableBoolean(true);
Winsone7f138c2015-10-22 16:15:21 -0700262 if (topTask == null || !ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700263 startRecentsActivity(topTask, isTopTaskHome.value || fromHome, animate);
Winson190fe3bf2015-10-20 14:57:24 -0700264 }
265 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800266 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700267 }
268 }
269
Winson190fe3bf2015-10-20 14:57:24 -0700270 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson2799eca2016-02-25 12:10:42 -0800271 if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
272 // The user has released alt-tab before the trigger has run, so just show the next
273 // task immediately
274 showNextTask();
Winson6b92c6e2015-11-06 13:11:16 -0800275
Winson2799eca2016-02-25 12:10:42 -0800276 // Cancel the fast alt-tab trigger
277 mFastAltTabTrigger.stopDozing();
Winson2799eca2016-02-25 12:10:42 -0800278 return;
Winson190fe3bf2015-10-20 14:57:24 -0700279 }
Winson2799eca2016-02-25 12:10:42 -0800280
281 // Defer to the activity to handle hiding recents, if it handles it, then it must still
282 // be visible
283 EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
284 triggeredFromHomeKey));
Winson190fe3bf2015-10-20 14:57:24 -0700285 }
286
Winson190fe3bf2015-10-20 14:57:24 -0700287 public void toggleRecents() {
Winson6b92c6e2015-11-06 13:11:16 -0800288 // Skip this toggle if we are already waiting to trigger recents via alt-tab
289 if (mFastAltTabTrigger.isDozing()) {
290 return;
291 }
292
Jorim Jaggidd98d412015-11-18 15:57:38 -0800293 mDraggingInRecents = false;
Jorim Jaggie161f082016-02-05 14:26:16 -0800294 mLaunchedWhileDocking = false;
Winson190fe3bf2015-10-20 14:57:24 -0700295 mTriggeredFromAltTab = false;
296
297 try {
Winsone7f138c2015-10-22 16:15:21 -0700298 SystemServicesProxy ssp = Recents.getSystemServices();
299 ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
Winson190fe3bf2015-10-20 14:57:24 -0700300 MutableBoolean isTopTaskHome = new MutableBoolean(true);
Winsonb61e6542016-02-04 14:37:18 -0800301 long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
302
Winsone7f138c2015-10-22 16:15:21 -0700303 if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
Winsone693aaf2016-03-01 12:05:59 -0800304 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
Winson5da43472015-11-04 17:39:55 -0800305 RecentsConfiguration config = Recents.getConfiguration();
306 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson Chungead5c0f2015-12-14 11:18:57 -0500307 if (!launchState.launchedWithAltTab) {
Winsonb61e6542016-02-04 14:37:18 -0800308 // If the user taps quickly
Winsone693aaf2016-03-01 12:05:59 -0800309 if (!debugFlags.isPagingEnabled() ||
310 (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
311 elapsedTime < ViewConfiguration.getDoubleTapTimeout())) {
Winsonb61e6542016-02-04 14:37:18 -0800312 // Launch the next focused task
313 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
314 } else {
315 // Notify recents to move onto the next task
316 EventBus.getDefault().post(new IterateRecentsEvent());
317 }
Winson0d14d4d2015-10-26 17:05:04 -0700318 } else {
319 // If the user has toggled it too quickly, then just eat up the event here (it's
320 // better than showing a janky screenshot).
321 // NOTE: Ideally, the screenshot mechanism would take the window transform into
322 // account
Winsonb61e6542016-02-04 14:37:18 -0800323 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700324 return;
325 }
326
327 EventBus.getDefault().post(new ToggleRecentsEvent());
328 mLastToggleTime = SystemClock.elapsedRealtime();
329 }
Winson190fe3bf2015-10-20 14:57:24 -0700330 return;
331 } else {
Winson0d14d4d2015-10-26 17:05:04 -0700332 // If the user has toggled it too quickly, then just eat up the event here (it's
333 // better than showing a janky screenshot).
334 // NOTE: Ideally, the screenshot mechanism would take the window transform into
335 // account
Winsonb61e6542016-02-04 14:37:18 -0800336 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700337 return;
338 }
339
Winson190fe3bf2015-10-20 14:57:24 -0700340 // Otherwise, start the recents activity
Jorim Jaggibb42a462015-11-20 16:27:16 -0800341 startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */);
Winsond8b1d632016-01-04 17:51:18 -0800342
343 // Only close the other system windows if we are actually showing recents
344 ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
Winson0d14d4d2015-10-26 17:05:04 -0700345 mLastToggleTime = SystemClock.elapsedRealtime();
Winson190fe3bf2015-10-20 14:57:24 -0700346 }
347 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800348 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700349 }
350 }
351
Winson190fe3bf2015-10-20 14:57:24 -0700352 public void preloadRecents() {
353 // Preload only the raw task list into a new load plan (which will be consumed by the
354 // RecentsActivity) only if there is a task to animate to.
Winsone7f138c2015-10-22 16:15:21 -0700355 SystemServicesProxy ssp = Recents.getSystemServices();
356 ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
Winson190fe3bf2015-10-20 14:57:24 -0700357 MutableBoolean topTaskHome = new MutableBoolean(true);
Winsone7f138c2015-10-22 16:15:21 -0700358 if (topTask != null && !ssp.isRecentsTopMost(topTask, topTaskHome)) {
Winson5b4e0d22016-02-16 18:11:35 -0800359 RecentsTaskLoader loader = Recents.getTaskLoader();
360 sInstanceLoadPlan = loader.createLoadPlan(mContext);
Winson190fe3bf2015-10-20 14:57:24 -0700361 sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
Winson65c851e2016-01-20 12:43:35 -0800362 loader.preloadTasks(sInstanceLoadPlan, topTask.id, topTaskHome.value);
Winson190fe3bf2015-10-20 14:57:24 -0700363 TaskStack stack = sInstanceLoadPlan.getTaskStack();
Winson4b057c62016-01-12 15:01:52 -0800364 if (stack.getTaskCount() > 0) {
Winsonaeb298c2016-04-05 13:08:11 -0700365 // Only preload the icon (but not the thumbnail since it may not have been taken for
366 // the pausing activity)
367 preloadIcon(topTask);
368
369 // At this point, we don't know anything about the stack state. So only calculate
370 // the dimensions of the thumbnail that we need for the transition into Recents, but
371 // do not draw it until we construct the activity options when we start Recents
372 updateHeaderBarLayout(stack);
Winson190fe3bf2015-10-20 14:57:24 -0700373 }
374 }
375 }
376
Winson190fe3bf2015-10-20 14:57:24 -0700377 public void cancelPreloadingRecents() {
378 // Do nothing
379 }
380
Jorim Jaggidd98d412015-11-18 15:57:38 -0800381 public void onDraggingInRecents(float distanceFromTop) {
382 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
383 }
384
Jorim Jaggidd98d412015-11-18 15:57:38 -0800385 public void onDraggingInRecentsEnded(float velocity) {
386 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
387 }
388
Winson6b92c6e2015-11-06 13:11:16 -0800389 /**
390 * Transitions to the next recent task in the stack.
391 */
392 public void showNextTask() {
Winsone7f138c2015-10-22 16:15:21 -0700393 SystemServicesProxy ssp = Recents.getSystemServices();
Winson6b92c6e2015-11-06 13:11:16 -0800394 RecentsTaskLoader loader = Recents.getTaskLoader();
395 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winson65c851e2016-01-20 12:43:35 -0800396 loader.preloadTasks(plan, -1, true /* isTopTaskHome */);
Winson6b92c6e2015-11-06 13:11:16 -0800397 TaskStack focusedStack = plan.getTaskStack();
398
399 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800400 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson6b92c6e2015-11-06 13:11:16 -0800401
402 ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
403 // Return early if there is no running task
404 if (runningTask == null) return;
Winson6b92c6e2015-11-06 13:11:16 -0800405
406 // Find the task in the recents list
Winsone86deb82015-11-12 09:32:10 -0800407 boolean isTopTaskHome = SystemServicesProxy.isHomeStack(runningTask.stackId);
Winson250608a2015-11-24 15:00:31 -0800408 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson6b92c6e2015-11-06 13:11:16 -0800409 Task toTask = null;
410 ActivityOptions launchOpts = null;
411 int taskCount = tasks.size();
412 for (int i = taskCount - 1; i >= 1; i--) {
413 Task task = tasks.get(i);
Winsone86deb82015-11-12 09:32:10 -0800414 if (isTopTaskHome) {
415 toTask = tasks.get(i - 1);
416 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
417 R.anim.recents_launch_next_affiliated_task_target,
418 R.anim.recents_fast_toggle_app_home_exit);
419 break;
420 } else if (task.key.id == runningTask.id) {
Winson6b92c6e2015-11-06 13:11:16 -0800421 toTask = tasks.get(i - 1);
422 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
423 R.anim.recents_launch_prev_affiliated_task_target,
424 R.anim.recents_launch_prev_affiliated_task_source);
425 break;
426 }
427 }
428
429 // Return early if there is no next task
430 if (toTask == null) {
431 ssp.startInPlaceAnimationOnFrontMostApplication(
432 ActivityOptions.makeCustomInPlaceAnimation(mContext,
433 R.anim.recents_launch_prev_affiliated_task_bounce));
434 return;
435 }
436
437 // Launch the task
Wale Ogunwale64ae08a2016-03-28 08:15:27 -0700438 ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts);
Winson6b92c6e2015-11-06 13:11:16 -0800439 }
440
441 /**
442 * Transitions to the next affiliated task.
443 */
444 public void showRelativeAffiliatedTask(boolean showNextTask) {
445 SystemServicesProxy ssp = Recents.getSystemServices();
Winsone7f138c2015-10-22 16:15:21 -0700446 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700447 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winson65c851e2016-01-20 12:43:35 -0800448 loader.preloadTasks(plan, -1, true /* isTopTaskHome */);
Winson190fe3bf2015-10-20 14:57:24 -0700449 TaskStack focusedStack = plan.getTaskStack();
450
451 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800452 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson190fe3bf2015-10-20 14:57:24 -0700453
Winsone7f138c2015-10-22 16:15:21 -0700454 ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
Winson190fe3bf2015-10-20 14:57:24 -0700455 // Return early if there is no running task (can't determine affiliated tasks in this case)
456 if (runningTask == null) return;
457 // Return early if the running task is in the home stack (optimization)
Winson5510f6c2015-10-27 12:11:26 -0700458 if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
Winson190fe3bf2015-10-20 14:57:24 -0700459
460 // Find the task in the recents list
Winson250608a2015-11-24 15:00:31 -0800461 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson190fe3bf2015-10-20 14:57:24 -0700462 Task toTask = null;
463 ActivityOptions launchOpts = null;
464 int taskCount = tasks.size();
465 int numAffiliatedTasks = 0;
466 for (int i = 0; i < taskCount; i++) {
467 Task task = tasks.get(i);
468 if (task.key.id == runningTask.id) {
469 TaskGrouping group = task.group;
470 Task.TaskKey toTaskKey;
471 if (showNextTask) {
472 toTaskKey = group.getNextTaskInGroup(task);
473 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
474 R.anim.recents_launch_next_affiliated_task_target,
475 R.anim.recents_launch_next_affiliated_task_source);
476 } else {
477 toTaskKey = group.getPrevTaskInGroup(task);
478 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
479 R.anim.recents_launch_prev_affiliated_task_target,
480 R.anim.recents_launch_prev_affiliated_task_source);
481 }
482 if (toTaskKey != null) {
483 toTask = focusedStack.findTaskWithId(toTaskKey.id);
484 }
485 numAffiliatedTasks = group.getTaskCount();
486 break;
487 }
488 }
489
490 // Return early if there is no next task
491 if (toTask == null) {
492 if (numAffiliatedTasks > 1) {
493 if (showNextTask) {
Winsone7f138c2015-10-22 16:15:21 -0700494 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700495 ActivityOptions.makeCustomInPlaceAnimation(mContext,
496 R.anim.recents_launch_next_affiliated_task_bounce));
497 } else {
Winsone7f138c2015-10-22 16:15:21 -0700498 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700499 ActivityOptions.makeCustomInPlaceAnimation(mContext,
500 R.anim.recents_launch_prev_affiliated_task_bounce));
501 }
502 }
503 return;
504 }
505
506 // Keep track of actually launched affiliated tasks
507 MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
508
509 // Launch the task
Wale Ogunwale64ae08a2016-03-28 08:15:27 -0700510 ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts);
Winson190fe3bf2015-10-20 14:57:24 -0700511 }
512
513 public void showNextAffiliatedTask() {
514 // Keep track of when the affiliated task is triggered
515 MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
516 showRelativeAffiliatedTask(true);
517 }
518
519 public void showPrevAffiliatedTask() {
520 // Keep track of when the affiliated task is triggered
521 MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
522 showRelativeAffiliatedTask(false);
523 }
524
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800525 public void dockTopTask(int topTaskId, int dragMode,
526 int stackCreateMode, Rect initialBounds) {
Jorim Jaggi75b25972015-10-21 14:51:10 +0200527 SystemServicesProxy ssp = Recents.getSystemServices();
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800528
529 // Make sure we inform DividerView before we actually start the activity so we can change
530 // the resize mode already.
Chong Zhange4fbd322016-03-01 14:44:03 -0800531 if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500532 EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
Chong Zhange4fbd322016-03-01 14:44:03 -0800533 showRecents(
534 false /* triggeredFromAltTab */,
535 dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
536 false /* animate */,
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700537 true /* launchedWhileDockingTask*/,
538 false /* fromHome */);
Chong Zhange4fbd322016-03-01 14:44:03 -0800539 }
Jorim Jaggi75b25972015-10-21 14:51:10 +0200540 }
541
Winson190fe3bf2015-10-20 14:57:24 -0700542 /**
543 * Returns the preloaded load plan and invalidates it.
544 */
545 public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
546 RecentsTaskLoadPlan plan = sInstanceLoadPlan;
547 sInstanceLoadPlan = null;
548 return plan;
549 }
550
551 /**
Winson670ea712016-04-12 17:02:26 -0700552 * Reloads all the resources for the current configuration.
Winsonb94443d2016-01-07 15:34:13 -0800553 */
Winson670ea712016-04-12 17:02:26 -0700554 private void reloadResources() {
Winsonb94443d2016-01-07 15:34:13 -0800555 Resources res = mContext.getResources();
Winsonb94443d2016-01-07 15:34:13 -0800556
557 mStatusBarHeight = res.getDimensionPixelSize(
558 com.android.internal.R.dimen.status_bar_height);
559 mNavBarHeight = res.getDimensionPixelSize(
560 com.android.internal.R.dimen.navigation_bar_height);
561 mNavBarWidth = res.getDimensionPixelSize(
562 com.android.internal.R.dimen.navigation_bar_width);
Winson21700932016-03-24 17:26:23 -0700563 mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
564 R.dimen.recents_task_view_header_height,
565 R.dimen.recents_task_view_header_height,
566 R.dimen.recents_task_view_header_height,
567 R.dimen.recents_task_view_header_height_tablet_land,
568 R.dimen.recents_task_view_header_height,
569 R.dimen.recents_task_view_header_height_tablet_land);
Winsonb94443d2016-01-07 15:34:13 -0800570 }
571
572 /**
Winson190fe3bf2015-10-20 14:57:24 -0700573 * Prepares the header bar layout for the next transition, if the task view bounds has changed
574 * since the last call, it will attempt to re-measure and layout the header bar to the new size.
575 *
Winsonf0d1c442015-12-01 11:04:45 -0800576 * @param stack the stack to initialize the stack layout with
Winson190fe3bf2015-10-20 14:57:24 -0700577 */
Winson008ee15f2016-03-18 17:17:25 -0700578 private void updateHeaderBarLayout(TaskStack stack) {
Winsone7f138c2015-10-22 16:15:21 -0700579 SystemServicesProxy ssp = Recents.getSystemServices();
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800580 Rect systemInsets = new Rect();
581 ssp.getStableInsets(systemInsets);
Winsone7f138c2015-10-22 16:15:21 -0700582 Rect windowRect = ssp.getWindowRect();
Winsoncf9b8322016-03-31 15:36:07 -0700583 // When docked, the nav bar insets are consumed and the activity is measured without insets.
584 // However, the window bounds include the insets, so we need to subtract them here to make
585 // them identical.
586 if (ssp.hasDockedTask()) {
587 windowRect.bottom -= systemInsets.bottom;
588 systemInsets.bottom = 0;
589 }
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800590 calculateWindowStableInsets(systemInsets, windowRect);
591 windowRect.offsetTo(0, 0);
Winson190fe3bf2015-10-20 14:57:24 -0700592
Winson59924fe2016-03-17 14:13:18 -0700593 TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Winson190fe3bf2015-10-20 14:57:24 -0700594
595 // Rebind the header bar and draw it for the transition
Winson88737542016-02-17 13:27:33 -0800596 stackLayout.setSystemInsets(systemInsets);
Winsonf0d1c442015-12-01 11:04:45 -0800597 if (stack != null) {
Winsoncf9b8322016-03-31 15:36:07 -0700598 stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
599 mTaskStackBounds);
Winsonaeb298c2016-04-05 13:08:11 -0700600 stackLayout.reset();
Winsoncf9b8322016-03-31 15:36:07 -0700601 stackLayout.initialize(windowRect, mTaskStackBounds,
Winsonf0d1c442015-12-01 11:04:45 -0800602 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
Winsona1ededd2016-03-25 12:23:12 -0700603 mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
Winson190fe3bf2015-10-20 14:57:24 -0700604
Winsonaeb298c2016-04-05 13:08:11 -0700605 Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
Winson94bc4f22016-04-07 14:22:12 -0700606 if (!taskViewBounds.isEmpty()) {
607 int taskViewWidth = taskViewBounds.width();
608 synchronized (mHeaderBarLock) {
609 if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
610 mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
611 mHeaderBar.measure(
612 MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
613 MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
614 }
615 mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
Winsonaeb298c2016-04-05 13:08:11 -0700616 }
Winsonaeb298c2016-04-05 13:08:11 -0700617
Winson94bc4f22016-04-07 14:22:12 -0700618 // Update the transition bitmap to match the new header bar height
619 if (mThumbTransitionBitmapCache == null ||
620 (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
621 (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
622 mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
623 mTaskBarHeight, Bitmap.Config.ARGB_8888);
624 }
Winsonaeb298c2016-04-05 13:08:11 -0700625 }
Winson190fe3bf2015-10-20 14:57:24 -0700626 }
627 }
628
629 /**
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800630 * Given the stable insets and the rect for our window, calculates the insets that affect our
631 * window.
632 */
633 private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect) {
634 Rect displayRect = Recents.getSystemServices().getDisplayRect();
635
636 // Display rect without insets - available app space
637 Rect appRect = new Rect(displayRect);
638 appRect.inset(inOutInsets);
639
640 // Our window intersected with available app space
641 Rect windowRectWithInsets = new Rect(windowRect);
642 windowRectWithInsets.intersect(appRect);
643 inOutInsets.left = windowRectWithInsets.left - windowRect.left;
644 inOutInsets.top = windowRectWithInsets.top - windowRect.top;
645 inOutInsets.right = windowRect.right - windowRectWithInsets.right;
646 inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
647 }
648
649 /**
Winson190fe3bf2015-10-20 14:57:24 -0700650 * Preloads the icon of a task.
651 */
652 private void preloadIcon(ActivityManager.RunningTaskInfo task) {
Winson190fe3bf2015-10-20 14:57:24 -0700653 // Ensure that we load the running task's icon
654 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
655 launchOpts.runningTaskId = task.id;
656 launchOpts.loadThumbnails = false;
657 launchOpts.onlyLoadForCache = true;
Winsone7f138c2015-10-22 16:15:21 -0700658 Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
Winson190fe3bf2015-10-20 14:57:24 -0700659 }
660
661 /**
Winson190fe3bf2015-10-20 14:57:24 -0700662 * Creates the activity options for a unknown state->recents transition.
663 */
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700664 protected ActivityOptions getUnknownTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700665 return ActivityOptions.makeCustomAnimation(mContext,
666 R.anim.recents_from_unknown_enter,
667 R.anim.recents_from_unknown_exit,
Winson3fb67562015-11-11 10:39:03 -0800668 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700669 }
670
671 /**
672 * Creates the activity options for a home->recents transition.
673 */
Winson008ee15f2016-03-18 17:17:25 -0700674 protected ActivityOptions getHomeTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700675 return ActivityOptions.makeCustomAnimation(mContext,
676 R.anim.recents_from_launcher_enter,
677 R.anim.recents_from_launcher_exit,
Winson3fb67562015-11-11 10:39:03 -0800678 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700679 }
680
681 /**
682 * Creates the activity options for an app->recents transition.
683 */
684 private ActivityOptions getThumbnailTransitionActivityOptions(
Winsone693aaf2016-03-01 12:05:59 -0800685 ActivityManager.RunningTaskInfo topTask, TaskStackView stackView) {
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700686 if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
687 ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
Winsone693aaf2016-03-01 12:05:59 -0800688 ArrayList<Task> tasks = stackView.getStack().getStackTasks();
689 TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
690 TaskStackViewScroller stackScroller = stackView.getScroller();
691
Winson003eda62016-03-11 14:56:00 -0800692 stackView.updateLayoutAlgorithm(true /* boundScroll */);
Winson67c79572016-04-13 14:02:18 -0700693 stackView.updateToInitialState();
Winsone693aaf2016-03-01 12:05:59 -0800694
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700695 for (int i = tasks.size() - 1; i >= 0; i--) {
696 Task task = tasks.get(i);
Winson387aac62015-11-25 11:18:56 -0800697 if (task.isFreeformTask()) {
Winsone693aaf2016-03-01 12:05:59 -0800698 mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
699 stackScroller.getStackScroll(), mTmpTransform, null);
Winsonaeb298c2016-04-05 13:08:11 -0700700 Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
701 mThumbTransitionBitmapCache);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700702 Rect toTaskRect = new Rect();
703 mTmpTransform.rect.round(toTaskRect);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700704 specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
705 }
706 }
707 AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
708 specs.toArray(specsArray);
709 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
Winson3fb67562015-11-11 10:39:03 -0800710 specsArray, mHandler, null, this);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700711 } else {
712 // Update the destination rect
713 Task toTask = new Task();
Winsone693aaf2016-03-01 12:05:59 -0800714 TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
Winsonaeb298c2016-04-05 13:08:11 -0700715 Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
716 mThumbTransitionBitmapCache);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700717 if (thumbnail != null) {
Winsonaeb298c2016-04-05 13:08:11 -0700718 RectF toTaskRect = toTransform.rect;
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700719 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
720 thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
Winson3fb67562015-11-11 10:39:03 -0800721 (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700722 }
723 // If both the screenshot and thumbnail fails, then just fall back to the default transition
724 return getUnknownTransitionActivityOptions();
725 }
726 }
Winson190fe3bf2015-10-20 14:57:24 -0700727
Winson190fe3bf2015-10-20 14:57:24 -0700728 /**
729 * Returns the transition rect for the given task id.
730 */
Winsone693aaf2016-03-01 12:05:59 -0800731 private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
732 Task runningTaskOut) {
Winson190fe3bf2015-10-20 14:57:24 -0700733 // Find the running task in the TaskStack
Winsone693aaf2016-03-01 12:05:59 -0800734 TaskStack stack = stackView.getStack();
Winson65c851e2016-01-20 12:43:35 -0800735 Task launchTask = stack.getLaunchTarget();
736 if (launchTask != null) {
737 runningTaskOut.copyFrom(launchTask);
738 } else {
Winson190fe3bf2015-10-20 14:57:24 -0700739 // If no task is specified or we can not find the task just use the front most one
Winson35a8b042016-01-22 09:41:09 -0800740 launchTask = stack.getStackFrontMostTask(true /* includeFreeform */);
Winson65c851e2016-01-20 12:43:35 -0800741 runningTaskOut.copyFrom(launchTask);
Winson190fe3bf2015-10-20 14:57:24 -0700742 }
743
744 // Get the transform for the running task
Winson003eda62016-03-11 14:56:00 -0800745 stackView.updateLayoutAlgorithm(true /* boundScroll */);
Winson67c79572016-04-13 14:02:18 -0700746 stackView.updateToInitialState();
Winson7845e8c2016-03-29 10:23:19 -0700747 stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
Winson190fe3bf2015-10-20 14:57:24 -0700748 stackView.getScroller().getStackScroll(), mTmpTransform, null);
749 return mTmpTransform;
750 }
751
752 /**
753 * Draws the header of a task used for the window animation into a bitmap.
754 */
Winsonaeb298c2016-04-05 13:08:11 -0700755 private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform,
756 Bitmap thumbnail) {
Winson8be16342016-02-09 11:53:27 -0800757 SystemServicesProxy ssp = Recents.getSystemServices();
Winson190fe3bf2015-10-20 14:57:24 -0700758 if (toTransform != null && toTask.key != null) {
Winson190fe3bf2015-10-20 14:57:24 -0700759 synchronized (mHeaderBarLock) {
Winson8be16342016-02-09 11:53:27 -0800760 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
Winson Chung509d0d02015-12-16 15:43:12 -0500761 mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
762 (int) toTransform.rect.height());
Winsonc742f972015-11-12 11:32:21 -0800763 if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
Winson190fe3bf2015-10-20 14:57:24 -0700764 thumbnail.eraseColor(0xFFff0000);
765 } else {
Winsonaeb298c2016-04-05 13:08:11 -0700766 thumbnail.eraseColor(0);
Winson190fe3bf2015-10-20 14:57:24 -0700767 Canvas c = new Canvas(thumbnail);
Winson22574af2016-03-23 19:00:28 -0700768 // Workaround for b/27815919, reset the callback so that we do not trigger an
769 // invalidate on the header bar as a result of updating the icon
770 Drawable icon = mHeaderBar.getIconView().getDrawable();
771 if (icon != null) {
772 icon.setCallback(null);
773 }
Winsond2a03062016-04-15 11:19:07 -0700774 mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
Winson8be16342016-02-09 11:53:27 -0800775 disabledInSafeMode);
Winsond2a03062016-04-15 11:19:07 -0700776 mHeaderBar.onTaskDataLoaded();
Winsone693aaf2016-03-01 12:05:59 -0800777 mHeaderBar.setDimAlpha(toTransform.dimAlpha);
Winson190fe3bf2015-10-20 14:57:24 -0700778 mHeaderBar.draw(c);
779 c.setBitmap(null);
780 }
781 }
782 return thumbnail.createAshmemBitmap();
783 }
784 return null;
785 }
786
787 /**
788 * Shows the recents activity
789 */
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700790 protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
Jorim Jaggibb42a462015-11-20 16:27:16 -0800791 boolean isTopTaskHome, boolean animate) {
Winsone7f138c2015-10-22 16:15:21 -0700792 RecentsTaskLoader loader = Recents.getTaskLoader();
Winsonc5b12dd2016-03-23 20:25:12 -0700793 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
Winson190fe3bf2015-10-20 14:57:24 -0700794
Winsone5f1faa2015-11-20 12:26:23 -0800795 // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
Jorim Jaggi435b2e42015-11-24 15:09:30 -0800796 // should always preload the tasks now. If we are dragging in recents, reload them as
797 // the stacks might have changed.
Jorim Jaggie161f082016-02-05 14:26:16 -0800798 if (mLaunchedWhileDocking || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
Winsone5f1faa2015-11-20 12:26:23 -0800799 // Create a new load plan if preloadRecents() was never triggered
Winson190fe3bf2015-10-20 14:57:24 -0700800 sInstanceLoadPlan = loader.createLoadPlan(mContext);
801 }
Jorim Jaggie161f082016-02-05 14:26:16 -0800802 if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
Winson65c851e2016-01-20 12:43:35 -0800803 loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
Winson190fe3bf2015-10-20 14:57:24 -0700804 }
Winsonc5b12dd2016-03-23 20:25:12 -0700805
Winson190fe3bf2015-10-20 14:57:24 -0700806 TaskStack stack = sInstanceLoadPlan.getTaskStack();
Winsonc5b12dd2016-03-23 20:25:12 -0700807 boolean hasRecentTasks = stack.getTaskCount() > 0;
808 boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
Winson190fe3bf2015-10-20 14:57:24 -0700809
Winsonaeb298c2016-04-05 13:08:11 -0700810 // Update the launch state that we need in updateHeaderBarLayout()
811 launchState.launchedFromHome = !useThumbnailTransition;
812 launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
813 launchState.launchedViaDockGesture = mLaunchedWhileDocking;
814 launchState.launchedViaDragGesture = mDraggingInRecents;
815 launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
816 launchState.launchedWithAltTab = mTriggeredFromAltTab;
817
818 // Preload the icon (this will be a null-op if we have preloaded the icon already in
819 // preloadRecents())
820 preloadIcon(topTask);
821
Winsonf0d1c442015-12-01 11:04:45 -0800822 // Update the header bar if necessary
Winson008ee15f2016-03-18 17:17:25 -0700823 updateHeaderBarLayout(stack);
Winsonf0d1c442015-12-01 11:04:45 -0800824
Winson190fe3bf2015-10-20 14:57:24 -0700825 // Prepare the dummy stack for the transition
Winson36a5a2c2015-10-29 18:04:39 -0700826 TaskStackLayoutAlgorithm.VisibilityReport stackVr =
Winson190fe3bf2015-10-20 14:57:24 -0700827 mDummyStackView.computeStackVisibilityReport();
Jorim Jaggibb42a462015-11-20 16:27:16 -0800828
Winsonaeb298c2016-04-05 13:08:11 -0700829 // Update the remaining launch state
Winsonc5b12dd2016-03-23 20:25:12 -0700830 launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
831 launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
Winsonc5b12dd2016-03-23 20:25:12 -0700832
Jorim Jaggibb42a462015-11-20 16:27:16 -0800833 if (!animate) {
Winsonc5b12dd2016-03-23 20:25:12 -0700834 startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1));
Jorim Jaggibb42a462015-11-20 16:27:16 -0800835 return;
836 }
837
Winsonaeb298c2016-04-05 13:08:11 -0700838 ActivityOptions opts;
Winson190fe3bf2015-10-20 14:57:24 -0700839 if (useThumbnailTransition) {
840 // Try starting with a thumbnail transition
Winsonaeb298c2016-04-05 13:08:11 -0700841 opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
842 } else {
Winson190fe3bf2015-10-20 14:57:24 -0700843 // If there is no thumbnail transition, but is launching from home into recents, then
Winson008ee15f2016-03-18 17:17:25 -0700844 // use a quick home transition
Winsonaeb298c2016-04-05 13:08:11 -0700845 opts = hasRecentTasks
846 ? getHomeTransitionActivityOptions()
847 : getUnknownTransitionActivityOptions();
Winson190fe3bf2015-10-20 14:57:24 -0700848 }
Winsonaeb298c2016-04-05 13:08:11 -0700849 startRecentsActivity(opts);
Winson190fe3bf2015-10-20 14:57:24 -0700850 mLastToggleTime = SystemClock.elapsedRealtime();
851 }
852
853 /**
854 * Starts the recents activity.
855 */
Winsonc5b12dd2016-03-23 20:25:12 -0700856 private void startRecentsActivity(ActivityOptions opts) {
Winson190fe3bf2015-10-20 14:57:24 -0700857 Intent intent = new Intent();
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700858 intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
Winson190fe3bf2015-10-20 14:57:24 -0700859 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
860 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
861 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Sid Soundararajanb58c46a2016-01-26 15:39:27 -0800862
Winson190fe3bf2015-10-20 14:57:24 -0700863 if (opts != null) {
864 mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
865 } else {
866 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
867 }
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800868 EventBus.getDefault().send(new RecentsActivityStartingEvent());
Winson190fe3bf2015-10-20 14:57:24 -0700869 }
870
Winson3fb67562015-11-11 10:39:03 -0800871 /**** OnAnimationFinishedListener Implementation ****/
Filip Gruszczynski1a5203d2015-10-29 17:43:49 -0700872
873 @Override
874 public void onAnimationFinished() {
875 EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
876 }
Winson190fe3bf2015-10-20 14:57:24 -0700877}