blob: 52ccb59eb66f5c490493c05ade3d9cff5c344547 [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
19import android.app.ActivityManager;
20import android.app.ActivityOptions;
21import android.content.ActivityNotFoundException;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.ServiceConnection;
Winson Chung7048fea2014-03-18 12:21:24 -070026import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.graphics.Bitmap;
29import android.graphics.Canvas;
Winson Chung7048fea2014-03-18 12:21:24 -070030import android.graphics.Rect;
Winson Chung7048fea2014-03-18 12:21:24 -070031import android.os.Bundle;
32import android.os.Handler;
33import android.os.IBinder;
34import android.os.Message;
35import android.os.Messenger;
36import android.os.RemoteException;
Winson Chung7048fea2014-03-18 12:21:24 -070037import android.os.UserHandle;
Winson Chung7048fea2014-03-18 12:21:24 -070038import android.view.View;
39import android.view.WindowManager;
40import com.android.systemui.R;
Winson Chung9214eff2014-06-12 13:59:25 -070041import com.android.systemui.RecentsComponent;
Winson Chung7048fea2014-03-18 12:21:24 -070042
Winson Chungd42a6cf2014-06-03 16:24:04 -070043import java.lang.ref.WeakReference;
Winson Chung0d767552014-04-09 14:33:23 -070044import java.util.Iterator;
Winson Chung7048fea2014-03-18 12:21:24 -070045import java.util.List;
Winson Chung1e8d71b2014-05-16 17:05:22 -070046import java.util.concurrent.atomic.AtomicBoolean;
Winson Chung7048fea2014-03-18 12:21:24 -070047
48/** A proxy implementation for the recents component */
Winson Chung24cf1522014-05-29 12:03:33 -070049public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
Winson Chung7048fea2014-03-18 12:21:24 -070050
51 /** A handler for messages from the recents implementation */
52 class RecentsMessageHandler extends Handler {
53 @Override
54 public void handleMessage(Message msg) {
55 if (msg.what == MSG_UPDATE_FOR_CONFIGURATION) {
56 Resources res = mContext.getResources();
57 float statusBarHeight = res.getDimensionPixelSize(
58 com.android.internal.R.dimen.status_bar_height);
Winson Chungb44c24f2014-04-09 15:17:43 -070059 Bundle replyData = msg.getData().getParcelable(KEY_CONFIGURATION_DATA);
60 mSingleCountFirstTaskRect = replyData.getParcelable(KEY_SINGLE_TASK_STACK_RECT);
Winson Chung0d767552014-04-09 14:33:23 -070061 mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
Winson Chung4f70dbb2014-05-27 18:02:52 -070062 mTwoCountFirstTaskRect = replyData.getParcelable(KEY_TWO_TASK_STACK_RECT);
63 mTwoCountFirstTaskRect.offset(0, (int) statusBarHeight);
Winson Chungb44c24f2014-04-09 15:17:43 -070064 mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
Winson Chung0d767552014-04-09 14:33:23 -070065 mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
Winson Chung10f81392014-05-20 16:21:31 -070066 if (Console.Enabled) {
67 Console.log(Constants.Log.App.RecentsComponent,
68 "[RecentsComponent|RecentsMessageHandler|handleMessage]",
69 "singleTaskRect: " + mSingleCountFirstTaskRect +
Winson Chung4f70dbb2014-05-27 18:02:52 -070070 " twoTaskRect: " + mTwoCountFirstTaskRect +
Winson Chung10f81392014-05-20 16:21:31 -070071 " multipleTaskRect: " + mMultipleCountFirstTaskRect);
72 }
Winson Chung96e3bc12014-05-06 16:44:12 -070073
74 // If we had the update the animation rects as a result of onServiceConnected, then
75 // we check for whether we need to toggle the recents here.
76 if (mToggleRecentsUponServiceBound) {
Winson Chung1e8d71b2014-05-16 17:05:22 -070077 startRecentsActivity();
Winson Chung96e3bc12014-05-06 16:44:12 -070078 mToggleRecentsUponServiceBound = false;
79 }
Winson Chung7048fea2014-03-18 12:21:24 -070080 }
81 }
82 }
83
84 /** A service connection to the recents implementation */
85 class RecentsServiceConnection implements ServiceConnection {
86 @Override
87 public void onServiceConnected(ComponentName className, IBinder service) {
Winson Chung10f81392014-05-20 16:21:31 -070088 if (Console.Enabled) {
89 Console.log(Constants.Log.App.RecentsComponent,
90 "[RecentsComponent|ServiceConnection|onServiceConnected]",
91 "toggleRecents: " + mToggleRecentsUponServiceBound);
92 }
Winson Chung7048fea2014-03-18 12:21:24 -070093 mService = new Messenger(service);
94 mServiceIsBound = true;
95
Winson Chung96e3bc12014-05-06 16:44:12 -070096 if (hasValidTaskRects()) {
Winson Chung1e8d71b2014-05-16 17:05:22 -070097 // Start recents if this new service connection was triggered by hitting recents
Winson Chung96e3bc12014-05-06 16:44:12 -070098 if (mToggleRecentsUponServiceBound) {
Winson Chung1e8d71b2014-05-16 17:05:22 -070099 startRecentsActivity();
Winson Chung96e3bc12014-05-06 16:44:12 -0700100 mToggleRecentsUponServiceBound = false;
101 }
102 } else {
103 // Otherwise, update the animation rects before starting the recents if requested
104 updateAnimationRects();
Winson Chung7048fea2014-03-18 12:21:24 -0700105 }
Winson Chung7048fea2014-03-18 12:21:24 -0700106 }
107
108 @Override
109 public void onServiceDisconnected(ComponentName className) {
Winson Chung10f81392014-05-20 16:21:31 -0700110 if (Console.Enabled) {
111 Console.log(Constants.Log.App.RecentsComponent,
112 "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
113 }
Winson Chung7048fea2014-03-18 12:21:24 -0700114 mService = null;
115 mServiceIsBound = false;
116 }
117 }
118
Winson Chungb44c24f2014-04-09 15:17:43 -0700119 final public static int MSG_UPDATE_FOR_CONFIGURATION = 0;
120 final public static int MSG_UPDATE_TASK_THUMBNAIL = 1;
121 final public static int MSG_PRELOAD_TASKS = 2;
122 final public static int MSG_CANCEL_PRELOAD_TASKS = 3;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700123 final public static int MSG_SHOW_RECENTS = 4;
124 final public static int MSG_HIDE_RECENTS = 5;
125 final public static int MSG_TOGGLE_RECENTS = 6;
Winson Chung24cf1522014-05-29 12:03:33 -0700126 final public static int MSG_START_ENTER_ANIMATION = 7;
Winson Chungb44c24f2014-04-09 15:17:43 -0700127
Winson Chungd42a6cf2014-06-03 16:24:04 -0700128 final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
129 final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
130 final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
131 final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
Winson Chungb44c24f2014-04-09 15:17:43 -0700132 final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
133 final public static String KEY_WINDOW_RECT = "recents.windowRect";
134 final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
135 final public static String KEY_SINGLE_TASK_STACK_RECT = "recents.singleCountTaskRect";
Winson Chung4f70dbb2014-05-27 18:02:52 -0700136 final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
Winson Chungb44c24f2014-04-09 15:17:43 -0700137 final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
138
Winson Chungbd912972014-03-18 14:36:35 -0700139 final static int sMinToggleDelay = 425;
Winson Chung7048fea2014-03-18 12:21:24 -0700140
141 final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
142 final static String sRecentsPackage = "com.android.systemui";
143 final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
144 final static String sRecentsService = "com.android.systemui.recents.RecentsService";
145
Winson Chungd42a6cf2014-06-03 16:24:04 -0700146 static Bitmap sLastScreenshot;
Winson Chung9214eff2014-06-12 13:59:25 -0700147 static RecentsComponent.Callbacks sRecentsComponentCallbacks;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700148
Winson Chung7048fea2014-03-18 12:21:24 -0700149 Context mContext;
Winson Chunga10370f2014-04-02 12:25:04 -0700150 SystemServicesProxy mSystemServicesProxy;
Winson Chung7048fea2014-03-18 12:21:24 -0700151
152 // Recents service binding
153 Messenger mService = null;
154 Messenger mMessenger;
Winson Chung521e7dc2014-06-02 15:31:56 -0700155 RecentsMessageHandler mHandler;
Winson Chung5abdceb2014-06-05 10:58:05 -0700156 boolean mBootCompleted = false;
Winson Chung7048fea2014-03-18 12:21:24 -0700157 boolean mServiceIsBound = false;
158 boolean mToggleRecentsUponServiceBound;
159 RecentsServiceConnection mConnection = new RecentsServiceConnection();
160
Winson Chung1e8d71b2014-05-16 17:05:22 -0700161 // Variables to keep track of if we need to start recents after binding
Winson Chung7048fea2014-03-18 12:21:24 -0700162 View mStatusBarView;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700163 boolean mTriggeredFromAltTab;
164
Winson Chung0d767552014-04-09 14:33:23 -0700165 Rect mSingleCountFirstTaskRect = new Rect();
Winson Chung4f70dbb2014-05-27 18:02:52 -0700166 Rect mTwoCountFirstTaskRect = new Rect();
Winson Chung0d767552014-04-09 14:33:23 -0700167 Rect mMultipleCountFirstTaskRect = new Rect();
Winson Chung7048fea2014-03-18 12:21:24 -0700168 long mLastToggleTime;
169
170 public AlternateRecentsComponent(Context context) {
171 mContext = context;
Winson Chunga10370f2014-04-02 12:25:04 -0700172 mSystemServicesProxy = new SystemServicesProxy(context);
Winson Chung521e7dc2014-06-02 15:31:56 -0700173 mHandler = new RecentsMessageHandler();
174 mMessenger = new Messenger(mHandler);
Winson Chung7048fea2014-03-18 12:21:24 -0700175 }
176
177 public void onStart() {
Winson Chung10f81392014-05-20 16:21:31 -0700178 if (Console.Enabled) {
179 Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
180 }
Winson Chung7048fea2014-03-18 12:21:24 -0700181
182 // Try to create a long-running connection to the recents service
183 bindToRecentsService(false);
184 }
185
Winson Chung5abdceb2014-06-05 10:58:05 -0700186 public void onBootCompleted() {
187 mBootCompleted = true;
188 }
189
Winson Chung1e8d71b2014-05-16 17:05:22 -0700190 /** Shows the recents */
191 public void onShowRecents(boolean triggeredFromAltTab, View statusBarView) {
Winson Chung10f81392014-05-20 16:21:31 -0700192 if (Console.Enabled) {
193 Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|showRecents]");
194 }
Winson Chung7048fea2014-03-18 12:21:24 -0700195 mStatusBarView = statusBarView;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700196 mTriggeredFromAltTab = triggeredFromAltTab;
Winson Chung7048fea2014-03-18 12:21:24 -0700197 if (!mServiceIsBound) {
198 // Try to create a long-running connection to the recents service before toggling
199 // recents
200 bindToRecentsService(true);
201 return;
202 }
203
204 try {
Winson Chung1e8d71b2014-05-16 17:05:22 -0700205 startRecentsActivity();
206 } catch (ActivityNotFoundException e) {
207 Console.logRawError("Failed to launch RecentAppsIntent", e);
208 }
209 }
210
211 /** Hides the recents */
Winson Chung6cb485f2014-05-19 10:30:43 -0700212 public void onHideRecents(boolean triggeredFromAltTab) {
Winson Chung10f81392014-05-20 16:21:31 -0700213 if (Console.Enabled) {
214 Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
215 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700216
Winson Chung5abdceb2014-06-05 10:58:05 -0700217 if (mServiceIsBound && mBootCompleted) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700218 if (isRecentsTopMost(null)) {
219 // Notify recents to close it
220 try {
221 Bundle data = new Bundle();
222 Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
223 triggeredFromAltTab ? 1 : 0, 0);
224 msg.setData(data);
225 mService.send(msg);
226 } catch (RemoteException re) {
227 re.printStackTrace();
228 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700229 }
230 }
231 }
232
233 /** Toggles the alternate recents activity */
234 public void onToggleRecents(View statusBarView) {
Winson Chung10f81392014-05-20 16:21:31 -0700235 if (Console.Enabled) {
236 Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup,
237 Constants.Log.App.TimeRecentsStartupKey);
238 Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
239 Constants.Log.App.TimeRecentsLaunchKey);
240 Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
241 "serviceIsBound: " + mServiceIsBound);
242 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700243 mStatusBarView = statusBarView;
244 mTriggeredFromAltTab = false;
245 if (!mServiceIsBound) {
246 // Try to create a long-running connection to the recents service before toggling
247 // recents
248 bindToRecentsService(true);
249 return;
250 }
251
252 try {
253 toggleRecentsActivity();
Winson Chung7048fea2014-03-18 12:21:24 -0700254 } catch (ActivityNotFoundException e) {
255 Console.logRawError("Failed to launch RecentAppsIntent", e);
256 }
257 }
258
259 public void onPreloadRecents() {
260 // Do nothing
261 }
262
263 public void onCancelPreloadingRecents() {
264 // Do nothing
265 }
266
Winson Chung7048fea2014-03-18 12:21:24 -0700267 public void onConfigurationChanged(Configuration newConfig) {
Winson Chung96e3bc12014-05-06 16:44:12 -0700268 updateAnimationRects();
269 }
270
271 /** Binds to the recents implementation */
272 private void bindToRecentsService(boolean toggleRecentsUponConnection) {
273 mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
274 Intent intent = new Intent();
275 intent.setClassName(sRecentsPackage, sRecentsService);
276 mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
277 }
278
279 /** Returns whether we have valid task rects to animate to. */
280 boolean hasValidTaskRects() {
281 return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
Winson Chung4f70dbb2014-05-27 18:02:52 -0700282 mSingleCountFirstTaskRect.height() > 0 && mTwoCountFirstTaskRect != null &&
283 mTwoCountFirstTaskRect.width() > 0 && mTwoCountFirstTaskRect.height() > 0 &&
284 mMultipleCountFirstTaskRect != null && mMultipleCountFirstTaskRect.width() > 0 &&
285 mMultipleCountFirstTaskRect.height() > 0;
Winson Chung96e3bc12014-05-06 16:44:12 -0700286 }
287
288 /** Updates each of the task animation rects. */
289 void updateAnimationRects() {
Winson Chung47f166d2014-06-13 08:55:33 -0700290 if (mServiceIsBound) {
Winson Chung7048fea2014-03-18 12:21:24 -0700291 Resources res = mContext.getResources();
292 int statusBarHeight = res.getDimensionPixelSize(
293 com.android.internal.R.dimen.status_bar_height);
294 int navBarHeight = res.getDimensionPixelSize(
295 com.android.internal.R.dimen.navigation_bar_height);
296 Rect rect = new Rect();
297 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
298 wm.getDefaultDisplay().getRectSize(rect);
299
300 // Try and update the recents configuration
301 try {
302 Bundle data = new Bundle();
Winson Chungb44c24f2014-04-09 15:17:43 -0700303 data.putParcelable(KEY_WINDOW_RECT, rect);
304 data.putParcelable(KEY_SYSTEM_INSETS, new Rect(0, statusBarHeight, 0, 0));
Winson Chung7048fea2014-03-18 12:21:24 -0700305 Message msg = Message.obtain(null, MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
306 msg.setData(data);
307 msg.replyTo = mMessenger;
308 mService.send(msg);
309 } catch (RemoteException re) {
310 re.printStackTrace();
311 }
312 }
313 }
314
Winson Chung7048fea2014-03-18 12:21:24 -0700315 /** Loads the first task thumbnail */
316 Bitmap loadFirstTaskThumbnail() {
Winson Chunga10370f2014-04-02 12:25:04 -0700317 SystemServicesProxy ssp = mSystemServicesProxy;
Winson Chung918c0722014-05-08 15:04:23 -0700318 List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
Winson Chung7048fea2014-03-18 12:21:24 -0700319
Winson Chung918c0722014-05-08 15:04:23 -0700320 for (ActivityManager.RunningTaskInfo t : tasks) {
321 return ssp.getTaskThumbnail(t.id);
Winson Chung7048fea2014-03-18 12:21:24 -0700322 }
323 return null;
324 }
325
Winson Chung4f70dbb2014-05-27 18:02:52 -0700326 /** Returns the proper rect to use for the animation, given the number of tasks. */
327 Rect getAnimationTaskRect(List<ActivityManager.RecentTaskInfo> tasks) {
Winson Chung0d767552014-04-09 14:33:23 -0700328 // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
329 // compute this ourselves
Winson Chunga10370f2014-04-02 12:25:04 -0700330 SystemServicesProxy ssp = mSystemServicesProxy;
Winson Chung0d767552014-04-09 14:33:23 -0700331 Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
332 while (iter.hasNext()) {
333 ActivityManager.RecentTaskInfo t = iter.next();
334
Winson Chung7048fea2014-03-18 12:21:24 -0700335 // Skip tasks in the home stack
Winson Chunga10370f2014-04-02 12:25:04 -0700336 if (ssp.isInHomeStack(t.persistentId)) {
Winson Chung0d767552014-04-09 14:33:23 -0700337 iter.remove();
Winson Chung7048fea2014-03-18 12:21:24 -0700338 continue;
339 }
Winson Chung7048fea2014-03-18 12:21:24 -0700340 }
Winson Chung4f70dbb2014-05-27 18:02:52 -0700341 if (tasks.size() <= 1) {
342 return mSingleCountFirstTaskRect;
343 } else if (tasks.size() <= 2) {
344 return mTwoCountFirstTaskRect;
345 } else {
346 return mMultipleCountFirstTaskRect;
347 }
Winson Chung7048fea2014-03-18 12:21:24 -0700348 }
349
Winson Chung1e8d71b2014-05-16 17:05:22 -0700350 /** Returns whether the recents is currently running */
351 boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
352 SystemServicesProxy ssp = mSystemServicesProxy;
353 List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
354 if (!tasks.isEmpty()) {
355 ActivityManager.RunningTaskInfo topTask = tasks.get(0);
356 ComponentName topActivity = topTask.topActivity;
357
358 // Check if the front most activity is recents
359 if (topActivity.getPackageName().equals(sRecentsPackage) &&
360 topActivity.getClassName().equals(sRecentsActivity)) {
361 if (isHomeTopMost != null) {
362 isHomeTopMost.set(false);
363 }
364 return true;
365 }
366
367 if (isHomeTopMost != null) {
368 isHomeTopMost.set(ssp.isInHomeStack(topTask.id));
369 }
370 }
371 return false;
372 }
373
374 /** Toggles the recents activity */
375 void toggleRecentsActivity() {
Winson Chung7048fea2014-03-18 12:21:24 -0700376 // If the user has toggled it too quickly, then just eat up the event here (it's better than
377 // showing a janky screenshot).
378 // NOTE: Ideally, the screenshot mechanism would take the window transform into account
379 if (System.currentTimeMillis() - mLastToggleTime < sMinToggleDelay) {
380 return;
381 }
382
383 // If Recents is the front most activity, then we should just communicate with it directly
384 // to launch the first task or dismiss itself
Winson Chung1e8d71b2014-05-16 17:05:22 -0700385 AtomicBoolean isTopTaskHome = new AtomicBoolean();
386 if (isRecentsTopMost(isTopTaskHome)) {
387 // Notify recents to close itself
388 try {
389 Bundle data = new Bundle();
390 Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
391 msg.setData(data);
392 mService.send(msg);
Winson Chung7048fea2014-03-18 12:21:24 -0700393
Winson Chung1e8d71b2014-05-16 17:05:22 -0700394 // Time this path
Winson Chungd42a6cf2014-06-03 16:24:04 -0700395 if (Console.Enabled) {
396 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
397 Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
398 Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
399 Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
400 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700401 } catch (RemoteException re) {
402 re.printStackTrace();
Winson Chung7048fea2014-03-18 12:21:24 -0700403 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700404 mLastToggleTime = System.currentTimeMillis();
405 return;
406 } else {
407 // Otherwise, start the recents activity
408 startRecentsActivity(isTopTaskHome.get());
Winson Chung7048fea2014-03-18 12:21:24 -0700409 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700410 }
Winson Chung7048fea2014-03-18 12:21:24 -0700411
Winson Chung1e8d71b2014-05-16 17:05:22 -0700412 /** Starts the recents activity if it is not already running */
413 void startRecentsActivity() {
414 // Check if the top task is in the home stack, and start the recents activity
415 AtomicBoolean isTopTaskHome = new AtomicBoolean();
416 if (!isRecentsTopMost(isTopTaskHome)) {
417 startRecentsActivity(isTopTaskHome.get());
418 }
419 }
420
Winson Chungd42a6cf2014-06-03 16:24:04 -0700421 /**
422 * Creates the activity options for a unknown state->recents transition.
423 */
424 ActivityOptions getUnknownTransitionActivityOptions() {
425 // Reset the last screenshot
426 consumeLastScreenshot();
427 return ActivityOptions.makeCustomAnimation(mContext,
428 R.anim.recents_from_unknown_enter,
429 R.anim.recents_from_unknown_exit, mHandler, this);
430 }
431
432 /**
433 * Creates the activity options for a home->recents transition.
434 */
435 ActivityOptions getHomeTransitionActivityOptions() {
436 // Reset the last screenshot
437 consumeLastScreenshot();
438 return ActivityOptions.makeCustomAnimation(mContext,
439 R.anim.recents_from_launcher_enter,
440 R.anim.recents_from_launcher_exit, mHandler, this);
441 }
442
443 /**
444 * Creates the activity options for an app->recents transition. If this method sets the static
445 * screenshot, then we will use that for the transition.
446 */
447 ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
448 // Recycle the last screenshot
449 consumeLastScreenshot();
450
451 // Take the full screenshot
452 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
453 sLastScreenshot = mSystemServicesProxy.takeScreenshot();
454 if (sLastScreenshot != null) {
455 return ActivityOptions.makeCustomAnimation(mContext,
456 R.anim.recents_from_app_enter,
457 R.anim.recents_from_app_exit, mHandler, this);
458 }
459 }
460
461 // If the screenshot fails, then load the first task thumbnail and use that
462 Bitmap firstThumbnail = loadFirstTaskThumbnail();
463 if (firstThumbnail != null) {
464 // Create the new thumbnail for the animation down
465 // XXX: We should find a way to optimize this so we don't need to create a new bitmap
466 Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
467 Bitmap.Config.ARGB_8888);
468 int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
469 Canvas c = new Canvas(thumbnail);
470 c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
471 new Rect(0, 0, taskRect.width(), taskRect.height()), null);
472 c.setBitmap(null);
473 // Recycle the old thumbnail
474 firstThumbnail.recycle();
475 return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
476 thumbnail, taskRect.left, taskRect.top, this);
477 }
478
479 // If both the screenshot and thumbnail fails, then just fall back to the default transition
480 return getUnknownTransitionActivityOptions();
481 }
482
Winson Chung1e8d71b2014-05-16 17:05:22 -0700483 /** Starts the recents activity */
484 void startRecentsActivity(boolean isTopTaskHome) {
485 // If Recents is not the front-most activity and we should animate into it. If
Winson Chung918c0722014-05-08 15:04:23 -0700486 // the activity at the root of the top task stack in the home stack, then we just do a
487 // simple transition. Otherwise, we animate to the rects defined by the Recents service,
488 // which can differ depending on the number of items in the list.
Winson Chung1e8d71b2014-05-16 17:05:22 -0700489 SystemServicesProxy ssp = mSystemServicesProxy;
Winson Chung9520d1a2014-04-21 11:27:43 -0700490 List<ActivityManager.RecentTaskInfo> recentTasks =
Winson Chung4f70dbb2014-05-27 18:02:52 -0700491 ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier());
492 Rect taskRect = getAnimationTaskRect(recentTasks);
Winson Chung918c0722014-05-08 15:04:23 -0700493 boolean useThumbnailTransition = !isTopTaskHome &&
Winson Chung96e3bc12014-05-06 16:44:12 -0700494 hasValidTaskRects();
Winson Chung743d5c92014-06-13 10:14:53 -0700495 boolean hasRecentTasks = !recentTasks.isEmpty();
Winson Chung7048fea2014-03-18 12:21:24 -0700496
Winson Chung96e3bc12014-05-06 16:44:12 -0700497 if (useThumbnailTransition) {
498 // Try starting with a thumbnail transition
499 ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
500 if (opts != null) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700501 if (sLastScreenshot != null) {
502 startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
503 } else {
504 startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
505 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700506 } else {
507 // Fall through below to the non-thumbnail transition
508 useThumbnailTransition = false;
509 }
Winson Chung743d5c92014-06-13 10:14:53 -0700510 } else {
511 // If there is no thumbnail transition, but is launching from home into recents, then
512 // use a quick home transition and do the animation from home
Winson Chungd7b2cb12014-06-26 15:08:50 -0700513 if (hasRecentTasks) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700514 ActivityOptions opts = getHomeTransitionActivityOptions();
515 startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
516 } else {
Winson Chung743d5c92014-06-13 10:14:53 -0700517 // Otherwise we do the normal fade from an unknown source
Winson Chungd42a6cf2014-06-03 16:24:04 -0700518 ActivityOptions opts = getUnknownTransitionActivityOptions();
519 startAlternateRecentsActivity(opts, null);
520 }
Winson Chung7048fea2014-03-18 12:21:24 -0700521 }
Winson Chungbd912972014-03-18 14:36:35 -0700522
Winson Chungd42a6cf2014-06-03 16:24:04 -0700523 if (Console.Enabled) {
524 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
525 Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
526 }
Winson Chung7048fea2014-03-18 12:21:24 -0700527 mLastToggleTime = System.currentTimeMillis();
528 }
529
530 /** Starts the recents activity */
Winson Chungd42a6cf2014-06-03 16:24:04 -0700531 void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
Winson Chung7048fea2014-03-18 12:21:24 -0700532 Intent intent = new Intent(sToggleRecentsAction);
533 intent.setClassName(sRecentsPackage, sRecentsActivity);
534 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Winson Chungd42a6cf2014-06-03 16:24:04 -0700535 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
536 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
537 if (extraFlag != null) {
538 intent.putExtra(extraFlag, true);
539 }
540 intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
Winson Chung7048fea2014-03-18 12:21:24 -0700541 if (opts != null) {
542 mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
543 UserHandle.USER_CURRENT));
544 } else {
545 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
546 }
547 }
Winson Chung24cf1522014-05-29 12:03:33 -0700548
Winson Chungd42a6cf2014-06-03 16:24:04 -0700549 /** Returns the last screenshot taken, this will be called by the RecentsActivity. */
550 public static Bitmap getLastScreenshot() {
551 return sLastScreenshot;
552 }
553
554 /** Recycles the last screenshot taken, this will be called by the RecentsActivity. */
555 public static void consumeLastScreenshot() {
556 if (sLastScreenshot != null) {
557 sLastScreenshot.recycle();
558 sLastScreenshot = null;
559 }
560 }
Winson Chung24cf1522014-05-29 12:03:33 -0700561
Winson Chung9214eff2014-06-12 13:59:25 -0700562 /** Sets the RecentsComponent callbacks. */
563 public void setRecentsComponentCallback(RecentsComponent.Callbacks cb) {
564 sRecentsComponentCallbacks = cb;
565 }
566
567 /** Notifies the callbacks that the visibility of Recents has changed. */
568 public static void notifyVisibilityChanged(boolean visible) {
569 if (sRecentsComponentCallbacks != null) {
570 sRecentsComponentCallbacks.onVisibilityChanged(visible);
571 }
572 }
573
Winson Chung24cf1522014-05-29 12:03:33 -0700574 /**** OnAnimationStartedListener Implementation ****/
575
576 @Override
577 public void onAnimationStarted() {
578 // Notify recents to start the enter animation
579 try {
580 Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
581 mService.send(msg);
582 } catch (RemoteException re) {
583 re.printStackTrace();
584 }
585 }
Winson Chung7048fea2014-03-18 12:21:24 -0700586}