blob: 9dd82fc8024af99c32bde789ba930db32bb9efd6 [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 Chungd751c012014-09-15 00:00:38 +020023import android.appwidget.AppWidgetHost;
Winson Chungbf8871d2014-08-28 20:57:16 -070024import android.appwidget.AppWidgetProviderInfo;
Winson Chung7048fea2014-03-18 12:21:24 -070025import android.content.ActivityNotFoundException;
Jim Millera237a312014-11-06 18:05:59 -080026import android.content.BroadcastReceiver;
Winson Chung7048fea2014-03-18 12:21:24 -070027import android.content.Context;
28import android.content.Intent;
Winson Chung2002cf52014-12-08 17:26:44 -080029import android.content.IntentFilter;
Winson Chung7048fea2014-03-18 12:21:24 -070030import android.content.res.Configuration;
Winson Chungdcfa7972014-07-22 12:27:13 -070031import android.content.res.Resources;
Winson Chung7048fea2014-03-18 12:21:24 -070032import android.graphics.Bitmap;
33import android.graphics.Canvas;
Winson Chung7048fea2014-03-18 12:21:24 -070034import android.graphics.Rect;
Jim Millera237a312014-11-06 18:05:59 -080035import android.os.Handler;
Winson Chung15a2ba82014-11-18 11:19:24 -080036import android.os.SystemClock;
Winson Chung7048fea2014-03-18 12:21:24 -070037import android.os.UserHandle;
Winson Chungd751c012014-09-15 00:00:38 +020038import android.util.Pair;
Jorim Jaggid61f2272014-12-19 20:35:35 +010039import android.view.Display;
Winson Chunga4ccb862014-08-22 15:26:27 -070040import android.view.LayoutInflater;
Winson Chung7048fea2014-03-18 12:21:24 -070041import android.view.View;
Winson Chung7048fea2014-03-18 12:21:24 -070042import com.android.systemui.R;
Winson Chung9214eff2014-06-12 13:59:25 -070043import com.android.systemui.RecentsComponent;
Jorim Jaggid61f2272014-12-19 20:35:35 +010044import com.android.systemui.SystemUI;
Winson Chungffa2ec62014-07-03 15:54:42 -070045import com.android.systemui.recents.misc.Console;
46import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chunga91c2932014-11-07 15:02:38 -080047import com.android.systemui.recents.model.RecentsTaskLoadPlan;
Winson Chungffa2ec62014-07-03 15:54:42 -070048import com.android.systemui.recents.model.RecentsTaskLoader;
49import com.android.systemui.recents.model.Task;
Winson Chungb1f74992014-08-08 12:53:09 -070050import com.android.systemui.recents.model.TaskGrouping;
Winson Chungffa2ec62014-07-03 15:54:42 -070051import com.android.systemui.recents.model.TaskStack;
52import com.android.systemui.recents.views.TaskStackView;
53import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
Winson Chunga4ccb862014-08-22 15:26:27 -070054import com.android.systemui.recents.views.TaskViewHeader;
Winson Chungffa2ec62014-07-03 15:54:42 -070055import com.android.systemui.recents.views.TaskViewTransform;
Winson Chung7048fea2014-03-18 12:21:24 -070056
Winson Chungffa2ec62014-07-03 15:54:42 -070057import java.util.ArrayList;
Winson Chung1e8d71b2014-05-16 17:05:22 -070058import java.util.concurrent.atomic.AtomicBoolean;
Winson Chung7048fea2014-03-18 12:21:24 -070059
Winson Chung2002cf52014-12-08 17:26:44 -080060/**
61 * Annotation for a method that is only called from the primary user's SystemUI process and will be
62 * proxied to the current user.
63 */
64@interface ProxyFromPrimaryToCurrentUser {}
65/**
66 * Annotation for a method that may be called from any user's SystemUI process and will be proxied
67 * to the primary user.
68 */
69@interface ProxyFromAnyToPrimaryUser {}
Winson Chung740c3ac2014-11-12 16:14:38 -080070
Winson Chung7048fea2014-03-18 12:21:24 -070071/** A proxy implementation for the recents component */
Jorim Jaggid61f2272014-12-19 20:35:35 +010072public class Recents extends SystemUI
73 implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
Winson Chung7048fea2014-03-18 12:21:24 -070074
Winson Chung2002cf52014-12-08 17:26:44 -080075 final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
76 final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
77 final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
78
79 // Owner proxy events
80 final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER =
81 "action_notify_recents_visibility_change";
Winson Chungcdcd4872014-08-05 18:00:13 -070082
Winson Chungb0a28ea2014-10-28 15:21:35 -070083 final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
Winson Chungcdcd4872014-08-05 18:00:13 -070084 final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
85 final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
Winson Chungb44c24f2014-04-09 15:17:43 -070086
Winson Chungbf8871d2014-08-28 20:57:16 -070087 final static int sMinToggleDelay = 350;
Winson Chung7048fea2014-03-18 12:21:24 -070088
Winson Chung2cf8b222015-01-20 11:44:05 -080089 public final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
90 public final static String sRecentsPackage = "com.android.systemui";
91 public final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
Winson Chung7048fea2014-03-18 12:21:24 -070092
Winson Chung740c3ac2014-11-12 16:14:38 -080093 /**
94 * An implementation of ITaskStackListener, that allows us to listen for changes to the system
95 * task stacks and update recents accordingly.
96 */
Winson Chung2002cf52014-12-08 17:26:44 -080097 class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
98 Handler mHandler;
99
100 public TaskStackListenerImpl(Handler handler) {
101 mHandler = handler;
102 }
103
Winson Chung740c3ac2014-11-12 16:14:38 -0800104 @Override
105 public void onTaskStackChanged() {
Winson Chung2002cf52014-12-08 17:26:44 -0800106 // Debounce any task stack changes
107 mHandler.removeCallbacks(this);
108 mHandler.post(this);
109 }
110
111 /** Preloads the next task */
112 public void run() {
Winson Chungd16c5652015-01-26 16:11:07 -0800113 // Temporarily skip this if multi stack is enabled
114 if (mConfig.multiStackEnabled) return;
115
Winson Chung740c3ac2014-11-12 16:14:38 -0800116 RecentsConfiguration config = RecentsConfiguration.getInstance();
117 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Winson Chung2cf8b222015-01-20 11:44:05 -0800118 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
119 SystemServicesProxy ssp = loader.getSystemServicesProxy();
120 ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
Winson Chung0eae5572014-12-11 11:04:19 -0800121
Winson Chung740c3ac2014-11-12 16:14:38 -0800122 // Load the next task only if we aren't svelte
Winson Chung740c3ac2014-11-12 16:14:38 -0800123 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
124 loader.preloadTasks(plan, true /* isTopTaskHome */);
125 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
Winson Chung0eae5572014-12-11 11:04:19 -0800126 // This callback is made when a new activity is launched and the old one is paused
127 // so ignore the current activity and try and preload the thumbnail for the
128 // previous one.
129 if (runningTaskInfo != null) {
130 launchOpts.runningTaskId = runningTaskInfo.id;
131 }
132 launchOpts.numVisibleTasks = 2;
133 launchOpts.numVisibleTaskThumbnails = 2;
Winson Chung740c3ac2014-11-12 16:14:38 -0800134 launchOpts.onlyLoadForCache = true;
Winson Chung0eae5572014-12-11 11:04:19 -0800135 launchOpts.onlyLoadPausedActivities = true;
Winson Chung740c3ac2014-11-12 16:14:38 -0800136 loader.loadTasks(mContext, plan, launchOpts);
137 }
138 }
139 }
140
Winson Chung2002cf52014-12-08 17:26:44 -0800141 /**
142 * A proxy for Recents events which happens strictly for the owner.
143 */
144 class RecentsOwnerEventProxyReceiver extends BroadcastReceiver {
145 @Override
146 public void onReceive(Context context, Intent intent) {
147 switch (intent.getAction()) {
148 case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
149 visibilityChanged(intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
150 break;
151 }
152 }
153 }
154
Winson Chung9214eff2014-06-12 13:59:25 -0700155 static RecentsComponent.Callbacks sRecentsComponentCallbacks;
Winson Chunga91c2932014-11-07 15:02:38 -0800156 static RecentsTaskLoadPlan sInstanceLoadPlan;
Jorim Jaggid61f2272014-12-19 20:35:35 +0100157 static Recents sInstance;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700158
Winson Chung48a10a52014-08-27 14:36:51 -0700159 LayoutInflater mInflater;
Winson Chunga10370f2014-04-02 12:25:04 -0700160 SystemServicesProxy mSystemServicesProxy;
Winson Chungb0a28ea2014-10-28 15:21:35 -0700161 Handler mHandler;
Winson Chung740c3ac2014-11-12 16:14:38 -0800162 TaskStackListenerImpl mTaskStackListener;
Winson Chung2002cf52014-12-08 17:26:44 -0800163 RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
Winson Chung85cfec82014-07-14 14:16:04 -0700164 boolean mBootCompleted;
Winson Chungb0a28ea2014-10-28 15:21:35 -0700165 boolean mStartAnimationTriggered;
166 boolean mCanReuseTaskStackViews = true;
Winson Chungffa2ec62014-07-03 15:54:42 -0700167
168 // Task launching
169 RecentsConfiguration mConfig;
Winson Chungdcfa7972014-07-22 12:27:13 -0700170 Rect mWindowRect = new Rect();
171 Rect mTaskStackBounds = new Rect();
172 Rect mSystemInsets = new Rect();
Winson Chungffa2ec62014-07-03 15:54:42 -0700173 TaskViewTransform mTmpTransform = new TaskViewTransform();
174 int mStatusBarHeight;
Winson Chungdcfa7972014-07-22 12:27:13 -0700175 int mNavBarHeight;
176 int mNavBarWidth;
Winson Chung7048fea2014-03-18 12:21:24 -0700177
Winson Chunga4ccb862014-08-22 15:26:27 -0700178 // Header (for transition)
179 TaskViewHeader mHeaderBar;
180 TaskStackView mDummyStackView;
181
Winson Chung1e8d71b2014-05-16 17:05:22 -0700182 // Variables to keep track of if we need to start recents after binding
Winson Chung1e8d71b2014-05-16 17:05:22 -0700183 boolean mTriggeredFromAltTab;
Winson Chung7048fea2014-03-18 12:21:24 -0700184 long mLastToggleTime;
185
Jorim Jaggid61f2272014-12-19 20:35:35 +0100186 public Recents() {
187 }
Winson Chung740c3ac2014-11-12 16:14:38 -0800188
Jorim Jaggid61f2272014-12-19 20:35:35 +0100189 /**
190 * Gets the singleton instance and starts it if needed. On the primary user on the device, this
191 * component gets started as a normal {@link SystemUI} component. On a secondary user, this
192 * lifecycle doesn't exist, so we need to start it manually here if needed.
193 */
194 public static Recents getInstanceAndStartIfNeeded(Context ctx) {
195 if (sInstance == null) {
196 sInstance = new Recents();
197 sInstance.mContext = ctx;
198 sInstance.start();
199 sInstance.onBootCompleted();
Winson Chung2002cf52014-12-08 17:26:44 -0800200 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100201 return sInstance;
Winson Chung7048fea2014-03-18 12:21:24 -0700202 }
203
Winson Chung2002cf52014-12-08 17:26:44 -0800204 /** Creates a new broadcast intent */
205 static Intent createLocalBroadcastIntent(Context context, String action) {
206 Intent intent = new Intent(action);
207 intent.setPackage(context.getPackageName());
208 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
209 Intent.FLAG_RECEIVER_FOREGROUND);
210 return intent;
211 }
212
213 /** Initializes the Recents. */
214 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100215 @Override
216 public void start() {
217 if (sInstance == null) {
218 sInstance = this;
219 }
220 RecentsTaskLoader.initialize(mContext);
221 mInflater = LayoutInflater.from(mContext);
222 mSystemServicesProxy = new SystemServicesProxy(mContext);
223 mHandler = new Handler();
224 mTaskStackBounds = new Rect();
225
226 // Register the task stack listener
227 mTaskStackListener = new TaskStackListenerImpl(mHandler);
228 mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
229
230 // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
231 // instances of AlternateRecentsComponent needs to notify the owner when the visibility
232 // changes.
233 if (mSystemServicesProxy.isForegroundUserOwner()) {
234 mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
235 IntentFilter filter = new IntentFilter();
236 filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
237 mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter,
238 null, mHandler);
239 }
240
Winson Chung48a10a52014-08-27 14:36:51 -0700241 // Initialize some static datastructures
242 TaskStackViewLayoutAlgorithm.initializeCurve();
243 // Load the header bar layout
Winson Chung2002cf52014-12-08 17:26:44 -0800244 reloadHeaderBarLayout(true);
Winson Chungbac679b2014-10-20 14:11:12 -0700245
Winson Chung90d51362014-11-13 14:30:26 -0800246 // When we start, preload the data associated with the previous recent tasks.
Winson Chunga91c2932014-11-07 15:02:38 -0800247 // We can use a new plan since the caches will be the same.
248 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
249 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
250 loader.preloadTasks(plan, true /* isTopTaskHome */);
251 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
252 launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
Winson Chung90d51362014-11-13 14:30:26 -0800253 launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
254 launchOpts.onlyLoadForCache = true;
Winson Chunga91c2932014-11-07 15:02:38 -0800255 loader.loadTasks(mContext, plan, launchOpts);
Jorim Jaggid61f2272014-12-19 20:35:35 +0100256 putComponent(Recents.class, this);
Winson Chung5abdceb2014-06-05 10:58:05 -0700257 }
258
Jorim Jaggid61f2272014-12-19 20:35:35 +0100259 @Override
Winson Chung8bf05af2014-09-29 13:42:49 -0700260 public void onBootCompleted() {
261 mBootCompleted = true;
262 }
263
Winson Chung2002cf52014-12-08 17:26:44 -0800264 /** Shows the Recents. */
265 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100266 @Override
267 public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
Winson Chung2002cf52014-12-08 17:26:44 -0800268 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100269 showRecentsInternal(triggeredFromAltTab);
Winson Chung2002cf52014-12-08 17:26:44 -0800270 } else {
271 Intent intent = createLocalBroadcastIntent(mContext,
272 RecentsUserEventProxyReceiver.ACTION_PROXY_SHOW_RECENTS_TO_USER);
273 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
274 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
275 }
276 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100277
278 void showRecentsInternal(boolean triggeredFromAltTab) {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700279 mTriggeredFromAltTab = triggeredFromAltTab;
Winson Chung7048fea2014-03-18 12:21:24 -0700280
281 try {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700282 startRecentsActivity();
283 } catch (ActivityNotFoundException e) {
284 Console.logRawError("Failed to launch RecentAppsIntent", e);
285 }
286 }
287
Winson Chung2002cf52014-12-08 17:26:44 -0800288 /** Hides the Recents. */
289 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100290 @Override
291 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung2002cf52014-12-08 17:26:44 -0800292 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100293 hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
Winson Chung2002cf52014-12-08 17:26:44 -0800294 } else {
295 Intent intent = createLocalBroadcastIntent(mContext,
296 RecentsUserEventProxyReceiver.ACTION_PROXY_HIDE_RECENTS_TO_USER);
297 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
298 intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
299 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
300 }
301 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100302
303 void hideRecentsInternal(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700304 if (mBootCompleted) {
Winson Chung2cf8b222015-01-20 11:44:05 -0800305 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
306 if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, null)) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700307 // Notify recents to hide itself
Winson Chung2002cf52014-12-08 17:26:44 -0800308 Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
Winson Chungcdcd4872014-08-05 18:00:13 -0700309 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
310 intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
Winson Chungc37e1292014-07-25 15:46:57 -0700311 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700312 }
313 }
314 }
315
Winson Chung2002cf52014-12-08 17:26:44 -0800316 /** Toggles the Recents activity. */
317 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100318 @Override
319 public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
Winson Chung2002cf52014-12-08 17:26:44 -0800320 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100321 toggleRecentsInternal();
Winson Chung2002cf52014-12-08 17:26:44 -0800322 } else {
323 Intent intent = createLocalBroadcastIntent(mContext,
324 RecentsUserEventProxyReceiver.ACTION_PROXY_TOGGLE_RECENTS_TO_USER);
325 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
326 }
327 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100328
329 void toggleRecentsInternal() {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700330 mTriggeredFromAltTab = false;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700331
332 try {
333 toggleRecentsActivity();
Winson Chung7048fea2014-03-18 12:21:24 -0700334 } catch (ActivityNotFoundException e) {
335 Console.logRawError("Failed to launch RecentAppsIntent", e);
336 }
337 }
338
Winson Chung2002cf52014-12-08 17:26:44 -0800339 /** Preloads info for the Recents activity. */
340 @ProxyFromPrimaryToCurrentUser
Jorim Jaggid61f2272014-12-19 20:35:35 +0100341 @Override
342 public void preloadRecents() {
Winson Chung2002cf52014-12-08 17:26:44 -0800343 if (mSystemServicesProxy.isForegroundUserOwner()) {
Jorim Jaggid61f2272014-12-19 20:35:35 +0100344 preloadRecentsInternal();
Winson Chung2002cf52014-12-08 17:26:44 -0800345 } else {
346 Intent intent = createLocalBroadcastIntent(mContext,
347 RecentsUserEventProxyReceiver.ACTION_PROXY_PRELOAD_RECENTS_TO_USER);
348 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
349 }
350 }
Jorim Jaggid61f2272014-12-19 20:35:35 +0100351
352 void preloadRecentsInternal() {
Winson Chunga91c2932014-11-07 15:02:38 -0800353 // Preload only the raw task list into a new load plan (which will be consumed by the
354 // RecentsActivity)
355 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
356 sInstanceLoadPlan = loader.createLoadPlan(mContext);
357 sInstanceLoadPlan.preloadRawTasks(true);
Winson Chung7048fea2014-03-18 12:21:24 -0700358 }
359
Jorim Jaggid61f2272014-12-19 20:35:35 +0100360 @Override
361 public void cancelPreloadingRecents() {
Winson Chung7048fea2014-03-18 12:21:24 -0700362 // Do nothing
363 }
364
Winson Chungb1f74992014-08-08 12:53:09 -0700365 void showRelativeAffiliatedTask(boolean showNextTask) {
Winson Chungd16c5652015-01-26 16:11:07 -0800366 // Return early if there is no focused stack
367 int focusedStackId = mSystemServicesProxy.getFocusedStack();
368 TaskStack focusedStack = null;
Winson Chungebfc6982014-08-26 12:25:34 -0700369 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
Winson Chunga91c2932014-11-07 15:02:38 -0800370 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
371 loader.preloadTasks(plan, true /* isTopTaskHome */);
Winson Chungd16c5652015-01-26 16:11:07 -0800372 if (mConfig.multiStackEnabled) {
373 if (focusedStackId < 0) return;
374 focusedStack = plan.getTaskStack(focusedStackId);
375 } else {
376 focusedStack = plan.getAllTaskStacks().get(0);
377 }
Winson Chunga91c2932014-11-07 15:02:38 -0800378
Winson Chungd16c5652015-01-26 16:11:07 -0800379 // Return early if there are no tasks in the focused stack
380 if (focusedStack.getTaskCount() == 0) return;
Winson Chungb1f74992014-08-08 12:53:09 -0700381
Winson Chung2cf8b222015-01-20 11:44:05 -0800382 ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
Winson Chungbc571a92014-11-19 17:09:03 -0800383 // Return early if there is no running task (can't determine affiliated tasks in this case)
384 if (runningTask == null) return;
Winson Chungb1f74992014-08-08 12:53:09 -0700385 // Return early if the running task is in the home stack (optimization)
386 if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
387
388 // Find the task in the recents list
Winson Chungd16c5652015-01-26 16:11:07 -0800389 ArrayList<Task> tasks = focusedStack.getTasks();
Winson Chungb1f74992014-08-08 12:53:09 -0700390 Task toTask = null;
391 ActivityOptions launchOpts = null;
392 int taskCount = tasks.size();
Winson Chung044d5292014-11-06 11:05:19 -0800393 int numAffiliatedTasks = 0;
Winson Chungb1f74992014-08-08 12:53:09 -0700394 for (int i = 0; i < taskCount; i++) {
395 Task task = tasks.get(i);
396 if (task.key.id == runningTask.id) {
397 TaskGrouping group = task.group;
398 Task.TaskKey toTaskKey;
399 if (showNextTask) {
400 toTaskKey = group.getNextTaskInGroup(task);
Winson Chung3aa9c222014-09-09 17:54:51 +0200401 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
402 R.anim.recents_launch_next_affiliated_task_target,
403 R.anim.recents_launch_next_affiliated_task_source);
Winson Chungb1f74992014-08-08 12:53:09 -0700404 } else {
405 toTaskKey = group.getPrevTaskInGroup(task);
Winson Chung3aa9c222014-09-09 17:54:51 +0200406 launchOpts = ActivityOptions.makeCustomAnimation(mContext,
407 R.anim.recents_launch_prev_affiliated_task_target,
408 R.anim.recents_launch_prev_affiliated_task_source);
Winson Chungb1f74992014-08-08 12:53:09 -0700409 }
410 if (toTaskKey != null) {
Winson Chungd16c5652015-01-26 16:11:07 -0800411 toTask = focusedStack.findTaskWithId(toTaskKey.id);
Winson Chungb1f74992014-08-08 12:53:09 -0700412 }
Winson Chung044d5292014-11-06 11:05:19 -0800413 numAffiliatedTasks = group.getTaskCount();
Winson Chungb1f74992014-08-08 12:53:09 -0700414 break;
415 }
416 }
417
418 // Return early if there is no next task
419 if (toTask == null) {
Winson Chung044d5292014-11-06 11:05:19 -0800420 if (numAffiliatedTasks > 1) {
421 if (showNextTask) {
422 mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
423 ActivityOptions.makeCustomInPlaceAnimation(mContext,
424 R.anim.recents_launch_next_affiliated_task_bounce));
425 } else {
426 mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
427 ActivityOptions.makeCustomInPlaceAnimation(mContext,
428 R.anim.recents_launch_prev_affiliated_task_bounce));
429 }
Winson Chung3aa9c222014-09-09 17:54:51 +0200430 }
Winson Chungb1f74992014-08-08 12:53:09 -0700431 return;
432 }
433
434 // Launch the task
435 if (toTask.isActive) {
436 // Bring an active task to the foreground
437 mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
438 } else {
Winson Chung4e96eb72014-09-17 15:16:09 +0200439 mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
440 toTask.activityLabel, launchOpts);
Winson Chungb1f74992014-08-08 12:53:09 -0700441 }
442 }
443
Jorim Jaggid61f2272014-12-19 20:35:35 +0100444 @Override
445 public void showNextAffiliatedTask() {
Winson Chungb1f74992014-08-08 12:53:09 -0700446 showRelativeAffiliatedTask(true);
447 }
448
Jorim Jaggid61f2272014-12-19 20:35:35 +0100449 @Override
450 public void showPrevAffiliatedTask() {
Winson Chungb1f74992014-08-08 12:53:09 -0700451 showRelativeAffiliatedTask(false);
452 }
453
Winson Chung2002cf52014-12-08 17:26:44 -0800454 /** Updates on configuration change. */
455 @ProxyFromPrimaryToCurrentUser
Winson Chung7048fea2014-03-18 12:21:24 -0700456 public void onConfigurationChanged(Configuration newConfig) {
Winson Chung2002cf52014-12-08 17:26:44 -0800457 if (mSystemServicesProxy.isForegroundUserOwner()) {
458 configurationChanged();
459 } else {
460 Intent intent = createLocalBroadcastIntent(mContext,
461 RecentsUserEventProxyReceiver.ACTION_PROXY_CONFIG_CHANGE_TO_USER);
462 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
463 }
464 }
465 void configurationChanged() {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700466 // Don't reuse task stack views if the configuration changes
467 mCanReuseTaskStackViews = false;
Winson Chungbf8871d2014-08-28 20:57:16 -0700468 // Reload the header bar layout
Winson Chung2002cf52014-12-08 17:26:44 -0800469 reloadHeaderBarLayout(false);
Winson Chung48a10a52014-08-27 14:36:51 -0700470 }
471
472 /** Prepares the header bar layout. */
Winson Chung2002cf52014-12-08 17:26:44 -0800473 void reloadHeaderBarLayout(boolean reloadWidget) {
Winson Chung48a10a52014-08-27 14:36:51 -0700474 Resources res = mContext.getResources();
475 mWindowRect = mSystemServicesProxy.getWindowRect();
476 mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
477 mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
478 mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
Jason Monk56e09b42014-07-18 10:29:14 -0400479 mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
Winson Chung6057c912014-07-08 14:39:08 -0700480 mConfig.updateOnConfigurationChange();
Winson Chung2002cf52014-12-08 17:26:44 -0800481 if (reloadWidget) {
482 // Reload the widget id before we get the task stack bounds
483 reloadSearchBarAppWidget(mContext, mSystemServicesProxy);
484 }
Winson Chungd16c5652015-01-26 16:11:07 -0800485 mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
486 mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
487 mTaskStackBounds);
Winson Chung27acf762014-09-05 17:24:20 +0200488 if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
Winson Chungdcfa7972014-07-22 12:27:13 -0700489 mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
490 } else {
491 mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
492 }
Winson Chunga4ccb862014-08-22 15:26:27 -0700493
Winson Chunga4ccb862014-08-22 15:26:27 -0700494 // Inflate the header bar layout so that we can rebind and draw it for the transition
Winson Chunga4ccb862014-08-22 15:26:27 -0700495 TaskStack stack = new TaskStack();
496 mDummyStackView = new TaskStackView(mContext, stack);
497 TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
498 Rect taskStackBounds = new Rect(mTaskStackBounds);
499 taskStackBounds.bottom -= mSystemInsets.bottom;
500 algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
501 Rect taskViewSize = algo.getUntransformedTaskViewSize();
502 int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
Winson Chung48a10a52014-08-27 14:36:51 -0700503 mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
Winson Chunga4ccb862014-08-22 15:26:27 -0700504 false);
505 mHeaderBar.measure(
506 View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
507 View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
508 mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
Winson Chung96e3bc12014-05-06 16:44:12 -0700509 }
510
Winson Chung2002cf52014-12-08 17:26:44 -0800511 /** Prepares the search bar app widget */
512 void reloadSearchBarAppWidget(Context context, SystemServicesProxy ssp) {
513 // Try and pre-emptively bind the search widget on startup to ensure that we
514 // have the right thumbnail bounds to animate to.
515 if (Constants.DebugFlags.App.EnableSearchLayout) {
516 // If there is no id, then bind a new search app widget
517 if (mConfig.searchBarAppWidgetId < 0) {
518 AppWidgetHost host = new RecentsAppWidgetHost(context,
519 Constants.Values.App.AppWidgetHostId);
520 Pair<Integer, AppWidgetProviderInfo> widgetInfo = ssp.bindSearchAppWidget(host);
521 if (widgetInfo != null) {
522 // Save the app widget id into the settings
523 mConfig.updateSearchBarAppWidgetId(context, widgetInfo.first);
524 }
525 }
526 }
527 }
528
Winson Chung1e8d71b2014-05-16 17:05:22 -0700529 /** Toggles the recents activity */
530 void toggleRecentsActivity() {
Winson Chung7048fea2014-03-18 12:21:24 -0700531 // If the user has toggled it too quickly, then just eat up the event here (it's better than
532 // showing a janky screenshot).
533 // NOTE: Ideally, the screenshot mechanism would take the window transform into account
Winson Chung15a2ba82014-11-18 11:19:24 -0800534 if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
Winson Chung7048fea2014-03-18 12:21:24 -0700535 return;
536 }
537
538 // If Recents is the front most activity, then we should just communicate with it directly
539 // to launch the first task or dismiss itself
Winson Chung2cf8b222015-01-20 11:44:05 -0800540 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
Winson Chungbc571a92014-11-19 17:09:03 -0800541 AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
Winson Chung2cf8b222015-01-20 11:44:05 -0800542 if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700543 // Notify recents to toggle itself
Winson Chung2002cf52014-12-08 17:26:44 -0800544 Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
Winson Chungc37e1292014-07-25 15:46:57 -0700545 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
Winson Chung15a2ba82014-11-18 11:19:24 -0800546 mLastToggleTime = SystemClock.elapsedRealtime();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700547 return;
548 } else {
549 // Otherwise, start the recents activity
Winson Chungffa2ec62014-07-03 15:54:42 -0700550 startRecentsActivity(topTask, isTopTaskHome.get());
Winson Chung7048fea2014-03-18 12:21:24 -0700551 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700552 }
Winson Chung7048fea2014-03-18 12:21:24 -0700553
Winson Chung1e8d71b2014-05-16 17:05:22 -0700554 /** Starts the recents activity if it is not already running */
555 void startRecentsActivity() {
556 // Check if the top task is in the home stack, and start the recents activity
Winson Chung2cf8b222015-01-20 11:44:05 -0800557 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
Winson Chungbc571a92014-11-19 17:09:03 -0800558 AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
Winson Chung2cf8b222015-01-20 11:44:05 -0800559 if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700560 startRecentsActivity(topTask, isTopTaskHome.get());
Winson Chung1e8d71b2014-05-16 17:05:22 -0700561 }
562 }
563
Winson Chungd42a6cf2014-06-03 16:24:04 -0700564 /**
565 * Creates the activity options for a unknown state->recents transition.
566 */
567 ActivityOptions getUnknownTransitionActivityOptions() {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700568 mStartAnimationTriggered = false;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700569 return ActivityOptions.makeCustomAnimation(mContext,
570 R.anim.recents_from_unknown_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700571 R.anim.recents_from_unknown_exit,
572 mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700573 }
574
575 /**
576 * Creates the activity options for a home->recents transition.
577 */
Winson Chungbf8871d2014-08-28 20:57:16 -0700578 ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700579 mStartAnimationTriggered = false;
Winson Chungbf8871d2014-08-28 20:57:16 -0700580 if (fromSearchHome) {
581 return ActivityOptions.makeCustomAnimation(mContext,
582 R.anim.recents_from_search_launcher_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700583 R.anim.recents_from_search_launcher_exit,
584 mHandler, this);
Winson Chungbf8871d2014-08-28 20:57:16 -0700585 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700586 return ActivityOptions.makeCustomAnimation(mContext,
587 R.anim.recents_from_launcher_enter,
Winson Chungb0a28ea2014-10-28 15:21:35 -0700588 R.anim.recents_from_launcher_exit,
589 mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700590 }
591
592 /**
Winson Chung353c0b92014-10-16 17:43:23 -0700593 * Creates the activity options for an app->recents transition.
Winson Chungd42a6cf2014-06-03 16:24:04 -0700594 */
Winson Chunga0e88b52014-08-11 19:25:42 -0700595 ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
Winson Chunga91c2932014-11-07 15:02:38 -0800596 TaskStack stack, TaskStackView stackView) {
Winson Chunga4ccb862014-08-22 15:26:27 -0700597 // Update the destination rect
598 Task toTask = new Task();
Winson Chunga91c2932014-11-07 15:02:38 -0800599 TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
600 topTask.id, toTask);
Winson Chunga4ccb862014-08-22 15:26:27 -0700601 if (toTransform != null && toTask.key != null) {
602 Rect toTaskRect = toTransform.rect;
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200603 int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
604 int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
605 Bitmap thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
Winson Chunga4ccb862014-08-22 15:26:27 -0700606 Bitmap.Config.ARGB_8888);
607 if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
608 thumbnail.eraseColor(0xFFff0000);
609 } else {
610 Canvas c = new Canvas(thumbnail);
611 c.scale(toTransform.scale, toTransform.scale);
612 mHeaderBar.rebindToTask(toTask);
613 mHeaderBar.draw(c);
614 c.setBitmap(null);
615 }
616
Winson Chungb0a28ea2014-10-28 15:21:35 -0700617 mStartAnimationTriggered = false;
Winson Chung2002cf52014-12-08 17:26:44 -0800618 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200619 thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
Winson Chunge494c382014-12-17 10:12:54 -0800620 toTaskRect.height(), mHandler, this);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700621 }
622
623 // If both the screenshot and thumbnail fails, then just fall back to the default transition
624 return getUnknownTransitionActivityOptions();
625 }
626
Winson Chungffa2ec62014-07-03 15:54:42 -0700627 /** Returns the transition rect for the given task id. */
Winson Chunga91c2932014-11-07 15:02:38 -0800628 TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
629 int runningTaskId, Task runningTaskOut) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700630 // Find the running task in the TaskStack
631 Task task = null;
632 ArrayList<Task> tasks = stack.getTasks();
633 if (runningTaskId != -1) {
634 // Otherwise, try and find the task with the
635 int taskCount = tasks.size();
636 for (int i = taskCount - 1; i >= 0; i--) {
637 Task t = tasks.get(i);
638 if (t.key.id == runningTaskId) {
639 task = t;
Winson Chunga4ccb862014-08-22 15:26:27 -0700640 runningTaskOut.copyFrom(t);
Winson Chungffa2ec62014-07-03 15:54:42 -0700641 break;
642 }
643 }
644 }
645 if (task == null) {
646 // If no task is specified or we can not find the task just use the front most one
647 task = tasks.get(tasks.size() - 1);
648 }
649
650 // Get the transform for the running task
Winson Chunga91c2932014-11-07 15:02:38 -0800651 stackView.getScroller().setStackScrollToInitialState();
652 mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
653 stackView.getScroller().getStackScroll(), mTmpTransform, null);
Winson Chunga4ccb862014-08-22 15:26:27 -0700654 return mTmpTransform;
Winson Chungffa2ec62014-07-03 15:54:42 -0700655 }
656
Winson Chung1e8d71b2014-05-16 17:05:22 -0700657 /** Starts the recents activity */
Winson Chungffa2ec62014-07-03 15:54:42 -0700658 void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
Winson Chung2cf8b222015-01-20 11:44:05 -0800659 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
660 RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
661
Winson Chunga91c2932014-11-07 15:02:38 -0800662 if (sInstanceLoadPlan == null) {
663 // Create a new load plan if onPreloadRecents() was never triggered
Winson Chunga91c2932014-11-07 15:02:38 -0800664 sInstanceLoadPlan = loader.createLoadPlan(mContext);
665 }
Winson Chungd16c5652015-01-26 16:11:07 -0800666
667 // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
668 // For multi-stack we need to figure out where each of the tasks are going.
669 if (mConfig.multiStackEnabled) {
670 loader.preloadTasks(sInstanceLoadPlan, true);
671 ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
672 TaskStack stack = stacks.get(0);
673 mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
674 TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
675 mDummyStackView.computeStackVisibilityReport();
676 ActivityOptions opts = getUnknownTransitionActivityOptions();
677 startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
678 false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
679 return;
680 }
681
Winson Chunga91c2932014-11-07 15:02:38 -0800682 loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
Winson Chungd16c5652015-01-26 16:11:07 -0800683 ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
684 TaskStack stack = stacks.get(0);
Winson Chunga91c2932014-11-07 15:02:38 -0800685
686 // Prepare the dummy stack for the transition
687 mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
688 TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
689 mDummyStackView.computeStackVisibilityReport();
690 boolean hasRecentTasks = stack.getTaskCount() > 0;
Winson Chungbc571a92014-11-19 17:09:03 -0800691 boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
Winson Chung7048fea2014-03-18 12:21:24 -0700692
Winson Chung96e3bc12014-05-06 16:44:12 -0700693 if (useThumbnailTransition) {
Winson Chunga91c2932014-11-07 15:02:38 -0800694 // Ensure that we load the running task's icon
695 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
696 launchOpts.runningTaskId = topTask.id;
697 launchOpts.loadThumbnails = false;
Winson Chung90d51362014-11-13 14:30:26 -0800698 launchOpts.onlyLoadForCache = true;
Winson Chunga91c2932014-11-07 15:02:38 -0800699 loader.loadTasks(mContext, sInstanceLoadPlan, launchOpts);
700
Winson Chung96e3bc12014-05-06 16:44:12 -0700701 // Try starting with a thumbnail transition
Winson Chunga91c2932014-11-07 15:02:38 -0800702 ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
703 mDummyStackView);
Winson Chung96e3bc12014-05-06 16:44:12 -0700704 if (opts != null) {
Winson Chung860e2d82014-12-04 11:43:02 -0800705 startAlternateRecentsActivity(topTask, opts, false /* fromHome */,
706 false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
Winson Chung96e3bc12014-05-06 16:44:12 -0700707 } else {
708 // Fall through below to the non-thumbnail transition
709 useThumbnailTransition = false;
710 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700711 }
712
713 if (!useThumbnailTransition) {
Winson Chung743d5c92014-06-13 10:14:53 -0700714 // If there is no thumbnail transition, but is launching from home into recents, then
715 // use a quick home transition and do the animation from home
Winson Chungd7b2cb12014-06-26 15:08:50 -0700716 if (hasRecentTasks) {
Winson Chungbf8871d2014-08-28 20:57:16 -0700717 // Get the home activity info
718 String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
719 // Get the search widget info
720 AppWidgetProviderInfo searchWidget = null;
721 String searchWidgetPackage = null;
722 if (mConfig.hasSearchBarAppWidget()) {
723 searchWidget = mSystemServicesProxy.getAppWidgetInfo(
724 mConfig.searchBarAppWidgetId);
725 } else {
726 searchWidget = mSystemServicesProxy.resolveSearchAppWidget();
727 }
728 if (searchWidget != null && searchWidget.provider != null) {
729 searchWidgetPackage = searchWidget.provider.getPackageName();
730 }
731 // Determine whether we are coming from a search owned home activity
732 boolean fromSearchHome = false;
733 if (homeActivityPackage != null && searchWidgetPackage != null &&
734 homeActivityPackage.equals(searchWidgetPackage)) {
735 fromSearchHome = true;
736 }
737
738 ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
Winson Chung860e2d82014-12-04 11:43:02 -0800739 startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
740 false /* fromThumbnail */, stackVr);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700741 } else {
Winson Chung743d5c92014-06-13 10:14:53 -0700742 // Otherwise we do the normal fade from an unknown source
Winson Chungd42a6cf2014-06-03 16:24:04 -0700743 ActivityOptions opts = getUnknownTransitionActivityOptions();
Winson Chung860e2d82014-12-04 11:43:02 -0800744 startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
745 false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700746 }
Winson Chung7048fea2014-03-18 12:21:24 -0700747 }
Winson Chung15a2ba82014-11-18 11:19:24 -0800748 mLastToggleTime = SystemClock.elapsedRealtime();
Winson Chung7048fea2014-03-18 12:21:24 -0700749 }
750
751 /** Starts the recents activity */
Winson Chungffa2ec62014-07-03 15:54:42 -0700752 void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
Winson Chung860e2d82014-12-04 11:43:02 -0800753 ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
Winson Chunga91c2932014-11-07 15:02:38 -0800754 TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
Winson Chung860e2d82014-12-04 11:43:02 -0800755 // Update the configuration based on the launch options
756 mConfig.launchedFromHome = fromSearchHome || fromHome;
757 mConfig.launchedFromSearchHome = fromSearchHome;
758 mConfig.launchedFromAppWithThumbnail = fromThumbnail;
759 mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
760 mConfig.launchedWithAltTab = mTriggeredFromAltTab;
761 mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
762 mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
763 mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
764 mConfig.launchedHasConfigurationChanged = false;
765
Winson Chung7048fea2014-03-18 12:21:24 -0700766 Intent intent = new Intent(sToggleRecentsAction);
767 intent.setClassName(sRecentsPackage, sRecentsActivity);
768 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Winson Chungd42a6cf2014-06-03 16:24:04 -0700769 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
770 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
Winson Chung7048fea2014-03-18 12:21:24 -0700771 if (opts != null) {
Winson Chung85cfec82014-07-14 14:16:04 -0700772 mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
Winson Chung7048fea2014-03-18 12:21:24 -0700773 } else {
Winson Chung85cfec82014-07-14 14:16:04 -0700774 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
Winson Chung7048fea2014-03-18 12:21:24 -0700775 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700776 mCanReuseTaskStackViews = true;
Winson Chung7048fea2014-03-18 12:21:24 -0700777 }
Winson Chung24cf1522014-05-29 12:03:33 -0700778
Winson Chung9214eff2014-06-12 13:59:25 -0700779 /** Sets the RecentsComponent callbacks. */
Jorim Jaggid61f2272014-12-19 20:35:35 +0100780 @Override
781 public void setCallback(RecentsComponent.Callbacks cb) {
Winson Chung9214eff2014-06-12 13:59:25 -0700782 sRecentsComponentCallbacks = cb;
783 }
784
785 /** Notifies the callbacks that the visibility of Recents has changed. */
Winson Chung2002cf52014-12-08 17:26:44 -0800786 @ProxyFromAnyToPrimaryUser
787 public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
788 boolean visible) {
789 if (ssp.isForegroundUserOwner()) {
790 visibilityChanged(visible);
791 } else {
792 Intent intent = createLocalBroadcastIntent(context,
793 ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
794 intent.putExtra(EXTRA_RECENTS_VISIBILITY, visible);
795 context.sendBroadcastAsUser(intent, UserHandle.OWNER);
796 }
797 }
798 static void visibilityChanged(boolean visible) {
Winson Chung9214eff2014-06-12 13:59:25 -0700799 if (sRecentsComponentCallbacks != null) {
800 sRecentsComponentCallbacks.onVisibilityChanged(visible);
801 }
802 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700803
Winson Chunga91c2932014-11-07 15:02:38 -0800804 /**
805 * Returns the preloaded load plan and invalidates it.
806 */
807 public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
808 RecentsTaskLoadPlan plan = sInstanceLoadPlan;
809 sInstanceLoadPlan = null;
810 return plan;
811 }
812
Winson Chungb0a28ea2014-10-28 15:21:35 -0700813 /**** OnAnimationStartedListener Implementation ****/
814
815 @Override
816 public void onAnimationStarted() {
817 // Notify recents to start the enter animation
818 if (!mStartAnimationTriggered) {
819 // There can be a race condition between the start animation callback and
820 // the start of the new activity (where we register the receiver that listens
821 // to this broadcast, so we add our own receiver and if that gets called, then
822 // we know the activity has not yet started and we can retry sending the broadcast.
823 BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
824 @Override
825 public void onReceive(Context context, Intent intent) {
826 if (getResultCode() == Activity.RESULT_OK) {
827 mStartAnimationTriggered = true;
828 return;
829 }
830
831 // Schedule for the broadcast to be sent again after some time
832 mHandler.postDelayed(new Runnable() {
833 @Override
834 public void run() {
835 onAnimationStarted();
836 }
837 }, 25);
838 }
839 };
840
841 // Send the broadcast to notify Recents that the animation has started
Winson Chung2002cf52014-12-08 17:26:44 -0800842 Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
Winson Chungb0a28ea2014-10-28 15:21:35 -0700843 mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
844 fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
845 }
846 }
Winson Chung7048fea2014-03-18 12:21:24 -0700847}