blob: 9d4fe66e6127260153084e397e84f33016c7eb70 [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);
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200180
Winson Chung93748a12014-07-13 17:43:31 -0700181 // Load the application icon if it is stale or we haven't cached one yet
182 if (cachedIcon == null) {
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200183 cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
184 mContext.getResources());
185
186 if (cachedIcon == null) {
187 ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
188 t.key.userId);
189 if (info != null) {
190 cachedIcon = ssp.getActivityIcon(info, t.key.userId);
191 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800192 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200193
Winson Chungff88d7b2014-07-17 12:30:07 -0700194 if (cachedIcon == null) {
195 cachedIcon = mDefaultApplicationIcon;
196 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200197
Winson Chungff88d7b2014-07-17 12:30:07 -0700198 // At this point, even if we can't load the icon, we will set the default
199 // icon.
Winson Chung93748a12014-07-13 17:43:31 -0700200 mApplicationIconCache.put(t.key, cachedIcon);
Winson Chunga10370f2014-04-02 12:25:04 -0700201 }
Winson Chung93748a12014-07-13 17:43:31 -0700202 // Load the thumbnail if it is stale or we haven't cached one yet
203 if (cachedThumbnail == null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700204 cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
205 if (cachedThumbnail != null) {
206 cachedThumbnail.setHasAlpha(false);
207 } else {
208 cachedThumbnail = mDefaultThumbnail;
Winson Chunga10370f2014-04-02 12:25:04 -0700209 }
Winson Chung93748a12014-07-13 17:43:31 -0700210 mThumbnailCache.put(t.key, cachedThumbnail);
Winson Chunga10370f2014-04-02 12:25:04 -0700211 }
212 if (!mCancelled) {
213 // Notify that the task data has changed
Winson Chung93748a12014-07-13 17:43:31 -0700214 final Drawable newIcon = cachedIcon;
Selim Cineke8199c52014-09-17 04:03:52 +0200215 final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
216 ? null : cachedThumbnail;
Winson Chunga10370f2014-04-02 12:25:04 -0700217 mMainThreadHandler.post(new Runnable() {
218 @Override
219 public void run() {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700220 t.notifyTaskDataLoaded(newThumbnail, newIcon);
Winson Chunga10370f2014-04-02 12:25:04 -0700221 }
222 });
Winson Chung303e1ff2014-03-07 15:06:19 -0800223 }
224 }
225
226 // If there are no other items in the list, then just wait until something is added
227 if (!mCancelled && mLoadQueue.isEmpty()) {
228 synchronized(mLoadQueue) {
229 try {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700230 mWaitingOnLoadQueue = true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800231 mLoadQueue.wait();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700232 mWaitingOnLoadQueue = false;
Winson Chung303e1ff2014-03-07 15:06:19 -0800233 } catch (InterruptedException ie) {
234 ie.printStackTrace();
235 }
236 }
237 }
238 }
239 }
240 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200241
242 Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
243 SystemServicesProxy ssp, Resources res) {
244 Bitmap tdIcon = iconBitmap != null
245 ? iconBitmap
246 : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename);
247 if (tdIcon != null) {
248 return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
249 }
250 return null;
251 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800252}
253
Winson Chung303e1ff2014-03-07 15:06:19 -0800254/* Recents task loader
255 * NOTE: We should not hold any references to a Context from a static instance */
256public class RecentsTaskLoader {
Kenny Guya734fc12014-08-28 21:06:27 +0100257 private static final String TAG = "RecentsTaskLoader";
258
Winson Chung303e1ff2014-03-07 15:06:19 -0800259 static RecentsTaskLoader sInstance;
260
Winson Chunga10370f2014-04-02 12:25:04 -0700261 SystemServicesProxy mSystemServicesProxy;
Winson Chung5e3e5d82014-04-02 15:44:55 -0700262 DrawableLruCache mApplicationIconCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800263 BitmapLruCache mThumbnailCache;
Winson Chunga4ccb862014-08-22 15:26:27 -0700264 StringLruCache mActivityLabelCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800265 TaskResourceLoadQueue mLoadQueue;
266 TaskResourceLoader mLoader;
267
Winson Chung9f49df92014-05-07 18:08:34 -0700268 RecentsPackageMonitor mPackageMonitor;
269
Winson Chung4d7b0922014-03-13 17:14:17 -0700270 int mMaxThumbnailCacheSize;
271 int mMaxIconCacheSize;
272
Winson Chung5e3e5d82014-04-02 15:44:55 -0700273 BitmapDrawable mDefaultApplicationIcon;
Winson Chung303e1ff2014-03-07 15:06:19 -0800274 Bitmap mDefaultThumbnail;
275
276 /** Private Constructor */
277 private RecentsTaskLoader(Context context) {
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200278 mMaxThumbnailCacheSize = context.getResources().getInteger(
279 R.integer.config_recents_max_thumbnail_count);
280 mMaxIconCacheSize = context.getResources().getInteger(
281 R.integer.config_recents_max_icon_count);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700282 int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700283 mMaxIconCacheSize;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700284 int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700285 mMaxThumbnailCacheSize;
286
Winson Chung7aceb9a2014-07-03 13:38:01 -0700287 // Create the default assets
288 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
289 icon.eraseColor(0x00000000);
290 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
291 mDefaultThumbnail.setHasAlpha(false);
292 mDefaultThumbnail.eraseColor(0xFFffffff);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700293 mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
294
Winson Chunga10370f2014-04-02 12:25:04 -0700295 // Initialize the proxy, cache and loaders
296 mSystemServicesProxy = new SystemServicesProxy(context);
Winson Chungd543c1b2014-06-23 15:06:45 -0700297 mPackageMonitor = new RecentsPackageMonitor();
Winson Chung303e1ff2014-03-07 15:06:19 -0800298 mLoadQueue = new TaskResourceLoadQueue();
Winson Chung5e3e5d82014-04-02 15:44:55 -0700299 mApplicationIconCache = new DrawableLruCache(iconCacheSize);
Winson Chung303e1ff2014-03-07 15:06:19 -0800300 mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
Winson Chunga4ccb862014-08-22 15:26:27 -0700301 mActivityLabelCache = new StringLruCache(100);
Winson Chungc9567c02014-06-16 20:25:51 -0700302 mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
Winson Chung93748a12014-07-13 17:43:31 -0700303 mDefaultThumbnail, mDefaultApplicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800304 }
305
306 /** Initializes the recents task loader */
307 public static RecentsTaskLoader initialize(Context context) {
308 if (sInstance == null) {
309 sInstance = new RecentsTaskLoader(context);
310 }
311 return sInstance;
312 }
313
314 /** Returns the current recents task loader */
315 public static RecentsTaskLoader getInstance() {
316 return sInstance;
317 }
318
Winson Chunga10370f2014-04-02 12:25:04 -0700319 /** Returns the system services proxy */
320 public SystemServicesProxy getSystemServicesProxy() {
321 return mSystemServicesProxy;
322 }
323
Winson Chungff88d7b2014-07-17 12:30:07 -0700324 /** Gets the list of recent tasks, ordered from back to front. */
Winson Chungebfc6982014-08-26 12:25:34 -0700325 private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
326 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chunga10370f2014-04-02 12:25:04 -0700327 List<ActivityManager.RecentTaskInfo> tasks =
Winson Chungebfc6982014-08-26 12:25:34 -0700328 ssp.getRecentTasks(config.maxNumTasksToLoad,
329 UserHandle.CURRENT.getIdentifier());
Winson Chunga10370f2014-04-02 12:25:04 -0700330 Collections.reverse(tasks);
Winson Chung0d767552014-04-09 14:33:23 -0700331 return tasks;
332 }
333
Winson Chungebfc6982014-08-26 12:25:34 -0700334 /** Returns the activity icon using as many cached values as we can. */
335 public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
336 ActivityManager.TaskDescription td, SystemServicesProxy ssp,
337 Resources res, ActivityInfoHandle infoHandle, boolean preloadTask) {
338 // Return the cached activity icon if it exists
339 Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
340 if (icon != null) {
341 return icon;
342 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200343
344 // If we are preloading this task, continue to load the task description icon or the
345 // activity icon
Winson Chungebfc6982014-08-26 12:25:34 -0700346 if (preloadTask) {
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200347
348 // Return and cache the task description icon if it exists
349 Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
350 td.getIconFilename(), ssp, res);
351 if (tdDrawable != null) {
352 mApplicationIconCache.put(taskKey, tdDrawable);
353 return tdDrawable;
354 }
355
356 // Load the icon from the activity info and cache it
Winson Chungebfc6982014-08-26 12:25:34 -0700357 if (infoHandle.info == null) {
358 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
359 taskKey.userId);
360 }
Kenny Guya734fc12014-08-28 21:06:27 +0100361 if (infoHandle.info != null) {
362 icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
363 if (icon != null) {
364 mApplicationIconCache.put(taskKey, icon);
365 return icon;
366 }
367 }
Winson Chungebfc6982014-08-26 12:25:34 -0700368 }
Kenny Guya734fc12014-08-28 21:06:27 +0100369 // If we couldn't load any icon, return null
Winson Chungebfc6982014-08-26 12:25:34 -0700370 return null;
371 }
372
373 /** Returns the activity label using as many cached values as we can. */
374 public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
375 ActivityManager.TaskDescription td, SystemServicesProxy ssp,
376 ActivityInfoHandle infoHandle) {
377 // Return the task description label if it exists
378 if (td != null && td.getLabel() != null) {
379 return td.getLabel();
380 }
381 // Return the cached activity label if it exists
382 String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
383 if (label != null) {
384 return label;
385 }
386 // All short paths failed, load the label from the activity info and cache it
387 if (infoHandle.info == null) {
388 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
389 taskKey.userId);
390 }
Kenny Guya734fc12014-08-28 21:06:27 +0100391 if (infoHandle.info != null) {
392 label = ssp.getActivityLabel(infoHandle.info);
393 mActivityLabelCache.put(taskKey, label);
394 } else {
395 Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
396 + " u=" + taskKey.userId);
397 }
Winson Chungebfc6982014-08-26 12:25:34 -0700398 return label;
399 }
400
401 /** Returns the activity's primary color. */
402 public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
403 RecentsConfiguration config) {
404 if (td != null && td.getPrimaryColor() != 0) {
405 return td.getPrimaryColor();
406 }
407 return config.taskBarViewDefaultBackgroundColor;
408 }
409
Winson Chung0d767552014-04-09 14:33:23 -0700410 /** Reload the set of recent tasks */
Winson Chungf1fbd772014-06-24 18:06:58 -0700411 public SpaceNode reload(Context context, int preloadCount) {
Winson Chungebfc6982014-08-26 12:25:34 -0700412 ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
413 ArrayList<Task> tasksToLoad = new ArrayList<Task>();
414 TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(),
415 -1, preloadCount, true, taskKeys, tasksToLoad);
Winson Chung083baf92014-07-11 10:32:42 -0700416 SpaceNode root = new SpaceNode();
Winson Chung0d767552014-04-09 14:33:23 -0700417 root.setStack(stack);
418
Winson Chungebfc6982014-08-26 12:25:34 -0700419 // Start the task loader and add all the tasks we need to load
420 mLoader.start(context);
421 mLoadQueue.addTasks(tasksToLoad);
Winson Chung0d767552014-04-09 14:33:23 -0700422
Winson Chungebfc6982014-08-26 12:25:34 -0700423 // Update the package monitor with the list of packages to listen for
424 mPackageMonitor.setTasks(taskKeys);
425
426 return root;
427 }
428
429 /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
430 public TaskStack getTaskStack(SystemServicesProxy ssp, Resources res,
431 int preloadTaskId, int preloadTaskCount,
432 boolean loadTaskThumbnails, List<Task.TaskKey> taskKeysOut,
433 List<Task> tasksToLoadOut) {
434 RecentsConfiguration config = RecentsConfiguration.getInstance();
435 List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
Kenny Guya734fc12014-08-28 21:06:27 +0100436 HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
437 new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
Winson Chungebfc6982014-08-26 12:25:34 -0700438 ArrayList<Task> tasksToAdd = new ArrayList<Task>();
439 TaskStack stack = new TaskStack();
440
Winson Chunga10370f2014-04-02 12:25:04 -0700441 int taskCount = tasks.size();
442 for (int i = 0; i < taskCount; i++) {
443 ActivityManager.RecentTaskInfo t = tasks.get(i);
Winson Chungebfc6982014-08-26 12:25:34 -0700444
Winson Chungebfc6982014-08-26 12:25:34 -0700445 // Compose the task key
Winson Chunga4ccb862014-08-22 15:26:27 -0700446 Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
447 t.firstActiveTime, t.lastActiveTime);
Winson Chung80693f92014-04-23 15:19:56 -0700448
Kenny Guya734fc12014-08-28 21:06:27 +0100449 // Get an existing activity info handle if possible
450 Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
451 ActivityInfoHandle infoHandle;
452 boolean hasCachedActivityInfo = false;
453 if (activityInfoCache.containsKey(cnKey)) {
454 infoHandle = activityInfoCache.get(cnKey);
455 hasCachedActivityInfo = true;
456 } else {
457 infoHandle = new ActivityInfoHandle();
458 }
459
Winson Chungebfc6982014-08-26 12:25:34 -0700460 // Determine whether to preload this task
461 boolean preloadTask = false;
462 if (preloadTaskId > 0) {
463 preloadTask = (t.id == preloadTaskId);
464 } else if (preloadTaskCount > 0) {
465 preloadTask = (i >= (taskCount - preloadTaskCount));
Winson Chung11e41ba2014-04-21 12:39:20 -0700466 }
Winson Chung04dfe0d2014-03-14 14:06:29 -0700467
Winson Chungebfc6982014-08-26 12:25:34 -0700468 // Load the label, icon, and color
469 String activityLabel = getAndUpdateActivityLabel(taskKey, t.taskDescription,
470 ssp, infoHandle);
471 Drawable activityIcon = getAndUpdateActivityIcon(taskKey, t.taskDescription,
472 ssp, res, infoHandle, preloadTask);
473 int activityColor = getActivityPrimaryColor(t.taskDescription, config);
474
475 // Update the activity info cache
476 if (!hasCachedActivityInfo && infoHandle.info != null) {
Kenny Guya734fc12014-08-28 21:06:27 +0100477 activityInfoCache.put(cnKey, infoHandle);
Winson Chungebfc6982014-08-26 12:25:34 -0700478 }
479
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200480 Bitmap icon = t.taskDescription != null
481 ? t.taskDescription.getInMemoryIcon()
482 : null;
483 String iconFilename = t.taskDescription != null
484 ? t.taskDescription.getIconFilename()
485 : null;
486
Winson Chungebfc6982014-08-26 12:25:34 -0700487 // Add the task to the stack
Winson Chunga4ccb862014-08-22 15:26:27 -0700488 Task task = new Task(taskKey, (t.id > -1), t.affiliatedTaskId, t.affiliatedTaskColor,
489 activityLabel, activityIcon, activityColor, (i == (taskCount - 1)),
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200490 config.lockToAppEnabled, icon, iconFilename);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700491
Winson Chungebfc6982014-08-26 12:25:34 -0700492 if (preloadTask && loadTaskThumbnails) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700493 // Load the thumbnail from the cache if possible
Winson Chunga4ccb862014-08-22 15:26:27 -0700494 task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
Winson Chunga10370f2014-04-02 12:25:04 -0700495 if (task.thumbnail == null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700496 // Load the thumbnail from the system
Winson Chunga4ccb862014-08-22 15:26:27 -0700497 task.thumbnail = ssp.getTaskThumbnail(taskKey.id);
Winson Chungff88d7b2014-07-17 12:30:07 -0700498 if (task.thumbnail != null) {
499 task.thumbnail.setHasAlpha(false);
Winson Chunga4ccb862014-08-22 15:26:27 -0700500 mThumbnailCache.put(taskKey, task.thumbnail);
Winson Chunga10370f2014-04-02 12:25:04 -0700501 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800502 }
Winson Chungebfc6982014-08-26 12:25:34 -0700503 if (task.thumbnail == null && tasksToLoadOut != null) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700504 // Either the task has changed since the last active time, or it was not
505 // previously cached, so try and load the task anew.
Winson Chungebfc6982014-08-26 12:25:34 -0700506 tasksToLoadOut.add(task);
Winson Chungff88d7b2014-07-17 12:30:07 -0700507 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800508 }
Winson Chung5e3e5d82014-04-02 15:44:55 -0700509
Winson Chungebfc6982014-08-26 12:25:34 -0700510 // Add to the list of task keys
511 if (taskKeysOut != null) {
512 taskKeysOut.add(taskKey);
513 }
Winson Chung5e3e5d82014-04-02 15:44:55 -0700514 // Add the task to the stack
Winson Chunga4ccb862014-08-22 15:26:27 -0700515 tasksToAdd.add(task);
Winson Chung303e1ff2014-03-07 15:06:19 -0800516 }
Winson Chunga4ccb862014-08-22 15:26:27 -0700517 stack.setTasks(tasksToAdd);
Winson Chungec396d62014-08-06 17:08:00 -0700518 stack.createAffiliatedGroupings(config);
Winson Chungffa2ec62014-07-03 15:54:42 -0700519 return stack;
520 }
521
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700522 /** Acquires the task resource data directly from the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800523 public void loadTaskData(Task t) {
Winson Chungff88d7b2014-07-17 12:30:07 -0700524 Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
525 Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
Winson Chung303e1ff2014-03-07 15:06:19 -0800526
Winson Chungff88d7b2014-07-17 12:30:07 -0700527 // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
528 // use the default assets in their place until they load
529 boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
530 applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700531 if (requiresLoad) {
Winson Chung93748a12014-07-13 17:43:31 -0700532 mLoadQueue.addTask(t);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700533 }
Selim Cineke8199c52014-09-17 04:03:52 +0200534 t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800535 }
536
Winson Chung4d7b0922014-03-13 17:14:17 -0700537 /** Releases the task resource data back into the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800538 public void unloadTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700539 mLoadQueue.removeTask(t);
Selim Cineke8199c52014-09-17 04:03:52 +0200540 t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800541 }
542
Winson Chung4d7b0922014-03-13 17:14:17 -0700543 /** Completely removes the resource data from the pool. */
Winson Chung5393dff2014-05-08 14:25:43 -0700544 public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700545 mLoadQueue.removeTask(t);
546 mThumbnailCache.remove(t.key);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700547 mApplicationIconCache.remove(t.key);
Winson Chung5393dff2014-05-08 14:25:43 -0700548 if (notifyTaskDataUnloaded) {
Selim Cineke8199c52014-09-17 04:03:52 +0200549 t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
Winson Chung5393dff2014-05-08 14:25:43 -0700550 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800551 }
552
Winson Chung04dfe0d2014-03-14 14:06:29 -0700553 /** Stops the task loader and clears all pending tasks */
Winson Chung303e1ff2014-03-07 15:06:19 -0800554 void stopLoader() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800555 mLoader.stop();
556 mLoadQueue.clearTasks();
557 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700558
Winson Chung9f49df92014-05-07 18:08:34 -0700559 /** Registers any broadcast receivers. */
560 public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
561 // Register the broadcast receiver to handle messages related to packages being added/removed
562 mPackageMonitor.register(context, cb);
563 }
564
565 /** Unregisters any broadcast receivers. */
566 public void unregisterReceivers() {
567 mPackageMonitor.unregister();
568 }
569
570 /**
571 * Handles signals from the system, trimming memory when requested to prevent us from running
572 * out of memory.
573 */
Winson Chungf1fbd772014-06-24 18:06:58 -0700574 public void onTrimMemory(int level) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700575 switch (level) {
576 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
Winson Chung47c4c692014-03-17 10:17:11 -0700577 // Stop the loader immediately when the UI is no longer visible
578 stopLoader();
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200579 mThumbnailCache.trimToSize(Math.max(
580 Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
581 mMaxThumbnailCacheSize / 2));
582 mApplicationIconCache.trimToSize(Math.max(
583 Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
584 mMaxIconCacheSize / 2));
Winson Chung04dfe0d2014-03-14 14:06:29 -0700585 break;
586 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
587 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
588 // We are leaving recents, so trim the data a bit
589 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700590 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700591 break;
592 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
593 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
594 // We are going to be low on memory
595 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
Winson Chung5e3e5d82014-04-02 15:44:55 -0700596 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700597 break;
598 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
599 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
600 // We are low on memory, so release everything
601 mThumbnailCache.evictAll();
Winson Chung5e3e5d82014-04-02 15:44:55 -0700602 mApplicationIconCache.evictAll();
Winson Chunga4ccb862014-08-22 15:26:27 -0700603 // The cache is small, only clear the label cache when we are critical
604 mActivityLabelCache.evictAll();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700605 break;
606 default:
607 break;
Winson Chung4d7b0922014-03-13 17:14:17 -0700608 }
609 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800610}