blob: 34430d9945f452a81c0c741081e110d8e46ad993 [file] [log] [blame]
Michael Jurkaab48b682011-09-12 15:39:45 -07001/*
2 * Copyright (C) 2011 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.recent;
18
Michael Jurkaab48b682011-09-12 15:39:45 -070019import android.app.ActivityManager;
Amith Yamasani4f0a49e2014-04-10 13:48:15 -070020import android.app.AppGlobals;
Michael Jurkaab48b682011-09-12 15:39:45 -070021import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ActivityInfo;
Amith Yamasani4f0a49e2014-04-10 13:48:15 -070025import android.content.pm.IPackageManager;
Michael Jurkaab48b682011-09-12 15:39:45 -070026import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.res.Resources;
29import android.graphics.Bitmap;
Michael Jurka99262722013-09-17 14:18:04 +020030import android.graphics.drawable.BitmapDrawable;
Michael Jurkaab48b682011-09-12 15:39:45 -070031import android.graphics.drawable.Drawable;
32import android.os.AsyncTask;
33import android.os.Handler;
34import android.os.Process;
Amith Yamasani4f0a49e2014-04-10 13:48:15 -070035import android.os.RemoteException;
Michael Jurka80343f62012-10-18 13:13:46 +020036import android.os.UserHandle;
Michael Jurkaab48b682011-09-12 15:39:45 -070037import android.util.Log;
Michael Jurkacb2522c2012-04-13 09:32:47 -070038import android.view.MotionEvent;
39import android.view.View;
Michael Jurkaab48b682011-09-12 15:39:45 -070040
41import com.android.systemui.R;
Winson Chungffa2ec62014-07-03 15:54:42 -070042import com.android.systemui.recents.misc.SystemServicesProxy;
Michael Jurkaab48b682011-09-12 15:39:45 -070043import com.android.systemui.statusbar.phone.PhoneStatusBar;
Michael Jurkaab48b682011-09-12 15:39:45 -070044
Michael Jurka99a96552012-01-27 17:23:38 -080045import java.util.ArrayList;
46import java.util.List;
47import java.util.concurrent.BlockingQueue;
48import java.util.concurrent.LinkedBlockingQueue;
49
Michael Jurkacb2522c2012-04-13 09:32:47 -070050public class RecentTasksLoader implements View.OnTouchListener {
Michael Jurkaab48b682011-09-12 15:39:45 -070051 static final String TAG = "RecentTasksLoader";
John Spurlockb0e49fc2013-06-12 15:50:50 -040052 static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
Michael Jurkaab48b682011-09-12 15:39:45 -070053
54 private static final int DISPLAY_TASKS = 20;
55 private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
56
57 private Context mContext;
58 private RecentsPanelView mRecentsPanel;
Michael Jurka80343f62012-10-18 13:13:46 +020059
60 private Object mFirstTaskLock = new Object();
Michael Jurkacb2522c2012-04-13 09:32:47 -070061 private TaskDescription mFirstTask;
62 private boolean mFirstTaskLoaded;
Michael Jurkaab48b682011-09-12 15:39:45 -070063
Michael Jurka99a96552012-01-27 17:23:38 -080064 private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
65 private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
Michael Jurkacb2522c2012-04-13 09:32:47 -070066 private Handler mHandler;
Michael Jurkaab48b682011-09-12 15:39:45 -070067
68 private int mIconDpi;
Michael Jurka99262722013-09-17 14:18:04 +020069 private ColorDrawableWithDimensions mDefaultThumbnailBackground;
70 private ColorDrawableWithDimensions mDefaultIconBackground;
Michael Jurkacb2522c2012-04-13 09:32:47 -070071 private int mNumTasksInFirstScreenful = Integer.MAX_VALUE;
72
73 private boolean mFirstScreenful;
74 private ArrayList<TaskDescription> mLoadedTasks;
75
76 private enum State { LOADING, LOADED, CANCELLED };
77 private State mState = State.CANCELLED;
78
Michael Jurka80343f62012-10-18 13:13:46 +020079
80 private static RecentTasksLoader sInstance;
81 public static RecentTasksLoader getInstance(Context context) {
82 if (sInstance == null) {
83 sInstance = new RecentTasksLoader(context);
Michael Jurkacb2522c2012-04-13 09:32:47 -070084 }
Michael Jurka80343f62012-10-18 13:13:46 +020085 return sInstance;
Michael Jurkacb2522c2012-04-13 09:32:47 -070086 }
Michael Jurkaab48b682011-09-12 15:39:45 -070087
Michael Jurka80343f62012-10-18 13:13:46 +020088 private RecentTasksLoader(Context context) {
Michael Jurkaab48b682011-09-12 15:39:45 -070089 mContext = context;
Michael Jurkacb2522c2012-04-13 09:32:47 -070090 mHandler = new Handler();
Michael Jurkaab48b682011-09-12 15:39:45 -070091
92 final Resources res = context.getResources();
93
94 // get the icon size we want -- on tablets, we use bigger icons
95 boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
Michael Jurkaab48b682011-09-12 15:39:45 -070096 if (isTablet) {
Winson Chung43e34f62012-01-24 15:26:59 -080097 ActivityManager activityManager =
98 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
99 mIconDpi = activityManager.getLauncherLargeIconDensity();
Michael Jurkaab48b682011-09-12 15:39:45 -0700100 } else {
101 mIconDpi = res.getDisplayMetrics().densityDpi;
102 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700103
Michael Jurka99a96552012-01-27 17:23:38 -0800104 // Render default icon (just a blank image)
105 int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
106 int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
Michael Jurka99262722013-09-17 14:18:04 +0200107 mDefaultIconBackground = new ColorDrawableWithDimensions(0x00000000, iconSize, iconSize);
Michael Jurka99a96552012-01-27 17:23:38 -0800108
Michael Jurkaab48b682011-09-12 15:39:45 -0700109 // Render the default thumbnail background
Michael Jurka99a96552012-01-27 17:23:38 -0800110 int thumbnailWidth =
111 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
112 int thumbnailHeight =
113 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
Michael Jurkaab48b682011-09-12 15:39:45 -0700114 int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
115
Michael Jurka99a96552012-01-27 17:23:38 -0800116 mDefaultThumbnailBackground =
Michael Jurka99262722013-09-17 14:18:04 +0200117 new ColorDrawableWithDimensions(color, thumbnailWidth, thumbnailHeight);
Michael Jurkaab48b682011-09-12 15:39:45 -0700118 }
119
Michael Jurkacb2522c2012-04-13 09:32:47 -0700120 public void setRecentsPanel(RecentsPanelView newRecentsPanel, RecentsPanelView caller) {
121 // Only allow clearing mRecentsPanel if the caller is the current recentsPanel
122 if (newRecentsPanel != null || mRecentsPanel == caller) {
123 mRecentsPanel = newRecentsPanel;
124 if (mRecentsPanel != null) {
125 mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
126 }
127 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700128 }
129
Michael Jurka99262722013-09-17 14:18:04 +0200130 public Drawable getDefaultThumbnail() {
Michael Jurka412cba82011-10-17 09:05:00 -0700131 return mDefaultThumbnailBackground;
132 }
133
Michael Jurka99262722013-09-17 14:18:04 +0200134 public Drawable getDefaultIcon() {
Michael Jurka99a96552012-01-27 17:23:38 -0800135 return mDefaultIconBackground;
136 }
137
Michael Jurkacb2522c2012-04-13 09:32:47 -0700138 public ArrayList<TaskDescription> getLoadedTasks() {
139 return mLoadedTasks;
140 }
141
Michael Jurkae57922c2012-11-26 16:05:12 -0800142 public void remove(TaskDescription td) {
143 mLoadedTasks.remove(td);
144 }
145
Michael Jurkacb2522c2012-04-13 09:32:47 -0700146 public boolean isFirstScreenful() {
147 return mFirstScreenful;
148 }
149
150 private boolean isCurrentHomeActivity(ComponentName component, ActivityInfo homeInfo) {
151 if (homeInfo == null) {
152 final PackageManager pm = mContext.getPackageManager();
153 homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
154 .resolveActivityInfo(pm, 0);
155 }
156 return homeInfo != null
157 && homeInfo.packageName.equals(component.getPackageName())
158 && homeInfo.name.equals(component.getClassName());
159 }
160
161 // Create an TaskDescription, returning null if the title or icon is null
Michael Jurkaab48b682011-09-12 15:39:45 -0700162 TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
Kenny Guy1f1cce12014-04-04 14:47:50 +0100163 ComponentName origActivity, CharSequence description, int userId) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700164 Intent intent = new Intent(baseIntent);
165 if (origActivity != null) {
166 intent.setComponent(origActivity);
167 }
168 final PackageManager pm = mContext.getPackageManager();
Amith Yamasani4f0a49e2014-04-10 13:48:15 -0700169 final IPackageManager ipm = AppGlobals.getPackageManager();
Michael Jurkaab48b682011-09-12 15:39:45 -0700170 intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
171 | Intent.FLAG_ACTIVITY_NEW_TASK);
Amith Yamasani4f0a49e2014-04-10 13:48:15 -0700172 ResolveInfo resolveInfo = null;
173 try {
174 resolveInfo = ipm.resolveIntent(intent, null, 0, userId);
175 } catch (RemoteException re) {
176 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700177 if (resolveInfo != null) {
178 final ActivityInfo info = resolveInfo.activityInfo;
179 final String title = info.loadLabel(pm).toString();
Michael Jurkaab48b682011-09-12 15:39:45 -0700180
Michael Jurka99a96552012-01-27 17:23:38 -0800181 if (title != null && title.length() > 0) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700182 if (DEBUG) Log.v(TAG, "creating activity desc for id="
183 + persistentTaskId + ", label=" + title);
184
185 TaskDescription item = new TaskDescription(taskId,
186 persistentTaskId, resolveInfo, baseIntent, info.packageName,
Kenny Guy1f1cce12014-04-04 14:47:50 +0100187 description, userId);
Michael Jurkaab48b682011-09-12 15:39:45 -0700188 item.setLabel(title);
Michael Jurkaab48b682011-09-12 15:39:45 -0700189
190 return item;
191 } else {
192 if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
193 }
194 }
195 return null;
196 }
197
Michael Jurka99a96552012-01-27 17:23:38 -0800198 void loadThumbnailAndIcon(TaskDescription td) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700199 final ActivityManager am = (ActivityManager)
200 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurka99a96552012-01-27 17:23:38 -0800201 final PackageManager pm = mContext.getPackageManager();
Winson Chungffa2ec62014-07-03 15:54:42 -0700202 final Bitmap thumbnail = SystemServicesProxy.getThumbnail(am, td.persistentTaskId);
Michael Jurka99a96552012-01-27 17:23:38 -0800203 Drawable icon = getFullResIcon(td.resolveInfo, pm);
Amith Yamasani4f0a49e2014-04-10 13:48:15 -0700204 if (td.userId != UserHandle.myUserId()) {
205 // Need to badge the icon
Amith Yamasanifdf169c2014-09-07 15:45:57 -0700206 icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(td.userId));
Amith Yamasani4f0a49e2014-04-10 13:48:15 -0700207 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700208 if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
Dianne Hackborn15491c62012-09-19 10:59:14 -0700209 + td + ": " + thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700210 synchronized (td) {
Dianne Hackborn15491c62012-09-19 10:59:14 -0700211 if (thumbnail != null) {
Michael Jurka99262722013-09-17 14:18:04 +0200212 td.setThumbnail(new BitmapDrawable(mContext.getResources(), thumbnail));
Michael Jurkaab48b682011-09-12 15:39:45 -0700213 } else {
214 td.setThumbnail(mDefaultThumbnailBackground);
215 }
Michael Jurka99a96552012-01-27 17:23:38 -0800216 if (icon != null) {
217 td.setIcon(icon);
218 }
219 td.setLoaded(true);
Michael Jurkaab48b682011-09-12 15:39:45 -0700220 }
221 }
222
223 Drawable getFullResDefaultActivityIcon() {
224 return getFullResIcon(Resources.getSystem(),
225 com.android.internal.R.mipmap.sym_def_app_icon);
226 }
227
228 Drawable getFullResIcon(Resources resources, int iconId) {
229 try {
230 return resources.getDrawableForDensity(iconId, mIconDpi);
231 } catch (Resources.NotFoundException e) {
232 return getFullResDefaultActivityIcon();
233 }
234 }
235
236 private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
237 Resources resources;
238 try {
239 resources = packageManager.getResourcesForApplication(
240 info.activityInfo.applicationInfo);
241 } catch (PackageManager.NameNotFoundException e) {
242 resources = null;
243 }
244 if (resources != null) {
245 int iconId = info.activityInfo.getIconResource();
246 if (iconId != 0) {
247 return getFullResIcon(resources, iconId);
248 }
249 }
250 return getFullResDefaultActivityIcon();
251 }
252
Michael Jurkacb2522c2012-04-13 09:32:47 -0700253 Runnable mPreloadTasksRunnable = new Runnable() {
254 public void run() {
255 loadTasksInBackground();
256 }
257 };
258
259 // additional optimization when we have software system buttons - start loading the recent
260 // tasks on touch down
261 @Override
262 public boolean onTouch(View v, MotionEvent ev) {
263 int action = ev.getAction() & MotionEvent.ACTION_MASK;
264 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700265 preloadRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700266 } else if (action == MotionEvent.ACTION_CANCEL) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700267 cancelPreloadingRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700268 } else if (action == MotionEvent.ACTION_UP) {
269 // Remove the preloader if we haven't called it yet
270 mHandler.removeCallbacks(mPreloadTasksRunnable);
271 if (!v.isPressed()) {
272 cancelLoadingThumbnailsAndIcons();
273 }
274
275 }
276 return false;
277 }
278
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700279 public void preloadRecentTasksList() {
280 mHandler.post(mPreloadTasksRunnable);
281 }
282
283 public void cancelPreloadingRecentTasksList() {
284 cancelLoadingThumbnailsAndIcons();
285 mHandler.removeCallbacks(mPreloadTasksRunnable);
286 }
287
Michael Jurkacb2522c2012-04-13 09:32:47 -0700288 public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
289 // Only oblige this request if it comes from the current RecentsPanel
290 // (eg when you rotate, the old RecentsPanel request should be ignored)
291 if (mRecentsPanel == caller) {
292 cancelLoadingThumbnailsAndIcons();
293 }
294 }
295
296
297 private void cancelLoadingThumbnailsAndIcons() {
Michael Jurka45eed3c2013-05-02 14:49:45 +0200298 if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
299 return;
300 }
301
Michael Jurka99a96552012-01-27 17:23:38 -0800302 if (mTaskLoader != null) {
303 mTaskLoader.cancel(false);
304 mTaskLoader = null;
305 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700306 if (mThumbnailLoader != null) {
307 mThumbnailLoader.cancel(false);
308 mThumbnailLoader = null;
309 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700310 mLoadedTasks = null;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700311 if (mRecentsPanel != null) {
312 mRecentsPanel.onTaskLoadingCancelled();
313 }
314 mFirstScreenful = false;
315 mState = State.CANCELLED;
Michael Jurkaab48b682011-09-12 15:39:45 -0700316 }
317
Michael Jurka80343f62012-10-18 13:13:46 +0200318 private void clearFirstTask() {
319 synchronized (mFirstTaskLock) {
320 mFirstTask = null;
321 mFirstTaskLoaded = false;
322 }
323 }
324
325 public void preloadFirstTask() {
326 Thread bgLoad = new Thread() {
327 public void run() {
328 TaskDescription first = loadFirstTask();
329 synchronized(mFirstTaskLock) {
330 if (mCancelPreloadingFirstTask) {
331 clearFirstTask();
332 } else {
333 mFirstTask = first;
334 mFirstTaskLoaded = true;
335 }
336 mPreloadingFirstTask = false;
337 }
338 }
339 };
340 synchronized(mFirstTaskLock) {
341 if (!mPreloadingFirstTask) {
342 clearFirstTask();
343 mPreloadingFirstTask = true;
344 bgLoad.start();
345 }
346 }
347 }
348
349 public void cancelPreloadingFirstTask() {
350 synchronized(mFirstTaskLock) {
351 if (mPreloadingFirstTask) {
352 mCancelPreloadingFirstTask = true;
353 } else {
354 clearFirstTask();
355 }
356 }
357 }
358
359 boolean mPreloadingFirstTask;
360 boolean mCancelPreloadingFirstTask;
361 public TaskDescription getFirstTask() {
362 while(true) {
363 synchronized(mFirstTaskLock) {
364 if (mFirstTaskLoaded) {
365 return mFirstTask;
366 } else if (!mFirstTaskLoaded && !mPreloadingFirstTask) {
367 mFirstTask = loadFirstTask();
368 mFirstTaskLoaded = true;
369 return mFirstTask;
370 }
371 }
372 try {
373 Thread.sleep(3);
374 } catch (InterruptedException e) {
375 }
376 }
377 }
378
379 public TaskDescription loadFirstTask() {
380 final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
381
Kenny Guy82326a92014-03-17 17:16:06 +0000382 final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasksForUser(1,
Kenny Guy2a764942014-04-02 13:29:20 +0100383 ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES,
Kenny Guy82326a92014-03-17 17:16:06 +0000384 UserHandle.CURRENT.getIdentifier());
Michael Jurka80343f62012-10-18 13:13:46 +0200385 TaskDescription item = null;
386 if (recentTasks.size() > 0) {
387 ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(0);
388
389 Intent intent = new Intent(recentInfo.baseIntent);
390 if (recentInfo.origActivity != null) {
391 intent.setComponent(recentInfo.origActivity);
392 }
393
394 // Don't load the current home activity.
395 if (isCurrentHomeActivity(intent.getComponent(), null)) {
396 return null;
397 }
398
399 // Don't load ourselves
400 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
401 return null;
402 }
403
404 item = createTaskDescription(recentInfo.id,
405 recentInfo.persistentId, recentInfo.baseIntent,
Kenny Guy1f1cce12014-04-04 14:47:50 +0100406 recentInfo.origActivity, recentInfo.description,
407 recentInfo.userId);
Michael Jurka2a430cc2012-10-22 10:56:18 -0700408 if (item != null) {
409 loadThumbnailAndIcon(item);
410 }
Michael Jurka80343f62012-10-18 13:13:46 +0200411 return item;
412 }
413 return null;
414 }
415
Michael Jurka99a96552012-01-27 17:23:38 -0800416 public void loadTasksInBackground() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700417 loadTasksInBackground(false);
418 }
419 public void loadTasksInBackground(final boolean zeroeth) {
420 if (mState != State.CANCELLED) {
421 return;
422 }
423 mState = State.LOADING;
424 mFirstScreenful = true;
425
Michael Jurka99a96552012-01-27 17:23:38 -0800426 final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
427 new LinkedBlockingQueue<TaskDescription>();
Michael Jurka99a96552012-01-27 17:23:38 -0800428 mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
429 @Override
430 protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
431 if (!isCancelled()) {
432 ArrayList<TaskDescription> newTasks = values[0];
433 // do a callback to RecentsPanelView to let it know we have more values
434 // how do we let it know we're all done? just always call back twice
Michael Jurkacb2522c2012-04-13 09:32:47 -0700435 if (mRecentsPanel != null) {
436 mRecentsPanel.onTasksLoaded(newTasks, mFirstScreenful);
437 }
438 if (mLoadedTasks == null) {
439 mLoadedTasks = new ArrayList<TaskDescription>();
440 }
441 mLoadedTasks.addAll(newTasks);
442 mFirstScreenful = false;
Michael Jurka99a96552012-01-27 17:23:38 -0800443 }
444 }
445 @Override
446 protected Void doInBackground(Void... params) {
447 // We load in two stages: first, we update progress with just the first screenful
448 // of items. Then, we update with the rest of the items
449 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700450 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800451 final PackageManager pm = mContext.getPackageManager();
452 final ActivityManager am = (ActivityManager)
Michael Jurkaab48b682011-09-12 15:39:45 -0700453 mContext.getSystemService(Context.ACTIVITY_SERVICE);
454
Michael Jurka99a96552012-01-27 17:23:38 -0800455 final List<ActivityManager.RecentTaskInfo> recentTasks =
Kenny Guy82326a92014-03-17 17:16:06 +0000456 am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
Kenny Guy2a764942014-04-02 13:29:20 +0100457 | ActivityManager.RECENT_INCLUDE_PROFILES);
Michael Jurka99a96552012-01-27 17:23:38 -0800458 int numTasks = recentTasks.size();
459 ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
460 .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
Michael Jurkaab48b682011-09-12 15:39:45 -0700461
Michael Jurka99a96552012-01-27 17:23:38 -0800462 boolean firstScreenful = true;
463 ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
Michael Jurkaab48b682011-09-12 15:39:45 -0700464
Michael Jurka99a96552012-01-27 17:23:38 -0800465 // skip the first task - assume it's either the home screen or the current activity.
Michael Jurkacb2522c2012-04-13 09:32:47 -0700466 final int first = 0;
Michael Jurka99a96552012-01-27 17:23:38 -0800467 for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
468 if (isCancelled()) {
469 break;
Michael Jurkaab48b682011-09-12 15:39:45 -0700470 }
Michael Jurka99a96552012-01-27 17:23:38 -0800471 final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700472
473 Intent intent = new Intent(recentInfo.baseIntent);
474 if (recentInfo.origActivity != null) {
475 intent.setComponent(recentInfo.origActivity);
476 }
477
478 // Don't load the current home activity.
479 if (isCurrentHomeActivity(intent.getComponent(), homeInfo)) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700480 continue;
481 }
482
483 // Don't load ourselves
484 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
485 continue;
486 }
487
Michael Jurka99a96552012-01-27 17:23:38 -0800488 TaskDescription item = createTaskDescription(recentInfo.id,
489 recentInfo.persistentId, recentInfo.baseIntent,
Kenny Guy1f1cce12014-04-04 14:47:50 +0100490 recentInfo.origActivity, recentInfo.description,
491 recentInfo.userId);
Michael Jurkaab48b682011-09-12 15:39:45 -0700492
Michael Jurka99a96552012-01-27 17:23:38 -0800493 if (item != null) {
494 while (true) {
495 try {
496 tasksWaitingForThumbnails.put(item);
Michael Jurkaab48b682011-09-12 15:39:45 -0700497 break;
Michael Jurka99a96552012-01-27 17:23:38 -0800498 } catch (InterruptedException e) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700499 }
500 }
Michael Jurka99a96552012-01-27 17:23:38 -0800501 tasks.add(item);
502 if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
503 publishProgress(tasks);
504 tasks = new ArrayList<TaskDescription>();
505 firstScreenful = false;
506 //break;
507 }
508 ++index;
Michael Jurkaab48b682011-09-12 15:39:45 -0700509 }
Michael Jurka99a96552012-01-27 17:23:38 -0800510 }
511
512 if (!isCancelled()) {
513 publishProgress(tasks);
514 if (firstScreenful) {
515 // always should publish two updates
516 publishProgress(new ArrayList<TaskDescription>());
517 }
518 }
519
520 while (true) {
521 try {
522 tasksWaitingForThumbnails.put(new TaskDescription());
523 break;
524 } catch (InterruptedException e) {
525 }
526 }
527
528 Process.setThreadPriority(origPri);
529 return null;
Michael Jurkaab48b682011-09-12 15:39:45 -0700530 }
Michael Jurka99a96552012-01-27 17:23:38 -0800531 };
532 mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
533 loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
Michael Jurkaab48b682011-09-12 15:39:45 -0700534 }
535
Michael Jurka99a96552012-01-27 17:23:38 -0800536 private void loadThumbnailsAndIconsInBackground(
537 final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
538 // continually read items from tasksWaitingForThumbnails and load
539 // thumbnails and icons for them. finish thread when cancelled or there
540 // is a null item in tasksWaitingForThumbnails
541 mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
542 @Override
543 protected void onProgressUpdate(TaskDescription... values) {
544 if (!isCancelled()) {
545 TaskDescription td = values[0];
Michael Jurkacb2522c2012-04-13 09:32:47 -0700546 if (td.isNull()) { // end sentinel
547 mState = State.LOADED;
548 } else {
549 if (mRecentsPanel != null) {
550 mRecentsPanel.onTaskThumbnailLoaded(td);
551 }
552 }
Michael Jurka99a96552012-01-27 17:23:38 -0800553 }
554 }
555 @Override
556 protected Void doInBackground(Void... params) {
557 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700558 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800559
560 while (true) {
561 if (isCancelled()) {
562 break;
563 }
564 TaskDescription td = null;
565 while (td == null) {
566 try {
567 td = tasksWaitingForThumbnails.take();
568 } catch (InterruptedException e) {
569 }
570 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700571 if (td.isNull()) { // end sentinel
572 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800573 break;
574 }
575 loadThumbnailAndIcon(td);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700576
Michael Jurkacb2522c2012-04-13 09:32:47 -0700577 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800578 }
579
580 Process.setThreadPriority(origPri);
581 return null;
582 }
583 };
584 mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
585 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700586}