blob: 9281c750b74964bf2ff91308721a46ecf99fc72d [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
Michael Jurkae57922c2012-11-26 16:05:12 -0800141 public void remove(TaskDescription td) {
142 mLoadedTasks.remove(td);
143 }
144
Michael Jurkacb2522c2012-04-13 09:32:47 -0700145 public boolean isFirstScreenful() {
146 return mFirstScreenful;
147 }
148
149 private boolean isCurrentHomeActivity(ComponentName component, ActivityInfo homeInfo) {
150 if (homeInfo == null) {
151 final PackageManager pm = mContext.getPackageManager();
152 homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
153 .resolveActivityInfo(pm, 0);
154 }
155 return homeInfo != null
156 && homeInfo.packageName.equals(component.getPackageName())
157 && homeInfo.name.equals(component.getClassName());
158 }
159
160 // Create an TaskDescription, returning null if the title or icon is null
Michael Jurkaab48b682011-09-12 15:39:45 -0700161 TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700162 ComponentName origActivity, CharSequence description) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700163 Intent intent = new Intent(baseIntent);
164 if (origActivity != null) {
165 intent.setComponent(origActivity);
166 }
167 final PackageManager pm = mContext.getPackageManager();
Michael Jurkaab48b682011-09-12 15:39:45 -0700168 intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
169 | Intent.FLAG_ACTIVITY_NEW_TASK);
170 final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
171 if (resolveInfo != null) {
172 final ActivityInfo info = resolveInfo.activityInfo;
173 final String title = info.loadLabel(pm).toString();
Michael Jurkaab48b682011-09-12 15:39:45 -0700174
Michael Jurka99a96552012-01-27 17:23:38 -0800175 if (title != null && title.length() > 0) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700176 if (DEBUG) Log.v(TAG, "creating activity desc for id="
177 + persistentTaskId + ", label=" + title);
178
179 TaskDescription item = new TaskDescription(taskId,
180 persistentTaskId, resolveInfo, baseIntent, info.packageName,
181 description);
182 item.setLabel(title);
Michael Jurkaab48b682011-09-12 15:39:45 -0700183
184 return item;
185 } else {
186 if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
187 }
188 }
189 return null;
190 }
191
Michael Jurka99a96552012-01-27 17:23:38 -0800192 void loadThumbnailAndIcon(TaskDescription td) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700193 final ActivityManager am = (ActivityManager)
194 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurka99a96552012-01-27 17:23:38 -0800195 final PackageManager pm = mContext.getPackageManager();
Dianne Hackborn15491c62012-09-19 10:59:14 -0700196 Bitmap thumbnail = am.getTaskTopThumbnail(td.persistentTaskId);
Michael Jurka99a96552012-01-27 17:23:38 -0800197 Drawable icon = getFullResIcon(td.resolveInfo, pm);
Michael Jurkaab48b682011-09-12 15:39:45 -0700198
199 if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
Dianne Hackborn15491c62012-09-19 10:59:14 -0700200 + td + ": " + thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700201 synchronized (td) {
Dianne Hackborn15491c62012-09-19 10:59:14 -0700202 if (thumbnail != null) {
203 td.setThumbnail(thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700204 } else {
205 td.setThumbnail(mDefaultThumbnailBackground);
206 }
Michael Jurka99a96552012-01-27 17:23:38 -0800207 if (icon != null) {
208 td.setIcon(icon);
209 }
210 td.setLoaded(true);
Michael Jurkaab48b682011-09-12 15:39:45 -0700211 }
212 }
213
214 Drawable getFullResDefaultActivityIcon() {
215 return getFullResIcon(Resources.getSystem(),
216 com.android.internal.R.mipmap.sym_def_app_icon);
217 }
218
219 Drawable getFullResIcon(Resources resources, int iconId) {
220 try {
221 return resources.getDrawableForDensity(iconId, mIconDpi);
222 } catch (Resources.NotFoundException e) {
223 return getFullResDefaultActivityIcon();
224 }
225 }
226
227 private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
228 Resources resources;
229 try {
230 resources = packageManager.getResourcesForApplication(
231 info.activityInfo.applicationInfo);
232 } catch (PackageManager.NameNotFoundException e) {
233 resources = null;
234 }
235 if (resources != null) {
236 int iconId = info.activityInfo.getIconResource();
237 if (iconId != 0) {
238 return getFullResIcon(resources, iconId);
239 }
240 }
241 return getFullResDefaultActivityIcon();
242 }
243
Michael Jurkacb2522c2012-04-13 09:32:47 -0700244 Runnable mPreloadTasksRunnable = new Runnable() {
245 public void run() {
246 loadTasksInBackground();
247 }
248 };
249
250 // additional optimization when we have software system buttons - start loading the recent
251 // tasks on touch down
252 @Override
253 public boolean onTouch(View v, MotionEvent ev) {
254 int action = ev.getAction() & MotionEvent.ACTION_MASK;
255 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700256 preloadRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700257 } else if (action == MotionEvent.ACTION_CANCEL) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700258 cancelPreloadingRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700259 } else if (action == MotionEvent.ACTION_UP) {
260 // Remove the preloader if we haven't called it yet
261 mHandler.removeCallbacks(mPreloadTasksRunnable);
262 if (!v.isPressed()) {
263 cancelLoadingThumbnailsAndIcons();
264 }
265
266 }
267 return false;
268 }
269
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700270 public void preloadRecentTasksList() {
271 mHandler.post(mPreloadTasksRunnable);
272 }
273
274 public void cancelPreloadingRecentTasksList() {
275 cancelLoadingThumbnailsAndIcons();
276 mHandler.removeCallbacks(mPreloadTasksRunnable);
277 }
278
Michael Jurkacb2522c2012-04-13 09:32:47 -0700279 public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
280 // Only oblige this request if it comes from the current RecentsPanel
281 // (eg when you rotate, the old RecentsPanel request should be ignored)
282 if (mRecentsPanel == caller) {
283 cancelLoadingThumbnailsAndIcons();
284 }
285 }
286
287
288 private void cancelLoadingThumbnailsAndIcons() {
Michael Jurka99a96552012-01-27 17:23:38 -0800289 if (mTaskLoader != null) {
290 mTaskLoader.cancel(false);
291 mTaskLoader = null;
292 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700293 if (mThumbnailLoader != null) {
294 mThumbnailLoader.cancel(false);
295 mThumbnailLoader = null;
296 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700297 mLoadedTasks = null;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700298 if (mRecentsPanel != null) {
299 mRecentsPanel.onTaskLoadingCancelled();
300 }
301 mFirstScreenful = false;
302 mState = State.CANCELLED;
Michael Jurkaab48b682011-09-12 15:39:45 -0700303 }
304
Michael Jurka80343f62012-10-18 13:13:46 +0200305 private void clearFirstTask() {
306 synchronized (mFirstTaskLock) {
307 mFirstTask = null;
308 mFirstTaskLoaded = false;
309 }
310 }
311
312 public void preloadFirstTask() {
313 Thread bgLoad = new Thread() {
314 public void run() {
315 TaskDescription first = loadFirstTask();
316 synchronized(mFirstTaskLock) {
317 if (mCancelPreloadingFirstTask) {
318 clearFirstTask();
319 } else {
320 mFirstTask = first;
321 mFirstTaskLoaded = true;
322 }
323 mPreloadingFirstTask = false;
324 }
325 }
326 };
327 synchronized(mFirstTaskLock) {
328 if (!mPreloadingFirstTask) {
329 clearFirstTask();
330 mPreloadingFirstTask = true;
331 bgLoad.start();
332 }
333 }
334 }
335
336 public void cancelPreloadingFirstTask() {
337 synchronized(mFirstTaskLock) {
338 if (mPreloadingFirstTask) {
339 mCancelPreloadingFirstTask = true;
340 } else {
341 clearFirstTask();
342 }
343 }
344 }
345
346 boolean mPreloadingFirstTask;
347 boolean mCancelPreloadingFirstTask;
348 public TaskDescription getFirstTask() {
349 while(true) {
350 synchronized(mFirstTaskLock) {
351 if (mFirstTaskLoaded) {
352 return mFirstTask;
353 } else if (!mFirstTaskLoaded && !mPreloadingFirstTask) {
354 mFirstTask = loadFirstTask();
355 mFirstTaskLoaded = true;
356 return mFirstTask;
357 }
358 }
359 try {
360 Thread.sleep(3);
361 } catch (InterruptedException e) {
362 }
363 }
364 }
365
366 public TaskDescription loadFirstTask() {
367 final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
368
369 final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasksForUser(
370 1, ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
371 TaskDescription item = null;
372 if (recentTasks.size() > 0) {
373 ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(0);
374
375 Intent intent = new Intent(recentInfo.baseIntent);
376 if (recentInfo.origActivity != null) {
377 intent.setComponent(recentInfo.origActivity);
378 }
379
380 // Don't load the current home activity.
381 if (isCurrentHomeActivity(intent.getComponent(), null)) {
382 return null;
383 }
384
385 // Don't load ourselves
386 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
387 return null;
388 }
389
390 item = createTaskDescription(recentInfo.id,
391 recentInfo.persistentId, recentInfo.baseIntent,
392 recentInfo.origActivity, recentInfo.description);
Michael Jurka2a430cc2012-10-22 10:56:18 -0700393 if (item != null) {
394 loadThumbnailAndIcon(item);
395 }
Michael Jurka80343f62012-10-18 13:13:46 +0200396 return item;
397 }
398 return null;
399 }
400
Michael Jurka99a96552012-01-27 17:23:38 -0800401 public void loadTasksInBackground() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700402 loadTasksInBackground(false);
403 }
404 public void loadTasksInBackground(final boolean zeroeth) {
405 if (mState != State.CANCELLED) {
406 return;
407 }
408 mState = State.LOADING;
409 mFirstScreenful = true;
410
Michael Jurka99a96552012-01-27 17:23:38 -0800411 final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
412 new LinkedBlockingQueue<TaskDescription>();
Michael Jurka99a96552012-01-27 17:23:38 -0800413 mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
414 @Override
415 protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
416 if (!isCancelled()) {
417 ArrayList<TaskDescription> newTasks = values[0];
418 // do a callback to RecentsPanelView to let it know we have more values
419 // how do we let it know we're all done? just always call back twice
Michael Jurkacb2522c2012-04-13 09:32:47 -0700420 if (mRecentsPanel != null) {
421 mRecentsPanel.onTasksLoaded(newTasks, mFirstScreenful);
422 }
423 if (mLoadedTasks == null) {
424 mLoadedTasks = new ArrayList<TaskDescription>();
425 }
426 mLoadedTasks.addAll(newTasks);
427 mFirstScreenful = false;
Michael Jurka99a96552012-01-27 17:23:38 -0800428 }
429 }
430 @Override
431 protected Void doInBackground(Void... params) {
432 // We load in two stages: first, we update progress with just the first screenful
433 // of items. Then, we update with the rest of the items
434 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700435 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800436 final PackageManager pm = mContext.getPackageManager();
437 final ActivityManager am = (ActivityManager)
Michael Jurkaab48b682011-09-12 15:39:45 -0700438 mContext.getSystemService(Context.ACTIVITY_SERVICE);
439
Michael Jurka99a96552012-01-27 17:23:38 -0800440 final List<ActivityManager.RecentTaskInfo> recentTasks =
Amith Yamasanid2e99562012-08-28 15:49:06 -0700441 am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
Michael Jurka99a96552012-01-27 17:23:38 -0800442 int numTasks = recentTasks.size();
443 ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
444 .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
Michael Jurkaab48b682011-09-12 15:39:45 -0700445
Michael Jurka99a96552012-01-27 17:23:38 -0800446 boolean firstScreenful = true;
447 ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
Michael Jurkaab48b682011-09-12 15:39:45 -0700448
Michael Jurka99a96552012-01-27 17:23:38 -0800449 // skip the first task - assume it's either the home screen or the current activity.
Michael Jurkacb2522c2012-04-13 09:32:47 -0700450 final int first = 0;
Michael Jurka99a96552012-01-27 17:23:38 -0800451 for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
452 if (isCancelled()) {
453 break;
Michael Jurkaab48b682011-09-12 15:39:45 -0700454 }
Michael Jurka99a96552012-01-27 17:23:38 -0800455 final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700456
457 Intent intent = new Intent(recentInfo.baseIntent);
458 if (recentInfo.origActivity != null) {
459 intent.setComponent(recentInfo.origActivity);
460 }
461
462 // Don't load the current home activity.
463 if (isCurrentHomeActivity(intent.getComponent(), homeInfo)) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700464 continue;
465 }
466
467 // Don't load ourselves
468 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
469 continue;
470 }
471
Michael Jurka99a96552012-01-27 17:23:38 -0800472 TaskDescription item = createTaskDescription(recentInfo.id,
473 recentInfo.persistentId, recentInfo.baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700474 recentInfo.origActivity, recentInfo.description);
Michael Jurkaab48b682011-09-12 15:39:45 -0700475
Michael Jurka99a96552012-01-27 17:23:38 -0800476 if (item != null) {
477 while (true) {
478 try {
479 tasksWaitingForThumbnails.put(item);
Michael Jurkaab48b682011-09-12 15:39:45 -0700480 break;
Michael Jurka99a96552012-01-27 17:23:38 -0800481 } catch (InterruptedException e) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700482 }
483 }
Michael Jurka99a96552012-01-27 17:23:38 -0800484 tasks.add(item);
485 if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
486 publishProgress(tasks);
487 tasks = new ArrayList<TaskDescription>();
488 firstScreenful = false;
489 //break;
490 }
491 ++index;
Michael Jurkaab48b682011-09-12 15:39:45 -0700492 }
Michael Jurka99a96552012-01-27 17:23:38 -0800493 }
494
495 if (!isCancelled()) {
496 publishProgress(tasks);
497 if (firstScreenful) {
498 // always should publish two updates
499 publishProgress(new ArrayList<TaskDescription>());
500 }
501 }
502
503 while (true) {
504 try {
505 tasksWaitingForThumbnails.put(new TaskDescription());
506 break;
507 } catch (InterruptedException e) {
508 }
509 }
510
511 Process.setThreadPriority(origPri);
512 return null;
Michael Jurkaab48b682011-09-12 15:39:45 -0700513 }
Michael Jurka99a96552012-01-27 17:23:38 -0800514 };
515 mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
516 loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
Michael Jurkaab48b682011-09-12 15:39:45 -0700517 }
518
Michael Jurka99a96552012-01-27 17:23:38 -0800519 private void loadThumbnailsAndIconsInBackground(
520 final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
521 // continually read items from tasksWaitingForThumbnails and load
522 // thumbnails and icons for them. finish thread when cancelled or there
523 // is a null item in tasksWaitingForThumbnails
524 mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
525 @Override
526 protected void onProgressUpdate(TaskDescription... values) {
527 if (!isCancelled()) {
528 TaskDescription td = values[0];
Michael Jurkacb2522c2012-04-13 09:32:47 -0700529 if (td.isNull()) { // end sentinel
530 mState = State.LOADED;
531 } else {
532 if (mRecentsPanel != null) {
533 mRecentsPanel.onTaskThumbnailLoaded(td);
534 }
535 }
Michael Jurka99a96552012-01-27 17:23:38 -0800536 }
537 }
538 @Override
539 protected Void doInBackground(Void... params) {
540 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700541 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800542
543 while (true) {
544 if (isCancelled()) {
545 break;
546 }
547 TaskDescription td = null;
548 while (td == null) {
549 try {
550 td = tasksWaitingForThumbnails.take();
551 } catch (InterruptedException e) {
552 }
553 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700554 if (td.isNull()) { // end sentinel
555 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800556 break;
557 }
558 loadThumbnailAndIcon(td);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700559
Michael Jurkacb2522c2012-04-13 09:32:47 -0700560 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800561 }
562
563 Process.setThreadPriority(origPri);
564 return null;
565 }
566 };
567 mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
568 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700569}