blob: f1550a09b2e2c8756aafbdd73818ae9abc212ad4 [file] [log] [blame]
Winson Chung7048fea2014-03-18 12:21:24 -07001/*
2 * Copyright (C) 2014 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
Jim Millera237a312014-11-06 18:05:59 -080019import android.app.Activity;
Winson Chung7048fea2014-03-18 12:21:24 -070020import android.app.ActivityManager;
21import android.app.ActivityOptions;
Winson Chung740c3ac2014-11-12 16:14:38 -080022import android.app.ITaskStackListener;
Winson Chung7048fea2014-03-18 12:21:24 -070023import android.content.ActivityNotFoundException;
Jim Millera237a312014-11-06 18:05:59 -080024import android.content.BroadcastReceiver;
Winson Chung7048fea2014-03-18 12:21:24 -070025import android.content.Context;
26import android.content.Intent;
Winson Chung2002cf52014-12-08 17:26:44 -080027import android.content.IntentFilter;
Winson Chung7048fea2014-03-18 12:21:24 -070028import android.content.res.Configuration;
Winson Chungdcfa7972014-07-22 12:27:13 -070029import android.content.res.Resources;
Winson Chung7048fea2014-03-18 12:21:24 -070030import android.graphics.Bitmap;
31import android.graphics.Canvas;
Winson Chung7048fea2014-03-18 12:21:24 -070032import android.graphics.Rect;
Jorim Jaggi900fb482015-06-02 15:07:33 -070033import android.os.AsyncTask;
Jim Millera237a312014-11-06 18:05:59 -080034import android.os.Handler;
Winson Chung15a2ba82014-11-18 11:19:24 -080035import android.os.SystemClock;
Winson Chung7048fea2014-03-18 12:21:24 -070036import android.os.UserHandle;
Winson Chunge1e20e12015-06-02 14:11:49 -070037import android.util.MutableBoolean;
Jorim Jaggid61f2272014-12-19 20:35:35 +010038import android.view.Display;
Winson Chunga4ccb862014-08-22 15:26:27 -070039import android.view.LayoutInflater;
Winson Chung7048fea2014-03-18 12:21:24 -070040import android.view.View;
Winson Chung5c9f4b92015-06-25 16:16:46 -070041
42import com.android.internal.logging.MetricsLogger;
Winson Chungaf3bb692015-06-03 17:31:39 -070043import com.android.systemui.Prefs;
Winson Chung7048fea2014-03-18 12:21:24 -070044import com.android.systemui.R;
Winson Chung9214eff2014-06-12 13:59:25 -070045import com.android.systemui.RecentsComponent;
Jorim Jaggid61f2272014-12-19 20:35:35 +010046import com.android.systemui.SystemUI;
Winson Chungd0aa8a32015-03-19 14:11:27 -070047import com.android.systemui.SystemUIApplication;
Winson Chungffa2ec62014-07-03 15:54:42 -070048import com.android.systemui.recents.misc.Console;
49import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chunga91c2932014-11-07 15:02:38 -080050import com.android.systemui.recents.model.RecentsTaskLoadPlan;
Winson Chungffa2ec62014-07-03 15:54:42 -070051import com.android.systemui.recents.model.RecentsTaskLoader;
52import com.android.systemui.recents.model.Task;
Winson Chungb1f74992014-08-08 12:53:09 -070053import com.android.systemui.recents.model.TaskGrouping;
Winson Chungffa2ec62014-07-03 15:54:42 -070054import com.android.systemui.recents.model.TaskStack;
55import com.android.systemui.recents.views.TaskStackView;
56import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
Winson Chunga4ccb862014-08-22 15:26:27 -070057import com.android.systemui.recents.views.TaskViewHeader;
Winson Chungffa2ec62014-07-03 15:54:42 -070058import com.android.systemui.recents.views.TaskViewTransform;
Winson Chungd0aa8a32015-03-19 14:11:27 -070059import com.android.systemui.statusbar.phone.PhoneStatusBar;
Winson Chung7048fea2014-03-18 12:21:24 -070060
Winson Chungffa2ec62014-07-03 15:54:42 -070061import java.util.ArrayList;
Winson Chung7048fea2014-03-18 12:21:24 -070062
Winson Chung2002cf52014-12-08 17:26:44 -080063/**
64 * Annotation for a method that is only called from the primary user's SystemUI process and will be
65 * proxied to the current user.
66 */
67@interface ProxyFromPrimaryToCurrentUser {}
68/**
69 * Annotation for a method that may be called from any user's SystemUI process and will be proxied
70 * to the primary user.
71 */
72@interface ProxyFromAnyToPrimaryUser {}
Winson Chung740c3ac2014-11-12 16:14:38 -080073
Winson Chung7048fea2014-03-18 12:21:24 -070074/** A proxy implementation for the recents component */
Jorim Jaggid61f2272014-12-19 20:35:35 +010075public class Recents extends SystemUI
76 implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
Winson Chung7048fea2014-03-18 12:21:24 -070077
Winson Chung2002cf52014-12-08 17:26:44 -080078 final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
79 final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
80 final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
81
82 // Owner proxy events
83 final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER =
84 "action_notify_recents_visibility_change";
Winson Chungd0aa8a32015-03-19 14:11:27 -070085 final public static String ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER =
86 "action_screen_pinning_request";
Winson Chungcdcd4872014-08-05 18:00:13 -070087
Winson Chungb0a28ea2014-10-28 15:21:35 -070088 final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
Winson Chungcdcd4872014-08-05 18:00:13 -070089 final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
90 final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
Winson Chungb44c24f2014-04-09 15:17:43 -070091
Winson Chungbf8871d2014-08-28 20:57:16 -070092 final static int sMinToggleDelay = 350;
Winson Chung7048fea2014-03-18 12:21:24 -070093
Winson Chung2cf8b222015-01-20 11:44:05 -080094 public final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
95 public final static String sRecentsPackage = "com.android.systemui";
96 public final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
Winson Chung7048fea2014-03-18 12:21:24 -070097
Winson Chung740c3ac2014-11-12 16:14:38 -080098 /**
99 * An implementation of ITaskStackListener, that allows us to listen for changes to the system
100 * task stacks and update recents accordingly.
101 */
Winson Chung2002cf52014-12-08 17:26:44 -0800102 class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
103 Handler mHandler;
104
105 public TaskStackListenerImpl(Handler handler) {
106 mHandler = handler;
107 }
108
Winson Chung740c3ac2014-11-12 16:14:38 -0800109 @Override
110 public void onTaskStackChanged() {
Winson Chung2002cf52014-12-08 17:26:44 -0800111 // Debounce any task stack changes
112 mHandler.removeCallbacks(this);
113 mHandler.post(this);
114 }
115
116 /** Preloads the next task */
117 public void run() {
Winson Chungd16c5652015-01-26 16:11:07 -0800118 // Temporarily skip this if multi stack is enabled
119 if (mConfig.multiStackEnabled) return;
120
Winson Chung740c3ac2014-11-12 16:14:38 -0800121 RecentsConfiguration config = RecentsConfiguration.getInstance();
122 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Winson Chung2cf8b222015-01-20 11:44:05 -0800123 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
124 SystemServicesProxy ssp = loader.getSystemServicesProxy();
125 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
Winson Chung0eae5572014-12-11 11:04:19 -0800126
Winson Chung740c3ac2014-11-12 16:14:38 -0800127 // Load the next task only if we aren't svelte
Winson Chung740c3ac2014-11-12 16:14:38 -0800128 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
129 loader.preloadTasks(plan, true /* isTopTaskHome */);
130 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chung0eae5572014-12-11 11:04:19 -0800131 // This callback is made when a new activity is launched and the old one is paused
132 // so ignore the current activity and try and preload the thumbnail for the
133 // previous one.
134 if (runningTaskInfo != null) {
135 launchOpts.runningTaskId = runningTaskInfo.id;
136 }
137 launchOpts.numVisibleTasks = 2;
138 launchOpts.numVisibleTaskThumbnails = 2;
Winson Chung740c3ac2014-11-12 16:14:38 -0800139 launchOpts.onlyLoadForCache = true;
Winson Chung0eae5572014-12-11 11:04:19 -0800140 launchOpts.onlyLoadPausedActivities = true;
Winson Chung740c3ac2014-11-12 16:14:38 -0800141 loader.loadTasks(mContext, plan, launchOpts);
142 }
143 }
144 }
145
Winson Chung2002cf52014-12-08 17:26:44 -0800146 /**
147 * A proxy for Recents events which happens strictly for the owner.
148 */
149 class RecentsOwnerEventProxyReceiver extends BroadcastReceiver {
150 @Override
151 public void onReceive(Context context, Intent intent) {
152 switch (intent.getAction()) {
153 case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
154 visibilityChanged(intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
155 break;
Winson Chungd0aa8a32015-03-19 14:11:27 -0700156 case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
157 onStartScreenPinning(context);
158 break;
Winson Chung2002cf52014-12-08 17:26:44 -0800159 }
160 }
161 }
162
Winson Chung9214eff2014-06-12 13:59:25 -0700163 static RecentsComponent.Callbacks sRecentsComponentCallbacks;
Winson Chunga91c2932014-11-07 15:02:38 -0800164 static RecentsTaskLoadPlan sInstanceLoadPlan;
Jorim Jaggid61f2272014-12-19 20:35:35 +0100165 static Recents sInstance;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700166
Winson Chung48a10a52014-08-27 14:36:51 -0700167 LayoutInflater mInflater;
Winson Chunga10370f2014-04-02 12:25:04 -0700168 SystemServicesProxy mSystemServicesProxy;
Winson Chungb0a28ea2014-10-28 15:21:35 -0700169 Handler mHandler;
Winson Chung740c3ac2014-11-12 16:14:38 -0800170 TaskStackListenerImpl mTaskStackListener;
Winson Chung2002cf52014-12-08 17:26:44 -0800171 RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
Winson Chungaf3bb692015-06-03 17:31:39 -0700172 RecentsAppWidgetHost mAppWidgetHost;
Winson Chung85cfec82014-07-14 14:16:04 -0700173 boolean mBootCompleted;
Winson Chungb0a28ea2014-10-28 15:21:35 -0700174 boolean mStartAnimationTriggered;
175 boolean mCanReuseTaskStackViews = true;
Winson Chungffa2ec62014-07-03 15:54:42 -0700176
177 // Task launching
178 RecentsConfiguration mConfig;
Winson Chungdcfa7972014-07-22 12:27:13 -0700179 Rect mWindowRect = new Rect();
180 Rect mTaskStackBounds = new Rect();
181 Rect mSystemInsets = new Rect();
Winson Chungffa2ec62014-07-03 15:54:42 -0700182 TaskViewTransform mTmpTransform = new TaskViewTransform();
183 int mStatusBarHeight;
Winson Chungdcfa7972014-07-22 12:27:13 -0700184 int mNavBarHeight;
185 int mNavBarWidth;
Winson Chung7048fea2014-03-18 12:21:24 -0700186
Winson Chunga4ccb862014-08-22 15:26:27 -0700187 // Header (for transition)
188 TaskViewHeader mHeaderBar;
Jorim Jaggi900fb482015-06-02 15:07:33 -0700189 final Object mHeaderBarLock = new Object();
Winson Chunga4ccb862014-08-22 15:26:27 -0700190 TaskStackView mDummyStackView;
191
Winson Chung1e8d71b2014-05-16 17:05:22 -0700192 // Variables to keep track of if we need to start recents after binding
Winson Chung1e8d71b2014-05-16 17:05:22 -0700193 boolean mTriggeredFromAltTab;
Winson Chung7048fea2014-03-18 12:21:24 -0700194 long mLastToggleTime;
195
Jorim Jaggi900fb482015-06-02 15:07:33 -0700196 Bitmap mThumbnailTransitionBitmapCache;
197 Task mThumbnailTransitionBitmapCacheKey;
198
Jorim Jaggid61f2272014-12-19 20:35:35 +0100199 public Recents() {
200 }
Winson Chung740c3ac2014-11-12 16:14:38 -0800201
Jorim Jaggid61f2272014-12-19 20:35:35 +0100202 /**
203 * Gets the singleton instance and starts it if needed. On the primary user on the device, this
204 * component gets started as a normal {@link SystemUI} component. On a secondary user, this
205 * lifecycle doesn't exist, so we need to start it manually here if needed.
206 */
207 public static Recents getInstanceAndStartIfNeeded(Context ctx) {
208 if (sInstance == null) {
209 sInstance = new Recents();
210 sInstance.mContext = ctx;
211 sInstance.start();
212 sInstance.onBootCompleted();
Winson Chung2002cf52014-12-08 17:26:44 -0800213 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100214 return sInstance;
Winson Chung7048fea2014-03-18 12:21:24 -0700215 }
216
Winson Chung2002cf52014-12-08 17:26:44 -0800217 /** Creates a new broadcast intent */
218 static Intent createLocalBroadcastIntent(Context context, String action) {
219 Intent intent = new Intent(action);
220 intent.setPackage(context.getPackageName());
221 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
222 Intent.FLAG_RECEIVER_FOREGROUND);
223 return intent;
224 }
225
226 /** Initializes the Recents. */
227 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100228 @Override
229 public void start() {
230 if (sInstance == null) {
231 sInstance = this;
232 }
233 RecentsTaskLoader.initialize(mContext);
234 mInflater = LayoutInflater.from(mContext);
235 mSystemServicesProxy = new SystemServicesProxy(mContext);
236 mHandler = new Handler();
237 mTaskStackBounds = new Rect();
Winson Chungaf3bb692015-06-03 17:31:39 -0700238 mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
Jorim Jaggid61f2272014-12-19 20:35:35 +0100239
240 // Register the task stack listener
241 mTaskStackListener = new TaskStackListenerImpl(mHandler);
242 mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
243
244 // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
245 // instances of AlternateRecentsComponent needs to notify the owner when the visibility
246 // changes.
247 if (mSystemServicesProxy.isForegroundUserOwner()) {
248 mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
249 IntentFilter filter = new IntentFilter();
250 filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
Winson Chungd0aa8a32015-03-19 14:11:27 -0700251 filter.addAction(Recents.ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
Jorim Jaggid61f2272014-12-19 20:35:35 +0100252 mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter,
253 null, mHandler);
254 }
255
Winson Chung48a10a52014-08-27 14:36:51 -0700256 // Initialize some static datastructures
257 TaskStackViewLayoutAlgorithm.initializeCurve();
258 // Load the header bar layout
Winson Chungaf3bb692015-06-03 17:31:39 -0700259 reloadHeaderBarLayout();
Winson Chungbac679b2014-10-20 14:11:12 -0700260
Winson Chung90d51362014-11-13 14:30:26 -0800261 // When we start, preload the data associated with the previous recent tasks.
Winson Chunga91c2932014-11-07 15:02:38 -0800262 // We can use a new plan since the caches will be the same.
263 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
264 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
265 loader.preloadTasks(plan, true /* isTopTaskHome */);
266 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
267 launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
Winson Chung90d51362014-11-13 14:30:26 -0800268 launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
269 launchOpts.onlyLoadForCache = true;
Winson Chunga91c2932014-11-07 15:02:38 -0800270 loader.loadTasks(mContext, plan, launchOpts);
Jorim Jaggid61f2272014-12-19 20:35:35 +0100271 putComponent(Recents.class, this);
Winson Chung5abdceb2014-06-05 10:58:05 -0700272 }
273
Jorim Jaggid61f2272014-12-19 20:35:35 +0100274 @Override
Winson Chung8bf05af2014-09-29 13:42:49 -0700275 public void onBootCompleted() {
276 mBootCompleted = true;
277 }
278
Winson Chung2002cf52014-12-08 17:26:44 -0800279 /** Shows the Recents. */
280 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100281 @Override
282 public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
Winson Chung2002cf52014-12-08 17:26:44 -0800283 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100284 showRecentsInternal(triggeredFromAltTab);
Winson Chung2002cf52014-12-08 17:26:44 -0800285 } else {
286 Intent intent = createLocalBroadcastIntent(mContext,
287 RecentsUserEventProxyReceiver.ACTION_PROXY_SHOW_RECENTS_TO_USER);
288 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
289 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
290 }
291 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100292
293 void showRecentsInternal(boolean triggeredFromAltTab) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700294 mTriggeredFromAltTab = triggeredFromAltTab;
Winson Chung7048fea2014-03-18 12:21:24 -0700295
296 try {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700297 startRecentsActivity();
298 } catch (ActivityNotFoundException e) {
299 Console.logRawError("Failed to launch RecentAppsIntent", e);
300 }
301 }
302
Winson Chung2002cf52014-12-08 17:26:44 -0800303 /** Hides the Recents. */
304 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100305 @Override
306 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung2002cf52014-12-08 17:26:44 -0800307 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100308 hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
Winson Chung2002cf52014-12-08 17:26:44 -0800309 } else {
310 Intent intent = createLocalBroadcastIntent(mContext,
311 RecentsUserEventProxyReceiver.ACTION_PROXY_HIDE_RECENTS_TO_USER);
312 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
313 intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
314 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
315 }
316 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100317
318 void hideRecentsInternal(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700319 if (mBootCompleted) {
Winson Chungfe855d12015-06-17 16:03:54 -0700320 // Defer to the activity to handle hiding recents, if it handles it, then it must still
321 // be visible
322 Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
323 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
324 intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
325 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700326 }
327 }
328
Winson Chung2002cf52014-12-08 17:26:44 -0800329 /** Toggles the Recents activity. */
330 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100331 @Override
332 public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
Winson Chung2002cf52014-12-08 17:26:44 -0800333 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100334 toggleRecentsInternal();
Winson Chung2002cf52014-12-08 17:26:44 -0800335 } else {
336 Intent intent = createLocalBroadcastIntent(mContext,
337 RecentsUserEventProxyReceiver.ACTION_PROXY_TOGGLE_RECENTS_TO_USER);
338 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
339 }
340 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100341
342 void toggleRecentsInternal() {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700343 mTriggeredFromAltTab = false;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700344
345 try {
346 toggleRecentsActivity();
Winson Chung7048fea2014-03-18 12:21:24 -0700347 } catch (ActivityNotFoundException e) {
348 Console.logRawError("Failed to launch RecentAppsIntent", e);
349 }
350 }
351
Winson Chung2002cf52014-12-08 17:26:44 -0800352 /** Preloads info for the Recents activity. */
353 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100354 @Override
355 public void preloadRecents() {
Winson Chung2002cf52014-12-08 17:26:44 -0800356 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100357 preloadRecentsInternal();
Winson Chung2002cf52014-12-08 17:26:44 -0800358 } else {
359 Intent intent = createLocalBroadcastIntent(mContext,
360 RecentsUserEventProxyReceiver.ACTION_PROXY_PRELOAD_RECENTS_TO_USER);
361 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
362 }
363 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100364
365 void preloadRecentsInternal() {
Winson Chunga91c2932014-11-07 15:02:38 -0800366 // Preload only the raw task list into a new load plan (which will be consumed by the
Winson Chunga278cae2015-06-09 13:51:13 -0700367 // RecentsActivity) only if there is a task to animate to.
Jorim Jaggi900fb482015-06-02 15:07:33 -0700368 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
369 MutableBoolean topTaskHome = new MutableBoolean(true);
Winson Chunga91c2932014-11-07 15:02:38 -0800370 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
371 sInstanceLoadPlan = loader.createLoadPlan(mContext);
Jorim Jaggi900fb482015-06-02 15:07:33 -0700372 if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
373 sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
374 loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
375 TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
Winson Chunga278cae2015-06-09 13:51:13 -0700376 if (top.getTaskCount() > 0) {
377 preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
378 topTaskHome.value);
379 }
Winson Chunge1e20e12015-06-02 14:11:49 -0700380 }
Winson Chung7048fea2014-03-18 12:21:24 -0700381 }
382
Jorim Jaggid61f2272014-12-19 20:35:35 +0100383 @Override
384 public void cancelPreloadingRecents() {
Winson Chung7048fea2014-03-18 12:21:24 -0700385 // Do nothing
386 }
387
Winson Chungb1f74992014-08-08 12:53:09 -0700388 void showRelativeAffiliatedTask(boolean showNextTask) {
Winson Chungd16c5652015-01-26 16:11:07 -0800389 // Return early if there is no focused stack
390 int focusedStackId = mSystemServicesProxy.getFocusedStack();
391 TaskStack focusedStack = null;
Winson Chungebfc6982014-08-26 12:25:34 -0700392 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
Winson Chunga91c2932014-11-07 15:02:38 -0800393 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
394 loader.preloadTasks(plan, true /* isTopTaskHome */);
Winson Chungd16c5652015-01-26 16:11:07 -0800395 if (mConfig.multiStackEnabled) {
396 if (focusedStackId < 0) return;
397 focusedStack = plan.getTaskStack(focusedStackId);
398 } else {
399 focusedStack = plan.getAllTaskStacks().get(0);
400 }
Winson Chunga91c2932014-11-07 15:02:38 -0800401
Winson Chungd16c5652015-01-26 16:11:07 -0800402 // Return early if there are no tasks in the focused stack
Winson Chung98127fb2015-03-23 15:12:53 -0700403 if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
Winson Chungb1f74992014-08-08 12:53:09 -0700404
Winson Chung2cf8b222015-01-20 11:44:05 -0800405 ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
Winson Chungbc571a92014-11-19 17:09:03 -0800406 // Return early if there is no running task (can't determine affiliated tasks in this case)
407 if (runningTask == null) return;
Winson Chungb1f74992014-08-08 12:53:09 -0700408 // Return early if the running task is in the home stack (optimization)
409 if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
410
411 // Find the task in the recents list
Winson Chungd16c5652015-01-26 16:11:07 -0800412 ArrayList<Task> tasks = focusedStack.getTasks();
Winson Chungb1f74992014-08-08 12:53:09 -0700413 Task toTask = null;
414 ActivityOptions launchOpts = null;
415 int taskCount = tasks.size();
Winson Chung044d5292014-11-06 11:05:19 -0800416 int numAffiliatedTasks = 0;
Winson Chungb1f74992014-08-08 12:53:09 -0700417 for (int i = 0; i < taskCount; i++) {
418 Task task = tasks.get(i);
419 if (task.key.id == runningTask.id) {
420 TaskGrouping group = task.group;
421 Task.TaskKey toTaskKey;
422 if (showNextTask) {
423 toTaskKey = group.getNextTaskInGroup(task);
Winson Chung3aa9c222014-09-09 17:54:51 +0200424 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
425 R.anim.recents_launch_next_affiliated_task_target,
426 R.anim.recents_launch_next_affiliated_task_source);
Winson Chungb1f74992014-08-08 12:53:09 -0700427 } else {
428 toTaskKey = group.getPrevTaskInGroup(task);
Winson Chung3aa9c222014-09-09 17:54:51 +0200429 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
430 R.anim.recents_launch_prev_affiliated_task_target,
431 R.anim.recents_launch_prev_affiliated_task_source);
Winson Chungb1f74992014-08-08 12:53:09 -0700432 }
433 if (toTaskKey != null) {
Winson Chungd16c5652015-01-26 16:11:07 -0800434 toTask = focusedStack.findTaskWithId(toTaskKey.id);
Winson Chungb1f74992014-08-08 12:53:09 -0700435 }
Winson Chung044d5292014-11-06 11:05:19 -0800436 numAffiliatedTasks = group.getTaskCount();
Winson Chungb1f74992014-08-08 12:53:09 -0700437 break;
438 }
439 }
440
441 // Return early if there is no next task
442 if (toTask == null) {
Winson Chung044d5292014-11-06 11:05:19 -0800443 if (numAffiliatedTasks > 1) {
444 if (showNextTask) {
445 mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
446 ActivityOptions.makeCustomInPlaceAnimation(mContext,
447 R.anim.recents_launch_next_affiliated_task_bounce));
448 } else {
449 mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
450 ActivityOptions.makeCustomInPlaceAnimation(mContext,
451 R.anim.recents_launch_prev_affiliated_task_bounce));
452 }
Winson Chung3aa9c222014-09-09 17:54:51 +0200453 }
Winson Chungb1f74992014-08-08 12:53:09 -0700454 return;
455 }
456
Winson Chung5c9f4b92015-06-25 16:16:46 -0700457 // Keep track of actually launched affiliated tasks
458 MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
459
Winson Chungb1f74992014-08-08 12:53:09 -0700460 // Launch the task
461 if (toTask.isActive) {
462 // Bring an active task to the foreground
463 mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
464 } else {
Winson Chung4e96eb72014-09-17 15:16:09 +0200465 mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
466 toTask.activityLabel, launchOpts);
Winson Chungb1f74992014-08-08 12:53:09 -0700467 }
468 }
469
Jorim Jaggid61f2272014-12-19 20:35:35 +0100470 @Override
471 public void showNextAffiliatedTask() {
Winson Chung5c9f4b92015-06-25 16:16:46 -0700472 // Keep track of when the affiliated task is triggered
473 MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
Winson Chungb1f74992014-08-08 12:53:09 -0700474 showRelativeAffiliatedTask(true);
475 }
476
Jorim Jaggid61f2272014-12-19 20:35:35 +0100477 @Override
478 public void showPrevAffiliatedTask() {
Winson Chung5c9f4b92015-06-25 16:16:46 -0700479 // Keep track of when the affiliated task is triggered
480 MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
Winson Chungb1f74992014-08-08 12:53:09 -0700481 showRelativeAffiliatedTask(false);
482 }
483
Winson Chung2002cf52014-12-08 17:26:44 -0800484 /** Updates on configuration change. */
485 @ProxyFromPrimaryToCurrentUser
Winson Chung7048fea2014-03-18 12:21:24 -0700486 public void onConfigurationChanged(Configuration newConfig) {
Winson Chung2002cf52014-12-08 17:26:44 -0800487 if (mSystemServicesProxy.isForegroundUserOwner()) {
488 configurationChanged();
489 } else {
490 Intent intent = createLocalBroadcastIntent(mContext,
491 RecentsUserEventProxyReceiver.ACTION_PROXY_CONFIG_CHANGE_TO_USER);
492 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
493 }
494 }
495 void configurationChanged() {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700496 // Don't reuse task stack views if the configuration changes
497 mCanReuseTaskStackViews = false;
Winson Chungbf8871d2014-08-28 20:57:16 -0700498 // Reload the header bar layout
Winson Chungaf3bb692015-06-03 17:31:39 -0700499 reloadHeaderBarLayout();
Winson Chung48a10a52014-08-27 14:36:51 -0700500 }
501
502 /** Prepares the header bar layout. */
Winson Chungaf3bb692015-06-03 17:31:39 -0700503 void reloadHeaderBarLayout() {
Winson Chung48a10a52014-08-27 14:36:51 -0700504 Resources res = mContext.getResources();
505 mWindowRect = mSystemServicesProxy.getWindowRect();
506 mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
507 mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
508 mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
Jason Monk56e09b42014-07-18 10:29:14 -0400509 mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
Winson Chung6057c912014-07-08 14:39:08 -0700510 mConfig.updateOnConfigurationChange();
Winson Chungaf3bb692015-06-03 17:31:39 -0700511 Rect searchBarBounds = new Rect();
512 // Try and pre-emptively bind the search widget on startup to ensure that we
513 // have the right thumbnail bounds to animate to.
514 // Note: We have to reload the widget id before we get the task stack bounds below
515 if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
516 mConfig.getSearchBarBounds(mWindowRect.width(), mWindowRect.height(),
517 mStatusBarHeight, searchBarBounds);
Winson Chung2002cf52014-12-08 17:26:44 -0800518 }
Winson Chungd16c5652015-01-26 16:11:07 -0800519 mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
Winson Chungaf3bb692015-06-03 17:31:39 -0700520 mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
Winson Chungd16c5652015-01-26 16:11:07 -0800521 mTaskStackBounds);
Winson Chung27acf762014-09-05 17:24:20 +0200522 if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
Winson Chungdcfa7972014-07-22 12:27:13 -0700523 mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
524 } else {
525 mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
526 }
Winson Chunga4ccb862014-08-22 15:26:27 -0700527
Winson Chunga4ccb862014-08-22 15:26:27 -0700528 // Inflate the header bar layout so that we can rebind and draw it for the transition
Winson Chunga4ccb862014-08-22 15:26:27 -0700529 TaskStack stack = new TaskStack();
530 mDummyStackView = new TaskStackView(mContext, stack);
531 TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
532 Rect taskStackBounds = new Rect(mTaskStackBounds);
533 taskStackBounds.bottom -= mSystemInsets.bottom;
534 algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
535 Rect taskViewSize = algo.getUntransformedTaskViewSize();
536 int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
Jorim Jaggi900fb482015-06-02 15:07:33 -0700537 synchronized (mHeaderBarLock) {
538 mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
539 false);
540 mHeaderBar.measure(
541 View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
542 View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
543 mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
544 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700545 }
546
Winson Chung1e8d71b2014-05-16 17:05:22 -0700547 /** Toggles the recents activity */
548 void toggleRecentsActivity() {
Winson Chung7048fea2014-03-18 12:21:24 -0700549 // If the user has toggled it too quickly, then just eat up the event here (it's better than
550 // showing a janky screenshot).
551 // NOTE: Ideally, the screenshot mechanism would take the window transform into account
Winson Chung15a2ba82014-11-18 11:19:24 -0800552 if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
Winson Chung7048fea2014-03-18 12:21:24 -0700553 return;
554 }
555
556 // If Recents is the front most activity, then we should just communicate with it directly
557 // to launch the first task or dismiss itself
Winson Chung2cf8b222015-01-20 11:44:05 -0800558 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
Winson Chunge1e20e12015-06-02 14:11:49 -0700559 MutableBoolean isTopTaskHome = new MutableBoolean(true);
Winson Chung2cf8b222015-01-20 11:44:05 -0800560 if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700561 // Notify recents to toggle itself
Winson Chung2002cf52014-12-08 17:26:44 -0800562 Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
Winson Chungc37e1292014-07-25 15:46:57 -0700563 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
Winson Chung15a2ba82014-11-18 11:19:24 -0800564 mLastToggleTime = SystemClock.elapsedRealtime();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700565 return;
566 } else {
567 // Otherwise, start the recents activity
Winson Chunge1e20e12015-06-02 14:11:49 -0700568 startRecentsActivity(topTask, isTopTaskHome.value);
Winson Chung7048fea2014-03-18 12:21:24 -0700569 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700570 }
Winson Chung7048fea2014-03-18 12:21:24 -0700571
Winson Chung1e8d71b2014-05-16 17:05:22 -0700572 /** Starts the recents activity if it is not already running */
573 void startRecentsActivity() {
574 // Check if the top task is in the home stack, and start the recents activity
Winson Chung2cf8b222015-01-20 11:44:05 -0800575 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
Winson Chunge1e20e12015-06-02 14:11:49 -0700576 MutableBoolean isTopTaskHome = new MutableBoolean(true);
Winson Chung2cf8b222015-01-20 11:44:05 -0800577 if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
Winson Chunge1e20e12015-06-02 14:11:49 -0700578 startRecentsActivity(topTask, isTopTaskHome.value);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700579 }
580 }
581
Winson Chungd42a6cf2014-06-03 16:24:04 -0700582 /**
583 * Creates the activity options for a unknown state->recents transition.
584 */
585 ActivityOptions getUnknownTransitionActivityOptions() {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700586 mStartAnimationTriggered = false;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700587 return ActivityOptions.makeCustomAnimation(mContext,
588 R.anim.recents_from_unknown_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700589 R.anim.recents_from_unknown_exit,
590 mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700591 }
592
593 /**
594 * Creates the activity options for a home->recents transition.
595 */
Winson Chungbf8871d2014-08-28 20:57:16 -0700596 ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700597 mStartAnimationTriggered = false;
Winson Chungbf8871d2014-08-28 20:57:16 -0700598 if (fromSearchHome) {
599 return ActivityOptions.makeCustomAnimation(mContext,
600 R.anim.recents_from_search_launcher_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700601 R.anim.recents_from_search_launcher_exit,
602 mHandler, this);
Winson Chungbf8871d2014-08-28 20:57:16 -0700603 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700604 return ActivityOptions.makeCustomAnimation(mContext,
605 R.anim.recents_from_launcher_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700606 R.anim.recents_from_launcher_exit,
607 mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700608 }
609
610 /**
Winson Chung353c0b92014-10-16 17:43:23 -0700611 * Creates the activity options for an app->recents transition.
Winson Chungd42a6cf2014-06-03 16:24:04 -0700612 */
Winson Chunga0e88b52014-08-11 19:25:42 -0700613 ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
Winson Chunga91c2932014-11-07 15:02:38 -0800614 TaskStack stack, TaskStackView stackView) {
Jorim Jaggi900fb482015-06-02 15:07:33 -0700615
Winson Chunga4ccb862014-08-22 15:26:27 -0700616 // Update the destination rect
617 Task toTask = new Task();
Winson Chunga91c2932014-11-07 15:02:38 -0800618 TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
619 topTask.id, toTask);
Jorim Jaggi900fb482015-06-02 15:07:33 -0700620 Rect toTaskRect = toTransform.rect;
621 Bitmap thumbnail;
622 if (mThumbnailTransitionBitmapCacheKey != null
623 && mThumbnailTransitionBitmapCacheKey.key != null
624 && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
625 thumbnail = mThumbnailTransitionBitmapCache;
626 mThumbnailTransitionBitmapCacheKey = null;
627 mThumbnailTransitionBitmapCache = null;
628 } else {
629 preloadIcon(topTask);
630 thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
631 }
632 if (thumbnail != null) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700633 mStartAnimationTriggered = false;
Winson Chung2002cf52014-12-08 17:26:44 -0800634 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
Jorim Jaggi900fb482015-06-02 15:07:33 -0700635 thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
Winson Chunge494c382014-12-17 10:12:54 -0800636 toTaskRect.height(), mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700637 }
638
639 // If both the screenshot and thumbnail fails, then just fall back to the default transition
640 return getUnknownTransitionActivityOptions();
641 }
642
Jorim Jaggi900fb482015-06-02 15:07:33 -0700643 /**
644 * Preloads the icon of a task.
645 */
646 void preloadIcon(ActivityManager.RunningTaskInfo task) {
647
648 // Ensure that we load the running task's icon
649 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
650 launchOpts.runningTaskId = task.id;
651 launchOpts.loadThumbnails = false;
652 launchOpts.onlyLoadForCache = true;
653 RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
654 }
655
656 /**
657 * Caches the header thumbnail used for a window animation asynchronously into
658 * {@link #mThumbnailTransitionBitmapCache}.
659 */
660 void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
661 TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
662 preloadIcon(topTask);
663
664 // Update the destination rect
665 mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
666 final Task toTask = new Task();
667 final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
668 topTask.id, toTask);
669 new AsyncTask<Void, Void, Bitmap>() {
670 @Override
671 protected Bitmap doInBackground(Void... params) {
672 return drawThumbnailTransitionBitmap(toTask, toTransform);
673 }
674
675 @Override
676 protected void onPostExecute(Bitmap bitmap) {
677 mThumbnailTransitionBitmapCache = bitmap;
678 mThumbnailTransitionBitmapCacheKey = toTask;
679 }
680 }.execute();
681 }
682
683 /**
684 * Draws the header of a task used for the window animation into a bitmap.
685 */
686 Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
687 if (toTransform != null && toTask.key != null) {
688 Bitmap thumbnail;
689 synchronized (mHeaderBarLock) {
690 int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
691 int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
692 thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
693 Bitmap.Config.ARGB_8888);
694 if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
695 thumbnail.eraseColor(0xFFff0000);
696 } else {
697 Canvas c = new Canvas(thumbnail);
698 c.scale(toTransform.scale, toTransform.scale);
699 mHeaderBar.rebindToTask(toTask);
700 mHeaderBar.draw(c);
701 c.setBitmap(null);
702 }
703 }
704 return thumbnail.createAshmemBitmap();
705 }
706 return null;
707 }
708
Winson Chungffa2ec62014-07-03 15:54:42 -0700709 /** Returns the transition rect for the given task id. */
Winson Chunga91c2932014-11-07 15:02:38 -0800710 TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
711 int runningTaskId, Task runningTaskOut) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700712 // Find the running task in the TaskStack
713 Task task = null;
714 ArrayList<Task> tasks = stack.getTasks();
715 if (runningTaskId != -1) {
716 // Otherwise, try and find the task with the
717 int taskCount = tasks.size();
718 for (int i = taskCount - 1; i >= 0; i--) {
719 Task t = tasks.get(i);
720 if (t.key.id == runningTaskId) {
721 task = t;
Winson Chunga4ccb862014-08-22 15:26:27 -0700722 runningTaskOut.copyFrom(t);
Winson Chungffa2ec62014-07-03 15:54:42 -0700723 break;
724 }
725 }
726 }
727 if (task == null) {
728 // If no task is specified or we can not find the task just use the front most one
729 task = tasks.get(tasks.size() - 1);
Winson Chunge1e20e12015-06-02 14:11:49 -0700730 runningTaskOut.copyFrom(task);
Winson Chungffa2ec62014-07-03 15:54:42 -0700731 }
732
733 // Get the transform for the running task
Winson Chunga91c2932014-11-07 15:02:38 -0800734 stackView.getScroller().setStackScrollToInitialState();
735 mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
736 stackView.getScroller().getStackScroll(), mTmpTransform, null);
Winson Chunga4ccb862014-08-22 15:26:27 -0700737 return mTmpTransform;
Winson Chungffa2ec62014-07-03 15:54:42 -0700738 }
739
Winson Chung1e8d71b2014-05-16 17:05:22 -0700740 /** Starts the recents activity */
Winson Chungffa2ec62014-07-03 15:54:42 -0700741 void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
Winson Chung2cf8b222015-01-20 11:44:05 -0800742 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
743 RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
744
Winson Chunga91c2932014-11-07 15:02:38 -0800745 if (sInstanceLoadPlan == null) {
746 // Create a new load plan if onPreloadRecents() was never triggered
Winson Chunga91c2932014-11-07 15:02:38 -0800747 sInstanceLoadPlan = loader.createLoadPlan(mContext);
748 }
Winson Chungd16c5652015-01-26 16:11:07 -0800749
750 // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
751 // For multi-stack we need to figure out where each of the tasks are going.
752 if (mConfig.multiStackEnabled) {
753 loader.preloadTasks(sInstanceLoadPlan, true);
754 ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
755 TaskStack stack = stacks.get(0);
756 mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
757 TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
758 mDummyStackView.computeStackVisibilityReport();
759 ActivityOptions opts = getUnknownTransitionActivityOptions();
760 startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
761 false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
762 return;
763 }
764
Jorim Jaggi900fb482015-06-02 15:07:33 -0700765 if (!sInstanceLoadPlan.hasTasks()) {
766 loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
767 }
Winson Chungd16c5652015-01-26 16:11:07 -0800768 ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
769 TaskStack stack = stacks.get(0);
Winson Chunga91c2932014-11-07 15:02:38 -0800770
771 // Prepare the dummy stack for the transition
772 mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
773 TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
774 mDummyStackView.computeStackVisibilityReport();
775 boolean hasRecentTasks = stack.getTaskCount() > 0;
Winson Chungbc571a92014-11-19 17:09:03 -0800776 boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
Winson Chung7048fea2014-03-18 12:21:24 -0700777
Winson Chung96e3bc12014-05-06 16:44:12 -0700778 if (useThumbnailTransition) {
Winson Chunga91c2932014-11-07 15:02:38 -0800779
Winson Chung96e3bc12014-05-06 16:44:12 -0700780 // Try starting with a thumbnail transition
Winson Chunga91c2932014-11-07 15:02:38 -0800781 ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
782 mDummyStackView);
Winson Chung96e3bc12014-05-06 16:44:12 -0700783 if (opts != null) {
Winson Chung860e2d82014-12-04 11:43:02 -0800784 startAlternateRecentsActivity(topTask, opts, false /* fromHome */,
785 false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
Winson Chung96e3bc12014-05-06 16:44:12 -0700786 } else {
787 // Fall through below to the non-thumbnail transition
788 useThumbnailTransition = false;
789 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700790 }
791
792 if (!useThumbnailTransition) {
Winson Chung743d5c92014-06-13 10:14:53 -0700793 // If there is no thumbnail transition, but is launching from home into recents, then
794 // use a quick home transition and do the animation from home
Winson Chungd7b2cb12014-06-26 15:08:50 -0700795 if (hasRecentTasks) {
Winson Chungbf8871d2014-08-28 20:57:16 -0700796 String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
Winson Chungaf3bb692015-06-03 17:31:39 -0700797 String searchWidgetPackage =
798 Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
Winson Chungbf8871d2014-08-28 20:57:16 -0700799
Winson Chungaf3bb692015-06-03 17:31:39 -0700800 // Determine whether we are coming from a search owned home activity
801 boolean fromSearchHome = (homeActivityPackage != null) &&
802 homeActivityPackage.equals(searchWidgetPackage);
Winson Chungbf8871d2014-08-28 20:57:16 -0700803 ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
Winson Chung860e2d82014-12-04 11:43:02 -0800804 startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
805 false /* fromThumbnail */, stackVr);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700806 } else {
Winson Chung743d5c92014-06-13 10:14:53 -0700807 // Otherwise we do the normal fade from an unknown source
Winson Chungd42a6cf2014-06-03 16:24:04 -0700808 ActivityOptions opts = getUnknownTransitionActivityOptions();
Winson Chung860e2d82014-12-04 11:43:02 -0800809 startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
810 false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700811 }
Winson Chung7048fea2014-03-18 12:21:24 -0700812 }
Winson Chung15a2ba82014-11-18 11:19:24 -0800813 mLastToggleTime = SystemClock.elapsedRealtime();
Winson Chung7048fea2014-03-18 12:21:24 -0700814 }
815
816 /** Starts the recents activity */
Winson Chungffa2ec62014-07-03 15:54:42 -0700817 void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
Winson Chung860e2d82014-12-04 11:43:02 -0800818 ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
Winson Chunga91c2932014-11-07 15:02:38 -0800819 TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
Winson Chung860e2d82014-12-04 11:43:02 -0800820 // Update the configuration based on the launch options
821 mConfig.launchedFromHome = fromSearchHome || fromHome;
822 mConfig.launchedFromSearchHome = fromSearchHome;
823 mConfig.launchedFromAppWithThumbnail = fromThumbnail;
824 mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
825 mConfig.launchedWithAltTab = mTriggeredFromAltTab;
826 mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
827 mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
828 mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
829 mConfig.launchedHasConfigurationChanged = false;
830
Winson Chung7048fea2014-03-18 12:21:24 -0700831 Intent intent = new Intent(sToggleRecentsAction);
832 intent.setClassName(sRecentsPackage, sRecentsActivity);
833 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Winson Chungd42a6cf2014-06-03 16:24:04 -0700834 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
835 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Winson Chung7048fea2014-03-18 12:21:24 -0700836 if (opts != null) {
Winson Chung85cfec82014-07-14 14:16:04 -0700837 mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
Winson Chung7048fea2014-03-18 12:21:24 -0700838 } else {
Winson Chung85cfec82014-07-14 14:16:04 -0700839 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
Winson Chung7048fea2014-03-18 12:21:24 -0700840 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700841 mCanReuseTaskStackViews = true;
Winson Chung7048fea2014-03-18 12:21:24 -0700842 }
Winson Chung24cf1522014-05-29 12:03:33 -0700843
Winson Chung9214eff2014-06-12 13:59:25 -0700844 /** Sets the RecentsComponent callbacks. */
Jorim Jaggid61f2272014-12-19 20:35:35 +0100845 @Override
846 public void setCallback(RecentsComponent.Callbacks cb) {
Winson Chung9214eff2014-06-12 13:59:25 -0700847 sRecentsComponentCallbacks = cb;
848 }
849
850 /** Notifies the callbacks that the visibility of Recents has changed. */
Winson Chung2002cf52014-12-08 17:26:44 -0800851 @ProxyFromAnyToPrimaryUser
852 public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
853 boolean visible) {
854 if (ssp.isForegroundUserOwner()) {
855 visibilityChanged(visible);
856 } else {
857 Intent intent = createLocalBroadcastIntent(context,
858 ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
859 intent.putExtra(EXTRA_RECENTS_VISIBILITY, visible);
860 context.sendBroadcastAsUser(intent, UserHandle.OWNER);
861 }
862 }
863 static void visibilityChanged(boolean visible) {
Winson Chung9214eff2014-06-12 13:59:25 -0700864 if (sRecentsComponentCallbacks != null) {
865 sRecentsComponentCallbacks.onVisibilityChanged(visible);
866 }
867 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700868
Winson Chungd0aa8a32015-03-19 14:11:27 -0700869 /** Notifies the status bar to trigger screen pinning. */
870 @ProxyFromAnyToPrimaryUser
871 public static void startScreenPinning(Context context, SystemServicesProxy ssp) {
872 if (ssp.isForegroundUserOwner()) {
873 onStartScreenPinning(context);
874 } else {
875 Intent intent = createLocalBroadcastIntent(context,
876 ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
877 context.sendBroadcastAsUser(intent, UserHandle.OWNER);
878 }
879 }
880 static void onStartScreenPinning(Context context) {
881 // For the primary user, the context for the SystemUI component is the SystemUIApplication
882 SystemUIApplication app = (SystemUIApplication)
883 getInstanceAndStartIfNeeded(context).mContext;
884 PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
885 if (statusBar != null) {
886 statusBar.showScreenPinningRequest(false);
887 }
888 }
889
Winson Chunga91c2932014-11-07 15:02:38 -0800890 /**
891 * Returns the preloaded load plan and invalidates it.
892 */
893 public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
894 RecentsTaskLoadPlan plan = sInstanceLoadPlan;
895 sInstanceLoadPlan = null;
896 return plan;
897 }
898
Winson Chungb0a28ea2014-10-28 15:21:35 -0700899 /**** OnAnimationStartedListener Implementation ****/
900
901 @Override
902 public void onAnimationStarted() {
903 // Notify recents to start the enter animation
904 if (!mStartAnimationTriggered) {
905 // There can be a race condition between the start animation callback and
906 // the start of the new activity (where we register the receiver that listens
907 // to this broadcast, so we add our own receiver and if that gets called, then
908 // we know the activity has not yet started and we can retry sending the broadcast.
909 BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
910 @Override
911 public void onReceive(Context context, Intent intent) {
912 if (getResultCode() == Activity.RESULT_OK) {
913 mStartAnimationTriggered = true;
914 return;
915 }
916
917 // Schedule for the broadcast to be sent again after some time
918 mHandler.postDelayed(new Runnable() {
919 @Override
920 public void run() {
921 onAnimationStarted();
922 }
923 }, 25);
924 }
925 };
926
927 // Send the broadcast to notify Recents that the animation has started
Winson Chung2002cf52014-12-08 17:26:44 -0800928 Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700929 mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
930 fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
931 }
932 }
Winson Chung7048fea2014-03-18 12:21:24 -0700933}