blob: 611169f575c1d40c4d3775b0d23d34a9d04cabb5 [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;
Vladislav Kaznacheev1f6b7092016-05-04 17:18:56 -070020import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
Matthew Ngae1ff4f2016-11-10 15:49:14 -080021import static android.app.ActivityManager.StackId.isHomeOrRecentsStack;
Winson94bc4f22016-04-07 14:22:12 -070022import static android.view.View.MeasureSpec;
Winsone693aaf2016-03-01 12:05:59 -080023
Winson190fe3bf2015-10-20 14:57:24 -070024import android.app.ActivityManager;
Jorim Jaggifb9d78a2017-01-05 18:57:12 +010025import android.app.ActivityManager.TaskSnapshot;
Winson190fe3bf2015-10-20 14:57:24 -070026import android.app.ActivityOptions;
Winson190fe3bf2015-10-20 14:57:24 -070027import android.content.ActivityNotFoundException;
Winson190fe3bf2015-10-20 14:57:24 -070028import android.content.Context;
29import android.content.Intent;
30import android.content.res.Resources;
31import android.graphics.Bitmap;
32import android.graphics.Canvas;
Winson Chungaa7fa012017-05-24 15:50:06 -070033import android.graphics.GraphicBuffer;
Winson190fe3bf2015-10-20 14:57:24 -070034import android.graphics.Rect;
Winson3150e572015-10-23 15:07:24 -070035import android.graphics.RectF;
Winson22574af2016-03-23 19:00:28 -070036import android.graphics.drawable.Drawable;
Winson190fe3bf2015-10-20 14:57:24 -070037import android.os.Handler;
38import android.os.SystemClock;
Winson1b585612015-11-06 09:16:26 -080039import android.util.Log;
Winson190fe3bf2015-10-20 14:57:24 -070040import android.util.MutableBoolean;
Jorim Jaggief496ea2017-05-13 23:16:44 +020041import android.util.Pair;
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -070042import android.view.AppTransitionAnimationSpec;
Winson190fe3bf2015-10-20 14:57:24 -070043import android.view.LayoutInflater;
Winsonb61e6542016-02-04 14:37:18 -080044import android.view.ViewConfiguration;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070045import android.view.WindowManager;
Winsonc0d70582016-01-29 10:24:39 -080046
Winson675c5d82016-08-23 17:12:22 -070047import android.widget.Toast;
Jorim Jaggief496ea2017-05-13 23:16:44 +020048
49import com.google.android.collect.Lists;
50
Winson190fe3bf2015-10-20 14:57:24 -070051import com.android.internal.logging.MetricsLogger;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070052import com.android.internal.policy.DockedDividerUtils;
Winson190fe3bf2015-10-20 14:57:24 -070053import com.android.systemui.R;
54import com.android.systemui.SystemUIApplication;
Winson412e1802015-10-20 16:57:57 -070055import com.android.systemui.recents.events.EventBus;
Jorim Jaggi899327f2016-02-25 20:44:18 -050056import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
Winson1b585612015-11-06 09:16:26 -080057import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
Winson412e1802015-10-20 16:57:57 -070058import com.android.systemui.recents.events.activity.HideRecentsEvent;
Winson0d14d4d2015-10-26 17:05:04 -070059import com.android.systemui.recents.events.activity.IterateRecentsEvent;
Manu Corneta96a6172017-01-19 12:40:44 -080060import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
Winsonb61e6542016-02-04 14:37:18 -080061import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080062import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
Winson412e1802015-10-20 16:57:57 -070063import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
Winson Chungb5026902017-05-03 12:45:13 -070064import com.android.systemui.recents.events.component.ActivityPinnedEvent;
65import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
66import com.android.systemui.recents.events.component.HidePipMenuEvent;
Winson190fe3bf2015-10-20 14:57:24 -070067import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
68import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
Jorim Jaggidd98d412015-11-18 15:57:38 -080069import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
70import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
Jorim Jaggifb9d78a2017-01-05 18:57:12 +010071import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
Winson6b92c6e2015-11-06 13:11:16 -080072import com.android.systemui.recents.misc.DozeTrigger;
Winsonab84fc52015-10-23 11:52:07 -070073import com.android.systemui.recents.misc.ForegroundThread;
Winson190fe3bf2015-10-20 14:57:24 -070074import com.android.systemui.recents.misc.SystemServicesProxy;
Jaewan Kim938a50b2016-03-14 17:35:43 +090075import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
Winson190fe3bf2015-10-20 14:57:24 -070076import com.android.systemui.recents.model.RecentsTaskLoadPlan;
77import com.android.systemui.recents.model.RecentsTaskLoader;
78import com.android.systemui.recents.model.Task;
79import com.android.systemui.recents.model.TaskGrouping;
80import com.android.systemui.recents.model.TaskStack;
Jorim Jaggief496ea2017-05-13 23:16:44 +020081import com.android.systemui.recents.views.RecentsTransitionHelper;
82import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
Winson36a5a2c2015-10-29 18:04:39 -070083import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
Jorim Jaggicdef5912017-04-03 17:24:19 +020084import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
Winson1b585612015-11-06 09:16:26 -080085import com.android.systemui.recents.views.TaskStackView;
Winsone693aaf2016-03-01 12:05:59 -080086import com.android.systemui.recents.views.TaskStackViewScroller;
Winson190fe3bf2015-10-20 14:57:24 -070087import com.android.systemui.recents.views.TaskViewHeader;
88import com.android.systemui.recents.views.TaskViewTransform;
Manu Cornetde3cfb02017-01-25 18:31:01 +090089import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070090import com.android.systemui.stackdivider.DividerView;
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080091import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050092import com.android.systemui.statusbar.phone.StatusBar;
Winson190fe3bf2015-10-20 14:57:24 -070093
94import java.util.ArrayList;
95
96/**
97 * An implementation of the Recents component for the current user. For secondary users, this can
98 * be called remotely from the system user.
99 */
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800100public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
Winson190fe3bf2015-10-20 14:57:24 -0700101
102 private final static String TAG = "RecentsImpl";
Winsonb61e6542016-02-04 14:37:18 -0800103
Winson6b92c6e2015-11-06 13:11:16 -0800104 // The minimum amount of time between each recents button press that we will handle
105 private final static int MIN_TOGGLE_DELAY_MS = 350;
Winsonb61e6542016-02-04 14:37:18 -0800106
Winson6b92c6e2015-11-06 13:11:16 -0800107 // The duration within which the user releasing the alt tab (from when they pressed alt tab)
108 // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
109 // duration, then we will toggle recents after this duration.
110 private final static int FAST_ALT_TAB_DELAY_MS = 225;
Winson190fe3bf2015-10-20 14:57:24 -0700111
112 public final static String RECENTS_PACKAGE = "com.android.systemui";
113 public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
Winsone693aaf2016-03-01 12:05:59 -0800114
Winson190fe3bf2015-10-20 14:57:24 -0700115 /**
Jaewan Kim938a50b2016-03-14 17:35:43 +0900116 * An implementation of TaskStackListener, that allows us to listen for changes to the system
Winson190fe3bf2015-10-20 14:57:24 -0700117 * task stacks and update recents accordingly.
118 */
Jaewan Kim938a50b2016-03-14 17:35:43 +0900119 class TaskStackListenerImpl extends TaskStackListener {
Winson Chungf3cfa892017-04-24 12:23:47 -0700120
Winson190fe3bf2015-10-20 14:57:24 -0700121 @Override
Winson Chungf3cfa892017-04-24 12:23:47 -0700122 public void onTaskStackChangedBackground() {
Winson Chungee697562017-05-18 14:29:43 -0700123 // Check this is for the right user
124 if (!checkCurrentUserId(mContext, false /* debug */)) {
125 return;
126 }
127
Jaewan Kim938a50b2016-03-14 17:35:43 +0900128 // Preloads the next task
Jorim Jaggia0fdeec2016-01-07 14:42:28 +0100129 RecentsConfiguration config = Recents.getConfiguration();
Winson190fe3bf2015-10-20 14:57:24 -0700130 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Winson190fe3bf2015-10-20 14:57:24 -0700131
132 // Load the next task only if we aren't svelte
Winson Chungf3cfa892017-04-24 12:23:47 -0700133 SystemServicesProxy ssp = Recents.getSystemServices();
134 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
135 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700136 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700137 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson Chungf3cfa892017-04-24 12:23:47 -0700138
Winson190fe3bf2015-10-20 14:57:24 -0700139 // This callback is made when a new activity is launched and the old one is paused
140 // so ignore the current activity and try and preload the thumbnail for the
141 // previous one.
Winson Chungf3cfa892017-04-24 12:23:47 -0700142 VisibilityReport visibilityReport;
143 synchronized (mDummyStackView) {
Winson Chungb5026902017-05-03 12:45:13 -0700144 mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
Winson Chungf3cfa892017-04-24 12:23:47 -0700145 mDummyStackView.setTasks(plan.getTaskStack(), false /* allowNotify */);
146 updateDummyStackViewLayout(plan.getTaskStack(),
147 getWindowRect(null /* windowRectOverride */));
148
149 // Launched from app is always the worst case (in terms of how many
150 // thumbnails/tasks visible)
151 RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
152 launchState.launchedFromApp = true;
153 mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */, launchState);
154 visibilityReport = mDummyStackView.computeStackVisibilityReport();
Winson190fe3bf2015-10-20 14:57:24 -0700155 }
Jorim Jaggicdef5912017-04-03 17:24:19 +0200156
Winson Chungf3cfa892017-04-24 12:23:47 -0700157 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
158 launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
Jorim Jaggicdef5912017-04-03 17:24:19 +0200159 launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
160 launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
Winson190fe3bf2015-10-20 14:57:24 -0700161 launchOpts.onlyLoadForCache = true;
162 launchOpts.onlyLoadPausedActivities = true;
Jorim Jaggicdef5912017-04-03 17:24:19 +0200163 launchOpts.loadThumbnails = true;
Winson190fe3bf2015-10-20 14:57:24 -0700164 loader.loadTasks(mContext, plan, launchOpts);
165 }
Winson190fe3bf2015-10-20 14:57:24 -0700166 }
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100167
168 @Override
Winson Chungb5026902017-05-03 12:45:13 -0700169 public void onActivityPinned(String packageName, int taskId) {
Winson Chungee697562017-05-18 14:29:43 -0700170 // Check this is for the right user
171 if (!checkCurrentUserId(mContext, false /* debug */)) {
172 return;
173 }
174
Winson Chungb5026902017-05-03 12:45:13 -0700175 // This time needs to be fetched the same way the last active time is fetched in
176 // {@link TaskRecord#touchActiveTime}
177 Recents.getConfiguration().getLaunchState().launchedFromPipApp = true;
178 Recents.getConfiguration().getLaunchState().launchedWithNextPipApp = false;
179 EventBus.getDefault().send(new ActivityPinnedEvent(taskId));
180 consumeInstanceLoadPlan();
181 sLastPipTime = System.currentTimeMillis();
182 }
183
184 @Override
185 public void onActivityUnpinned() {
Winson Chungee697562017-05-18 14:29:43 -0700186 // Check this is for the right user
187 if (!checkCurrentUserId(mContext, false /* debug */)) {
188 return;
189 }
190
Winson Chungb5026902017-05-03 12:45:13 -0700191 EventBus.getDefault().send(new ActivityUnpinnedEvent());
192 sLastPipTime = -1;
193 }
194
195 @Override
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100196 public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
Winson Chungee697562017-05-18 14:29:43 -0700197 // Check this is for the right user
198 if (!checkCurrentUserId(mContext, false /* debug */)) {
199 return;
200 }
201
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100202 EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
203 }
Winson190fe3bf2015-10-20 14:57:24 -0700204 }
205
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700206 protected static RecentsTaskLoadPlan sInstanceLoadPlan;
Winson Chungb5026902017-05-03 12:45:13 -0700207 // Stores the last pinned task time
208 protected static long sLastPipTime = -1;
Winson190fe3bf2015-10-20 14:57:24 -0700209
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700210 protected Context mContext;
211 protected Handler mHandler;
Winson190fe3bf2015-10-20 14:57:24 -0700212 TaskStackListenerImpl mTaskStackListener;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800213 boolean mDraggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800214 boolean mLaunchedWhileDocking;
Winson190fe3bf2015-10-20 14:57:24 -0700215
216 // Task launching
Winson190fe3bf2015-10-20 14:57:24 -0700217 Rect mTaskStackBounds = new Rect();
Winson190fe3bf2015-10-20 14:57:24 -0700218 TaskViewTransform mTmpTransform = new TaskViewTransform();
219 int mStatusBarHeight;
220 int mNavBarHeight;
221 int mNavBarWidth;
222 int mTaskBarHeight;
223
224 // Header (for transition)
225 TaskViewHeader mHeaderBar;
226 final Object mHeaderBarLock = new Object();
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700227 protected TaskStackView mDummyStackView;
Winson190fe3bf2015-10-20 14:57:24 -0700228
229 // Variables to keep track of if we need to start recents after binding
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700230 protected boolean mTriggeredFromAltTab;
231 protected long mLastToggleTime;
Winson6b92c6e2015-11-06 13:11:16 -0800232 DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
233 @Override
234 public void run() {
235 // When this fires, then the user has not released alt-tab for at least
236 // FAST_ALT_TAB_DELAY_MS milliseconds
Jorim Jaggi435b2e42015-11-24 15:09:30 -0800237 showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700238 false /* reloadTasks */, false /* fromHome */,
239 DividerView.INVALID_RECENTS_GROW_TARGET);
Winson6b92c6e2015-11-06 13:11:16 -0800240 }
241 });
Winson190fe3bf2015-10-20 14:57:24 -0700242
Winson190fe3bf2015-10-20 14:57:24 -0700243 public RecentsImpl(Context context) {
244 mContext = context;
Winson190fe3bf2015-10-20 14:57:24 -0700245 mHandler = new Handler();
Winson190fe3bf2015-10-20 14:57:24 -0700246
Winsonab84fc52015-10-23 11:52:07 -0700247 // Initialize the static foreground thread
248 ForegroundThread.get();
249
Winson190fe3bf2015-10-20 14:57:24 -0700250 // Register the task stack listener
Jaewan Kim938a50b2016-03-14 17:35:43 +0900251 mTaskStackListener = new TaskStackListenerImpl();
Winsone7f138c2015-10-22 16:15:21 -0700252 SystemServicesProxy ssp = Recents.getSystemServices();
253 ssp.registerTaskStackListener(mTaskStackListener);
Winson190fe3bf2015-10-20 14:57:24 -0700254
255 // Initialize the static configuration resources
Winson670ea712016-04-12 17:02:26 -0700256 mDummyStackView = new TaskStackView(mContext);
Winson670ea712016-04-12 17:02:26 -0700257 reloadResources();
Winson16ef39a2016-06-28 18:25:15 -0700258 }
Winson190fe3bf2015-10-20 14:57:24 -0700259
Winson Chung501d59d2016-10-05 17:49:09 +0000260 public void onBootCompleted() {
Winson190fe3bf2015-10-20 14:57:24 -0700261 // When we start, preload the data associated with the previous recent tasks.
262 // We can use a new plan since the caches will be the same.
Winsone7f138c2015-10-22 16:15:21 -0700263 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700264 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700265 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson190fe3bf2015-10-20 14:57:24 -0700266 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chung296278a2015-12-17 12:09:02 -0500267 launchOpts.numVisibleTasks = loader.getIconCacheSize();
Winson190fe3bf2015-10-20 14:57:24 -0700268 launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
269 launchOpts.onlyLoadForCache = true;
270 loader.loadTasks(mContext, plan, launchOpts);
271 }
272
Winson190fe3bf2015-10-20 14:57:24 -0700273 public void onConfigurationChanged() {
Winson670ea712016-04-12 17:02:26 -0700274 reloadResources();
Winson Chungf3cfa892017-04-24 12:23:47 -0700275 synchronized (mDummyStackView) {
276 mDummyStackView.reloadOnConfigurationChange();
277 }
Winson190fe3bf2015-10-20 14:57:24 -0700278 }
279
280 /**
281 * This is only called from the system user's Recents. Secondary users will instead proxy their
282 * visibility change events through to the system user via
283 * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
284 */
285 public void onVisibilityChanged(Context context, boolean visible) {
Winsonab216602016-08-09 14:05:20 -0700286 Recents.getSystemServices().setRecentsVisibility(visible);
Winson190fe3bf2015-10-20 14:57:24 -0700287 }
288
289 /**
290 * This is only called from the system user's Recents. Secondary users will instead proxy their
291 * visibility change events through to the system user via
292 * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
293 */
Andrii Kulian0f051f52016-04-14 00:41:51 -0700294 public void onStartScreenPinning(Context context, int taskId) {
Winson190fe3bf2015-10-20 14:57:24 -0700295 SystemUIApplication app = (SystemUIApplication) context;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500296 StatusBar statusBar = app.getComponent(StatusBar.class);
Winson190fe3bf2015-10-20 14:57:24 -0700297 if (statusBar != null) {
Andrii Kulian0f051f52016-04-14 00:41:51 -0700298 statusBar.showScreenPinningRequest(taskId, false);
Winson190fe3bf2015-10-20 14:57:24 -0700299 }
300 }
301
Jorim Jaggibb42a462015-11-20 16:27:16 -0800302 public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700303 boolean animate, boolean launchedWhileDockingTask, boolean fromHome,
304 int growTarget) {
Winson190fe3bf2015-10-20 14:57:24 -0700305 mTriggeredFromAltTab = triggeredFromAltTab;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800306 mDraggingInRecents = draggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800307 mLaunchedWhileDocking = launchedWhileDockingTask;
Winsone693aaf2016-03-01 12:05:59 -0800308 if (mFastAltTabTrigger.isAsleep()) {
309 // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
310 mFastAltTabTrigger.stopDozing();
Winson6b92c6e2015-11-06 13:11:16 -0800311 } else if (mFastAltTabTrigger.isDozing()) {
Winsone693aaf2016-03-01 12:05:59 -0800312 // Fast alt-tab duration has not elapsed. If this is triggered by a different
313 // showRecents() call, then ignore that call for now.
314 // TODO: We can not handle quick tabs that happen between the initial showRecents() call
315 // that started the activity and the activity starting up. The severity of this
316 // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
Winson6b92c6e2015-11-06 13:11:16 -0800317 if (!triggeredFromAltTab) {
318 return;
319 }
320 mFastAltTabTrigger.stopDozing();
Winsone693aaf2016-03-01 12:05:59 -0800321 } else if (triggeredFromAltTab) {
322 // The fast alt-tab detector is not yet running, so start the trigger and wait for the
323 // hideRecents() call, or for the fast alt-tab duration to elapse
324 mFastAltTabTrigger.startDozing();
325 return;
Winson6b92c6e2015-11-06 13:11:16 -0800326 }
Winson190fe3bf2015-10-20 14:57:24 -0700327
328 try {
329 // Check if the top task is in the home stack, and start the recents activity
Winsone7f138c2015-10-22 16:15:21 -0700330 SystemServicesProxy ssp = Recents.getSystemServices();
Winsond7cb5172016-05-04 16:14:19 -0700331 boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
332 MutableBoolean isHomeStackVisible = new MutableBoolean(forceVisible);
333 if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
Winsond46b7272016-04-20 11:54:27 -0700334 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
335 startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate,
336 growTarget);
Winson190fe3bf2015-10-20 14:57:24 -0700337 }
338 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800339 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700340 }
341 }
342
Winson190fe3bf2015-10-20 14:57:24 -0700343 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson2799eca2016-02-25 12:10:42 -0800344 if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
345 // The user has released alt-tab before the trigger has run, so just show the next
346 // task immediately
347 showNextTask();
Winson6b92c6e2015-11-06 13:11:16 -0800348
Winson2799eca2016-02-25 12:10:42 -0800349 // Cancel the fast alt-tab trigger
350 mFastAltTabTrigger.stopDozing();
Winson2799eca2016-02-25 12:10:42 -0800351 return;
Winson190fe3bf2015-10-20 14:57:24 -0700352 }
Winson2799eca2016-02-25 12:10:42 -0800353
354 // Defer to the activity to handle hiding recents, if it handles it, then it must still
355 // be visible
356 EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
357 triggeredFromHomeKey));
Winson190fe3bf2015-10-20 14:57:24 -0700358 }
359
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700360 public void toggleRecents(int growTarget) {
Winson6b92c6e2015-11-06 13:11:16 -0800361 // Skip this toggle if we are already waiting to trigger recents via alt-tab
362 if (mFastAltTabTrigger.isDozing()) {
363 return;
364 }
365
Jorim Jaggidd98d412015-11-18 15:57:38 -0800366 mDraggingInRecents = false;
Jorim Jaggie161f082016-02-05 14:26:16 -0800367 mLaunchedWhileDocking = false;
Winson190fe3bf2015-10-20 14:57:24 -0700368 mTriggeredFromAltTab = false;
369
370 try {
Winsone7f138c2015-10-22 16:15:21 -0700371 SystemServicesProxy ssp = Recents.getSystemServices();
Winsond46b7272016-04-20 11:54:27 -0700372 MutableBoolean isHomeStackVisible = new MutableBoolean(true);
Winsonb61e6542016-02-04 14:37:18 -0800373 long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
374
Winsond46b7272016-04-20 11:54:27 -0700375 if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
Winsone693aaf2016-03-01 12:05:59 -0800376 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
Winson5da43472015-11-04 17:39:55 -0800377 RecentsConfiguration config = Recents.getConfiguration();
378 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson Chungead5c0f2015-12-14 11:18:57 -0500379 if (!launchState.launchedWithAltTab) {
Manu Corneta96a6172017-01-19 12:40:44 -0800380 // Has the user tapped quickly?
Matthew Ngda3dd7c2017-04-28 17:33:22 -0700381 boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
Manu Corneta96a6172017-01-19 12:40:44 -0800382 if (Recents.getConfiguration().isGridEnabled) {
383 if (isQuickTap) {
384 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
385 } else {
386 EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
387 }
Winsonb61e6542016-02-04 14:37:18 -0800388 } else {
Manu Corneta96a6172017-01-19 12:40:44 -0800389 if (!debugFlags.isPagingEnabled() || isQuickTap) {
390 // Launch the next focused task
391 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
392 } else {
393 // Notify recents to move onto the next task
394 EventBus.getDefault().post(new IterateRecentsEvent());
395 }
Winsonb61e6542016-02-04 14:37:18 -0800396 }
Winson0d14d4d2015-10-26 17:05:04 -0700397 } else {
398 // If the user has toggled it too quickly, then just eat up the event here (it's
399 // better than showing a janky screenshot).
400 // NOTE: Ideally, the screenshot mechanism would take the window transform into
401 // account
Winsonb61e6542016-02-04 14:37:18 -0800402 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700403 return;
404 }
405
406 EventBus.getDefault().post(new ToggleRecentsEvent());
407 mLastToggleTime = SystemClock.elapsedRealtime();
408 }
Winson190fe3bf2015-10-20 14:57:24 -0700409 return;
410 } else {
Winson0d14d4d2015-10-26 17:05:04 -0700411 // If the user has toggled it too quickly, then just eat up the event here (it's
412 // better than showing a janky screenshot).
413 // NOTE: Ideally, the screenshot mechanism would take the window transform into
414 // account
Winsonb61e6542016-02-04 14:37:18 -0800415 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700416 return;
417 }
418
Winson190fe3bf2015-10-20 14:57:24 -0700419 // Otherwise, start the recents activity
Winsond46b7272016-04-20 11:54:27 -0700420 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
421 startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
422 growTarget);
Winsond8b1d632016-01-04 17:51:18 -0800423
424 // Only close the other system windows if we are actually showing recents
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500425 ssp.sendCloseSystemWindows(StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
Winson0d14d4d2015-10-26 17:05:04 -0700426 mLastToggleTime = SystemClock.elapsedRealtime();
Winson190fe3bf2015-10-20 14:57:24 -0700427 }
428 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800429 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700430 }
431 }
432
Winson190fe3bf2015-10-20 14:57:24 -0700433 public void preloadRecents() {
434 // Preload only the raw task list into a new load plan (which will be consumed by the
Winson Chungebe1ba12017-05-18 15:47:14 -0700435 // RecentsActivity) only if there is a task to animate to. Post this to ensure that we
436 // don't block the touch feedback on the nav bar button which triggers this.
437 mHandler.post(() -> {
438 SystemServicesProxy ssp = Recents.getSystemServices();
439 MutableBoolean isHomeStackVisible = new MutableBoolean(true);
440 if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
441 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
442 if (runningTask == null) {
443 return;
444 }
Winson Chung96b7a512017-02-08 12:20:00 -0800445
Winson Chungebe1ba12017-05-18 15:47:14 -0700446 RecentsTaskLoader loader = Recents.getTaskLoader();
447 sInstanceLoadPlan = loader.createLoadPlan(mContext);
448 loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value);
449 TaskStack stack = sInstanceLoadPlan.getTaskStack();
450 if (stack.getTaskCount() > 0) {
451 // Only preload the icon (but not the thumbnail since it may not have been taken
452 // for the pausing activity)
453 preloadIcon(runningTask.id);
Winsonaeb298c2016-04-05 13:08:11 -0700454
Winson Chungebe1ba12017-05-18 15:47:14 -0700455 // At this point, we don't know anything about the stack state. So only
456 // calculate the dimensions of the thumbnail that we need for the transition
457 // into Recents, but do not draw it until we construct the activity options when
458 // we start Recents
459 updateHeaderBarLayout(stack, null /* window rect override*/);
460 }
Winson190fe3bf2015-10-20 14:57:24 -0700461 }
Winson Chungebe1ba12017-05-18 15:47:14 -0700462 });
Winson190fe3bf2015-10-20 14:57:24 -0700463 }
464
Winson190fe3bf2015-10-20 14:57:24 -0700465 public void cancelPreloadingRecents() {
466 // Do nothing
467 }
468
Jorim Jaggidd98d412015-11-18 15:57:38 -0800469 public void onDraggingInRecents(float distanceFromTop) {
470 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
471 }
472
Jorim Jaggidd98d412015-11-18 15:57:38 -0800473 public void onDraggingInRecentsEnded(float velocity) {
474 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
475 }
476
Winson675c5d82016-08-23 17:12:22 -0700477 public void onShowCurrentUserToast(int msgResId, int msgLength) {
478 Toast.makeText(mContext, msgResId, msgLength).show();
479 }
480
Winson6b92c6e2015-11-06 13:11:16 -0800481 /**
482 * Transitions to the next recent task in the stack.
483 */
484 public void showNextTask() {
Winsone7f138c2015-10-22 16:15:21 -0700485 SystemServicesProxy ssp = Recents.getSystemServices();
Winson6b92c6e2015-11-06 13:11:16 -0800486 RecentsTaskLoader loader = Recents.getTaskLoader();
487 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700488 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson6b92c6e2015-11-06 13:11:16 -0800489 TaskStack focusedStack = plan.getTaskStack();
490
491 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800492 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson6b92c6e2015-11-06 13:11:16 -0800493
Winson6b92c6e2015-11-06 13:11:16 -0800494 // Return early if there is no running task
Winsond46b7272016-04-20 11:54:27 -0700495 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
Winson6b92c6e2015-11-06 13:11:16 -0800496 if (runningTask == null) return;
Winson6b92c6e2015-11-06 13:11:16 -0800497
498 // Find the task in the recents list
Winsond46b7272016-04-20 11:54:27 -0700499 boolean isRunningTaskInHomeStack = SystemServicesProxy.isHomeStack(runningTask.stackId);
Winson250608a2015-11-24 15:00:31 -0800500 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson6b92c6e2015-11-06 13:11:16 -0800501 Task toTask = null;
502 ActivityOptions launchOpts = null;
503 int taskCount = tasks.size();
504 for (int i = taskCount - 1; i >= 1; i--) {
505 Task task = tasks.get(i);
Winsond46b7272016-04-20 11:54:27 -0700506 if (isRunningTaskInHomeStack) {
Winsone86deb82015-11-12 09:32:10 -0800507 toTask = tasks.get(i - 1);
508 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
509 R.anim.recents_launch_next_affiliated_task_target,
510 R.anim.recents_fast_toggle_app_home_exit);
511 break;
512 } else if (task.key.id == runningTask.id) {
Winson6b92c6e2015-11-06 13:11:16 -0800513 toTask = tasks.get(i - 1);
514 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
515 R.anim.recents_launch_prev_affiliated_task_target,
516 R.anim.recents_launch_prev_affiliated_task_source);
517 break;
518 }
519 }
520
521 // Return early if there is no next task
522 if (toTask == null) {
523 ssp.startInPlaceAnimationOnFrontMostApplication(
524 ActivityOptions.makeCustomInPlaceAnimation(mContext,
525 R.anim.recents_launch_prev_affiliated_task_bounce));
526 return;
527 }
528
529 // Launch the task
Vladislav Kaznacheev1f6b7092016-05-04 17:18:56 -0700530 ssp.startActivityFromRecents(
Jorim Jaggi34795e32017-05-12 17:27:46 +0200531 mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
532 null /* resultListener */);
Winson6b92c6e2015-11-06 13:11:16 -0800533 }
534
535 /**
536 * Transitions to the next affiliated task.
537 */
538 public void showRelativeAffiliatedTask(boolean showNextTask) {
539 SystemServicesProxy ssp = Recents.getSystemServices();
Winsone7f138c2015-10-22 16:15:21 -0700540 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700541 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700542 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson190fe3bf2015-10-20 14:57:24 -0700543 TaskStack focusedStack = plan.getTaskStack();
544
545 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800546 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson190fe3bf2015-10-20 14:57:24 -0700547
Winson190fe3bf2015-10-20 14:57:24 -0700548 // Return early if there is no running task (can't determine affiliated tasks in this case)
Winsond46b7272016-04-20 11:54:27 -0700549 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
Winson190fe3bf2015-10-20 14:57:24 -0700550 if (runningTask == null) return;
Matthew Ngae1ff4f2016-11-10 15:49:14 -0800551 // Return early if the running task is in the home/recents stack (optimization)
552 if (isHomeOrRecentsStack(runningTask.stackId)) return;
Winson190fe3bf2015-10-20 14:57:24 -0700553
554 // Find the task in the recents list
Winson250608a2015-11-24 15:00:31 -0800555 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson190fe3bf2015-10-20 14:57:24 -0700556 Task toTask = null;
557 ActivityOptions launchOpts = null;
558 int taskCount = tasks.size();
559 int numAffiliatedTasks = 0;
560 for (int i = 0; i < taskCount; i++) {
561 Task task = tasks.get(i);
562 if (task.key.id == runningTask.id) {
563 TaskGrouping group = task.group;
564 Task.TaskKey toTaskKey;
565 if (showNextTask) {
566 toTaskKey = group.getNextTaskInGroup(task);
567 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
568 R.anim.recents_launch_next_affiliated_task_target,
569 R.anim.recents_launch_next_affiliated_task_source);
570 } else {
571 toTaskKey = group.getPrevTaskInGroup(task);
572 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
573 R.anim.recents_launch_prev_affiliated_task_target,
574 R.anim.recents_launch_prev_affiliated_task_source);
575 }
576 if (toTaskKey != null) {
577 toTask = focusedStack.findTaskWithId(toTaskKey.id);
578 }
579 numAffiliatedTasks = group.getTaskCount();
580 break;
581 }
582 }
583
584 // Return early if there is no next task
585 if (toTask == null) {
586 if (numAffiliatedTasks > 1) {
587 if (showNextTask) {
Winsone7f138c2015-10-22 16:15:21 -0700588 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700589 ActivityOptions.makeCustomInPlaceAnimation(mContext,
590 R.anim.recents_launch_next_affiliated_task_bounce));
591 } else {
Winsone7f138c2015-10-22 16:15:21 -0700592 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700593 ActivityOptions.makeCustomInPlaceAnimation(mContext,
594 R.anim.recents_launch_prev_affiliated_task_bounce));
595 }
596 }
597 return;
598 }
599
600 // Keep track of actually launched affiliated tasks
601 MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
602
603 // Launch the task
Vladislav Kaznacheev1f6b7092016-05-04 17:18:56 -0700604 ssp.startActivityFromRecents(
Jorim Jaggi34795e32017-05-12 17:27:46 +0200605 mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
606 null /* resultListener */);
Winson190fe3bf2015-10-20 14:57:24 -0700607 }
608
609 public void showNextAffiliatedTask() {
610 // Keep track of when the affiliated task is triggered
611 MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
612 showRelativeAffiliatedTask(true);
613 }
614
615 public void showPrevAffiliatedTask() {
616 // Keep track of when the affiliated task is triggered
617 MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
618 showRelativeAffiliatedTask(false);
619 }
620
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800621 public void dockTopTask(int topTaskId, int dragMode,
622 int stackCreateMode, Rect initialBounds) {
Jorim Jaggi75b25972015-10-21 14:51:10 +0200623 SystemServicesProxy ssp = Recents.getSystemServices();
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800624
625 // Make sure we inform DividerView before we actually start the activity so we can change
626 // the resize mode already.
Chong Zhange4fbd322016-03-01 14:44:03 -0800627 if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500628 EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
Chong Zhange4fbd322016-03-01 14:44:03 -0800629 showRecents(
630 false /* triggeredFromAltTab */,
631 dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
632 false /* animate */,
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700633 true /* launchedWhileDockingTask*/,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700634 false /* fromHome */,
635 DividerView.INVALID_RECENTS_GROW_TARGET);
Chong Zhange4fbd322016-03-01 14:44:03 -0800636 }
Jorim Jaggi75b25972015-10-21 14:51:10 +0200637 }
638
Winson190fe3bf2015-10-20 14:57:24 -0700639 /**
640 * Returns the preloaded load plan and invalidates it.
641 */
642 public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
643 RecentsTaskLoadPlan plan = sInstanceLoadPlan;
644 sInstanceLoadPlan = null;
645 return plan;
646 }
647
648 /**
Winson Chungb5026902017-05-03 12:45:13 -0700649 * @return the time at which a task last entered picture-in-picture.
650 */
651 public static long getLastPipTime() {
652 return sLastPipTime;
653 }
654
655 /**
656 * Clears the time at which a task last entered picture-in-picture.
657 */
658 public static void clearLastPipTime() {
659 sLastPipTime = -1;
660 }
661
662 /**
Winson670ea712016-04-12 17:02:26 -0700663 * Reloads all the resources for the current configuration.
Winsonb94443d2016-01-07 15:34:13 -0800664 */
Winson670ea712016-04-12 17:02:26 -0700665 private void reloadResources() {
Winsonb94443d2016-01-07 15:34:13 -0800666 Resources res = mContext.getResources();
Winsonb94443d2016-01-07 15:34:13 -0800667
668 mStatusBarHeight = res.getDimensionPixelSize(
669 com.android.internal.R.dimen.status_bar_height);
670 mNavBarHeight = res.getDimensionPixelSize(
671 com.android.internal.R.dimen.navigation_bar_height);
672 mNavBarWidth = res.getDimensionPixelSize(
673 com.android.internal.R.dimen.navigation_bar_width);
Jorim Jaggi25160db2016-04-18 16:03:36 -0700674 mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
Winson21700932016-03-24 17:26:23 -0700675 R.dimen.recents_task_view_header_height,
676 R.dimen.recents_task_view_header_height,
677 R.dimen.recents_task_view_header_height,
678 R.dimen.recents_task_view_header_height_tablet_land,
679 R.dimen.recents_task_view_header_height,
Manu Cornetf78b11d2016-12-16 19:50:55 -0800680 R.dimen.recents_task_view_header_height_tablet_land,
681 R.dimen.recents_grid_task_view_header_height);
Matthew Ng384e60e2016-10-31 16:44:01 -0700682
683 LayoutInflater inflater = LayoutInflater.from(mContext);
684 mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
685 null, false);
686 mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
Winsonb94443d2016-01-07 15:34:13 -0800687 }
688
Jorim Jaggicdef5912017-04-03 17:24:19 +0200689 private void updateDummyStackViewLayout(TaskStack stack, Rect windowRect) {
Winsone7f138c2015-10-22 16:15:21 -0700690 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonfc48b072016-04-21 11:20:11 -0700691 Rect displayRect = ssp.getDisplayRect();
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800692 Rect systemInsets = new Rect();
693 ssp.getStableInsets(systemInsets);
Jorim Jaggicdef5912017-04-03 17:24:19 +0200694
Winsoncf9b8322016-03-31 15:36:07 -0700695 // When docked, the nav bar insets are consumed and the activity is measured without insets.
696 // However, the window bounds include the insets, so we need to subtract them here to make
697 // them identical.
698 if (ssp.hasDockedTask()) {
699 windowRect.bottom -= systemInsets.bottom;
700 systemInsets.bottom = 0;
701 }
Jorim Jaggief496ea2017-05-13 23:16:44 +0200702 calculateWindowStableInsets(systemInsets, windowRect, displayRect);
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800703 windowRect.offsetTo(0, 0);
Winson190fe3bf2015-10-20 14:57:24 -0700704
Winson Chungf3cfa892017-04-24 12:23:47 -0700705 synchronized (mDummyStackView) {
706 TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Winson190fe3bf2015-10-20 14:57:24 -0700707
Winson Chungf3cfa892017-04-24 12:23:47 -0700708 // Rebind the header bar and draw it for the transition
709 stackLayout.setSystemInsets(systemInsets);
710 if (stack != null) {
711 stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
712 systemInsets.left, systemInsets.right, mTaskStackBounds);
713 stackLayout.reset();
714 stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
715 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
716 }
Jorim Jaggicdef5912017-04-03 17:24:19 +0200717 }
718 }
719
720 private Rect getWindowRect(Rect windowRectOverride) {
721 return windowRectOverride != null
722 ? new Rect(windowRectOverride)
723 : Recents.getSystemServices().getWindowRect();
724 }
725
726 /**
727 * Prepares the header bar layout for the next transition, if the task view bounds has changed
728 * since the last call, it will attempt to re-measure and layout the header bar to the new size.
729 *
730 * @param stack the stack to initialize the stack layout with
731 * @param windowRectOverride the rectangle to use when calculating the stack state which can
732 * be different from the current window rect if recents is resizing
733 * while being launched
734 */
735 private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
736 Rect windowRect = getWindowRect(windowRectOverride);
Winson Chungf3cfa892017-04-24 12:23:47 -0700737 int taskViewWidth = 0;
738 boolean useGridLayout = false;
739 synchronized (mDummyStackView) {
740 useGridLayout = mDummyStackView.useGridLayout();
741 updateDummyStackViewLayout(stack, windowRect);
742 if (stack != null) {
743 TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Winson Chungb5026902017-05-03 12:45:13 -0700744 mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
Winson Chungf3cfa892017-04-24 12:23:47 -0700745 mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
746 // Get the width of a task view so that we know how wide to draw the header bar.
747 if (useGridLayout) {
748 TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
749 gridLayout.initialize(windowRect);
750 taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
751 stack.getTaskCount(), new TaskViewTransform(),
752 stackLayout).rect.width();
753 } else {
754 Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
755 if (!taskViewBounds.isEmpty()) {
756 taskViewWidth = taskViewBounds.width();
757 }
Manu Cornetde3cfb02017-01-25 18:31:01 +0900758 }
759 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700760 }
Winson190fe3bf2015-10-20 14:57:24 -0700761
Winson Chungf3cfa892017-04-24 12:23:47 -0700762 if (stack != null && taskViewWidth > 0) {
763 synchronized (mHeaderBarLock) {
764 if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
765 mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
766 if (useGridLayout) {
767 mHeaderBar.setShouldDarkenBackgroundColor(true);
768 mHeaderBar.setNoUserInteractionState();
Winson94bc4f22016-04-07 14:22:12 -0700769 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700770 mHeaderBar.forceLayout();
771 mHeaderBar.measure(
772 MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
773 MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
Winsonaeb298c2016-04-05 13:08:11 -0700774 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700775 mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
776 }
Winson190fe3bf2015-10-20 14:57:24 -0700777 }
778 }
779
780 /**
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800781 * Given the stable insets and the rect for our window, calculates the insets that affect our
782 * window.
783 */
Jorim Jaggief496ea2017-05-13 23:16:44 +0200784 private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect, Rect displayRect) {
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800785
786 // Display rect without insets - available app space
787 Rect appRect = new Rect(displayRect);
788 appRect.inset(inOutInsets);
789
790 // Our window intersected with available app space
791 Rect windowRectWithInsets = new Rect(windowRect);
792 windowRectWithInsets.intersect(appRect);
793 inOutInsets.left = windowRectWithInsets.left - windowRect.left;
794 inOutInsets.top = windowRectWithInsets.top - windowRect.top;
795 inOutInsets.right = windowRect.right - windowRectWithInsets.right;
796 inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
797 }
798
799 /**
Winson190fe3bf2015-10-20 14:57:24 -0700800 * Preloads the icon of a task.
801 */
Winsonc4a038a2016-05-26 16:42:15 -0700802 private void preloadIcon(int runningTaskId) {
Winson190fe3bf2015-10-20 14:57:24 -0700803 // Ensure that we load the running task's icon
804 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winsonc4a038a2016-05-26 16:42:15 -0700805 launchOpts.runningTaskId = runningTaskId;
Winson190fe3bf2015-10-20 14:57:24 -0700806 launchOpts.loadThumbnails = false;
807 launchOpts.onlyLoadForCache = true;
Winsone7f138c2015-10-22 16:15:21 -0700808 Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
Winson190fe3bf2015-10-20 14:57:24 -0700809 }
810
811 /**
Winson190fe3bf2015-10-20 14:57:24 -0700812 * Creates the activity options for a unknown state->recents transition.
813 */
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700814 protected ActivityOptions getUnknownTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700815 return ActivityOptions.makeCustomAnimation(mContext,
816 R.anim.recents_from_unknown_enter,
817 R.anim.recents_from_unknown_exit,
Winson3fb67562015-11-11 10:39:03 -0800818 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700819 }
820
821 /**
822 * Creates the activity options for a home->recents transition.
823 */
Winson008ee15f2016-03-18 17:17:25 -0700824 protected ActivityOptions getHomeTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700825 return ActivityOptions.makeCustomAnimation(mContext,
826 R.anim.recents_from_launcher_enter,
827 R.anim.recents_from_launcher_exit,
Winson3fb67562015-11-11 10:39:03 -0800828 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700829 }
830
831 /**
832 * Creates the activity options for an app->recents transition.
833 */
Jorim Jaggief496ea2017-05-13 23:16:44 +0200834 private Pair<ActivityOptions, AppTransitionAnimationSpecsFuture>
835 getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
836 Rect windowOverrideRect) {
Winsonc4a038a2016-05-26 16:42:15 -0700837 if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700838 ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
Winson Chungf3cfa892017-04-24 12:23:47 -0700839 ArrayList<Task> tasks;
840 TaskStackLayoutAlgorithm stackLayout;
841 TaskStackViewScroller stackScroller;
Winsone693aaf2016-03-01 12:05:59 -0800842
Winson Chungf3cfa892017-04-24 12:23:47 -0700843 synchronized (mDummyStackView) {
844 tasks = mDummyStackView.getStack().getStackTasks();
845 stackLayout = mDummyStackView.getStackAlgorithm();
846 stackScroller = mDummyStackView.getScroller();
847
848 mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */);
849 mDummyStackView.updateToInitialState();
850 }
Winsone693aaf2016-03-01 12:05:59 -0800851
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700852 for (int i = tasks.size() - 1; i >= 0; i--) {
853 Task task = tasks.get(i);
Winson387aac62015-11-25 11:18:56 -0800854 if (task.isFreeformTask()) {
Winsone693aaf2016-03-01 12:05:59 -0800855 mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700856 stackScroller.getStackScroll(), mTmpTransform, null,
Jiaquan He26f637b2016-12-27 14:44:14 -0800857 windowOverrideRect);
Winson Chungaa7fa012017-05-24 15:50:06 -0700858 GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700859 Rect toTaskRect = new Rect();
860 mTmpTransform.rect.round(toTaskRect);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700861 specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
862 }
863 }
864 AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
865 specs.toArray(specsArray);
Jorim Jaggief496ea2017-05-13 23:16:44 +0200866 return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
867 specsArray, mHandler, null, this), null);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700868 } else {
869 // Update the destination rect
870 Task toTask = new Task();
Winson Chungf3cfa892017-04-24 12:23:47 -0700871 TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700872 windowOverrideRect);
Jorim Jaggief496ea2017-05-13 23:16:44 +0200873
874 RectF toTaskRect = toTransform.rect;
875 AppTransitionAnimationSpecsFuture future =
876 new RecentsTransitionHelper(mContext).getAppTransitionFuture(
877 () -> {
878 Rect rect = new Rect();
879 toTaskRect.round(rect);
Winson Chungaa7fa012017-05-24 15:50:06 -0700880 GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
881 toTransform);
Jorim Jaggief496ea2017-05-13 23:16:44 +0200882 return Lists.newArrayList(new AppTransitionAnimationSpec(
883 toTask.key.id, thumbnail, rect));
884 });
885 return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
886 mHandler, future.getFuture(), null, false /* scaleUp */), future);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700887 }
888 }
Winson190fe3bf2015-10-20 14:57:24 -0700889
Winson190fe3bf2015-10-20 14:57:24 -0700890 /**
891 * Returns the transition rect for the given task id.
892 */
Winsone693aaf2016-03-01 12:05:59 -0800893 private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700894 Task runningTaskOut, Rect windowOverrideRect) {
Winson190fe3bf2015-10-20 14:57:24 -0700895 // Find the running task in the TaskStack
Winsone693aaf2016-03-01 12:05:59 -0800896 TaskStack stack = stackView.getStack();
Winson65c851e2016-01-20 12:43:35 -0800897 Task launchTask = stack.getLaunchTarget();
898 if (launchTask != null) {
899 runningTaskOut.copyFrom(launchTask);
900 } else {
Winson190fe3bf2015-10-20 14:57:24 -0700901 // If no task is specified or we can not find the task just use the front most one
Winson35a8b042016-01-22 09:41:09 -0800902 launchTask = stack.getStackFrontMostTask(true /* includeFreeform */);
Winson65c851e2016-01-20 12:43:35 -0800903 runningTaskOut.copyFrom(launchTask);
Winson190fe3bf2015-10-20 14:57:24 -0700904 }
905
906 // Get the transform for the running task
Winson003eda62016-03-11 14:56:00 -0800907 stackView.updateLayoutAlgorithm(true /* boundScroll */);
Winson67c79572016-04-13 14:02:18 -0700908 stackView.updateToInitialState();
Winson7845e8c2016-03-29 10:23:19 -0700909 stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
Jiaquan He26f637b2016-12-27 14:44:14 -0800910 stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
Winson190fe3bf2015-10-20 14:57:24 -0700911 return mTmpTransform;
912 }
913
914 /**
915 * Draws the header of a task used for the window animation into a bitmap.
916 */
Winson Chungaa7fa012017-05-24 15:50:06 -0700917 private GraphicBuffer drawThumbnailTransitionBitmap(Task toTask,
918 TaskViewTransform toTransform) {
Winson8be16342016-02-09 11:53:27 -0800919 SystemServicesProxy ssp = Recents.getSystemServices();
Winson190fe3bf2015-10-20 14:57:24 -0700920 if (toTransform != null && toTask.key != null) {
Winson190fe3bf2015-10-20 14:57:24 -0700921 synchronized (mHeaderBarLock) {
Winson8be16342016-02-09 11:53:27 -0800922 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
Winson Chungaa7fa012017-05-24 15:50:06 -0700923 int width = (int) toTransform.rect.width();
924 int height = (int) toTransform.rect.height();
925 mHeaderBar.onTaskViewSizeChanged(width, height);
Winsonc742f972015-11-12 11:32:21 -0800926 if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
Winson Chungaa7fa012017-05-24 15:50:06 -0700927 return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
928 null, 1f, 0xFFff0000);
Winson190fe3bf2015-10-20 14:57:24 -0700929 } else {
Winson22574af2016-03-23 19:00:28 -0700930 // Workaround for b/27815919, reset the callback so that we do not trigger an
931 // invalidate on the header bar as a result of updating the icon
932 Drawable icon = mHeaderBar.getIconView().getDrawable();
933 if (icon != null) {
934 icon.setCallback(null);
935 }
Winsond2a03062016-04-15 11:19:07 -0700936 mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
Winson8be16342016-02-09 11:53:27 -0800937 disabledInSafeMode);
Winsond2a03062016-04-15 11:19:07 -0700938 mHeaderBar.onTaskDataLoaded();
Winsone693aaf2016-03-01 12:05:59 -0800939 mHeaderBar.setDimAlpha(toTransform.dimAlpha);
Winson Chungaa7fa012017-05-24 15:50:06 -0700940 return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
941 mHeaderBar, 1f, 0);
Winson190fe3bf2015-10-20 14:57:24 -0700942 }
943 }
Winson190fe3bf2015-10-20 14:57:24 -0700944 }
945 return null;
946 }
947
948 /**
949 * Shows the recents activity
950 */
Winsond46b7272016-04-20 11:54:27 -0700951 protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
952 boolean isHomeStackVisible, boolean animate, int growTarget) {
Winsone7f138c2015-10-22 16:15:21 -0700953 RecentsTaskLoader loader = Recents.getTaskLoader();
Winsonc5b12dd2016-03-23 20:25:12 -0700954 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
Winsonfc73eec2016-08-01 15:48:34 -0700955 SystemServicesProxy ssp = Recents.getSystemServices();
956 boolean isBlacklisted = (runningTask != null)
957 ? ssp.isBlackListedActivity(runningTask.baseActivity.getClassName())
958 : false;
Winson190fe3bf2015-10-20 14:57:24 -0700959
Winsonfc73eec2016-08-01 15:48:34 -0700960 int runningTaskId = !mLaunchedWhileDocking && !isBlacklisted && (runningTask != null)
Winsonc4a038a2016-05-26 16:42:15 -0700961 ? runningTask.id
962 : -1;
963
Winsone5f1faa2015-11-20 12:26:23 -0800964 // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
Jorim Jaggi435b2e42015-11-24 15:09:30 -0800965 // should always preload the tasks now. If we are dragging in recents, reload them as
966 // the stacks might have changed.
Winsonfc73eec2016-08-01 15:48:34 -0700967 if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
Winsone5f1faa2015-11-20 12:26:23 -0800968 // Create a new load plan if preloadRecents() was never triggered
Winson190fe3bf2015-10-20 14:57:24 -0700969 sInstanceLoadPlan = loader.createLoadPlan(mContext);
970 }
Jorim Jaggie161f082016-02-05 14:26:16 -0800971 if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
Winsonc4a038a2016-05-26 16:42:15 -0700972 loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible);
Winson190fe3bf2015-10-20 14:57:24 -0700973 }
Winsonc5b12dd2016-03-23 20:25:12 -0700974
Winson190fe3bf2015-10-20 14:57:24 -0700975 TaskStack stack = sInstanceLoadPlan.getTaskStack();
Winsonc5b12dd2016-03-23 20:25:12 -0700976 boolean hasRecentTasks = stack.getTaskCount() > 0;
Winsonfc73eec2016-08-01 15:48:34 -0700977 boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
978 hasRecentTasks;
Winson190fe3bf2015-10-20 14:57:24 -0700979
Winsonaeb298c2016-04-05 13:08:11 -0700980 // Update the launch state that we need in updateHeaderBarLayout()
Jorim Jaggic5887ea2016-05-13 18:21:48 -0700981 launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
Winsonaeb298c2016-04-05 13:08:11 -0700982 launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
Winsonfc73eec2016-08-01 15:48:34 -0700983 launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted;
Winson Chungb5026902017-05-03 12:45:13 -0700984 launchState.launchedFromPipApp = false;
985 launchState.launchedWithNextPipApp =
986 stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
Winsonaeb298c2016-04-05 13:08:11 -0700987 launchState.launchedViaDockGesture = mLaunchedWhileDocking;
988 launchState.launchedViaDragGesture = mDraggingInRecents;
Winsonc4a038a2016-05-26 16:42:15 -0700989 launchState.launchedToTaskId = runningTaskId;
Winsonaeb298c2016-04-05 13:08:11 -0700990 launchState.launchedWithAltTab = mTriggeredFromAltTab;
991
992 // Preload the icon (this will be a null-op if we have preloaded the icon already in
993 // preloadRecents())
Winsonc4a038a2016-05-26 16:42:15 -0700994 preloadIcon(runningTaskId);
Winsonaeb298c2016-04-05 13:08:11 -0700995
Winsonf0d1c442015-12-01 11:04:45 -0800996 // Update the header bar if necessary
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700997 Rect windowOverrideRect = getWindowRectOverride(growTarget);
998 updateHeaderBarLayout(stack, windowOverrideRect);
Winsonf0d1c442015-12-01 11:04:45 -0800999
Winson190fe3bf2015-10-20 14:57:24 -07001000 // Prepare the dummy stack for the transition
Winson Chungf3cfa892017-04-24 12:23:47 -07001001 TaskStackLayoutAlgorithm.VisibilityReport stackVr;
1002 synchronized (mDummyStackView) {
1003 stackVr = mDummyStackView.computeStackVisibilityReport();
1004 }
Jorim Jaggibb42a462015-11-20 16:27:16 -08001005
Winsonaeb298c2016-04-05 13:08:11 -07001006 // Update the remaining launch state
Winsonc5b12dd2016-03-23 20:25:12 -07001007 launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
1008 launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
Winsonc5b12dd2016-03-23 20:25:12 -07001009
Jorim Jaggibb42a462015-11-20 16:27:16 -08001010 if (!animate) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001011 startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
1012 null /* future */);
Jorim Jaggibb42a462015-11-20 16:27:16 -08001013 return;
1014 }
1015
Jorim Jaggief496ea2017-05-13 23:16:44 +02001016 Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
Winsonfc73eec2016-08-01 15:48:34 -07001017 if (isBlacklisted) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001018 pair = new Pair<>(getUnknownTransitionActivityOptions(), null);
Winsonfc73eec2016-08-01 15:48:34 -07001019 } else if (useThumbnailTransition) {
Winson190fe3bf2015-10-20 14:57:24 -07001020 // Try starting with a thumbnail transition
Jorim Jaggief496ea2017-05-13 23:16:44 +02001021 pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
Winsonaeb298c2016-04-05 13:08:11 -07001022 } else {
Winson190fe3bf2015-10-20 14:57:24 -07001023 // If there is no thumbnail transition, but is launching from home into recents, then
Winson008ee15f2016-03-18 17:17:25 -07001024 // use a quick home transition
Jorim Jaggief496ea2017-05-13 23:16:44 +02001025 pair = new Pair<>(hasRecentTasks
1026 ? getHomeTransitionActivityOptions()
1027 : getUnknownTransitionActivityOptions(), null);
Winson190fe3bf2015-10-20 14:57:24 -07001028 }
Jorim Jaggief496ea2017-05-13 23:16:44 +02001029 startRecentsActivity(pair.first, pair.second);
Winson190fe3bf2015-10-20 14:57:24 -07001030 mLastToggleTime = SystemClock.elapsedRealtime();
1031 }
1032
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001033 private Rect getWindowRectOverride(int growTarget) {
1034 if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001035 return SystemServicesProxy.getInstance(mContext).getWindowRect();
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001036 }
1037 Rect result = new Rect();
1038 Rect displayRect = Recents.getSystemServices().getDisplayRect();
1039 DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
1040 result, displayRect.width(), displayRect.height(),
1041 Recents.getSystemServices().getDockedDividerSize(mContext));
1042 return result;
1043 }
1044
Winson190fe3bf2015-10-20 14:57:24 -07001045 /**
1046 * Starts the recents activity.
1047 */
Jorim Jaggief496ea2017-05-13 23:16:44 +02001048 private void startRecentsActivity(ActivityOptions opts,
1049 final AppTransitionAnimationSpecsFuture future) {
Winson190fe3bf2015-10-20 14:57:24 -07001050 Intent intent = new Intent();
Sid Soundararajan4bdb6872016-03-18 13:42:10 -07001051 intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
Winson190fe3bf2015-10-20 14:57:24 -07001052 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1053 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
1054 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Winson Chungb5026902017-05-03 12:45:13 -07001055 HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
1056 hideMenuEvent.addPostAnimationCallback(() -> {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001057 Recents.getSystemServices().startActivityAsUserAsync(intent, opts);
Winson Chungb5026902017-05-03 12:45:13 -07001058 EventBus.getDefault().send(new RecentsActivityStartingEvent());
Jorim Jaggief496ea2017-05-13 23:16:44 +02001059 if (future != null) {
1060 future.precacheSpecs();
1061 }
Winson Chungb5026902017-05-03 12:45:13 -07001062 });
1063 EventBus.getDefault().send(hideMenuEvent);
Winson190fe3bf2015-10-20 14:57:24 -07001064 }
1065
Winson3fb67562015-11-11 10:39:03 -08001066 /**** OnAnimationFinishedListener Implementation ****/
Filip Gruszczynski1a5203d2015-10-29 17:43:49 -07001067
1068 @Override
1069 public void onAnimationFinished() {
1070 EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
1071 }
Winson190fe3bf2015-10-20 14:57:24 -07001072}