blob: 4338fa04cea420720c8d2bf212c61f879482d970 [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;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.ActivityInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.res.Resources;
27import android.graphics.Bitmap;
28import android.graphics.Canvas;
29import android.graphics.drawable.Drawable;
30import android.os.AsyncTask;
31import android.os.Handler;
32import android.os.Process;
Michael Jurka80343f62012-10-18 13:13:46 +020033import android.os.UserHandle;
Michael Jurkaab48b682011-09-12 15:39:45 -070034import android.util.Log;
Michael Jurkacb2522c2012-04-13 09:32:47 -070035import android.view.MotionEvent;
36import android.view.View;
Michael Jurkaab48b682011-09-12 15:39:45 -070037
38import com.android.systemui.R;
39import com.android.systemui.statusbar.phone.PhoneStatusBar;
40import com.android.systemui.statusbar.tablet.TabletStatusBar;
41
Michael Jurka99a96552012-01-27 17:23:38 -080042import java.util.ArrayList;
43import java.util.List;
44import java.util.concurrent.BlockingQueue;
45import java.util.concurrent.LinkedBlockingQueue;
46
Michael Jurkacb2522c2012-04-13 09:32:47 -070047public class RecentTasksLoader implements View.OnTouchListener {
Michael Jurkaab48b682011-09-12 15:39:45 -070048 static final String TAG = "RecentTasksLoader";
49 static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
50
51 private static final int DISPLAY_TASKS = 20;
52 private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
53
54 private Context mContext;
55 private RecentsPanelView mRecentsPanel;
Michael Jurka80343f62012-10-18 13:13:46 +020056
57 private Object mFirstTaskLock = new Object();
Michael Jurkacb2522c2012-04-13 09:32:47 -070058 private TaskDescription mFirstTask;
59 private boolean mFirstTaskLoaded;
Michael Jurkaab48b682011-09-12 15:39:45 -070060
Michael Jurka99a96552012-01-27 17:23:38 -080061 private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
62 private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
Michael Jurkacb2522c2012-04-13 09:32:47 -070063 private Handler mHandler;
Michael Jurkaab48b682011-09-12 15:39:45 -070064
65 private int mIconDpi;
66 private Bitmap mDefaultThumbnailBackground;
Michael Jurka99a96552012-01-27 17:23:38 -080067 private Bitmap mDefaultIconBackground;
Michael Jurkacb2522c2012-04-13 09:32:47 -070068 private int mNumTasksInFirstScreenful = Integer.MAX_VALUE;
69
70 private boolean mFirstScreenful;
71 private ArrayList<TaskDescription> mLoadedTasks;
72
73 private enum State { LOADING, LOADED, CANCELLED };
74 private State mState = State.CANCELLED;
75
Michael Jurka80343f62012-10-18 13:13:46 +020076
77 private static RecentTasksLoader sInstance;
78 public static RecentTasksLoader getInstance(Context context) {
79 if (sInstance == null) {
80 sInstance = new RecentTasksLoader(context);
Michael Jurkacb2522c2012-04-13 09:32:47 -070081 }
Michael Jurka80343f62012-10-18 13:13:46 +020082 return sInstance;
Michael Jurkacb2522c2012-04-13 09:32:47 -070083 }
Michael Jurkaab48b682011-09-12 15:39:45 -070084
Michael Jurka80343f62012-10-18 13:13:46 +020085 private RecentTasksLoader(Context context) {
Michael Jurkaab48b682011-09-12 15:39:45 -070086 mContext = context;
Michael Jurkacb2522c2012-04-13 09:32:47 -070087 mHandler = new Handler();
Michael Jurkaab48b682011-09-12 15:39:45 -070088
89 final Resources res = context.getResources();
90
91 // get the icon size we want -- on tablets, we use bigger icons
92 boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
Michael Jurkaab48b682011-09-12 15:39:45 -070093 if (isTablet) {
Winson Chung43e34f62012-01-24 15:26:59 -080094 ActivityManager activityManager =
95 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
96 mIconDpi = activityManager.getLauncherLargeIconDensity();
Michael Jurkaab48b682011-09-12 15:39:45 -070097 } else {
98 mIconDpi = res.getDisplayMetrics().densityDpi;
99 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700100
Michael Jurka99a96552012-01-27 17:23:38 -0800101 // Render default icon (just a blank image)
102 int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
103 int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
104 mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
105
Michael Jurkaab48b682011-09-12 15:39:45 -0700106 // Render the default thumbnail background
Michael Jurka99a96552012-01-27 17:23:38 -0800107 int thumbnailWidth =
108 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
109 int thumbnailHeight =
110 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
Michael Jurkaab48b682011-09-12 15:39:45 -0700111 int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
112
Michael Jurka99a96552012-01-27 17:23:38 -0800113 mDefaultThumbnailBackground =
114 Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
Michael Jurkaab48b682011-09-12 15:39:45 -0700115 Canvas c = new Canvas(mDefaultThumbnailBackground);
116 c.drawColor(color);
Michael Jurkaab48b682011-09-12 15:39:45 -0700117 }
118
Michael Jurkacb2522c2012-04-13 09:32:47 -0700119 public void setRecentsPanel(RecentsPanelView newRecentsPanel, RecentsPanelView caller) {
120 // Only allow clearing mRecentsPanel if the caller is the current recentsPanel
121 if (newRecentsPanel != null || mRecentsPanel == caller) {
122 mRecentsPanel = newRecentsPanel;
123 if (mRecentsPanel != null) {
124 mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
125 }
126 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700127 }
128
Michael Jurka412cba82011-10-17 09:05:00 -0700129 public Bitmap getDefaultThumbnail() {
130 return mDefaultThumbnailBackground;
131 }
132
Michael Jurka99a96552012-01-27 17:23:38 -0800133 public Bitmap getDefaultIcon() {
134 return mDefaultIconBackground;
135 }
136
Michael Jurkacb2522c2012-04-13 09:32:47 -0700137 public ArrayList<TaskDescription> getLoadedTasks() {
138 return mLoadedTasks;
139 }
140
141 public boolean isFirstScreenful() {
142 return mFirstScreenful;
143 }
144
145 private boolean isCurrentHomeActivity(ComponentName component, ActivityInfo homeInfo) {
146 if (homeInfo == null) {
147 final PackageManager pm = mContext.getPackageManager();
148 homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
149 .resolveActivityInfo(pm, 0);
150 }
151 return homeInfo != null
152 && homeInfo.packageName.equals(component.getPackageName())
153 && homeInfo.name.equals(component.getClassName());
154 }
155
156 // Create an TaskDescription, returning null if the title or icon is null
Michael Jurkaab48b682011-09-12 15:39:45 -0700157 TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700158 ComponentName origActivity, CharSequence description) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700159 Intent intent = new Intent(baseIntent);
160 if (origActivity != null) {
161 intent.setComponent(origActivity);
162 }
163 final PackageManager pm = mContext.getPackageManager();
Michael Jurkaab48b682011-09-12 15:39:45 -0700164 intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
165 | Intent.FLAG_ACTIVITY_NEW_TASK);
166 final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
167 if (resolveInfo != null) {
168 final ActivityInfo info = resolveInfo.activityInfo;
169 final String title = info.loadLabel(pm).toString();
Michael Jurkaab48b682011-09-12 15:39:45 -0700170
Michael Jurka99a96552012-01-27 17:23:38 -0800171 if (title != null && title.length() > 0) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700172 if (DEBUG) Log.v(TAG, "creating activity desc for id="
173 + persistentTaskId + ", label=" + title);
174
175 TaskDescription item = new TaskDescription(taskId,
176 persistentTaskId, resolveInfo, baseIntent, info.packageName,
177 description);
178 item.setLabel(title);
Michael Jurkaab48b682011-09-12 15:39:45 -0700179
180 return item;
181 } else {
182 if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
183 }
184 }
185 return null;
186 }
187
Michael Jurka99a96552012-01-27 17:23:38 -0800188 void loadThumbnailAndIcon(TaskDescription td) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700189 final ActivityManager am = (ActivityManager)
190 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurka99a96552012-01-27 17:23:38 -0800191 final PackageManager pm = mContext.getPackageManager();
Dianne Hackborn15491c62012-09-19 10:59:14 -0700192 Bitmap thumbnail = am.getTaskTopThumbnail(td.persistentTaskId);
Michael Jurka99a96552012-01-27 17:23:38 -0800193 Drawable icon = getFullResIcon(td.resolveInfo, pm);
Michael Jurkaab48b682011-09-12 15:39:45 -0700194
195 if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
Dianne Hackborn15491c62012-09-19 10:59:14 -0700196 + td + ": " + thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700197 synchronized (td) {
Dianne Hackborn15491c62012-09-19 10:59:14 -0700198 if (thumbnail != null) {
199 td.setThumbnail(thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700200 } else {
201 td.setThumbnail(mDefaultThumbnailBackground);
202 }
Michael Jurka99a96552012-01-27 17:23:38 -0800203 if (icon != null) {
204 td.setIcon(icon);
205 }
206 td.setLoaded(true);
Michael Jurkaab48b682011-09-12 15:39:45 -0700207 }
208 }
209
210 Drawable getFullResDefaultActivityIcon() {
211 return getFullResIcon(Resources.getSystem(),
212 com.android.internal.R.mipmap.sym_def_app_icon);
213 }
214
215 Drawable getFullResIcon(Resources resources, int iconId) {
216 try {
217 return resources.getDrawableForDensity(iconId, mIconDpi);
218 } catch (Resources.NotFoundException e) {
219 return getFullResDefaultActivityIcon();
220 }
221 }
222
223 private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
224 Resources resources;
225 try {
226 resources = packageManager.getResourcesForApplication(
227 info.activityInfo.applicationInfo);
228 } catch (PackageManager.NameNotFoundException e) {
229 resources = null;
230 }
231 if (resources != null) {
232 int iconId = info.activityInfo.getIconResource();
233 if (iconId != 0) {
234 return getFullResIcon(resources, iconId);
235 }
236 }
237 return getFullResDefaultActivityIcon();
238 }
239
Michael Jurkacb2522c2012-04-13 09:32:47 -0700240 Runnable mPreloadTasksRunnable = new Runnable() {
241 public void run() {
242 loadTasksInBackground();
243 }
244 };
245
246 // additional optimization when we have software system buttons - start loading the recent
247 // tasks on touch down
248 @Override
249 public boolean onTouch(View v, MotionEvent ev) {
250 int action = ev.getAction() & MotionEvent.ACTION_MASK;
251 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700252 preloadRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700253 } else if (action == MotionEvent.ACTION_CANCEL) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700254 cancelPreloadingRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700255 } else if (action == MotionEvent.ACTION_UP) {
256 // Remove the preloader if we haven't called it yet
257 mHandler.removeCallbacks(mPreloadTasksRunnable);
258 if (!v.isPressed()) {
259 cancelLoadingThumbnailsAndIcons();
260 }
261
262 }
263 return false;
264 }
265
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700266 public void preloadRecentTasksList() {
267 mHandler.post(mPreloadTasksRunnable);
268 }
269
270 public void cancelPreloadingRecentTasksList() {
271 cancelLoadingThumbnailsAndIcons();
272 mHandler.removeCallbacks(mPreloadTasksRunnable);
273 }
274
Michael Jurkacb2522c2012-04-13 09:32:47 -0700275 public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
276 // Only oblige this request if it comes from the current RecentsPanel
277 // (eg when you rotate, the old RecentsPanel request should be ignored)
278 if (mRecentsPanel == caller) {
279 cancelLoadingThumbnailsAndIcons();
280 }
281 }
282
283
284 private void cancelLoadingThumbnailsAndIcons() {
Michael Jurka99a96552012-01-27 17:23:38 -0800285 if (mTaskLoader != null) {
286 mTaskLoader.cancel(false);
287 mTaskLoader = null;
288 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700289 if (mThumbnailLoader != null) {
290 mThumbnailLoader.cancel(false);
291 mThumbnailLoader = null;
292 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700293 mLoadedTasks = null;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700294 if (mRecentsPanel != null) {
295 mRecentsPanel.onTaskLoadingCancelled();
296 }
297 mFirstScreenful = false;
298 mState = State.CANCELLED;
Michael Jurkaab48b682011-09-12 15:39:45 -0700299 }
300
Michael Jurka80343f62012-10-18 13:13:46 +0200301 private void clearFirstTask() {
302 synchronized (mFirstTaskLock) {
303 mFirstTask = null;
304 mFirstTaskLoaded = false;
305 }
306 }
307
308 public void preloadFirstTask() {
309 Thread bgLoad = new Thread() {
310 public void run() {
311 TaskDescription first = loadFirstTask();
312 synchronized(mFirstTaskLock) {
313 if (mCancelPreloadingFirstTask) {
314 clearFirstTask();
315 } else {
316 mFirstTask = first;
317 mFirstTaskLoaded = true;
318 }
319 mPreloadingFirstTask = false;
320 }
321 }
322 };
323 synchronized(mFirstTaskLock) {
324 if (!mPreloadingFirstTask) {
325 clearFirstTask();
326 mPreloadingFirstTask = true;
327 bgLoad.start();
328 }
329 }
330 }
331
332 public void cancelPreloadingFirstTask() {
333 synchronized(mFirstTaskLock) {
334 if (mPreloadingFirstTask) {
335 mCancelPreloadingFirstTask = true;
336 } else {
337 clearFirstTask();
338 }
339 }
340 }
341
342 boolean mPreloadingFirstTask;
343 boolean mCancelPreloadingFirstTask;
344 public TaskDescription getFirstTask() {
345 while(true) {
346 synchronized(mFirstTaskLock) {
347 if (mFirstTaskLoaded) {
348 return mFirstTask;
349 } else if (!mFirstTaskLoaded && !mPreloadingFirstTask) {
350 mFirstTask = loadFirstTask();
351 mFirstTaskLoaded = true;
352 return mFirstTask;
353 }
354 }
355 try {
356 Thread.sleep(3);
357 } catch (InterruptedException e) {
358 }
359 }
360 }
361
362 public TaskDescription loadFirstTask() {
363 final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
364
365 final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasksForUser(
366 1, ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
367 TaskDescription item = null;
368 if (recentTasks.size() > 0) {
369 ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(0);
370
371 Intent intent = new Intent(recentInfo.baseIntent);
372 if (recentInfo.origActivity != null) {
373 intent.setComponent(recentInfo.origActivity);
374 }
375
376 // Don't load the current home activity.
377 if (isCurrentHomeActivity(intent.getComponent(), null)) {
378 return null;
379 }
380
381 // Don't load ourselves
382 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
383 return null;
384 }
385
386 item = createTaskDescription(recentInfo.id,
387 recentInfo.persistentId, recentInfo.baseIntent,
388 recentInfo.origActivity, recentInfo.description);
Michael Jurka2a430cc2012-10-22 10:56:18 -0700389 if (item != null) {
390 loadThumbnailAndIcon(item);
391 }
Michael Jurka80343f62012-10-18 13:13:46 +0200392 return item;
393 }
394 return null;
395 }
396
Michael Jurka99a96552012-01-27 17:23:38 -0800397 public void loadTasksInBackground() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700398 loadTasksInBackground(false);
399 }
400 public void loadTasksInBackground(final boolean zeroeth) {
401 if (mState != State.CANCELLED) {
402 return;
403 }
404 mState = State.LOADING;
405 mFirstScreenful = true;
406
Michael Jurka99a96552012-01-27 17:23:38 -0800407 final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
408 new LinkedBlockingQueue<TaskDescription>();
Michael Jurka99a96552012-01-27 17:23:38 -0800409 mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
410 @Override
411 protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
412 if (!isCancelled()) {
413 ArrayList<TaskDescription> newTasks = values[0];
414 // do a callback to RecentsPanelView to let it know we have more values
415 // how do we let it know we're all done? just always call back twice
Michael Jurkacb2522c2012-04-13 09:32:47 -0700416 if (mRecentsPanel != null) {
417 mRecentsPanel.onTasksLoaded(newTasks, mFirstScreenful);
418 }
419 if (mLoadedTasks == null) {
420 mLoadedTasks = new ArrayList<TaskDescription>();
421 }
422 mLoadedTasks.addAll(newTasks);
423 mFirstScreenful = false;
Michael Jurka99a96552012-01-27 17:23:38 -0800424 }
425 }
426 @Override
427 protected Void doInBackground(Void... params) {
428 // We load in two stages: first, we update progress with just the first screenful
429 // of items. Then, we update with the rest of the items
430 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700431 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800432 final PackageManager pm = mContext.getPackageManager();
433 final ActivityManager am = (ActivityManager)
Michael Jurkaab48b682011-09-12 15:39:45 -0700434 mContext.getSystemService(Context.ACTIVITY_SERVICE);
435
Michael Jurka99a96552012-01-27 17:23:38 -0800436 final List<ActivityManager.RecentTaskInfo> recentTasks =
Amith Yamasanid2e99562012-08-28 15:49:06 -0700437 am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
Michael Jurka99a96552012-01-27 17:23:38 -0800438 int numTasks = recentTasks.size();
439 ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
440 .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
Michael Jurkaab48b682011-09-12 15:39:45 -0700441
Michael Jurka99a96552012-01-27 17:23:38 -0800442 boolean firstScreenful = true;
443 ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
Michael Jurkaab48b682011-09-12 15:39:45 -0700444
Michael Jurka99a96552012-01-27 17:23:38 -0800445 // skip the first task - assume it's either the home screen or the current activity.
Michael Jurkacb2522c2012-04-13 09:32:47 -0700446 final int first = 0;
Michael Jurka99a96552012-01-27 17:23:38 -0800447 for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
448 if (isCancelled()) {
449 break;
Michael Jurkaab48b682011-09-12 15:39:45 -0700450 }
Michael Jurka99a96552012-01-27 17:23:38 -0800451 final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700452
453 Intent intent = new Intent(recentInfo.baseIntent);
454 if (recentInfo.origActivity != null) {
455 intent.setComponent(recentInfo.origActivity);
456 }
457
458 // Don't load the current home activity.
459 if (isCurrentHomeActivity(intent.getComponent(), homeInfo)) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700460 continue;
461 }
462
463 // Don't load ourselves
464 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
465 continue;
466 }
467
Michael Jurka99a96552012-01-27 17:23:38 -0800468 TaskDescription item = createTaskDescription(recentInfo.id,
469 recentInfo.persistentId, recentInfo.baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700470 recentInfo.origActivity, recentInfo.description);
Michael Jurkaab48b682011-09-12 15:39:45 -0700471
Michael Jurka99a96552012-01-27 17:23:38 -0800472 if (item != null) {
473 while (true) {
474 try {
475 tasksWaitingForThumbnails.put(item);
Michael Jurkaab48b682011-09-12 15:39:45 -0700476 break;
Michael Jurka99a96552012-01-27 17:23:38 -0800477 } catch (InterruptedException e) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700478 }
479 }
Michael Jurka99a96552012-01-27 17:23:38 -0800480 tasks.add(item);
481 if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
482 publishProgress(tasks);
483 tasks = new ArrayList<TaskDescription>();
484 firstScreenful = false;
485 //break;
486 }
487 ++index;
Michael Jurkaab48b682011-09-12 15:39:45 -0700488 }
Michael Jurka99a96552012-01-27 17:23:38 -0800489 }
490
491 if (!isCancelled()) {
492 publishProgress(tasks);
493 if (firstScreenful) {
494 // always should publish two updates
495 publishProgress(new ArrayList<TaskDescription>());
496 }
497 }
498
499 while (true) {
500 try {
501 tasksWaitingForThumbnails.put(new TaskDescription());
502 break;
503 } catch (InterruptedException e) {
504 }
505 }
506
507 Process.setThreadPriority(origPri);
508 return null;
Michael Jurkaab48b682011-09-12 15:39:45 -0700509 }
Michael Jurka99a96552012-01-27 17:23:38 -0800510 };
511 mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
512 loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
Michael Jurkaab48b682011-09-12 15:39:45 -0700513 }
514
Michael Jurka99a96552012-01-27 17:23:38 -0800515 private void loadThumbnailsAndIconsInBackground(
516 final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
517 // continually read items from tasksWaitingForThumbnails and load
518 // thumbnails and icons for them. finish thread when cancelled or there
519 // is a null item in tasksWaitingForThumbnails
520 mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
521 @Override
522 protected void onProgressUpdate(TaskDescription... values) {
523 if (!isCancelled()) {
524 TaskDescription td = values[0];
Michael Jurkacb2522c2012-04-13 09:32:47 -0700525 if (td.isNull()) { // end sentinel
526 mState = State.LOADED;
527 } else {
528 if (mRecentsPanel != null) {
529 mRecentsPanel.onTaskThumbnailLoaded(td);
530 }
531 }
Michael Jurka99a96552012-01-27 17:23:38 -0800532 }
533 }
534 @Override
535 protected Void doInBackground(Void... params) {
536 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700537 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800538
539 while (true) {
540 if (isCancelled()) {
541 break;
542 }
543 TaskDescription td = null;
544 while (td == null) {
545 try {
546 td = tasksWaitingForThumbnails.take();
547 } catch (InterruptedException e) {
548 }
549 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700550 if (td.isNull()) { // end sentinel
551 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800552 break;
553 }
554 loadThumbnailAndIcon(td);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700555
Michael Jurkacb2522c2012-04-13 09:32:47 -0700556 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800557 }
558
559 Process.setThreadPriority(origPri);
560 return null;
561 }
562 };
563 mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
564 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700565}