blob: d4b403d1f127c4876f9eea933694101e89c8b7cf [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
Winson Chungf1fbd772014-06-24 18:06:58 -070017package com.android.systemui.recents.model;
Winson Chung303e1ff2014-03-07 15:06:19 -080018
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;
Winson Chung37c8d8e2014-03-24 14:53:07 -070023import android.content.res.Resources;
Winson Chung303e1ff2014-03-07 15:06:19 -080024import android.graphics.Bitmap;
Winson Chung303e1ff2014-03-07 15:06:19 -080025import android.graphics.drawable.BitmapDrawable;
26import android.graphics.drawable.Drawable;
27import android.os.Handler;
28import android.os.HandlerThread;
29import android.os.UserHandle;
Kenny Guya734fc12014-08-28 21:06:27 +010030import android.util.Log;
31
Jorim Jaggi81e0c842014-09-12 23:28:58 +020032import com.android.systemui.R;
Winson Chungf1fbd772014-06-24 18:06:58 -070033import com.android.systemui.recents.Constants;
Winson Chung8eaeb7d2014-06-25 15:10:59 -070034import com.android.systemui.recents.RecentsConfiguration;
Winson Chungffa2ec62014-07-03 15:54:42 -070035import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung303e1ff2014-03-07 15:06:19 -080036
Winson Chunga4ccb862014-08-22 15:26:27 -070037import java.util.ArrayList;
Winson Chungff88d7b2014-07-17 12:30:07 -070038import java.util.Collection;
Winson Chung303e1ff2014-03-07 15:06:19 -080039import java.util.Collections;
Winson Chungebfc6982014-08-26 12:25:34 -070040import java.util.HashMap;
Winson Chung303e1ff2014-03-07 15:06:19 -080041import java.util.List;
42import java.util.concurrent.ConcurrentLinkedQueue;
43
44
Winson Chungebfc6982014-08-26 12:25:34 -070045/** Handle to an ActivityInfo */
46class ActivityInfoHandle {
47 ActivityInfo info;
48}
49
Winson Chung303e1ff2014-03-07 15:06:19 -080050/** A bitmap load queue */
51class TaskResourceLoadQueue {
52 ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
Winson Chung303e1ff2014-03-07 15:06:19 -080053
Winson Chung4c71aef2014-03-21 15:15:11 -070054 /** Adds a new task to the load queue */
Winson Chungff88d7b2014-07-17 12:30:07 -070055 void addTasks(Collection<Task> tasks) {
56 for (Task t : tasks) {
57 if (!mQueue.contains(t)) {
58 mQueue.add(t);
59 }
60 }
61 synchronized(this) {
62 notifyAll();
63 }
64 }
65
66 /** Adds a new task to the load queue */
Winson Chung93748a12014-07-13 17:43:31 -070067 void addTask(Task t) {
Winson Chung303e1ff2014-03-07 15:06:19 -080068 if (!mQueue.contains(t)) {
69 mQueue.add(t);
70 }
71 synchronized(this) {
72 notifyAll();
73 }
74 }
75
Winson Chung4c71aef2014-03-21 15:15:11 -070076 /**
77 * Retrieves the next task from the load queue, as well as whether we want that task to be
78 * force reloaded.
79 */
Winson Chung93748a12014-07-13 17:43:31 -070080 Task nextTask() {
Winson Chung93748a12014-07-13 17:43:31 -070081 return mQueue.poll();
Winson Chung4c71aef2014-03-21 15:15:11 -070082 }
83
84 /** Removes a task from the load queue */
Winson Chung303e1ff2014-03-07 15:06:19 -080085 void removeTask(Task t) {
Winson Chung303e1ff2014-03-07 15:06:19 -080086 mQueue.remove(t);
87 }
88
Winson Chung4c71aef2014-03-21 15:15:11 -070089 /** Clears all the tasks from the load queue */
Winson Chung303e1ff2014-03-07 15:06:19 -080090 void clearTasks() {
Winson Chung303e1ff2014-03-07 15:06:19 -080091 mQueue.clear();
92 }
93
Winson Chung4c71aef2014-03-21 15:15:11 -070094 /** Returns whether the load queue is empty */
Winson Chung303e1ff2014-03-07 15:06:19 -080095 boolean isEmpty() {
96 return mQueue.isEmpty();
97 }
98}
99
100/* Task resource loader */
101class TaskResourceLoader implements Runnable {
102 Context mContext;
103 HandlerThread mLoadThread;
104 Handler mLoadThreadHandler;
105 Handler mMainThreadHandler;
106
Winson Chunga10370f2014-04-02 12:25:04 -0700107 SystemServicesProxy mSystemServicesProxy;
Winson Chung303e1ff2014-03-07 15:06:19 -0800108 TaskResourceLoadQueue mLoadQueue;
Winson Chung5e3e5d82014-04-02 15:44:55 -0700109 DrawableLruCache mApplicationIconCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800110 BitmapLruCache mThumbnailCache;
Winson Chungc9567c02014-06-16 20:25:51 -0700111 Bitmap mDefaultThumbnail;
Winson Chung93748a12014-07-13 17:43:31 -0700112 BitmapDrawable mDefaultApplicationIcon;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700113
Winson Chung303e1ff2014-03-07 15:06:19 -0800114 boolean mCancelled;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700115 boolean mWaitingOnLoadQueue;
Winson Chung303e1ff2014-03-07 15:06:19 -0800116
117 /** Constructor, creates a new loading thread that loads task resources in the background */
Winson Chung93748a12014-07-13 17:43:31 -0700118 public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache,
119 BitmapLruCache thumbnailCache, Bitmap defaultThumbnail,
120 BitmapDrawable defaultApplicationIcon) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800121 mLoadQueue = loadQueue;
Winson Chung5e3e5d82014-04-02 15:44:55 -0700122 mApplicationIconCache = applicationIconCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800123 mThumbnailCache = thumbnailCache;
Winson Chungc9567c02014-06-16 20:25:51 -0700124 mDefaultThumbnail = defaultThumbnail;
Winson Chung93748a12014-07-13 17:43:31 -0700125 mDefaultApplicationIcon = defaultApplicationIcon;
Winson Chung303e1ff2014-03-07 15:06:19 -0800126 mMainThreadHandler = new Handler();
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200127 mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
Jorim Jaggi50949ea2014-09-15 16:07:29 +0200128 android.os.Process.THREAD_PRIORITY_BACKGROUND);
Winson Chung303e1ff2014-03-07 15:06:19 -0800129 mLoadThread.start();
130 mLoadThreadHandler = new Handler(mLoadThread.getLooper());
131 mLoadThreadHandler.post(this);
132 }
133
134 /** Restarts the loader thread */
135 void start(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800136 mContext = context;
137 mCancelled = false;
Winson Chunga10370f2014-04-02 12:25:04 -0700138 mSystemServicesProxy = new SystemServicesProxy(context);
Winson Chung303e1ff2014-03-07 15:06:19 -0800139 // Notify the load thread to start loading
140 synchronized(mLoadThread) {
141 mLoadThread.notifyAll();
142 }
143 }
144
145 /** Requests the loader thread to stop after the current iteration */
146 void stop() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800147 // Mark as cancelled for the thread to pick up
148 mCancelled = true;
Winson Chunga10370f2014-04-02 12:25:04 -0700149 mSystemServicesProxy = null;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700150 // If we are waiting for the load queue for more tasks, then we can just reset the
151 // Context now, since nothing is using it
152 if (mWaitingOnLoadQueue) {
153 mContext = null;
154 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800155 }
156
157 @Override
158 public void run() {
159 while (true) {
Winson Chung10f81392014-05-20 16:21:31 -0700160 if (mCancelled) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800161 // We have to unset the context here, since the background thread may be using it
162 // when we call stop()
163 mContext = null;
164 // If we are cancelled, then wait until we are started again
165 synchronized(mLoadThread) {
166 try {
Winson Chung303e1ff2014-03-07 15:06:19 -0800167 mLoadThread.wait();
168 } catch (InterruptedException ie) {
169 ie.printStackTrace();
170 }
171 }
172 } else {
Winson Chunga10370f2014-04-02 12:25:04 -0700173 SystemServicesProxy ssp = mSystemServicesProxy;
174
Winson Chung303e1ff2014-03-07 15:06:19 -0800175 // Load the next item from the queue
Winson Chung93748a12014-07-13 17:43:31 -0700176 final Task t = mLoadQueue.nextTask();
Winson Chung303e1ff2014-03-07 15:06:19 -0800177 if (t != null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700178 Drawable cachedIcon = mApplicationIconCache.get(t.key);
179 Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
Winson Chung93748a12014-07-13 17:43:31 -0700180 // Load the application icon if it is stale or we haven't cached one yet
181 if (cachedIcon == null) {
Amith Yamasani4f0a49e2014-04-10 13:48:15 -0700182 ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
Winson Chunga4ccb862014-08-22 15:26:27 -0700183 t.key.userId);
Winson Chung93748a12014-07-13 17:43:31 -0700184 if (info != null) {
Winson Chunga4ccb862014-08-22 15:26:27 -0700185 cachedIcon = ssp.getActivityIcon(info, t.key.userId);
Winson Chung303e1ff2014-03-07 15:06:19 -0800186 }
Winson Chungff88d7b2014-07-17 12:30:07 -0700187 if (cachedIcon == null) {
188 cachedIcon = mDefaultApplicationIcon;
189 }
190 // At this point, even if we can't load the icon, we will set the default
191 // icon.
Winson Chung93748a12014-07-13 17:43:31 -0700192 mApplicationIconCache.put(t.key, cachedIcon);
Winson Chunga10370f2014-04-02 12:25:04 -0700193 }
Winson Chung93748a12014-07-13 17:43:31 -0700194 // Load the thumbnail if it is stale or we haven't cached one yet
195 if (cachedThumbnail == null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700196 cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
197 if (cachedThumbnail != null) {
198 cachedThumbnail.setHasAlpha(false);
199 } else {
200 cachedThumbnail = mDefaultThumbnail;
Winson Chunga10370f2014-04-02 12:25:04 -0700201 }
Winson Chung93748a12014-07-13 17:43:31 -0700202 mThumbnailCache.put(t.key, cachedThumbnail);
Winson Chunga10370f2014-04-02 12:25:04 -0700203 }
204 if (!mCancelled) {
205 // Notify that the task data has changed
Winson Chung93748a12014-07-13 17:43:31 -0700206 final Drawable newIcon = cachedIcon;
Selim Cineke8199c52014-09-17 04:03:52 +0200207 final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
208 ? null : cachedThumbnail;
Winson Chunga10370f2014-04-02 12:25:04 -0700209 mMainThreadHandler.post(new Runnable() {
210 @Override
211 public void run() {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700212 t.notifyTaskDataLoaded(newThumbnail, newIcon);
Winson Chunga10370f2014-04-02 12:25:04 -0700213 }
214 });
Winson Chung303e1ff2014-03-07 15:06:19 -0800215 }
216 }
217
218 // If there are no other items in the list, then just wait until something is added
219 if (!mCancelled && mLoadQueue.isEmpty()) {
220 synchronized(mLoadQueue) {
221 try {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700222 mWaitingOnLoadQueue = true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800223 mLoadQueue.wait();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700224 mWaitingOnLoadQueue = false;
Winson Chung303e1ff2014-03-07 15:06:19 -0800225 } catch (InterruptedException ie) {
226 ie.printStackTrace();
227 }
228 }
229 }
230 }
231 }
232 }
233}
234
Winson Chung303e1ff2014-03-07 15:06:19 -0800235/* Recents task loader
236 * NOTE: We should not hold any references to a Context from a static instance */
237public class RecentsTaskLoader {
Kenny Guya734fc12014-08-28 21:06:27 +0100238 private static final String TAG = "RecentsTaskLoader";
239
Winson Chung303e1ff2014-03-07 15:06:19 -0800240 static RecentsTaskLoader sInstance;
241
Winson Chunga10370f2014-04-02 12:25:04 -0700242 SystemServicesProxy mSystemServicesProxy;
Winson Chung5e3e5d82014-04-02 15:44:55 -0700243 DrawableLruCache mApplicationIconCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800244 BitmapLruCache mThumbnailCache;
Winson Chunga4ccb862014-08-22 15:26:27 -0700245 StringLruCache mActivityLabelCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800246 TaskResourceLoadQueue mLoadQueue;
247 TaskResourceLoader mLoader;
248
Winson Chung9f49df92014-05-07 18:08:34 -0700249 RecentsPackageMonitor mPackageMonitor;
250
Winson Chung4d7b0922014-03-13 17:14:17 -0700251 int mMaxThumbnailCacheSize;
252 int mMaxIconCacheSize;
253
Winson Chung5e3e5d82014-04-02 15:44:55 -0700254 BitmapDrawable mDefaultApplicationIcon;
Winson Chung303e1ff2014-03-07 15:06:19 -0800255 Bitmap mDefaultThumbnail;
256
257 /** Private Constructor */
258 private RecentsTaskLoader(Context context) {
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200259 mMaxThumbnailCacheSize = context.getResources().getInteger(
260 R.integer.config_recents_max_thumbnail_count);
261 mMaxIconCacheSize = context.getResources().getInteger(
262 R.integer.config_recents_max_icon_count);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700263 int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700264 mMaxIconCacheSize;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700265 int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700266 mMaxThumbnailCacheSize;
267
Winson Chung7aceb9a2014-07-03 13:38:01 -0700268 // Create the default assets
269 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
270 icon.eraseColor(0x00000000);
271 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
272 mDefaultThumbnail.setHasAlpha(false);
273 mDefaultThumbnail.eraseColor(0xFFffffff);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700274 mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
275
Winson Chunga10370f2014-04-02 12:25:04 -0700276 // Initialize the proxy, cache and loaders
277 mSystemServicesProxy = new SystemServicesProxy(context);
Winson Chungd543c1b2014-06-23 15:06:45 -0700278 mPackageMonitor = new RecentsPackageMonitor();
Winson Chung303e1ff2014-03-07 15:06:19 -0800279 mLoadQueue = new TaskResourceLoadQueue();
Winson Chung5e3e5d82014-04-02 15:44:55 -0700280 mApplicationIconCache = new DrawableLruCache(iconCacheSize);
Winson Chung303e1ff2014-03-07 15:06:19 -0800281 mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
Winson Chunga4ccb862014-08-22 15:26:27 -0700282 mActivityLabelCache = new StringLruCache(100);
Winson Chungc9567c02014-06-16 20:25:51 -0700283 mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
Winson Chung93748a12014-07-13 17:43:31 -0700284 mDefaultThumbnail, mDefaultApplicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800285 }
286
287 /** Initializes the recents task loader */
288 public static RecentsTaskLoader initialize(Context context) {
289 if (sInstance == null) {
290 sInstance = new RecentsTaskLoader(context);
291 }
292 return sInstance;
293 }
294
295 /** Returns the current recents task loader */
296 public static RecentsTaskLoader getInstance() {
297 return sInstance;
298 }
299
Winson Chunga10370f2014-04-02 12:25:04 -0700300 /** Returns the system services proxy */
301 public SystemServicesProxy getSystemServicesProxy() {
302 return mSystemServicesProxy;
303 }
304
Winson Chungff88d7b2014-07-17 12:30:07 -0700305 /** Gets the list of recent tasks, ordered from back to front. */
Winson Chungebfc6982014-08-26 12:25:34 -0700306 private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
307 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chunga10370f2014-04-02 12:25:04 -0700308 List<ActivityManager.RecentTaskInfo> tasks =
Winson Chungebfc6982014-08-26 12:25:34 -0700309 ssp.getRecentTasks(config.maxNumTasksToLoad,
310 UserHandle.CURRENT.getIdentifier());
Winson Chunga10370f2014-04-02 12:25:04 -0700311 Collections.reverse(tasks);
Winson Chung0d767552014-04-09 14:33:23 -0700312 return tasks;
313 }
314
Winson Chungebfc6982014-08-26 12:25:34 -0700315 /** Returns the activity icon using as many cached values as we can. */
316 public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
317 ActivityManager.TaskDescription td, SystemServicesProxy ssp,
318 Resources res, ActivityInfoHandle infoHandle, boolean preloadTask) {
319 // Return the cached activity icon if it exists
320 Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
321 if (icon != null) {
322 return icon;
323 }
324 // Return the task description icon if it exists
325 if (td != null && td.getIcon() != null) {
326 icon = ssp.getBadgedIcon(new BitmapDrawable(res, td.getIcon()), taskKey.userId);
327 mApplicationIconCache.put(taskKey, icon);
328 return icon;
329 }
330 // If we are preloading this task, continue to load the activity icon
331 if (preloadTask) {
332 // All short paths failed, load the icon from the activity info and cache it
333 if (infoHandle.info == null) {
334 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
335 taskKey.userId);
336 }
Kenny Guya734fc12014-08-28 21:06:27 +0100337 if (infoHandle.info != null) {
338 icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
339 if (icon != null) {
340 mApplicationIconCache.put(taskKey, icon);
341 return icon;
342 }
343 }
Winson Chungebfc6982014-08-26 12:25:34 -0700344 }
Kenny Guya734fc12014-08-28 21:06:27 +0100345 // If we couldn't load any icon, return null
Winson Chungebfc6982014-08-26 12:25:34 -0700346 return null;
347 }
348
349 /** Returns the activity label using as many cached values as we can. */
350 public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
351 ActivityManager.TaskDescription td, SystemServicesProxy ssp,
352 ActivityInfoHandle infoHandle) {
353 // Return the task description label if it exists
354 if (td != null && td.getLabel() != null) {
355 return td.getLabel();
356 }
357 // Return the cached activity label if it exists
358 String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
359 if (label != null) {
360 return label;
361 }
362 // All short paths failed, load the label from the activity info and cache it
363 if (infoHandle.info == null) {
364 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
365 taskKey.userId);
366 }
Kenny Guya734fc12014-08-28 21:06:27 +0100367 if (infoHandle.info != null) {
368 label = ssp.getActivityLabel(infoHandle.info);
369 mActivityLabelCache.put(taskKey, label);
370 } else {
371 Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
372 + " u=" + taskKey.userId);
373 }
Winson Chungebfc6982014-08-26 12:25:34 -0700374 return label;
375 }
376
377 /** Returns the activity's primary color. */
378 public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
379 RecentsConfiguration config) {
380 if (td != null && td.getPrimaryColor() != 0) {
381 return td.getPrimaryColor();
382 }
383 return config.taskBarViewDefaultBackgroundColor;
384 }
385
Winson Chung0d767552014-04-09 14:33:23 -0700386 /** Reload the set of recent tasks */
Winson Chungf1fbd772014-06-24 18:06:58 -0700387 public SpaceNode reload(Context context, int preloadCount) {
Winson Chungebfc6982014-08-26 12:25:34 -0700388 ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
389 ArrayList<Task> tasksToLoad = new ArrayList<Task>();
390 TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(),
391 -1, preloadCount, true, taskKeys, tasksToLoad);
Winson Chung083baf92014-07-11 10:32:42 -0700392 SpaceNode root = new SpaceNode();
Winson Chung0d767552014-04-09 14:33:23 -0700393 root.setStack(stack);
394
Winson Chungebfc6982014-08-26 12:25:34 -0700395 // Start the task loader and add all the tasks we need to load
396 mLoader.start(context);
397 mLoadQueue.addTasks(tasksToLoad);
Winson Chung0d767552014-04-09 14:33:23 -0700398
Winson Chungebfc6982014-08-26 12:25:34 -0700399 // Update the package monitor with the list of packages to listen for
400 mPackageMonitor.setTasks(taskKeys);
401
402 return root;
403 }
404
405 /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
406 public TaskStack getTaskStack(SystemServicesProxy ssp, Resources res,
407 int preloadTaskId, int preloadTaskCount,
408 boolean loadTaskThumbnails, List<Task.TaskKey> taskKeysOut,
409 List<Task> tasksToLoadOut) {
410 RecentsConfiguration config = RecentsConfiguration.getInstance();
411 List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
Kenny Guya734fc12014-08-28 21:06:27 +0100412 HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
413 new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
Winson Chungebfc6982014-08-26 12:25:34 -0700414 ArrayList<Task> tasksToAdd = new ArrayList<Task>();
415 TaskStack stack = new TaskStack();
416
Winson Chunga10370f2014-04-02 12:25:04 -0700417 int taskCount = tasks.size();
418 for (int i = 0; i < taskCount; i++) {
419 ActivityManager.RecentTaskInfo t = tasks.get(i);
Winson Chungebfc6982014-08-26 12:25:34 -0700420
Winson Chungebfc6982014-08-26 12:25:34 -0700421 // Compose the task key
Winson Chunga4ccb862014-08-22 15:26:27 -0700422 Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
423 t.firstActiveTime, t.lastActiveTime);
Winson Chung80693f92014-04-23 15:19:56 -0700424
Kenny Guya734fc12014-08-28 21:06:27 +0100425 // Get an existing activity info handle if possible
426 Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
427 ActivityInfoHandle infoHandle;
428 boolean hasCachedActivityInfo = false;
429 if (activityInfoCache.containsKey(cnKey)) {
430 infoHandle = activityInfoCache.get(cnKey);
431 hasCachedActivityInfo = true;
432 } else {
433 infoHandle = new ActivityInfoHandle();
434 }
435
Winson Chungebfc6982014-08-26 12:25:34 -0700436 // Determine whether to preload this task
437 boolean preloadTask = false;
438 if (preloadTaskId > 0) {
439 preloadTask = (t.id == preloadTaskId);
440 } else if (preloadTaskCount > 0) {
441 preloadTask = (i >= (taskCount - preloadTaskCount));
Winson Chung11e41ba2014-04-21 12:39:20 -0700442 }
Winson Chung04dfe0d2014-03-14 14:06:29 -0700443
Winson Chungebfc6982014-08-26 12:25:34 -0700444 // Load the label, icon, and color
445 String activityLabel = getAndUpdateActivityLabel(taskKey, t.taskDescription,
446 ssp, infoHandle);
447 Drawable activityIcon = getAndUpdateActivityIcon(taskKey, t.taskDescription,
448 ssp, res, infoHandle, preloadTask);
449 int activityColor = getActivityPrimaryColor(t.taskDescription, config);
450
451 // Update the activity info cache
452 if (!hasCachedActivityInfo && infoHandle.info != null) {
Kenny Guya734fc12014-08-28 21:06:27 +0100453 activityInfoCache.put(cnKey, infoHandle);
Winson Chungebfc6982014-08-26 12:25:34 -0700454 }
455
456 // Add the task to the stack
Winson Chunga4ccb862014-08-22 15:26:27 -0700457 Task task = new Task(taskKey, (t.id > -1), t.affiliatedTaskId, t.affiliatedTaskColor,
458 activityLabel, activityIcon, activityColor, (i == (taskCount - 1)),
Winson Chungec396d62014-08-06 17:08:00 -0700459 config.lockToAppEnabled);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700460
Winson Chungebfc6982014-08-26 12:25:34 -0700461 if (preloadTask && loadTaskThumbnails) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700462 // Load the thumbnail from the cache if possible
Winson Chunga4ccb862014-08-22 15:26:27 -0700463 task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
Winson Chunga10370f2014-04-02 12:25:04 -0700464 if (task.thumbnail == null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700465 // Load the thumbnail from the system
Winson Chunga4ccb862014-08-22 15:26:27 -0700466 task.thumbnail = ssp.getTaskThumbnail(taskKey.id);
Winson Chungff88d7b2014-07-17 12:30:07 -0700467 if (task.thumbnail != null) {
468 task.thumbnail.setHasAlpha(false);
Winson Chunga4ccb862014-08-22 15:26:27 -0700469 mThumbnailCache.put(taskKey, task.thumbnail);
Winson Chunga10370f2014-04-02 12:25:04 -0700470 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800471 }
Winson Chungebfc6982014-08-26 12:25:34 -0700472 if (task.thumbnail == null && tasksToLoadOut != null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700473 // Either the task has changed since the last active time, or it was not
474 // previously cached, so try and load the task anew.
Winson Chungebfc6982014-08-26 12:25:34 -0700475 tasksToLoadOut.add(task);
Winson Chungff88d7b2014-07-17 12:30:07 -0700476 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800477 }
Winson Chung5e3e5d82014-04-02 15:44:55 -0700478
Winson Chungebfc6982014-08-26 12:25:34 -0700479 // Add to the list of task keys
480 if (taskKeysOut != null) {
481 taskKeysOut.add(taskKey);
482 }
Winson Chung5e3e5d82014-04-02 15:44:55 -0700483 // Add the task to the stack
Winson Chunga4ccb862014-08-22 15:26:27 -0700484 tasksToAdd.add(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800485 }
Winson Chunga4ccb862014-08-22 15:26:27 -0700486 stack.setTasks(tasksToAdd);
Winson Chungec396d62014-08-06 17:08:00 -0700487 stack.createAffiliatedGroupings(config);
Winson Chungffa2ec62014-07-03 15:54:42 -0700488 return stack;
489 }
490
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700491 /** Acquires the task resource data directly from the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800492 public void loadTaskData(Task t) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700493 Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
494 Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
Winson Chung303e1ff2014-03-07 15:06:19 -0800495
Winson Chungff88d7b2014-07-17 12:30:07 -0700496 // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
497 // use the default assets in their place until they load
498 boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
499 applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700500 if (requiresLoad) {
Winson Chung93748a12014-07-13 17:43:31 -0700501 mLoadQueue.addTask(t);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700502 }
Selim Cineke8199c52014-09-17 04:03:52 +0200503 t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800504 }
505
Winson Chung4d7b0922014-03-13 17:14:17 -0700506 /** Releases the task resource data back into the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800507 public void unloadTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700508 mLoadQueue.removeTask(t);
Selim Cineke8199c52014-09-17 04:03:52 +0200509 t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800510 }
511
Winson Chung4d7b0922014-03-13 17:14:17 -0700512 /** Completely removes the resource data from the pool. */
Winson Chung5393dff2014-05-08 14:25:43 -0700513 public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700514 mLoadQueue.removeTask(t);
515 mThumbnailCache.remove(t.key);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700516 mApplicationIconCache.remove(t.key);
Winson Chung5393dff2014-05-08 14:25:43 -0700517 if (notifyTaskDataUnloaded) {
Selim Cineke8199c52014-09-17 04:03:52 +0200518 t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
Winson Chung5393dff2014-05-08 14:25:43 -0700519 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800520 }
521
Winson Chung04dfe0d2014-03-14 14:06:29 -0700522 /** Stops the task loader and clears all pending tasks */
Winson Chung303e1ff2014-03-07 15:06:19 -0800523 void stopLoader() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800524 mLoader.stop();
525 mLoadQueue.clearTasks();
526 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700527
Winson Chung9f49df92014-05-07 18:08:34 -0700528 /** Registers any broadcast receivers. */
529 public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
530 // Register the broadcast receiver to handle messages related to packages being added/removed
531 mPackageMonitor.register(context, cb);
532 }
533
534 /** Unregisters any broadcast receivers. */
535 public void unregisterReceivers() {
536 mPackageMonitor.unregister();
537 }
538
539 /**
540 * Handles signals from the system, trimming memory when requested to prevent us from running
541 * out of memory.
542 */
Winson Chungf1fbd772014-06-24 18:06:58 -0700543 public void onTrimMemory(int level) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700544 switch (level) {
545 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
Winson Chung47c4c692014-03-17 10:17:11 -0700546 // Stop the loader immediately when the UI is no longer visible
547 stopLoader();
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200548 mThumbnailCache.trimToSize(Math.max(
549 Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
550 mMaxThumbnailCacheSize / 2));
551 mApplicationIconCache.trimToSize(Math.max(
552 Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
553 mMaxIconCacheSize / 2));
Winson Chung04dfe0d2014-03-14 14:06:29 -0700554 break;
555 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
556 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
557 // We are leaving recents, so trim the data a bit
558 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700559 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700560 break;
561 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
562 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
563 // We are going to be low on memory
564 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700565 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700566 break;
567 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
568 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
569 // We are low on memory, so release everything
570 mThumbnailCache.evictAll();
Winson Chung5e3e5d82014-04-02 15:44:55 -0700571 mApplicationIconCache.evictAll();
Winson Chunga4ccb862014-08-22 15:26:27 -0700572 // The cache is small, only clear the label cache when we are critical
573 mActivityLabelCache.evictAll();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700574 break;
575 default:
576 break;
Winson Chung4d7b0922014-03-13 17:14:17 -0700577 }
578 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800579}