blob: 5f9162d83582d2ea59a6b7e15b1947aa68ba2e2f [file] [log] [blame]
Winson Chung303e1ff2014-03-07 15:06:19 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents;
18
19import android.app.ActivityManager;
Winson Chung4d7b0922014-03-13 17:14:17 -070020import android.content.ComponentCallbacks2;
Winson Chung303e1ff2014-03-07 15:06:19 -080021import android.content.Context;
22import android.content.pm.ActivityInfo;
23import android.content.pm.PackageManager;
24import android.graphics.Bitmap;
25import android.graphics.Canvas;
26import android.graphics.drawable.BitmapDrawable;
27import android.graphics.drawable.Drawable;
28import android.os.Handler;
29import android.os.HandlerThread;
30import android.os.UserHandle;
31import android.util.LruCache;
32import com.android.systemui.recents.model.SpaceNode;
33import com.android.systemui.recents.model.Task;
34import com.android.systemui.recents.model.TaskStack;
35
36import java.util.Collections;
37import java.util.Iterator;
38import java.util.List;
39import java.util.concurrent.ConcurrentLinkedQueue;
40
41
42/** A bitmap load queue */
43class TaskResourceLoadQueue {
44 ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
45
46 Task nextTask() {
47 Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]");
48 return mQueue.poll();
49 }
50
51 void addTask(Task t) {
52 Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|addTask]");
53 if (!mQueue.contains(t)) {
54 mQueue.add(t);
55 }
56 synchronized(this) {
57 notifyAll();
58 }
59 }
60
61 void removeTask(Task t) {
62 Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|removeTask]");
63 mQueue.remove(t);
64 }
65
66 void clearTasks() {
67 Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|clearTasks]");
68 mQueue.clear();
69 }
70
71 boolean isEmpty() {
72 return mQueue.isEmpty();
73 }
74}
75
76/* Task resource loader */
77class TaskResourceLoader implements Runnable {
78 Context mContext;
79 HandlerThread mLoadThread;
80 Handler mLoadThreadHandler;
81 Handler mMainThreadHandler;
82
83 TaskResourceLoadQueue mLoadQueue;
84 DrawableLruCache mIconCache;
85 BitmapLruCache mThumbnailCache;
Winson Chung04dfe0d2014-03-14 14:06:29 -070086
Winson Chung303e1ff2014-03-07 15:06:19 -080087 boolean mCancelled;
Winson Chung04dfe0d2014-03-14 14:06:29 -070088 boolean mWaitingOnLoadQueue;
Winson Chung303e1ff2014-03-07 15:06:19 -080089
90 /** Constructor, creates a new loading thread that loads task resources in the background */
91 public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
92 BitmapLruCache thumbnailCache) {
93 mLoadQueue = loadQueue;
94 mIconCache = iconCache;
95 mThumbnailCache = thumbnailCache;
96 mMainThreadHandler = new Handler();
97 mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
98 mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
99 mLoadThread.start();
100 mLoadThreadHandler = new Handler(mLoadThread.getLooper());
101 mLoadThreadHandler.post(this);
102 }
103
104 /** Restarts the loader thread */
105 void start(Context context) {
106 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|start]");
107 mContext = context;
108 mCancelled = false;
109 // Notify the load thread to start loading
110 synchronized(mLoadThread) {
111 mLoadThread.notifyAll();
112 }
113 }
114
115 /** Requests the loader thread to stop after the current iteration */
116 void stop() {
117 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
118 // Mark as cancelled for the thread to pick up
119 mCancelled = true;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700120 // If we are waiting for the load queue for more tasks, then we can just reset the
121 // Context now, since nothing is using it
122 if (mWaitingOnLoadQueue) {
123 mContext = null;
124 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800125 }
126
127 @Override
128 public void run() {
129 while (true) {
130 Console.log(Constants.DebugFlags.App.TaskDataLoader,
131 "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
132 if (mCancelled) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700133 Console.log(Constants.DebugFlags.App.TaskDataLoader,
134 "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
Winson Chung303e1ff2014-03-07 15:06:19 -0800135 // We have to unset the context here, since the background thread may be using it
136 // when we call stop()
137 mContext = null;
138 // If we are cancelled, then wait until we are started again
139 synchronized(mLoadThread) {
140 try {
141 Console.log(Constants.DebugFlags.App.TaskDataLoader,
142 "[TaskResourceLoader|waitOnLoadThreadCancelled]");
143 mLoadThread.wait();
144 } catch (InterruptedException ie) {
145 ie.printStackTrace();
146 }
147 }
148 } else {
149 // Load the next item from the queue
150 final Task t = mLoadQueue.nextTask();
151 if (t != null) {
152 try {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700153 Drawable loadIcon = mIconCache.get(t.key);
154 Bitmap loadThumbnail = mThumbnailCache.get(t.key);
Winson Chung303e1ff2014-03-07 15:06:19 -0800155 Console.log(Constants.DebugFlags.App.TaskDataLoader,
156 " [TaskResourceLoader|load]",
Winson Chung04dfe0d2014-03-14 14:06:29 -0700157 t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
Winson Chung303e1ff2014-03-07 15:06:19 -0800158 // Load the icon
Winson Chung04dfe0d2014-03-14 14:06:29 -0700159 if (loadIcon == null) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800160 PackageManager pm = mContext.getPackageManager();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700161 ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
Winson Chung303e1ff2014-03-07 15:06:19 -0800162 PackageManager.GET_META_DATA);
163 Drawable icon = info.loadIcon(pm);
164 if (!mCancelled) {
Winson Chung71243902014-03-14 17:52:47 -0700165 if (icon != null) {
166 Console.log(Constants.DebugFlags.App.TaskDataLoader,
167 " [TaskResourceLoader|loadIcon]",
168 icon);
169 loadIcon = icon;
170 mIconCache.put(t.key, icon);
171 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800172 }
173 }
174 // Load the thumbnail
Winson Chung04dfe0d2014-03-14 14:06:29 -0700175 if (loadThumbnail == null) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800176 ActivityManager am = (ActivityManager)
177 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700178 Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
Winson Chung303e1ff2014-03-07 15:06:19 -0800179 if (!mCancelled) {
180 if (thumbnail != null) {
181 Console.log(Constants.DebugFlags.App.TaskDataLoader,
182 " [TaskResourceLoader|loadThumbnail]",
183 thumbnail);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700184 loadThumbnail = thumbnail;
185 mThumbnailCache.put(t.key, thumbnail);
Winson Chung303e1ff2014-03-07 15:06:19 -0800186 } else {
187 Console.logError(mContext,
188 "Failed to load task top thumbnail for: " +
Winson Chung04dfe0d2014-03-14 14:06:29 -0700189 t.key.intent.getComponent().getPackageName());
Winson Chung303e1ff2014-03-07 15:06:19 -0800190 }
191 }
192 }
193 if (!mCancelled) {
194 // Notify that the task data has changed
Winson Chung04dfe0d2014-03-14 14:06:29 -0700195 final Drawable newIcon = loadIcon;
196 final Bitmap newThumbnail = loadThumbnail;
Winson Chung303e1ff2014-03-07 15:06:19 -0800197 mMainThreadHandler.post(new Runnable() {
198 @Override
199 public void run() {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700200 t.notifyTaskDataLoaded(newThumbnail, newIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800201 }
202 });
203 }
204 } catch (PackageManager.NameNotFoundException ne) {
205 ne.printStackTrace();
206 }
207 }
208
209 // If there are no other items in the list, then just wait until something is added
210 if (!mCancelled && mLoadQueue.isEmpty()) {
211 synchronized(mLoadQueue) {
212 try {
213 Console.log(Constants.DebugFlags.App.TaskDataLoader,
214 "[TaskResourceLoader|waitOnLoadQueue]");
Winson Chung04dfe0d2014-03-14 14:06:29 -0700215 mWaitingOnLoadQueue = true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800216 mLoadQueue.wait();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700217 mWaitingOnLoadQueue = false;
Winson Chung303e1ff2014-03-07 15:06:19 -0800218 } catch (InterruptedException ie) {
219 ie.printStackTrace();
220 }
221 }
222 }
223 }
224 }
225 }
226}
227
Winson Chung04dfe0d2014-03-14 14:06:29 -0700228/**
229 * The drawable cache. By using the Task's key, we can prevent holding onto a reference to the Task
230 * resource data, while keeping the cache data in memory where necessary.
231 */
232class DrawableLruCache extends LruCache<Task.TaskKey, Drawable> {
Winson Chung303e1ff2014-03-07 15:06:19 -0800233 public DrawableLruCache(int cacheSize) {
234 super(cacheSize);
235 }
236
237 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700238 protected int sizeOf(Task.TaskKey t, Drawable d) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800239 // The cache size will be measured in kilobytes rather than number of items
240 // NOTE: this isn't actually correct, as the icon may be smaller
241 int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
242 return maxBytes / 1024;
243 }
244}
245
Winson Chung04dfe0d2014-03-14 14:06:29 -0700246/**
247 * The bitmap cache. By using the Task's key, we can prevent holding onto a reference to the Task
248 * resource data, while keeping the cache data in memory where necessary.
249 */
250class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> {
Winson Chung303e1ff2014-03-07 15:06:19 -0800251 public BitmapLruCache(int cacheSize) {
252 super(cacheSize);
253 }
254
255 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700256 protected int sizeOf(Task.TaskKey t, Bitmap bitmap) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800257 // The cache size will be measured in kilobytes rather than number of items
Winson Chung4d7b0922014-03-13 17:14:17 -0700258 return bitmap.getAllocationByteCount() / 1024;
Winson Chung303e1ff2014-03-07 15:06:19 -0800259 }
260}
261
262/* Recents task loader
263 * NOTE: We should not hold any references to a Context from a static instance */
264public class RecentsTaskLoader {
265 static RecentsTaskLoader sInstance;
266
267 DrawableLruCache mIconCache;
268 BitmapLruCache mThumbnailCache;
269 TaskResourceLoadQueue mLoadQueue;
270 TaskResourceLoader mLoader;
271
Winson Chung4d7b0922014-03-13 17:14:17 -0700272 int mMaxThumbnailCacheSize;
273 int mMaxIconCacheSize;
274
Winson Chung303e1ff2014-03-07 15:06:19 -0800275 BitmapDrawable mDefaultIcon;
276 Bitmap mDefaultThumbnail;
277
278 /** Private Constructor */
279 private RecentsTaskLoader(Context context) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700280 // Calculate the cache sizes, we just use a reasonable number here similar to those
281 // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
282 // for icons.
Winson Chung303e1ff2014-03-07 15:06:19 -0800283 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
Winson Chung4d7b0922014-03-13 17:14:17 -0700284 mMaxThumbnailCacheSize = maxMemory / 8;
285 mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700286 int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700287 mMaxIconCacheSize;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700288 int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700289 mMaxThumbnailCacheSize;
290
Winson Chung04dfe0d2014-03-14 14:06:29 -0700291 Console.log(Constants.DebugFlags.App.TaskDataLoader,
Winson Chung303e1ff2014-03-07 15:06:19 -0800292 "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
293 " iconCache: " + iconCacheSize);
Winson Chung4d7b0922014-03-13 17:14:17 -0700294
295 // Initialize the cache and loaders
Winson Chung303e1ff2014-03-07 15:06:19 -0800296 mLoadQueue = new TaskResourceLoadQueue();
297 mIconCache = new DrawableLruCache(iconCacheSize);
298 mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
299 mLoader = new TaskResourceLoader(mLoadQueue, mIconCache, mThumbnailCache);
300
301 // Create the default assets
302 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
303 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
304 Canvas c = new Canvas();
305 c.setBitmap(icon);
306 c.drawColor(0x00000000);
307 c.setBitmap(mDefaultThumbnail);
308 c.drawColor(0x00000000);
309 c.setBitmap(null);
310 mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
311 Console.log(Constants.DebugFlags.App.TaskDataLoader,
312 "[RecentsTaskLoader|defaultBitmaps]",
313 "icon: " + mDefaultIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
314 }
315
316 /** Initializes the recents task loader */
317 public static RecentsTaskLoader initialize(Context context) {
318 if (sInstance == null) {
319 sInstance = new RecentsTaskLoader(context);
320 }
321 return sInstance;
322 }
323
324 /** Returns the current recents task loader */
325 public static RecentsTaskLoader getInstance() {
326 return sInstance;
327 }
328
329 /** Reload the set of recent tasks */
330 SpaceNode reload(Context context, int preloadCount) {
Winson Chung4d7b0922014-03-13 17:14:17 -0700331 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
Winson Chung303e1ff2014-03-07 15:06:19 -0800332 TaskStack stack = new TaskStack(context);
333 SpaceNode root = new SpaceNode(context);
334 root.setStack(stack);
335 try {
336 long t1 = System.currentTimeMillis();
337
338 PackageManager pm = context.getPackageManager();
339 ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
340
341 // Get the recent tasks
342 List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(25,
343 ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
344 Collections.reverse(tasks);
345 Console.log(Constants.DebugFlags.App.TimeSystemCalls,
346 "[RecentsTaskLoader|getRecentTasks]",
347 "" + (System.currentTimeMillis() - t1) + "ms");
Winson Chung4d7b0922014-03-13 17:14:17 -0700348 Console.log(Constants.DebugFlags.App.TaskDataLoader,
Winson Chung303e1ff2014-03-07 15:06:19 -0800349 "[RecentsTaskLoader|tasks]", "" + tasks.size());
350
351 // Remove home/recents tasks
352 Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
353 while (iter.hasNext()) {
354 ActivityManager.RecentTaskInfo t = iter.next();
355
356 // Skip tasks in the home stack
357 if (am.isInHomeStack(t.persistentId)) {
358 iter.remove();
359 continue;
360 }
361 // Skip tasks from this Recents package
362 if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
363 iter.remove();
364 continue;
365 }
366 }
367
368 // Add each task to the task stack
369 t1 = System.currentTimeMillis();
370 int taskCount = tasks.size();
371 for (int i = 0; i < taskCount; i++) {
372 ActivityManager.RecentTaskInfo t = tasks.get(i);
Winson Chung303e1ff2014-03-07 15:06:19 -0800373 ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
374 PackageManager.GET_META_DATA);
375 String title = info.loadLabel(pm).toString();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700376 boolean isForemostTask = (i == (taskCount - 1));
377
Winson Chung4d7b0922014-03-13 17:14:17 -0700378 // Preload the specified number of apps
Winson Chung04dfe0d2014-03-14 14:06:29 -0700379 if (i >= (taskCount - preloadCount)) {
Winson Chung4d7b0922014-03-13 17:14:17 -0700380 Console.log(Constants.DebugFlags.App.TaskDataLoader,
Winson Chung303e1ff2014-03-07 15:06:19 -0800381 "[RecentsTaskLoader|preloadTask]",
382 "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
Winson Chung4d7b0922014-03-13 17:14:17 -0700383
Winson Chung04dfe0d2014-03-14 14:06:29 -0700384 Task task = new Task(t.persistentId, t.baseIntent, title);
Winson Chung4d7b0922014-03-13 17:14:17 -0700385
Winson Chung04dfe0d2014-03-14 14:06:29 -0700386 // Load the icon (if possible and not the foremost task, from the cache)
387 if (!isForemostTask) {
388 task.icon = mIconCache.get(task.key);
Winson Chung4d7b0922014-03-13 17:14:17 -0700389 }
390 if (task.icon == null) {
391 task.icon = info.loadIcon(pm);
Winson Chung71243902014-03-14 17:52:47 -0700392 if (task.icon != null) {
393 mIconCache.put(task.key, task.icon);
394 } else {
395 task.icon = mDefaultIcon;
396 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700397 }
398
Winson Chung04dfe0d2014-03-14 14:06:29 -0700399 // Load the thumbnail (if possible and not the foremost task, from the cache)
400 if (!isForemostTask) {
401 task.thumbnail = mThumbnailCache.get(task.key);
Winson Chung4d7b0922014-03-13 17:14:17 -0700402 }
403 if (task.thumbnail == null) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700404 Console.log(Constants.DebugFlags.App.TaskDataLoader,
405 "[RecentsTaskLoader|loadingTaskThumbnail]");
Winson Chung4d7b0922014-03-13 17:14:17 -0700406 task.thumbnail = am.getTaskTopThumbnail(t.id);
Winson Chung71243902014-03-14 17:52:47 -0700407 if (task.thumbnail != null) {
408 mThumbnailCache.put(task.key, task.thumbnail);
409 } else {
410 task.thumbnail = mDefaultThumbnail;
411 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700412 }
413
414 // Create as many tasks a we want to multiply by
415 for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
416 Console.log(Constants.DebugFlags.App.TaskDataLoader,
417 " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
Winson Chung303e1ff2014-03-07 15:06:19 -0800418 stack.addTask(task);
419 }
420 } else {
Winson Chung4d7b0922014-03-13 17:14:17 -0700421 // Create as many tasks a we want to multiply by
Winson Chung303e1ff2014-03-07 15:06:19 -0800422 for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
Winson Chung4d7b0922014-03-13 17:14:17 -0700423 Console.log(Constants.DebugFlags.App.TaskDataLoader,
Winson Chung303e1ff2014-03-07 15:06:19 -0800424 " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
Winson Chung04dfe0d2014-03-14 14:06:29 -0700425 stack.addTask(new Task(t.persistentId, t.baseIntent, title));
Winson Chung303e1ff2014-03-07 15:06:19 -0800426 }
427 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800428 }
429 Console.log(Constants.DebugFlags.App.TimeSystemCalls,
430 "[RecentsTaskLoader|getAllTaskTopThumbnail]",
431 "" + (System.currentTimeMillis() - t1) + "ms");
432
433 /*
434 // Get all the stacks
435 t1 = System.currentTimeMillis();
436 List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
437 Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
Winson Chung4d7b0922014-03-13 17:14:17 -0700438 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
Winson Chung303e1ff2014-03-07 15:06:19 -0800439 for (ActivityManager.StackInfo s : stackInfos) {
Winson Chung4d7b0922014-03-13 17:14:17 -0700440 Console.log(Constants.DebugFlags.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString());
Winson Chung303e1ff2014-03-07 15:06:19 -0800441 if (stacks.containsKey(s.stackId)) {
442 stacks.get(s.stackId).setRect(s.bounds);
443 }
444 }
445 */
446 } catch (Exception e) {
447 e.printStackTrace();
448 }
Winson Chung04dfe0d2014-03-14 14:06:29 -0700449
450 // Start the task loader
Winson Chung303e1ff2014-03-07 15:06:19 -0800451 mLoader.start(context);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700452
Winson Chung303e1ff2014-03-07 15:06:19 -0800453 return root;
454 }
455
Winson Chung4d7b0922014-03-13 17:14:17 -0700456 /** Acquires the task resource data from the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800457 public void loadTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700458 Drawable icon = mIconCache.get(t.key);
459 Bitmap thumbnail = mThumbnailCache.get(t.key);
Winson Chung303e1ff2014-03-07 15:06:19 -0800460
Winson Chung04dfe0d2014-03-14 14:06:29 -0700461 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
462 t + " icon: " + icon + " thumbnail: " + thumbnail +
463 " thumbnailCacheSize: " + mThumbnailCache.size());
Winson Chung303e1ff2014-03-07 15:06:19 -0800464
Winson Chung04dfe0d2014-03-14 14:06:29 -0700465 boolean requiresLoad = false;
466 if (icon == null) {
467 icon = mDefaultIcon;
468 requiresLoad = true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800469 }
Winson Chung04dfe0d2014-03-14 14:06:29 -0700470 if (thumbnail == null) {
471 thumbnail = mDefaultThumbnail;
472 requiresLoad = true;
473 }
474 if (requiresLoad) {
475 mLoadQueue.addTask(t);
476 }
477 t.notifyTaskDataLoaded(thumbnail, icon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800478 }
479
Winson Chung4d7b0922014-03-13 17:14:17 -0700480 /** Releases the task resource data back into the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800481 public void unloadTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700482 Console.log(Constants.DebugFlags.App.TaskDataLoader,
483 "[RecentsTaskLoader|unloadTask]", t +
484 " thumbnailCacheSize: " + mThumbnailCache.size());
485
486 mLoadQueue.removeTask(t);
487 t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800488 }
489
Winson Chung4d7b0922014-03-13 17:14:17 -0700490 /** Completely removes the resource data from the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800491 public void deleteTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700492 Console.log(Constants.DebugFlags.App.TaskDataLoader,
493 "[RecentsTaskLoader|deleteTask]", t);
494
495 mLoadQueue.removeTask(t);
496 mThumbnailCache.remove(t.key);
497 mIconCache.remove(t.key);
498 t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800499 }
500
Winson Chung04dfe0d2014-03-14 14:06:29 -0700501 /** Stops the task loader and clears all pending tasks */
Winson Chung303e1ff2014-03-07 15:06:19 -0800502 void stopLoader() {
503 Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
504 mLoader.stop();
505 mLoadQueue.clearTasks();
506 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700507
508 void onTrimMemory(int level) {
509 Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
510 Console.trimMemoryLevelToString(level));
511
Winson Chung04dfe0d2014-03-14 14:06:29 -0700512 switch (level) {
513 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
Winson Chung47c4c692014-03-17 10:17:11 -0700514 // Stop the loader immediately when the UI is no longer visible
515 stopLoader();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700516 break;
517 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
518 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
519 // We are leaving recents, so trim the data a bit
520 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
521 mIconCache.trimToSize(mMaxIconCacheSize / 2);
522 break;
523 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
524 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
525 // We are going to be low on memory
526 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
527 mIconCache.trimToSize(mMaxIconCacheSize / 4);
528 break;
529 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
530 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
531 // We are low on memory, so release everything
532 mThumbnailCache.evictAll();
533 mIconCache.evictAll();
534 break;
535 default:
536 break;
Winson Chung4d7b0922014-03-13 17:14:17 -0700537 }
538 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800539}