blob: 8b2cd3f25171df042675c66d89f7406b220a1106 [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;
Michael Jurkaab48b682011-09-12 15:39:45 -070040
Michael Jurka99a96552012-01-27 17:23:38 -080041import java.util.ArrayList;
42import java.util.List;
43import java.util.concurrent.BlockingQueue;
44import java.util.concurrent.LinkedBlockingQueue;
45
Michael Jurkacb2522c2012-04-13 09:32:47 -070046public class RecentTasksLoader implements View.OnTouchListener {
Michael Jurkaab48b682011-09-12 15:39:45 -070047 static final String TAG = "RecentTasksLoader";
John Spurlockb0e49fc2013-06-12 15:50:50 -040048 static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
Michael Jurkaab48b682011-09-12 15:39:45 -070049
50 private static final int DISPLAY_TASKS = 20;
51 private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
52
53 private Context mContext;
54 private RecentsPanelView mRecentsPanel;
Michael Jurka80343f62012-10-18 13:13:46 +020055
56 private Object mFirstTaskLock = new Object();
Michael Jurkacb2522c2012-04-13 09:32:47 -070057 private TaskDescription mFirstTask;
58 private boolean mFirstTaskLoaded;
Michael Jurkaab48b682011-09-12 15:39:45 -070059
Michael Jurka99a96552012-01-27 17:23:38 -080060 private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
61 private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
Michael Jurkacb2522c2012-04-13 09:32:47 -070062 private Handler mHandler;
Michael Jurkaab48b682011-09-12 15:39:45 -070063
64 private int mIconDpi;
65 private Bitmap mDefaultThumbnailBackground;
Michael Jurka99a96552012-01-27 17:23:38 -080066 private Bitmap mDefaultIconBackground;
Michael Jurkacb2522c2012-04-13 09:32:47 -070067 private int mNumTasksInFirstScreenful = Integer.MAX_VALUE;
68
69 private boolean mFirstScreenful;
70 private ArrayList<TaskDescription> mLoadedTasks;
71
72 private enum State { LOADING, LOADED, CANCELLED };
73 private State mState = State.CANCELLED;
74
Michael Jurka80343f62012-10-18 13:13:46 +020075
76 private static RecentTasksLoader sInstance;
77 public static RecentTasksLoader getInstance(Context context) {
78 if (sInstance == null) {
79 sInstance = new RecentTasksLoader(context);
Michael Jurkacb2522c2012-04-13 09:32:47 -070080 }
Michael Jurka80343f62012-10-18 13:13:46 +020081 return sInstance;
Michael Jurkacb2522c2012-04-13 09:32:47 -070082 }
Michael Jurkaab48b682011-09-12 15:39:45 -070083
Michael Jurka80343f62012-10-18 13:13:46 +020084 private RecentTasksLoader(Context context) {
Michael Jurkaab48b682011-09-12 15:39:45 -070085 mContext = context;
Michael Jurkacb2522c2012-04-13 09:32:47 -070086 mHandler = new Handler();
Michael Jurkaab48b682011-09-12 15:39:45 -070087
88 final Resources res = context.getResources();
89
90 // get the icon size we want -- on tablets, we use bigger icons
91 boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
Michael Jurkaab48b682011-09-12 15:39:45 -070092 if (isTablet) {
Winson Chung43e34f62012-01-24 15:26:59 -080093 ActivityManager activityManager =
94 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
95 mIconDpi = activityManager.getLauncherLargeIconDensity();
Michael Jurkaab48b682011-09-12 15:39:45 -070096 } else {
97 mIconDpi = res.getDisplayMetrics().densityDpi;
98 }
Michael Jurkaab48b682011-09-12 15:39:45 -070099
Michael Jurka99a96552012-01-27 17:23:38 -0800100 // Render default icon (just a blank image)
101 int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
102 int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
103 mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
104
Michael Jurkaab48b682011-09-12 15:39:45 -0700105 // Render the default thumbnail background
Michael Jurka99a96552012-01-27 17:23:38 -0800106 int thumbnailWidth =
107 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
108 int thumbnailHeight =
109 (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
Michael Jurkaab48b682011-09-12 15:39:45 -0700110 int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
111
Michael Jurka99a96552012-01-27 17:23:38 -0800112 mDefaultThumbnailBackground =
113 Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
Michael Jurkaab48b682011-09-12 15:39:45 -0700114 Canvas c = new Canvas(mDefaultThumbnailBackground);
115 c.drawColor(color);
Michael Jurkaab48b682011-09-12 15:39:45 -0700116 }
117
Michael Jurkacb2522c2012-04-13 09:32:47 -0700118 public void setRecentsPanel(RecentsPanelView newRecentsPanel, RecentsPanelView caller) {
119 // Only allow clearing mRecentsPanel if the caller is the current recentsPanel
120 if (newRecentsPanel != null || mRecentsPanel == caller) {
121 mRecentsPanel = newRecentsPanel;
122 if (mRecentsPanel != null) {
123 mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
124 }
125 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700126 }
127
Michael Jurka412cba82011-10-17 09:05:00 -0700128 public Bitmap getDefaultThumbnail() {
129 return mDefaultThumbnailBackground;
130 }
131
Michael Jurka99a96552012-01-27 17:23:38 -0800132 public Bitmap getDefaultIcon() {
133 return mDefaultIconBackground;
134 }
135
Michael Jurkacb2522c2012-04-13 09:32:47 -0700136 public ArrayList<TaskDescription> getLoadedTasks() {
137 return mLoadedTasks;
138 }
139
Michael Jurkae57922c2012-11-26 16:05:12 -0800140 public void remove(TaskDescription td) {
141 mLoadedTasks.remove(td);
142 }
143
Michael Jurkacb2522c2012-04-13 09:32:47 -0700144 public boolean isFirstScreenful() {
145 return mFirstScreenful;
146 }
147
148 private boolean isCurrentHomeActivity(ComponentName component, ActivityInfo homeInfo) {
149 if (homeInfo == null) {
150 final PackageManager pm = mContext.getPackageManager();
151 homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
152 .resolveActivityInfo(pm, 0);
153 }
154 return homeInfo != null
155 && homeInfo.packageName.equals(component.getPackageName())
156 && homeInfo.name.equals(component.getClassName());
157 }
158
159 // Create an TaskDescription, returning null if the title or icon is null
Michael Jurkaab48b682011-09-12 15:39:45 -0700160 TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700161 ComponentName origActivity, CharSequence description) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700162 Intent intent = new Intent(baseIntent);
163 if (origActivity != null) {
164 intent.setComponent(origActivity);
165 }
166 final PackageManager pm = mContext.getPackageManager();
Michael Jurkaab48b682011-09-12 15:39:45 -0700167 intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
168 | Intent.FLAG_ACTIVITY_NEW_TASK);
169 final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
170 if (resolveInfo != null) {
171 final ActivityInfo info = resolveInfo.activityInfo;
172 final String title = info.loadLabel(pm).toString();
Michael Jurkaab48b682011-09-12 15:39:45 -0700173
Michael Jurka99a96552012-01-27 17:23:38 -0800174 if (title != null && title.length() > 0) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700175 if (DEBUG) Log.v(TAG, "creating activity desc for id="
176 + persistentTaskId + ", label=" + title);
177
178 TaskDescription item = new TaskDescription(taskId,
179 persistentTaskId, resolveInfo, baseIntent, info.packageName,
180 description);
181 item.setLabel(title);
Michael Jurkaab48b682011-09-12 15:39:45 -0700182
183 return item;
184 } else {
185 if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
186 }
187 }
188 return null;
189 }
190
Michael Jurka99a96552012-01-27 17:23:38 -0800191 void loadThumbnailAndIcon(TaskDescription td) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700192 final ActivityManager am = (ActivityManager)
193 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurka99a96552012-01-27 17:23:38 -0800194 final PackageManager pm = mContext.getPackageManager();
Dianne Hackborn15491c62012-09-19 10:59:14 -0700195 Bitmap thumbnail = am.getTaskTopThumbnail(td.persistentTaskId);
Michael Jurka99a96552012-01-27 17:23:38 -0800196 Drawable icon = getFullResIcon(td.resolveInfo, pm);
Michael Jurkaab48b682011-09-12 15:39:45 -0700197
198 if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
Dianne Hackborn15491c62012-09-19 10:59:14 -0700199 + td + ": " + thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700200 synchronized (td) {
Dianne Hackborn15491c62012-09-19 10:59:14 -0700201 if (thumbnail != null) {
202 td.setThumbnail(thumbnail);
Michael Jurkaab48b682011-09-12 15:39:45 -0700203 } else {
204 td.setThumbnail(mDefaultThumbnailBackground);
205 }
Michael Jurka99a96552012-01-27 17:23:38 -0800206 if (icon != null) {
207 td.setIcon(icon);
208 }
209 td.setLoaded(true);
Michael Jurkaab48b682011-09-12 15:39:45 -0700210 }
211 }
212
213 Drawable getFullResDefaultActivityIcon() {
214 return getFullResIcon(Resources.getSystem(),
215 com.android.internal.R.mipmap.sym_def_app_icon);
216 }
217
218 Drawable getFullResIcon(Resources resources, int iconId) {
219 try {
220 return resources.getDrawableForDensity(iconId, mIconDpi);
221 } catch (Resources.NotFoundException e) {
222 return getFullResDefaultActivityIcon();
223 }
224 }
225
226 private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
227 Resources resources;
228 try {
229 resources = packageManager.getResourcesForApplication(
230 info.activityInfo.applicationInfo);
231 } catch (PackageManager.NameNotFoundException e) {
232 resources = null;
233 }
234 if (resources != null) {
235 int iconId = info.activityInfo.getIconResource();
236 if (iconId != 0) {
237 return getFullResIcon(resources, iconId);
238 }
239 }
240 return getFullResDefaultActivityIcon();
241 }
242
Michael Jurkacb2522c2012-04-13 09:32:47 -0700243 Runnable mPreloadTasksRunnable = new Runnable() {
244 public void run() {
245 loadTasksInBackground();
246 }
247 };
248
249 // additional optimization when we have software system buttons - start loading the recent
250 // tasks on touch down
251 @Override
252 public boolean onTouch(View v, MotionEvent ev) {
253 int action = ev.getAction() & MotionEvent.ACTION_MASK;
254 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700255 preloadRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700256 } else if (action == MotionEvent.ACTION_CANCEL) {
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700257 cancelPreloadingRecentTasksList();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700258 } else if (action == MotionEvent.ACTION_UP) {
259 // Remove the preloader if we haven't called it yet
260 mHandler.removeCallbacks(mPreloadTasksRunnable);
261 if (!v.isPressed()) {
262 cancelLoadingThumbnailsAndIcons();
263 }
264
265 }
266 return false;
267 }
268
Michael Jurkad0d4bb82012-09-04 06:25:50 -0700269 public void preloadRecentTasksList() {
270 mHandler.post(mPreloadTasksRunnable);
271 }
272
273 public void cancelPreloadingRecentTasksList() {
274 cancelLoadingThumbnailsAndIcons();
275 mHandler.removeCallbacks(mPreloadTasksRunnable);
276 }
277
Michael Jurkacb2522c2012-04-13 09:32:47 -0700278 public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
279 // Only oblige this request if it comes from the current RecentsPanel
280 // (eg when you rotate, the old RecentsPanel request should be ignored)
281 if (mRecentsPanel == caller) {
282 cancelLoadingThumbnailsAndIcons();
283 }
284 }
285
286
287 private void cancelLoadingThumbnailsAndIcons() {
Michael Jurka45eed3c2013-05-02 14:49:45 +0200288 if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
289 return;
290 }
291
Michael Jurka99a96552012-01-27 17:23:38 -0800292 if (mTaskLoader != null) {
293 mTaskLoader.cancel(false);
294 mTaskLoader = null;
295 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700296 if (mThumbnailLoader != null) {
297 mThumbnailLoader.cancel(false);
298 mThumbnailLoader = null;
299 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700300 mLoadedTasks = null;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700301 if (mRecentsPanel != null) {
302 mRecentsPanel.onTaskLoadingCancelled();
303 }
304 mFirstScreenful = false;
305 mState = State.CANCELLED;
Michael Jurkaab48b682011-09-12 15:39:45 -0700306 }
307
Michael Jurka80343f62012-10-18 13:13:46 +0200308 private void clearFirstTask() {
309 synchronized (mFirstTaskLock) {
310 mFirstTask = null;
311 mFirstTaskLoaded = false;
312 }
313 }
314
315 public void preloadFirstTask() {
316 Thread bgLoad = new Thread() {
317 public void run() {
318 TaskDescription first = loadFirstTask();
319 synchronized(mFirstTaskLock) {
320 if (mCancelPreloadingFirstTask) {
321 clearFirstTask();
322 } else {
323 mFirstTask = first;
324 mFirstTaskLoaded = true;
325 }
326 mPreloadingFirstTask = false;
327 }
328 }
329 };
330 synchronized(mFirstTaskLock) {
331 if (!mPreloadingFirstTask) {
332 clearFirstTask();
333 mPreloadingFirstTask = true;
334 bgLoad.start();
335 }
336 }
337 }
338
339 public void cancelPreloadingFirstTask() {
340 synchronized(mFirstTaskLock) {
341 if (mPreloadingFirstTask) {
342 mCancelPreloadingFirstTask = true;
343 } else {
344 clearFirstTask();
345 }
346 }
347 }
348
349 boolean mPreloadingFirstTask;
350 boolean mCancelPreloadingFirstTask;
351 public TaskDescription getFirstTask() {
352 while(true) {
353 synchronized(mFirstTaskLock) {
354 if (mFirstTaskLoaded) {
355 return mFirstTask;
356 } else if (!mFirstTaskLoaded && !mPreloadingFirstTask) {
357 mFirstTask = loadFirstTask();
358 mFirstTaskLoaded = true;
359 return mFirstTask;
360 }
361 }
362 try {
363 Thread.sleep(3);
364 } catch (InterruptedException e) {
365 }
366 }
367 }
368
369 public TaskDescription loadFirstTask() {
370 final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
371
372 final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasksForUser(
373 1, ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
374 TaskDescription item = null;
375 if (recentTasks.size() > 0) {
376 ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(0);
377
378 Intent intent = new Intent(recentInfo.baseIntent);
379 if (recentInfo.origActivity != null) {
380 intent.setComponent(recentInfo.origActivity);
381 }
382
383 // Don't load the current home activity.
384 if (isCurrentHomeActivity(intent.getComponent(), null)) {
385 return null;
386 }
387
388 // Don't load ourselves
389 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
390 return null;
391 }
392
393 item = createTaskDescription(recentInfo.id,
394 recentInfo.persistentId, recentInfo.baseIntent,
395 recentInfo.origActivity, recentInfo.description);
Michael Jurka2a430cc2012-10-22 10:56:18 -0700396 if (item != null) {
397 loadThumbnailAndIcon(item);
398 }
Michael Jurka80343f62012-10-18 13:13:46 +0200399 return item;
400 }
401 return null;
402 }
403
Michael Jurka99a96552012-01-27 17:23:38 -0800404 public void loadTasksInBackground() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700405 loadTasksInBackground(false);
406 }
407 public void loadTasksInBackground(final boolean zeroeth) {
408 if (mState != State.CANCELLED) {
409 return;
410 }
411 mState = State.LOADING;
412 mFirstScreenful = true;
413
Michael Jurka99a96552012-01-27 17:23:38 -0800414 final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
415 new LinkedBlockingQueue<TaskDescription>();
Michael Jurka99a96552012-01-27 17:23:38 -0800416 mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
417 @Override
418 protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
419 if (!isCancelled()) {
420 ArrayList<TaskDescription> newTasks = values[0];
421 // do a callback to RecentsPanelView to let it know we have more values
422 // how do we let it know we're all done? just always call back twice
Michael Jurkacb2522c2012-04-13 09:32:47 -0700423 if (mRecentsPanel != null) {
424 mRecentsPanel.onTasksLoaded(newTasks, mFirstScreenful);
425 }
426 if (mLoadedTasks == null) {
427 mLoadedTasks = new ArrayList<TaskDescription>();
428 }
429 mLoadedTasks.addAll(newTasks);
430 mFirstScreenful = false;
Michael Jurka99a96552012-01-27 17:23:38 -0800431 }
432 }
433 @Override
434 protected Void doInBackground(Void... params) {
435 // We load in two stages: first, we update progress with just the first screenful
436 // of items. Then, we update with the rest of the items
437 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700438 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800439 final PackageManager pm = mContext.getPackageManager();
440 final ActivityManager am = (ActivityManager)
Michael Jurkaab48b682011-09-12 15:39:45 -0700441 mContext.getSystemService(Context.ACTIVITY_SERVICE);
442
Michael Jurka99a96552012-01-27 17:23:38 -0800443 final List<ActivityManager.RecentTaskInfo> recentTasks =
Amith Yamasanid2e99562012-08-28 15:49:06 -0700444 am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
Michael Jurka99a96552012-01-27 17:23:38 -0800445 int numTasks = recentTasks.size();
446 ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
447 .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
Michael Jurkaab48b682011-09-12 15:39:45 -0700448
Michael Jurka99a96552012-01-27 17:23:38 -0800449 boolean firstScreenful = true;
450 ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
Michael Jurkaab48b682011-09-12 15:39:45 -0700451
Michael Jurka99a96552012-01-27 17:23:38 -0800452 // skip the first task - assume it's either the home screen or the current activity.
Michael Jurkacb2522c2012-04-13 09:32:47 -0700453 final int first = 0;
Michael Jurka99a96552012-01-27 17:23:38 -0800454 for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
455 if (isCancelled()) {
456 break;
Michael Jurkaab48b682011-09-12 15:39:45 -0700457 }
Michael Jurka99a96552012-01-27 17:23:38 -0800458 final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700459
460 Intent intent = new Intent(recentInfo.baseIntent);
461 if (recentInfo.origActivity != null) {
462 intent.setComponent(recentInfo.origActivity);
463 }
464
465 // Don't load the current home activity.
466 if (isCurrentHomeActivity(intent.getComponent(), homeInfo)) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700467 continue;
468 }
469
470 // Don't load ourselves
471 if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) {
472 continue;
473 }
474
Michael Jurka99a96552012-01-27 17:23:38 -0800475 TaskDescription item = createTaskDescription(recentInfo.id,
476 recentInfo.persistentId, recentInfo.baseIntent,
Michael Jurkacb2522c2012-04-13 09:32:47 -0700477 recentInfo.origActivity, recentInfo.description);
Michael Jurkaab48b682011-09-12 15:39:45 -0700478
Michael Jurka99a96552012-01-27 17:23:38 -0800479 if (item != null) {
480 while (true) {
481 try {
482 tasksWaitingForThumbnails.put(item);
Michael Jurkaab48b682011-09-12 15:39:45 -0700483 break;
Michael Jurka99a96552012-01-27 17:23:38 -0800484 } catch (InterruptedException e) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700485 }
486 }
Michael Jurka99a96552012-01-27 17:23:38 -0800487 tasks.add(item);
488 if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
489 publishProgress(tasks);
490 tasks = new ArrayList<TaskDescription>();
491 firstScreenful = false;
492 //break;
493 }
494 ++index;
Michael Jurkaab48b682011-09-12 15:39:45 -0700495 }
Michael Jurka99a96552012-01-27 17:23:38 -0800496 }
497
498 if (!isCancelled()) {
499 publishProgress(tasks);
500 if (firstScreenful) {
501 // always should publish two updates
502 publishProgress(new ArrayList<TaskDescription>());
503 }
504 }
505
506 while (true) {
507 try {
508 tasksWaitingForThumbnails.put(new TaskDescription());
509 break;
510 } catch (InterruptedException e) {
511 }
512 }
513
514 Process.setThreadPriority(origPri);
515 return null;
Michael Jurkaab48b682011-09-12 15:39:45 -0700516 }
Michael Jurka99a96552012-01-27 17:23:38 -0800517 };
518 mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
519 loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
Michael Jurkaab48b682011-09-12 15:39:45 -0700520 }
521
Michael Jurka99a96552012-01-27 17:23:38 -0800522 private void loadThumbnailsAndIconsInBackground(
523 final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
524 // continually read items from tasksWaitingForThumbnails and load
525 // thumbnails and icons for them. finish thread when cancelled or there
526 // is a null item in tasksWaitingForThumbnails
527 mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
528 @Override
529 protected void onProgressUpdate(TaskDescription... values) {
530 if (!isCancelled()) {
531 TaskDescription td = values[0];
Michael Jurkacb2522c2012-04-13 09:32:47 -0700532 if (td.isNull()) { // end sentinel
533 mState = State.LOADED;
534 } else {
535 if (mRecentsPanel != null) {
536 mRecentsPanel.onTaskThumbnailLoaded(td);
537 }
538 }
Michael Jurka99a96552012-01-27 17:23:38 -0800539 }
540 }
541 @Override
542 protected Void doInBackground(Void... params) {
543 final int origPri = Process.getThreadPriority(Process.myTid());
Glenn Kasten252030b2012-03-15 16:34:01 -0700544 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Michael Jurka99a96552012-01-27 17:23:38 -0800545
546 while (true) {
547 if (isCancelled()) {
548 break;
549 }
550 TaskDescription td = null;
551 while (td == null) {
552 try {
553 td = tasksWaitingForThumbnails.take();
554 } catch (InterruptedException e) {
555 }
556 }
Michael Jurkacb2522c2012-04-13 09:32:47 -0700557 if (td.isNull()) { // end sentinel
558 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800559 break;
560 }
561 loadThumbnailAndIcon(td);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700562
Michael Jurkacb2522c2012-04-13 09:32:47 -0700563 publishProgress(td);
Michael Jurka99a96552012-01-27 17:23:38 -0800564 }
565
566 Process.setThreadPriority(origPri);
567 return null;
568 }
569 };
570 mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
571 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700572}