blob: 5d5c4a0c69c6a4ef7c77c6fee0d33fe3207f6021 [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;
Matthew Ng912c7f72017-08-02 22:12:04 +000027import android.app.ActivityOptions.OnAnimationStartedListener;
Winson190fe3bf2015-10-20 14:57:24 -070028import android.content.ActivityNotFoundException;
Winson190fe3bf2015-10-20 14:57:24 -070029import android.content.Context;
30import android.content.Intent;
31import android.content.res.Resources;
Winson Chungaa7fa012017-05-24 15:50:06 -070032import android.graphics.GraphicBuffer;
Winson190fe3bf2015-10-20 14:57:24 -070033import android.graphics.Rect;
Winson3150e572015-10-23 15:07:24 -070034import android.graphics.RectF;
Winson22574af2016-03-23 19:00:28 -070035import android.graphics.drawable.Drawable;
Winson190fe3bf2015-10-20 14:57:24 -070036import android.os.Handler;
37import android.os.SystemClock;
Winson Chung3e7e4642017-08-22 10:08:06 -070038import android.util.ArraySet;
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;
Winson Chung3e7e4642017-08-22 10:08:06 -070079import com.android.systemui.recents.model.Task.TaskKey;
Winson190fe3bf2015-10-20 14:57:24 -070080import com.android.systemui.recents.model.TaskGrouping;
81import com.android.systemui.recents.model.TaskStack;
Winson Chung46249762017-06-09 14:38:39 -070082import com.android.systemui.recents.model.ThumbnailData;
Jorim Jaggief496ea2017-05-13 23:16:44 +020083import com.android.systemui.recents.views.RecentsTransitionHelper;
84import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
Winson36a5a2c2015-10-29 18:04:39 -070085import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
Jorim Jaggicdef5912017-04-03 17:24:19 +020086import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
Winson1b585612015-11-06 09:16:26 -080087import com.android.systemui.recents.views.TaskStackView;
Winsone693aaf2016-03-01 12:05:59 -080088import com.android.systemui.recents.views.TaskStackViewScroller;
Winson190fe3bf2015-10-20 14:57:24 -070089import com.android.systemui.recents.views.TaskViewHeader;
90import com.android.systemui.recents.views.TaskViewTransform;
Manu Cornetde3cfb02017-01-25 18:31:01 +090091import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
Jorim Jaggidb21bbd2016-04-18 15:32:07 -070092import com.android.systemui.stackdivider.DividerView;
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080093import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050094import com.android.systemui.statusbar.phone.StatusBar;
Winson190fe3bf2015-10-20 14:57:24 -070095
96import java.util.ArrayList;
97
98/**
99 * An implementation of the Recents component for the current user. For secondary users, this can
100 * be called remotely from the system user.
101 */
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800102public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
Winson190fe3bf2015-10-20 14:57:24 -0700103
104 private final static String TAG = "RecentsImpl";
Winsonb61e6542016-02-04 14:37:18 -0800105
Winson6b92c6e2015-11-06 13:11:16 -0800106 // The minimum amount of time between each recents button press that we will handle
107 private final static int MIN_TOGGLE_DELAY_MS = 350;
Winsonb61e6542016-02-04 14:37:18 -0800108
Winson6b92c6e2015-11-06 13:11:16 -0800109 // The duration within which the user releasing the alt tab (from when they pressed alt tab)
110 // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
111 // duration, then we will toggle recents after this duration.
112 private final static int FAST_ALT_TAB_DELAY_MS = 225;
Winson190fe3bf2015-10-20 14:57:24 -0700113
Winson Chung3e7e4642017-08-22 10:08:06 -0700114 private final static ArraySet<TaskKey> EMPTY_SET = new ArraySet<>();
115
Winson190fe3bf2015-10-20 14:57:24 -0700116 public final static String RECENTS_PACKAGE = "com.android.systemui";
117 public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
Winsone693aaf2016-03-01 12:05:59 -0800118
Winson190fe3bf2015-10-20 14:57:24 -0700119 /**
Jaewan Kim938a50b2016-03-14 17:35:43 +0900120 * An implementation of TaskStackListener, that allows us to listen for changes to the system
Winson190fe3bf2015-10-20 14:57:24 -0700121 * task stacks and update recents accordingly.
122 */
Jaewan Kim938a50b2016-03-14 17:35:43 +0900123 class TaskStackListenerImpl extends TaskStackListener {
Winson Chungf3cfa892017-04-24 12:23:47 -0700124
Winson190fe3bf2015-10-20 14:57:24 -0700125 @Override
Winson Chungf3cfa892017-04-24 12:23:47 -0700126 public void onTaskStackChangedBackground() {
Winson Chungee697562017-05-18 14:29:43 -0700127 // Check this is for the right user
128 if (!checkCurrentUserId(mContext, false /* debug */)) {
129 return;
130 }
131
Jaewan Kim938a50b2016-03-14 17:35:43 +0900132 // Preloads the next task
Jorim Jaggia0fdeec2016-01-07 14:42:28 +0100133 RecentsConfiguration config = Recents.getConfiguration();
Winson190fe3bf2015-10-20 14:57:24 -0700134 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Matthew Nga3d09db2017-08-29 14:06:37 -0700135 Rect windowRect = getWindowRect(null /* windowRectOverride */);
136 if (windowRect.isEmpty()) {
137 return;
138 }
139
Winson190fe3bf2015-10-20 14:57:24 -0700140 // Load the next task only if we aren't svelte
Winson Chungf3cfa892017-04-24 12:23:47 -0700141 SystemServicesProxy ssp = Recents.getSystemServices();
142 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
143 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700144 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700145 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson Chung3e7e4642017-08-22 10:08:06 -0700146 TaskStack stack = plan.getTaskStack();
147 RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
148 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chungf3cfa892017-04-24 12:23:47 -0700149
Winson Chung3e7e4642017-08-22 10:08:06 -0700150 synchronized (mBackgroundLayoutAlgorithm) {
151 // This callback is made when a new activity is launched and the old one is
152 // paused so ignore the current activity and try and preload the thumbnail for
153 // the previous one.
Matthew Nga3d09db2017-08-29 14:06:37 -0700154 updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
Winson Chungf3cfa892017-04-24 12:23:47 -0700155
156 // Launched from app is always the worst case (in terms of how many
157 // thumbnails/tasks visible)
Winson Chungf3cfa892017-04-24 12:23:47 -0700158 launchState.launchedFromApp = true;
Winson Chung3e7e4642017-08-22 10:08:06 -0700159 mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState);
160 VisibilityReport visibilityReport =
161 mBackgroundLayoutAlgorithm.computeStackVisibilityReport(
162 stack.getStackTasks());
Jorim Jaggicdef5912017-04-03 17:24:19 +0200163
Winson Chung3e7e4642017-08-22 10:08:06 -0700164 launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
165 launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
166 launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
167 launchOpts.onlyLoadForCache = true;
168 launchOpts.onlyLoadPausedActivities = true;
169 launchOpts.loadThumbnails = true;
170 }
Winson190fe3bf2015-10-20 14:57:24 -0700171 loader.loadTasks(mContext, plan, launchOpts);
172 }
Winson190fe3bf2015-10-20 14:57:24 -0700173 }
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100174
175 @Override
Winson Chung85d3c8a2017-09-15 15:41:00 -0700176 public void onActivityPinned(String packageName, int userId, int taskId) {
Winson Chungee697562017-05-18 14:29:43 -0700177 // Check this is for the right user
178 if (!checkCurrentUserId(mContext, false /* debug */)) {
179 return;
180 }
181
Winson Chungb5026902017-05-03 12:45:13 -0700182 // This time needs to be fetched the same way the last active time is fetched in
183 // {@link TaskRecord#touchActiveTime}
184 Recents.getConfiguration().getLaunchState().launchedFromPipApp = true;
185 Recents.getConfiguration().getLaunchState().launchedWithNextPipApp = false;
186 EventBus.getDefault().send(new ActivityPinnedEvent(taskId));
187 consumeInstanceLoadPlan();
188 sLastPipTime = System.currentTimeMillis();
189 }
190
191 @Override
192 public void onActivityUnpinned() {
Winson Chungee697562017-05-18 14:29:43 -0700193 // Check this is for the right user
194 if (!checkCurrentUserId(mContext, false /* debug */)) {
195 return;
196 }
197
Winson Chungb5026902017-05-03 12:45:13 -0700198 EventBus.getDefault().send(new ActivityUnpinnedEvent());
199 sLastPipTime = -1;
200 }
201
202 @Override
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100203 public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
Winson Chungee697562017-05-18 14:29:43 -0700204 // Check this is for the right user
205 if (!checkCurrentUserId(mContext, false /* debug */)) {
206 return;
207 }
208
Winson Chung46249762017-06-09 14:38:39 -0700209 EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId,
210 ThumbnailData.createFromTaskSnapshot(snapshot)));
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100211 }
Winson190fe3bf2015-10-20 14:57:24 -0700212 }
213
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700214 protected static RecentsTaskLoadPlan sInstanceLoadPlan;
Winson Chungb5026902017-05-03 12:45:13 -0700215 // Stores the last pinned task time
216 protected static long sLastPipTime = -1;
Matthew Ng912c7f72017-08-02 22:12:04 +0000217 // Stores whether we are waiting for a transition to/from recents to start. During this time,
218 // we disallow the user from manually toggling recents until the transition has started.
219 private static boolean mWaitingForTransitionStart = false;
220 // Stores whether or not the user toggled while we were waiting for a transition to/from
221 // recents. In this case, we defer the toggle state until then and apply it immediately after.
222 private static boolean mToggleFollowingTransitionStart = true;
223
224 private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener =
225 new OnAnimationStartedListener() {
226 @Override
227 public void onAnimationStarted() {
228 setWaitingForTransitionStart(false);
229 }
230 };
Winson190fe3bf2015-10-20 14:57:24 -0700231
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700232 protected Context mContext;
233 protected Handler mHandler;
Winson190fe3bf2015-10-20 14:57:24 -0700234 TaskStackListenerImpl mTaskStackListener;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800235 boolean mDraggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800236 boolean mLaunchedWhileDocking;
Winson190fe3bf2015-10-20 14:57:24 -0700237
238 // Task launching
Winson Chung3e7e4642017-08-22 10:08:06 -0700239 Rect mTmpBounds = new Rect();
Winson190fe3bf2015-10-20 14:57:24 -0700240 TaskViewTransform mTmpTransform = new TaskViewTransform();
Winson190fe3bf2015-10-20 14:57:24 -0700241 int mTaskBarHeight;
242
243 // Header (for transition)
244 TaskViewHeader mHeaderBar;
245 final Object mHeaderBarLock = new Object();
Winson Chung3e7e4642017-08-22 10:08:06 -0700246 private TaskStackView mDummyStackView;
247 private TaskStackLayoutAlgorithm mBackgroundLayoutAlgorithm;
Winson190fe3bf2015-10-20 14:57:24 -0700248
249 // Variables to keep track of if we need to start recents after binding
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700250 protected boolean mTriggeredFromAltTab;
251 protected long mLastToggleTime;
Winson6b92c6e2015-11-06 13:11:16 -0800252 DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
253 @Override
254 public void run() {
255 // When this fires, then the user has not released alt-tab for at least
256 // FAST_ALT_TAB_DELAY_MS milliseconds
Jorim Jaggi435b2e42015-11-24 15:09:30 -0800257 showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700258 false /* reloadTasks */, false /* fromHome */,
259 DividerView.INVALID_RECENTS_GROW_TARGET);
Winson6b92c6e2015-11-06 13:11:16 -0800260 }
261 });
Winson190fe3bf2015-10-20 14:57:24 -0700262
Winson190fe3bf2015-10-20 14:57:24 -0700263 public RecentsImpl(Context context) {
264 mContext = context;
Winson190fe3bf2015-10-20 14:57:24 -0700265 mHandler = new Handler();
Winson Chung3e7e4642017-08-22 10:08:06 -0700266 mBackgroundLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
Winson190fe3bf2015-10-20 14:57:24 -0700267
Winsonab84fc52015-10-23 11:52:07 -0700268 // Initialize the static foreground thread
269 ForegroundThread.get();
270
Winson190fe3bf2015-10-20 14:57:24 -0700271 // Register the task stack listener
Jaewan Kim938a50b2016-03-14 17:35:43 +0900272 mTaskStackListener = new TaskStackListenerImpl();
Winsone7f138c2015-10-22 16:15:21 -0700273 SystemServicesProxy ssp = Recents.getSystemServices();
274 ssp.registerTaskStackListener(mTaskStackListener);
Winson190fe3bf2015-10-20 14:57:24 -0700275
276 // Initialize the static configuration resources
Winson670ea712016-04-12 17:02:26 -0700277 mDummyStackView = new TaskStackView(mContext);
Winson670ea712016-04-12 17:02:26 -0700278 reloadResources();
Winson16ef39a2016-06-28 18:25:15 -0700279 }
Winson190fe3bf2015-10-20 14:57:24 -0700280
Winson Chung501d59d2016-10-05 17:49:09 +0000281 public void onBootCompleted() {
Winson190fe3bf2015-10-20 14:57:24 -0700282 // When we start, preload the data associated with the previous recent tasks.
283 // We can use a new plan since the caches will be the same.
Winsone7f138c2015-10-22 16:15:21 -0700284 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700285 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700286 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson190fe3bf2015-10-20 14:57:24 -0700287 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chung296278a2015-12-17 12:09:02 -0500288 launchOpts.numVisibleTasks = loader.getIconCacheSize();
Winson190fe3bf2015-10-20 14:57:24 -0700289 launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
290 launchOpts.onlyLoadForCache = true;
291 loader.loadTasks(mContext, plan, launchOpts);
292 }
293
Winson190fe3bf2015-10-20 14:57:24 -0700294 public void onConfigurationChanged() {
Winson670ea712016-04-12 17:02:26 -0700295 reloadResources();
Winson Chung3e7e4642017-08-22 10:08:06 -0700296 mDummyStackView.reloadOnConfigurationChange();
297 synchronized (mBackgroundLayoutAlgorithm) {
298 mBackgroundLayoutAlgorithm.reloadOnConfigurationChange(mContext);
Winson Chungf3cfa892017-04-24 12:23:47 -0700299 }
Winson190fe3bf2015-10-20 14:57:24 -0700300 }
301
302 /**
303 * This is only called from the system user's Recents. Secondary users will instead proxy their
304 * visibility change events through to the system user via
305 * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
306 */
307 public void onVisibilityChanged(Context context, boolean visible) {
Winsonab216602016-08-09 14:05:20 -0700308 Recents.getSystemServices().setRecentsVisibility(visible);
Winson190fe3bf2015-10-20 14:57:24 -0700309 }
310
311 /**
312 * This is only called from the system user's Recents. Secondary users will instead proxy their
313 * visibility change events through to the system user via
314 * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
315 */
Andrii Kulian0f051f52016-04-14 00:41:51 -0700316 public void onStartScreenPinning(Context context, int taskId) {
Winson190fe3bf2015-10-20 14:57:24 -0700317 SystemUIApplication app = (SystemUIApplication) context;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500318 StatusBar statusBar = app.getComponent(StatusBar.class);
Winson190fe3bf2015-10-20 14:57:24 -0700319 if (statusBar != null) {
Andrii Kulian0f051f52016-04-14 00:41:51 -0700320 statusBar.showScreenPinningRequest(taskId, false);
Winson190fe3bf2015-10-20 14:57:24 -0700321 }
322 }
323
Jorim Jaggibb42a462015-11-20 16:27:16 -0800324 public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700325 boolean animate, boolean launchedWhileDockingTask, boolean fromHome,
326 int growTarget) {
Winson190fe3bf2015-10-20 14:57:24 -0700327 mTriggeredFromAltTab = triggeredFromAltTab;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800328 mDraggingInRecents = draggingInRecents;
Jorim Jaggie161f082016-02-05 14:26:16 -0800329 mLaunchedWhileDocking = launchedWhileDockingTask;
Winsone693aaf2016-03-01 12:05:59 -0800330 if (mFastAltTabTrigger.isAsleep()) {
331 // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
332 mFastAltTabTrigger.stopDozing();
Winson6b92c6e2015-11-06 13:11:16 -0800333 } else if (mFastAltTabTrigger.isDozing()) {
Winsone693aaf2016-03-01 12:05:59 -0800334 // Fast alt-tab duration has not elapsed. If this is triggered by a different
335 // showRecents() call, then ignore that call for now.
336 // TODO: We can not handle quick tabs that happen between the initial showRecents() call
337 // that started the activity and the activity starting up. The severity of this
338 // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
Winson6b92c6e2015-11-06 13:11:16 -0800339 if (!triggeredFromAltTab) {
340 return;
341 }
342 mFastAltTabTrigger.stopDozing();
Winsone693aaf2016-03-01 12:05:59 -0800343 } else if (triggeredFromAltTab) {
344 // The fast alt-tab detector is not yet running, so start the trigger and wait for the
345 // hideRecents() call, or for the fast alt-tab duration to elapse
346 mFastAltTabTrigger.startDozing();
347 return;
Winson6b92c6e2015-11-06 13:11:16 -0800348 }
Winson190fe3bf2015-10-20 14:57:24 -0700349
350 try {
351 // Check if the top task is in the home stack, and start the recents activity
Winsone7f138c2015-10-22 16:15:21 -0700352 SystemServicesProxy ssp = Recents.getSystemServices();
Winsond7cb5172016-05-04 16:14:19 -0700353 boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
354 MutableBoolean isHomeStackVisible = new MutableBoolean(forceVisible);
355 if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
Winsond46b7272016-04-20 11:54:27 -0700356 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
357 startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate,
358 growTarget);
Winson190fe3bf2015-10-20 14:57:24 -0700359 }
360 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800361 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700362 }
363 }
364
Winson190fe3bf2015-10-20 14:57:24 -0700365 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson2799eca2016-02-25 12:10:42 -0800366 if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
367 // The user has released alt-tab before the trigger has run, so just show the next
368 // task immediately
369 showNextTask();
Winson6b92c6e2015-11-06 13:11:16 -0800370
Winson2799eca2016-02-25 12:10:42 -0800371 // Cancel the fast alt-tab trigger
372 mFastAltTabTrigger.stopDozing();
Winson2799eca2016-02-25 12:10:42 -0800373 return;
Winson190fe3bf2015-10-20 14:57:24 -0700374 }
Winson2799eca2016-02-25 12:10:42 -0800375
376 // Defer to the activity to handle hiding recents, if it handles it, then it must still
377 // be visible
378 EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
379 triggeredFromHomeKey));
Winson190fe3bf2015-10-20 14:57:24 -0700380 }
381
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700382 public void toggleRecents(int growTarget) {
Winson Chungee70fbd2017-08-29 14:56:01 -0700383 // Skip preloading if the task is locked
384 SystemServicesProxy ssp = Recents.getSystemServices();
385 if (ssp.isScreenPinningActive()) {
386 return;
387 }
388
Winson6b92c6e2015-11-06 13:11:16 -0800389 // Skip this toggle if we are already waiting to trigger recents via alt-tab
390 if (mFastAltTabTrigger.isDozing()) {
391 return;
392 }
393
Matthew Ng912c7f72017-08-02 22:12:04 +0000394 if (mWaitingForTransitionStart) {
395 mToggleFollowingTransitionStart = true;
396 return;
397 }
398
Jorim Jaggidd98d412015-11-18 15:57:38 -0800399 mDraggingInRecents = false;
Jorim Jaggie161f082016-02-05 14:26:16 -0800400 mLaunchedWhileDocking = false;
Winson190fe3bf2015-10-20 14:57:24 -0700401 mTriggeredFromAltTab = false;
402
403 try {
Winsond46b7272016-04-20 11:54:27 -0700404 MutableBoolean isHomeStackVisible = new MutableBoolean(true);
Winsonb61e6542016-02-04 14:37:18 -0800405 long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
406
Winsond46b7272016-04-20 11:54:27 -0700407 if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
Winsone693aaf2016-03-01 12:05:59 -0800408 RecentsDebugFlags debugFlags = Recents.getDebugFlags();
Winson5da43472015-11-04 17:39:55 -0800409 RecentsConfiguration config = Recents.getConfiguration();
410 RecentsActivityLaunchState launchState = config.getLaunchState();
Winson Chungead5c0f2015-12-14 11:18:57 -0500411 if (!launchState.launchedWithAltTab) {
Manu Corneta96a6172017-01-19 12:40:44 -0800412 // Has the user tapped quickly?
Matthew Ngda3dd7c2017-04-28 17:33:22 -0700413 boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
Manu Corneta96a6172017-01-19 12:40:44 -0800414 if (Recents.getConfiguration().isGridEnabled) {
415 if (isQuickTap) {
416 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
417 } else {
418 EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
419 }
Winsonb61e6542016-02-04 14:37:18 -0800420 } else {
Manu Corneta96a6172017-01-19 12:40:44 -0800421 if (!debugFlags.isPagingEnabled() || isQuickTap) {
422 // Launch the next focused task
423 EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
424 } else {
425 // Notify recents to move onto the next task
426 EventBus.getDefault().post(new IterateRecentsEvent());
427 }
Winsonb61e6542016-02-04 14:37:18 -0800428 }
Winson0d14d4d2015-10-26 17:05:04 -0700429 } else {
430 // If the user has toggled it too quickly, then just eat up the event here (it's
431 // better than showing a janky screenshot).
432 // NOTE: Ideally, the screenshot mechanism would take the window transform into
433 // account
Winsonb61e6542016-02-04 14:37:18 -0800434 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700435 return;
436 }
437
438 EventBus.getDefault().post(new ToggleRecentsEvent());
439 mLastToggleTime = SystemClock.elapsedRealtime();
440 }
Winson190fe3bf2015-10-20 14:57:24 -0700441 return;
442 } else {
Winson0d14d4d2015-10-26 17:05:04 -0700443 // If the user has toggled it too quickly, then just eat up the event here (it's
444 // better than showing a janky screenshot).
445 // NOTE: Ideally, the screenshot mechanism would take the window transform into
446 // account
Winsonb61e6542016-02-04 14:37:18 -0800447 if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
Winson0d14d4d2015-10-26 17:05:04 -0700448 return;
449 }
450
Winson190fe3bf2015-10-20 14:57:24 -0700451 // Otherwise, start the recents activity
Winsond46b7272016-04-20 11:54:27 -0700452 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
453 startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
454 growTarget);
Winsond8b1d632016-01-04 17:51:18 -0800455
456 // Only close the other system windows if we are actually showing recents
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500457 ssp.sendCloseSystemWindows(StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
Winson0d14d4d2015-10-26 17:05:04 -0700458 mLastToggleTime = SystemClock.elapsedRealtime();
Winson190fe3bf2015-10-20 14:57:24 -0700459 }
460 } catch (ActivityNotFoundException e) {
Winson1b585612015-11-06 09:16:26 -0800461 Log.e(TAG, "Failed to launch RecentsActivity", e);
Winson190fe3bf2015-10-20 14:57:24 -0700462 }
463 }
464
Winson190fe3bf2015-10-20 14:57:24 -0700465 public void preloadRecents() {
Winson Chungee70fbd2017-08-29 14:56:01 -0700466 // Skip preloading if the task is locked
467 SystemServicesProxy ssp = Recents.getSystemServices();
468 if (ssp.isScreenPinningActive()) {
469 return;
470 }
471
Winson190fe3bf2015-10-20 14:57:24 -0700472 // Preload only the raw task list into a new load plan (which will be consumed by the
Winson Chungebe1ba12017-05-18 15:47:14 -0700473 // RecentsActivity) only if there is a task to animate to. Post this to ensure that we
474 // don't block the touch feedback on the nav bar button which triggers this.
475 mHandler.post(() -> {
Winson Chungebe1ba12017-05-18 15:47:14 -0700476 MutableBoolean isHomeStackVisible = new MutableBoolean(true);
477 if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
478 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
479 if (runningTask == null) {
480 return;
481 }
Winson Chung96b7a512017-02-08 12:20:00 -0800482
Winson Chungebe1ba12017-05-18 15:47:14 -0700483 RecentsTaskLoader loader = Recents.getTaskLoader();
484 sInstanceLoadPlan = loader.createLoadPlan(mContext);
485 loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value);
486 TaskStack stack = sInstanceLoadPlan.getTaskStack();
487 if (stack.getTaskCount() > 0) {
488 // Only preload the icon (but not the thumbnail since it may not have been taken
489 // for the pausing activity)
490 preloadIcon(runningTask.id);
Winsonaeb298c2016-04-05 13:08:11 -0700491
Winson Chungebe1ba12017-05-18 15:47:14 -0700492 // At this point, we don't know anything about the stack state. So only
493 // calculate the dimensions of the thumbnail that we need for the transition
494 // into Recents, but do not draw it until we construct the activity options when
495 // we start Recents
496 updateHeaderBarLayout(stack, null /* window rect override*/);
497 }
Winson190fe3bf2015-10-20 14:57:24 -0700498 }
Winson Chungebe1ba12017-05-18 15:47:14 -0700499 });
Winson190fe3bf2015-10-20 14:57:24 -0700500 }
501
Winson190fe3bf2015-10-20 14:57:24 -0700502 public void cancelPreloadingRecents() {
503 // Do nothing
504 }
505
Jorim Jaggidd98d412015-11-18 15:57:38 -0800506 public void onDraggingInRecents(float distanceFromTop) {
507 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
508 }
509
Jorim Jaggidd98d412015-11-18 15:57:38 -0800510 public void onDraggingInRecentsEnded(float velocity) {
511 EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
512 }
513
Winson675c5d82016-08-23 17:12:22 -0700514 public void onShowCurrentUserToast(int msgResId, int msgLength) {
515 Toast.makeText(mContext, msgResId, msgLength).show();
516 }
517
Winson6b92c6e2015-11-06 13:11:16 -0800518 /**
519 * Transitions to the next recent task in the stack.
520 */
521 public void showNextTask() {
Winsone7f138c2015-10-22 16:15:21 -0700522 SystemServicesProxy ssp = Recents.getSystemServices();
Winson6b92c6e2015-11-06 13:11:16 -0800523 RecentsTaskLoader loader = Recents.getTaskLoader();
524 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700525 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson6b92c6e2015-11-06 13:11:16 -0800526 TaskStack focusedStack = plan.getTaskStack();
527
528 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800529 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson6b92c6e2015-11-06 13:11:16 -0800530
Winson6b92c6e2015-11-06 13:11:16 -0800531 // Return early if there is no running task
Winsond46b7272016-04-20 11:54:27 -0700532 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
Winson6b92c6e2015-11-06 13:11:16 -0800533 if (runningTask == null) return;
Winson6b92c6e2015-11-06 13:11:16 -0800534
535 // Find the task in the recents list
Winsond46b7272016-04-20 11:54:27 -0700536 boolean isRunningTaskInHomeStack = SystemServicesProxy.isHomeStack(runningTask.stackId);
Winson250608a2015-11-24 15:00:31 -0800537 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson6b92c6e2015-11-06 13:11:16 -0800538 Task toTask = null;
539 ActivityOptions launchOpts = null;
540 int taskCount = tasks.size();
541 for (int i = taskCount - 1; i >= 1; i--) {
542 Task task = tasks.get(i);
Winsond46b7272016-04-20 11:54:27 -0700543 if (isRunningTaskInHomeStack) {
Winsone86deb82015-11-12 09:32:10 -0800544 toTask = tasks.get(i - 1);
545 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
546 R.anim.recents_launch_next_affiliated_task_target,
547 R.anim.recents_fast_toggle_app_home_exit);
548 break;
549 } else if (task.key.id == runningTask.id) {
Winson6b92c6e2015-11-06 13:11:16 -0800550 toTask = tasks.get(i - 1);
551 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
552 R.anim.recents_launch_prev_affiliated_task_target,
553 R.anim.recents_launch_prev_affiliated_task_source);
554 break;
555 }
556 }
557
558 // Return early if there is no next task
559 if (toTask == null) {
560 ssp.startInPlaceAnimationOnFrontMostApplication(
561 ActivityOptions.makeCustomInPlaceAnimation(mContext,
562 R.anim.recents_launch_prev_affiliated_task_bounce));
563 return;
564 }
565
566 // Launch the task
Vladislav Kaznacheev1f6b7092016-05-04 17:18:56 -0700567 ssp.startActivityFromRecents(
Jorim Jaggi34795e32017-05-12 17:27:46 +0200568 mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
569 null /* resultListener */);
Winson6b92c6e2015-11-06 13:11:16 -0800570 }
571
572 /**
573 * Transitions to the next affiliated task.
574 */
575 public void showRelativeAffiliatedTask(boolean showNextTask) {
576 SystemServicesProxy ssp = Recents.getSystemServices();
Winsone7f138c2015-10-22 16:15:21 -0700577 RecentsTaskLoader loader = Recents.getTaskLoader();
Winson190fe3bf2015-10-20 14:57:24 -0700578 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
Winsondfd7be02016-05-10 13:30:37 -0700579 loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
Winson190fe3bf2015-10-20 14:57:24 -0700580 TaskStack focusedStack = plan.getTaskStack();
581
582 // Return early if there are no tasks in the focused stack
Winson4b057c62016-01-12 15:01:52 -0800583 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson190fe3bf2015-10-20 14:57:24 -0700584
Winson190fe3bf2015-10-20 14:57:24 -0700585 // Return early if there is no running task (can't determine affiliated tasks in this case)
Winsond46b7272016-04-20 11:54:27 -0700586 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
Winson190fe3bf2015-10-20 14:57:24 -0700587 if (runningTask == null) return;
Matthew Ngae1ff4f2016-11-10 15:49:14 -0800588 // Return early if the running task is in the home/recents stack (optimization)
589 if (isHomeOrRecentsStack(runningTask.stackId)) return;
Winson190fe3bf2015-10-20 14:57:24 -0700590
591 // Find the task in the recents list
Winson250608a2015-11-24 15:00:31 -0800592 ArrayList<Task> tasks = focusedStack.getStackTasks();
Winson190fe3bf2015-10-20 14:57:24 -0700593 Task toTask = null;
594 ActivityOptions launchOpts = null;
595 int taskCount = tasks.size();
596 int numAffiliatedTasks = 0;
597 for (int i = 0; i < taskCount; i++) {
598 Task task = tasks.get(i);
599 if (task.key.id == runningTask.id) {
600 TaskGrouping group = task.group;
601 Task.TaskKey toTaskKey;
602 if (showNextTask) {
603 toTaskKey = group.getNextTaskInGroup(task);
604 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
605 R.anim.recents_launch_next_affiliated_task_target,
606 R.anim.recents_launch_next_affiliated_task_source);
607 } else {
608 toTaskKey = group.getPrevTaskInGroup(task);
609 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
610 R.anim.recents_launch_prev_affiliated_task_target,
611 R.anim.recents_launch_prev_affiliated_task_source);
612 }
613 if (toTaskKey != null) {
614 toTask = focusedStack.findTaskWithId(toTaskKey.id);
615 }
616 numAffiliatedTasks = group.getTaskCount();
617 break;
618 }
619 }
620
621 // Return early if there is no next task
622 if (toTask == null) {
623 if (numAffiliatedTasks > 1) {
624 if (showNextTask) {
Winsone7f138c2015-10-22 16:15:21 -0700625 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700626 ActivityOptions.makeCustomInPlaceAnimation(mContext,
627 R.anim.recents_launch_next_affiliated_task_bounce));
628 } else {
Winsone7f138c2015-10-22 16:15:21 -0700629 ssp.startInPlaceAnimationOnFrontMostApplication(
Winson190fe3bf2015-10-20 14:57:24 -0700630 ActivityOptions.makeCustomInPlaceAnimation(mContext,
631 R.anim.recents_launch_prev_affiliated_task_bounce));
632 }
633 }
634 return;
635 }
636
637 // Keep track of actually launched affiliated tasks
638 MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
639
640 // Launch the task
Vladislav Kaznacheev1f6b7092016-05-04 17:18:56 -0700641 ssp.startActivityFromRecents(
Jorim Jaggi34795e32017-05-12 17:27:46 +0200642 mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
643 null /* resultListener */);
Winson190fe3bf2015-10-20 14:57:24 -0700644 }
645
646 public void showNextAffiliatedTask() {
647 // Keep track of when the affiliated task is triggered
648 MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
649 showRelativeAffiliatedTask(true);
650 }
651
652 public void showPrevAffiliatedTask() {
653 // Keep track of when the affiliated task is triggered
654 MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
655 showRelativeAffiliatedTask(false);
656 }
657
Jorim Jaggicdb06ca2016-01-25 19:15:12 -0800658 public void dockTopTask(int topTaskId, int dragMode,
659 int stackCreateMode, Rect initialBounds) {
Jorim Jaggi75b25972015-10-21 14:51:10 +0200660 SystemServicesProxy ssp = Recents.getSystemServices();
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800661
662 // Make sure we inform DividerView before we actually start the activity so we can change
663 // the resize mode already.
Chong Zhange4fbd322016-03-01 14:44:03 -0800664 if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
Jorim Jaggi899327f2016-02-25 20:44:18 -0500665 EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
Chong Zhange4fbd322016-03-01 14:44:03 -0800666 showRecents(
667 false /* triggeredFromAltTab */,
668 dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
669 false /* animate */,
Jorim Jaggi681fc7b2016-04-14 22:02:39 -0700670 true /* launchedWhileDockingTask*/,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700671 false /* fromHome */,
672 DividerView.INVALID_RECENTS_GROW_TARGET);
Chong Zhange4fbd322016-03-01 14:44:03 -0800673 }
Jorim Jaggi75b25972015-10-21 14:51:10 +0200674 }
675
Matthew Ng912c7f72017-08-02 22:12:04 +0000676 public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
677 if (mWaitingForTransitionStart == waitingForTransitionStart) {
678 return;
679 }
680
681 mWaitingForTransitionStart = waitingForTransitionStart;
682 if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
Matthew Ngc57b7f62017-08-10 15:37:19 -0700683 mHandler.post(() -> toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET));
Matthew Ng912c7f72017-08-02 22:12:04 +0000684 }
685 mToggleFollowingTransitionStart = false;
686 }
687
Winson190fe3bf2015-10-20 14:57:24 -0700688 /**
689 * Returns the preloaded load plan and invalidates it.
690 */
691 public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
692 RecentsTaskLoadPlan plan = sInstanceLoadPlan;
693 sInstanceLoadPlan = null;
694 return plan;
695 }
696
697 /**
Winson Chungb5026902017-05-03 12:45:13 -0700698 * @return the time at which a task last entered picture-in-picture.
699 */
700 public static long getLastPipTime() {
701 return sLastPipTime;
702 }
703
704 /**
705 * Clears the time at which a task last entered picture-in-picture.
706 */
707 public static void clearLastPipTime() {
708 sLastPipTime = -1;
709 }
710
711 /**
Winson670ea712016-04-12 17:02:26 -0700712 * Reloads all the resources for the current configuration.
Winsonb94443d2016-01-07 15:34:13 -0800713 */
Winson670ea712016-04-12 17:02:26 -0700714 private void reloadResources() {
Winsonb94443d2016-01-07 15:34:13 -0800715 Resources res = mContext.getResources();
Winsonb94443d2016-01-07 15:34:13 -0800716
Jorim Jaggi25160db2016-04-18 16:03:36 -0700717 mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
Winson21700932016-03-24 17:26:23 -0700718 R.dimen.recents_task_view_header_height,
719 R.dimen.recents_task_view_header_height,
720 R.dimen.recents_task_view_header_height,
721 R.dimen.recents_task_view_header_height_tablet_land,
722 R.dimen.recents_task_view_header_height,
Manu Cornetf78b11d2016-12-16 19:50:55 -0800723 R.dimen.recents_task_view_header_height_tablet_land,
724 R.dimen.recents_grid_task_view_header_height);
Matthew Ng384e60e2016-10-31 16:44:01 -0700725
726 LayoutInflater inflater = LayoutInflater.from(mContext);
727 mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
728 null, false);
729 mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
Winsonb94443d2016-01-07 15:34:13 -0800730 }
731
Winson Chung3e7e4642017-08-22 10:08:06 -0700732 private void updateDummyStackViewLayout(TaskStackLayoutAlgorithm stackLayout,
733 TaskStack stack, Rect windowRect) {
Winsone7f138c2015-10-22 16:15:21 -0700734 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonfc48b072016-04-21 11:20:11 -0700735 Rect displayRect = ssp.getDisplayRect();
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800736 Rect systemInsets = new Rect();
737 ssp.getStableInsets(systemInsets);
Jorim Jaggicdef5912017-04-03 17:24:19 +0200738
Winsoncf9b8322016-03-31 15:36:07 -0700739 // When docked, the nav bar insets are consumed and the activity is measured without insets.
740 // However, the window bounds include the insets, so we need to subtract them here to make
741 // them identical.
742 if (ssp.hasDockedTask()) {
743 windowRect.bottom -= systemInsets.bottom;
744 systemInsets.bottom = 0;
745 }
Jorim Jaggief496ea2017-05-13 23:16:44 +0200746 calculateWindowStableInsets(systemInsets, windowRect, displayRect);
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800747 windowRect.offsetTo(0, 0);
Winson190fe3bf2015-10-20 14:57:24 -0700748
Winson Chung3e7e4642017-08-22 10:08:06 -0700749 // Rebind the header bar and draw it for the transition
750 stackLayout.setSystemInsets(systemInsets);
751 if (stack != null) {
752 stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
753 systemInsets.left, systemInsets.right, mTmpBounds);
754 stackLayout.reset();
755 stackLayout.initialize(displayRect, windowRect, mTmpBounds,
756 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
Jorim Jaggicdef5912017-04-03 17:24:19 +0200757 }
758 }
759
760 private Rect getWindowRect(Rect windowRectOverride) {
761 return windowRectOverride != null
762 ? new Rect(windowRectOverride)
763 : Recents.getSystemServices().getWindowRect();
764 }
765
766 /**
767 * Prepares the header bar layout for the next transition, if the task view bounds has changed
768 * since the last call, it will attempt to re-measure and layout the header bar to the new size.
769 *
770 * @param stack the stack to initialize the stack layout with
771 * @param windowRectOverride the rectangle to use when calculating the stack state which can
772 * be different from the current window rect if recents is resizing
773 * while being launched
774 */
775 private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
776 Rect windowRect = getWindowRect(windowRectOverride);
Winson Chungf3cfa892017-04-24 12:23:47 -0700777 int taskViewWidth = 0;
Winson Chung3e7e4642017-08-22 10:08:06 -0700778 boolean useGridLayout = mDummyStackView.useGridLayout();
779 updateDummyStackViewLayout(mDummyStackView.getStackAlgorithm(), stack, windowRect);
780 if (stack != null) {
781 TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
782 mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
783 mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
784 // Get the width of a task view so that we know how wide to draw the header bar.
785 if (useGridLayout) {
786 TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
787 gridLayout.initialize(windowRect);
788 taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
789 stack.getTaskCount(), new TaskViewTransform(),
790 stackLayout).rect.width();
791 } else {
792 Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
793 if (!taskViewBounds.isEmpty()) {
794 taskViewWidth = taskViewBounds.width();
Manu Cornetde3cfb02017-01-25 18:31:01 +0900795 }
796 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700797 }
Winson190fe3bf2015-10-20 14:57:24 -0700798
Winson Chungf3cfa892017-04-24 12:23:47 -0700799 if (stack != null && taskViewWidth > 0) {
800 synchronized (mHeaderBarLock) {
801 if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
802 mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
803 if (useGridLayout) {
804 mHeaderBar.setShouldDarkenBackgroundColor(true);
805 mHeaderBar.setNoUserInteractionState();
Winson94bc4f22016-04-07 14:22:12 -0700806 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700807 mHeaderBar.forceLayout();
808 mHeaderBar.measure(
809 MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
810 MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
Winsonaeb298c2016-04-05 13:08:11 -0700811 }
Winson Chungf3cfa892017-04-24 12:23:47 -0700812 mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
813 }
Winson190fe3bf2015-10-20 14:57:24 -0700814 }
815 }
816
817 /**
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800818 * Given the stable insets and the rect for our window, calculates the insets that affect our
819 * window.
820 */
Jorim Jaggief496ea2017-05-13 23:16:44 +0200821 private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect, Rect displayRect) {
Jorim Jaggic6c89a82016-01-28 17:48:21 -0800822
823 // Display rect without insets - available app space
824 Rect appRect = new Rect(displayRect);
825 appRect.inset(inOutInsets);
826
827 // Our window intersected with available app space
828 Rect windowRectWithInsets = new Rect(windowRect);
829 windowRectWithInsets.intersect(appRect);
830 inOutInsets.left = windowRectWithInsets.left - windowRect.left;
831 inOutInsets.top = windowRectWithInsets.top - windowRect.top;
832 inOutInsets.right = windowRect.right - windowRectWithInsets.right;
833 inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
834 }
835
836 /**
Winson190fe3bf2015-10-20 14:57:24 -0700837 * Preloads the icon of a task.
838 */
Winsonc4a038a2016-05-26 16:42:15 -0700839 private void preloadIcon(int runningTaskId) {
Winson190fe3bf2015-10-20 14:57:24 -0700840 // Ensure that we load the running task's icon
841 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winsonc4a038a2016-05-26 16:42:15 -0700842 launchOpts.runningTaskId = runningTaskId;
Winson190fe3bf2015-10-20 14:57:24 -0700843 launchOpts.loadThumbnails = false;
844 launchOpts.onlyLoadForCache = true;
Winsone7f138c2015-10-22 16:15:21 -0700845 Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
Winson190fe3bf2015-10-20 14:57:24 -0700846 }
847
848 /**
Winson190fe3bf2015-10-20 14:57:24 -0700849 * Creates the activity options for a unknown state->recents transition.
850 */
Sid Soundararajan4bdb6872016-03-18 13:42:10 -0700851 protected ActivityOptions getUnknownTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700852 return ActivityOptions.makeCustomAnimation(mContext,
853 R.anim.recents_from_unknown_enter,
854 R.anim.recents_from_unknown_exit,
Winson3fb67562015-11-11 10:39:03 -0800855 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700856 }
857
858 /**
859 * Creates the activity options for a home->recents transition.
860 */
Winson008ee15f2016-03-18 17:17:25 -0700861 protected ActivityOptions getHomeTransitionActivityOptions() {
Winson190fe3bf2015-10-20 14:57:24 -0700862 return ActivityOptions.makeCustomAnimation(mContext,
863 R.anim.recents_from_launcher_enter,
864 R.anim.recents_from_launcher_exit,
Winson3fb67562015-11-11 10:39:03 -0800865 mHandler, null);
Winson190fe3bf2015-10-20 14:57:24 -0700866 }
867
868 /**
869 * Creates the activity options for an app->recents transition.
870 */
Jorim Jaggief496ea2017-05-13 23:16:44 +0200871 private Pair<ActivityOptions, AppTransitionAnimationSpecsFuture>
872 getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
873 Rect windowOverrideRect) {
Matthew Ngc57b7f62017-08-10 15:37:19 -0700874 final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
Winsonc4a038a2016-05-26 16:42:15 -0700875 if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700876 ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
Winson Chung3e7e4642017-08-22 10:08:06 -0700877 ArrayList<Task> tasks = mDummyStackView.getStack().getStackTasks();
878 TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
879 TaskStackViewScroller stackScroller = mDummyStackView.getScroller();
Winsone693aaf2016-03-01 12:05:59 -0800880
Winson Chung3e7e4642017-08-22 10:08:06 -0700881 mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */);
882 mDummyStackView.updateToInitialState();
Winsone693aaf2016-03-01 12:05:59 -0800883
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700884 for (int i = tasks.size() - 1; i >= 0; i--) {
885 Task task = tasks.get(i);
Winson387aac62015-11-25 11:18:56 -0800886 if (task.isFreeformTask()) {
Winsone693aaf2016-03-01 12:05:59 -0800887 mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700888 stackScroller.getStackScroll(), mTmpTransform, null,
Jiaquan He26f637b2016-12-27 14:44:14 -0800889 windowOverrideRect);
Winson Chungaa7fa012017-05-24 15:50:06 -0700890 GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700891 Rect toTaskRect = new Rect();
892 mTmpTransform.rect.round(toTaskRect);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700893 specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
894 }
895 }
896 AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
897 specs.toArray(specsArray);
Matthew Ng912c7f72017-08-02 22:12:04 +0000898
Matthew Ngc57b7f62017-08-10 15:37:19 -0700899 // For low end ram devices, wait for transition flag is reset when Recents entrance
900 // animation is complete instead of when the transition animation starts
Jorim Jaggief496ea2017-05-13 23:16:44 +0200901 return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
Matthew Ngc57b7f62017-08-10 15:37:19 -0700902 specsArray, mHandler, isLowRamDevice ? null : mResetToggleFlagListener, this),
903 null);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700904 } else {
905 // Update the destination rect
906 Task toTask = new Task();
Winson Chungf3cfa892017-04-24 12:23:47 -0700907 TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700908 windowOverrideRect);
Jorim Jaggief496ea2017-05-13 23:16:44 +0200909
910 RectF toTaskRect = toTransform.rect;
911 AppTransitionAnimationSpecsFuture future =
912 new RecentsTransitionHelper(mContext).getAppTransitionFuture(
913 () -> {
914 Rect rect = new Rect();
915 toTaskRect.round(rect);
Winson Chungaa7fa012017-05-24 15:50:06 -0700916 GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
917 toTransform);
Jorim Jaggief496ea2017-05-13 23:16:44 +0200918 return Lists.newArrayList(new AppTransitionAnimationSpec(
919 toTask.key.id, thumbnail, rect));
920 });
Matthew Ng912c7f72017-08-02 22:12:04 +0000921
Matthew Ngc57b7f62017-08-10 15:37:19 -0700922 // For low end ram devices, wait for transition flag is reset when Recents entrance
923 // animation is complete instead of when the transition animation starts
Jorim Jaggief496ea2017-05-13 23:16:44 +0200924 return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
Matthew Ngc57b7f62017-08-10 15:37:19 -0700925 mHandler, future.getFuture(), isLowRamDevice ? null : mResetToggleFlagListener,
926 false /* scaleUp */), future);
Filip Gruszczynskid64ef3e2015-10-27 17:58:02 -0700927 }
928 }
Winson190fe3bf2015-10-20 14:57:24 -0700929
Winson190fe3bf2015-10-20 14:57:24 -0700930 /**
931 * Returns the transition rect for the given task id.
932 */
Winsone693aaf2016-03-01 12:05:59 -0800933 private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
Jorim Jaggidb21bbd2016-04-18 15:32:07 -0700934 Task runningTaskOut, Rect windowOverrideRect) {
Winson190fe3bf2015-10-20 14:57:24 -0700935 // Find the running task in the TaskStack
Winsone693aaf2016-03-01 12:05:59 -0800936 TaskStack stack = stackView.getStack();
Winson65c851e2016-01-20 12:43:35 -0800937 Task launchTask = stack.getLaunchTarget();
938 if (launchTask != null) {
939 runningTaskOut.copyFrom(launchTask);
940 } else {
Winson190fe3bf2015-10-20 14:57:24 -0700941 // If no task is specified or we can not find the task just use the front most one
Winson35a8b042016-01-22 09:41:09 -0800942 launchTask = stack.getStackFrontMostTask(true /* includeFreeform */);
Winson65c851e2016-01-20 12:43:35 -0800943 runningTaskOut.copyFrom(launchTask);
Winson190fe3bf2015-10-20 14:57:24 -0700944 }
945
946 // Get the transform for the running task
Winson003eda62016-03-11 14:56:00 -0800947 stackView.updateLayoutAlgorithm(true /* boundScroll */);
Winson67c79572016-04-13 14:02:18 -0700948 stackView.updateToInitialState();
Winson7845e8c2016-03-29 10:23:19 -0700949 stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
Jiaquan He26f637b2016-12-27 14:44:14 -0800950 stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
Winson190fe3bf2015-10-20 14:57:24 -0700951 return mTmpTransform;
952 }
953
954 /**
955 * Draws the header of a task used for the window animation into a bitmap.
956 */
Winson Chungaa7fa012017-05-24 15:50:06 -0700957 private GraphicBuffer drawThumbnailTransitionBitmap(Task toTask,
958 TaskViewTransform toTransform) {
Winson8be16342016-02-09 11:53:27 -0800959 SystemServicesProxy ssp = Recents.getSystemServices();
Winson Chung91c5bb02017-07-24 13:52:49 -0700960 int width = (int) toTransform.rect.width();
961 int height = (int) toTransform.rect.height();
962 if (toTransform != null && toTask.key != null && width > 0 && height > 0) {
Winson190fe3bf2015-10-20 14:57:24 -0700963 synchronized (mHeaderBarLock) {
Winson8be16342016-02-09 11:53:27 -0800964 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
Winson Chungaa7fa012017-05-24 15:50:06 -0700965 mHeaderBar.onTaskViewSizeChanged(width, height);
Winsonc742f972015-11-12 11:32:21 -0800966 if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
Winson Chungaa7fa012017-05-24 15:50:06 -0700967 return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
968 null, 1f, 0xFFff0000);
Winson190fe3bf2015-10-20 14:57:24 -0700969 } else {
Winson22574af2016-03-23 19:00:28 -0700970 // Workaround for b/27815919, reset the callback so that we do not trigger an
971 // invalidate on the header bar as a result of updating the icon
972 Drawable icon = mHeaderBar.getIconView().getDrawable();
973 if (icon != null) {
974 icon.setCallback(null);
975 }
Winsond2a03062016-04-15 11:19:07 -0700976 mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
Winson8be16342016-02-09 11:53:27 -0800977 disabledInSafeMode);
Winsond2a03062016-04-15 11:19:07 -0700978 mHeaderBar.onTaskDataLoaded();
Winsone693aaf2016-03-01 12:05:59 -0800979 mHeaderBar.setDimAlpha(toTransform.dimAlpha);
Winson Chungaa7fa012017-05-24 15:50:06 -0700980 return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
981 mHeaderBar, 1f, 0);
Winson190fe3bf2015-10-20 14:57:24 -0700982 }
983 }
Winson190fe3bf2015-10-20 14:57:24 -0700984 }
985 return null;
986 }
987
988 /**
989 * Shows the recents activity
990 */
Winsond46b7272016-04-20 11:54:27 -0700991 protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
992 boolean isHomeStackVisible, boolean animate, int growTarget) {
Winsone7f138c2015-10-22 16:15:21 -0700993 RecentsTaskLoader loader = Recents.getTaskLoader();
Winsonc5b12dd2016-03-23 20:25:12 -0700994 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
Winsonfc73eec2016-08-01 15:48:34 -0700995 SystemServicesProxy ssp = Recents.getSystemServices();
996 boolean isBlacklisted = (runningTask != null)
997 ? ssp.isBlackListedActivity(runningTask.baseActivity.getClassName())
998 : false;
Winson190fe3bf2015-10-20 14:57:24 -0700999
Winsonfc73eec2016-08-01 15:48:34 -07001000 int runningTaskId = !mLaunchedWhileDocking && !isBlacklisted && (runningTask != null)
Winsonc4a038a2016-05-26 16:42:15 -07001001 ? runningTask.id
1002 : -1;
1003
Winsone5f1faa2015-11-20 12:26:23 -08001004 // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
Jorim Jaggi435b2e42015-11-24 15:09:30 -08001005 // should always preload the tasks now. If we are dragging in recents, reload them as
1006 // the stacks might have changed.
Winsonfc73eec2016-08-01 15:48:34 -07001007 if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
Winsone5f1faa2015-11-20 12:26:23 -08001008 // Create a new load plan if preloadRecents() was never triggered
Winson190fe3bf2015-10-20 14:57:24 -07001009 sInstanceLoadPlan = loader.createLoadPlan(mContext);
1010 }
Jorim Jaggie161f082016-02-05 14:26:16 -08001011 if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
Winsonc4a038a2016-05-26 16:42:15 -07001012 loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible);
Winson190fe3bf2015-10-20 14:57:24 -07001013 }
Winsonc5b12dd2016-03-23 20:25:12 -07001014
Winson190fe3bf2015-10-20 14:57:24 -07001015 TaskStack stack = sInstanceLoadPlan.getTaskStack();
Winsonc5b12dd2016-03-23 20:25:12 -07001016 boolean hasRecentTasks = stack.getTaskCount() > 0;
Winsonfc73eec2016-08-01 15:48:34 -07001017 boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
1018 hasRecentTasks;
Winson190fe3bf2015-10-20 14:57:24 -07001019
Winsonaeb298c2016-04-05 13:08:11 -07001020 // Update the launch state that we need in updateHeaderBarLayout()
Jorim Jaggic5887ea2016-05-13 18:21:48 -07001021 launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
Winsonaeb298c2016-04-05 13:08:11 -07001022 launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
Winsonfc73eec2016-08-01 15:48:34 -07001023 launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted;
Winson Chungb5026902017-05-03 12:45:13 -07001024 launchState.launchedFromPipApp = false;
1025 launchState.launchedWithNextPipApp =
1026 stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
Winsonaeb298c2016-04-05 13:08:11 -07001027 launchState.launchedViaDockGesture = mLaunchedWhileDocking;
1028 launchState.launchedViaDragGesture = mDraggingInRecents;
Winsonc4a038a2016-05-26 16:42:15 -07001029 launchState.launchedToTaskId = runningTaskId;
Winsonaeb298c2016-04-05 13:08:11 -07001030 launchState.launchedWithAltTab = mTriggeredFromAltTab;
1031
Matthew Ng912c7f72017-08-02 22:12:04 +00001032 // Disable toggling of recents between starting the activity and it is visible and the app
1033 // has started its transition into recents.
1034 setWaitingForTransitionStart(useThumbnailTransition);
1035
Winsonaeb298c2016-04-05 13:08:11 -07001036 // Preload the icon (this will be a null-op if we have preloaded the icon already in
1037 // preloadRecents())
Winsonc4a038a2016-05-26 16:42:15 -07001038 preloadIcon(runningTaskId);
Winsonaeb298c2016-04-05 13:08:11 -07001039
Winsonf0d1c442015-12-01 11:04:45 -08001040 // Update the header bar if necessary
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001041 Rect windowOverrideRect = getWindowRectOverride(growTarget);
1042 updateHeaderBarLayout(stack, windowOverrideRect);
Winsonf0d1c442015-12-01 11:04:45 -08001043
Winson190fe3bf2015-10-20 14:57:24 -07001044 // Prepare the dummy stack for the transition
Winson Chung3e7e4642017-08-22 10:08:06 -07001045 TaskStackLayoutAlgorithm.VisibilityReport stackVr =
1046 mDummyStackView.computeStackVisibilityReport();
Jorim Jaggibb42a462015-11-20 16:27:16 -08001047
Winsonaeb298c2016-04-05 13:08:11 -07001048 // Update the remaining launch state
Winsonc5b12dd2016-03-23 20:25:12 -07001049 launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
1050 launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
Winsonc5b12dd2016-03-23 20:25:12 -07001051
Jorim Jaggibb42a462015-11-20 16:27:16 -08001052 if (!animate) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001053 startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
1054 null /* future */);
Jorim Jaggibb42a462015-11-20 16:27:16 -08001055 return;
1056 }
1057
Jorim Jaggief496ea2017-05-13 23:16:44 +02001058 Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
Winsonfc73eec2016-08-01 15:48:34 -07001059 if (isBlacklisted) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001060 pair = new Pair<>(getUnknownTransitionActivityOptions(), null);
Winsonfc73eec2016-08-01 15:48:34 -07001061 } else if (useThumbnailTransition) {
Winson190fe3bf2015-10-20 14:57:24 -07001062 // Try starting with a thumbnail transition
Jorim Jaggief496ea2017-05-13 23:16:44 +02001063 pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
Winsonaeb298c2016-04-05 13:08:11 -07001064 } else {
Winson190fe3bf2015-10-20 14:57:24 -07001065 // If there is no thumbnail transition, but is launching from home into recents, then
Winson008ee15f2016-03-18 17:17:25 -07001066 // use a quick home transition
Jorim Jaggief496ea2017-05-13 23:16:44 +02001067 pair = new Pair<>(hasRecentTasks
1068 ? getHomeTransitionActivityOptions()
1069 : getUnknownTransitionActivityOptions(), null);
Winson190fe3bf2015-10-20 14:57:24 -07001070 }
Jorim Jaggief496ea2017-05-13 23:16:44 +02001071 startRecentsActivity(pair.first, pair.second);
Winson190fe3bf2015-10-20 14:57:24 -07001072 mLastToggleTime = SystemClock.elapsedRealtime();
1073 }
1074
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001075 private Rect getWindowRectOverride(int growTarget) {
1076 if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001077 return SystemServicesProxy.getInstance(mContext).getWindowRect();
Jorim Jaggidb21bbd2016-04-18 15:32:07 -07001078 }
1079 Rect result = new Rect();
1080 Rect displayRect = Recents.getSystemServices().getDisplayRect();
1081 DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
1082 result, displayRect.width(), displayRect.height(),
1083 Recents.getSystemServices().getDockedDividerSize(mContext));
1084 return result;
1085 }
1086
Winson190fe3bf2015-10-20 14:57:24 -07001087 /**
1088 * Starts the recents activity.
1089 */
Jorim Jaggief496ea2017-05-13 23:16:44 +02001090 private void startRecentsActivity(ActivityOptions opts,
1091 final AppTransitionAnimationSpecsFuture future) {
Winson190fe3bf2015-10-20 14:57:24 -07001092 Intent intent = new Intent();
Sid Soundararajan4bdb6872016-03-18 13:42:10 -07001093 intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
Winson190fe3bf2015-10-20 14:57:24 -07001094 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1095 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
1096 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Winson Chungb5026902017-05-03 12:45:13 -07001097 HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
1098 hideMenuEvent.addPostAnimationCallback(() -> {
Jorim Jaggief496ea2017-05-13 23:16:44 +02001099 Recents.getSystemServices().startActivityAsUserAsync(intent, opts);
Winson Chungb5026902017-05-03 12:45:13 -07001100 EventBus.getDefault().send(new RecentsActivityStartingEvent());
Jorim Jaggief496ea2017-05-13 23:16:44 +02001101 if (future != null) {
1102 future.precacheSpecs();
1103 }
Winson Chungb5026902017-05-03 12:45:13 -07001104 });
1105 EventBus.getDefault().send(hideMenuEvent);
Winson190fe3bf2015-10-20 14:57:24 -07001106 }
1107
Winson3fb67562015-11-11 10:39:03 -08001108 /**** OnAnimationFinishedListener Implementation ****/
Filip Gruszczynski1a5203d2015-10-29 17:43:49 -07001109
1110 @Override
1111 public void onAnimationFinished() {
1112 EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
1113 }
Winson190fe3bf2015-10-20 14:57:24 -07001114}