blob: 82c81ae3e5d3c3d599a20c3cf87c874ac7a8e4b6 [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;
Winsone7f138c2015-10-22 16:15:21 -070021import android.content.ComponentName;
Winson Chung303e1ff2014-03-07 15:06:19 -080022import android.content.Context;
23import android.content.pm.ActivityInfo;
Winson Chung37c8d8e2014-03-24 14:53:07 -070024import android.content.res.Resources;
Winson Chung303e1ff2014-03-07 15:06:19 -080025import android.graphics.Bitmap;
Winson Chung303e1ff2014-03-07 15:06:19 -080026import android.graphics.drawable.BitmapDrawable;
27import android.graphics.drawable.Drawable;
28import android.os.Handler;
29import android.os.HandlerThread;
Kenny Guya734fc12014-08-28 21:06:27 +010030import android.util.Log;
Winsone7f138c2015-10-22 16:15:21 -070031import android.util.LruCache;
Winsonc0d70582016-01-29 10:24:39 -080032
Jorim Jaggi81e0c842014-09-12 23:28:58 +020033import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070034import com.android.systemui.recents.Recents;
Winson Chung8eaeb7d2014-06-25 15:10:59 -070035import com.android.systemui.recents.RecentsConfiguration;
Winsonc742f972015-11-12 11:32:21 -080036import com.android.systemui.recents.RecentsDebugFlags;
Winsone7f138c2015-10-22 16:15:21 -070037import com.android.systemui.recents.events.activity.PackagesChangedEvent;
Winson Chungffa2ec62014-07-03 15:54:42 -070038import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung303e1ff2014-03-07 15:06:19 -080039
Winsone7f138c2015-10-22 16:15:21 -070040import java.util.Map;
Winson Chung303e1ff2014-03-07 15:06:19 -080041import java.util.concurrent.ConcurrentLinkedQueue;
42
43
Winsone7f138c2015-10-22 16:15:21 -070044/**
45 * A Task load queue
46 */
Winson Chung303e1ff2014-03-07 15:06:19 -080047class TaskResourceLoadQueue {
48 ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
Winson Chung303e1ff2014-03-07 15:06:19 -080049
Winson Chung4c71aef2014-03-21 15:15:11 -070050 /** Adds a new task to the load queue */
Winson Chung93748a12014-07-13 17:43:31 -070051 void addTask(Task t) {
Winson Chung303e1ff2014-03-07 15:06:19 -080052 if (!mQueue.contains(t)) {
53 mQueue.add(t);
54 }
55 synchronized(this) {
56 notifyAll();
57 }
58 }
59
Winson Chung4c71aef2014-03-21 15:15:11 -070060 /**
61 * Retrieves the next task from the load queue, as well as whether we want that task to be
62 * force reloaded.
63 */
Winson Chung93748a12014-07-13 17:43:31 -070064 Task nextTask() {
Winson Chung93748a12014-07-13 17:43:31 -070065 return mQueue.poll();
Winson Chung4c71aef2014-03-21 15:15:11 -070066 }
67
68 /** Removes a task from the load queue */
Winson Chung303e1ff2014-03-07 15:06:19 -080069 void removeTask(Task t) {
Winson Chung303e1ff2014-03-07 15:06:19 -080070 mQueue.remove(t);
71 }
72
Winson Chung4c71aef2014-03-21 15:15:11 -070073 /** Clears all the tasks from the load queue */
Winson Chung303e1ff2014-03-07 15:06:19 -080074 void clearTasks() {
Winson Chung303e1ff2014-03-07 15:06:19 -080075 mQueue.clear();
76 }
77
Winson Chung4c71aef2014-03-21 15:15:11 -070078 /** Returns whether the load queue is empty */
Winson Chung303e1ff2014-03-07 15:06:19 -080079 boolean isEmpty() {
80 return mQueue.isEmpty();
81 }
82}
83
Winsone7f138c2015-10-22 16:15:21 -070084/**
85 * Task resource loader
86 */
87class BackgroundTaskLoader implements Runnable {
Winson Chung96d70412014-11-12 14:17:17 -080088 static String TAG = "TaskResourceLoader";
89 static boolean DEBUG = false;
90
Winson Chung303e1ff2014-03-07 15:06:19 -080091 Context mContext;
92 HandlerThread mLoadThread;
93 Handler mLoadThreadHandler;
94 Handler mMainThreadHandler;
95
96 TaskResourceLoadQueue mLoadQueue;
Winson Chung296278a2015-12-17 12:09:02 -050097 TaskKeyLruCache<Drawable> mIconCache;
Winson21700932016-03-24 17:26:23 -070098 TaskKeyLruCache<ThumbnailData> mThumbnailCache;
Winson Chungc9567c02014-06-16 20:25:51 -070099 Bitmap mDefaultThumbnail;
Winson Chung296278a2015-12-17 12:09:02 -0500100 BitmapDrawable mDefaultIcon;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700101
Winson Chung303e1ff2014-03-07 15:06:19 -0800102 boolean mCancelled;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700103 boolean mWaitingOnLoadQueue;
Winson Chung303e1ff2014-03-07 15:06:19 -0800104
105 /** Constructor, creates a new loading thread that loads task resources in the background */
Winsone7f138c2015-10-22 16:15:21 -0700106 public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
Winson21700932016-03-24 17:26:23 -0700107 TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<ThumbnailData> thumbnailCache,
Winson Chung296278a2015-12-17 12:09:02 -0500108 Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800109 mLoadQueue = loadQueue;
Winson Chung296278a2015-12-17 12:09:02 -0500110 mIconCache = iconCache;
Winson Chung303e1ff2014-03-07 15:06:19 -0800111 mThumbnailCache = thumbnailCache;
Winson Chungc9567c02014-06-16 20:25:51 -0700112 mDefaultThumbnail = defaultThumbnail;
Winson Chung296278a2015-12-17 12:09:02 -0500113 mDefaultIcon = defaultIcon;
Winson Chung303e1ff2014-03-07 15:06:19 -0800114 mMainThreadHandler = new Handler();
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200115 mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
Jorim Jaggi50949ea2014-09-15 16:07:29 +0200116 android.os.Process.THREAD_PRIORITY_BACKGROUND);
Winson Chung303e1ff2014-03-07 15:06:19 -0800117 mLoadThread.start();
118 mLoadThreadHandler = new Handler(mLoadThread.getLooper());
119 mLoadThreadHandler.post(this);
120 }
121
122 /** Restarts the loader thread */
123 void start(Context context) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800124 mContext = context;
125 mCancelled = false;
126 // Notify the load thread to start loading
127 synchronized(mLoadThread) {
128 mLoadThread.notifyAll();
129 }
130 }
131
132 /** Requests the loader thread to stop after the current iteration */
133 void stop() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800134 // Mark as cancelled for the thread to pick up
135 mCancelled = true;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700136 // If we are waiting for the load queue for more tasks, then we can just reset the
137 // Context now, since nothing is using it
138 if (mWaitingOnLoadQueue) {
139 mContext = null;
140 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800141 }
142
143 @Override
144 public void run() {
145 while (true) {
Winson Chung10f81392014-05-20 16:21:31 -0700146 if (mCancelled) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800147 // We have to unset the context here, since the background thread may be using it
148 // when we call stop()
149 mContext = null;
150 // If we are cancelled, then wait until we are started again
151 synchronized(mLoadThread) {
152 try {
Winson Chung303e1ff2014-03-07 15:06:19 -0800153 mLoadThread.wait();
154 } catch (InterruptedException ie) {
155 ie.printStackTrace();
156 }
157 }
158 } else {
Winson53ec42c2015-10-28 15:55:35 -0700159 RecentsConfiguration config = Recents.getConfiguration();
Winsone7f138c2015-10-22 16:15:21 -0700160 SystemServicesProxy ssp = Recents.getSystemServices();
Winson Chung66e86f92014-11-18 10:35:50 -0800161 // If we've stopped the loader, then fall through to the above logic to wait on
winsonchungf14fdda2014-10-27 12:01:14 -0700162 // the load thread
Winson Chung66e86f92014-11-18 10:35:50 -0800163 if (ssp != null) {
164 // Load the next item from the queue
165 final Task t = mLoadQueue.nextTask();
166 if (t != null) {
Winson Chung296278a2015-12-17 12:09:02 -0500167 Drawable cachedIcon = mIconCache.get(t.key);
Winson21700932016-03-24 17:26:23 -0700168 ThumbnailData cachedThumbnailData = mThumbnailCache.get(t.key);
Winson Chunga10370f2014-04-02 12:25:04 -0700169
Winson Chung296278a2015-12-17 12:09:02 -0500170 // Load the icon if it is stale or we haven't cached one yet
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200171 if (cachedIcon == null) {
Winson Chung296278a2015-12-17 12:09:02 -0500172 cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
173 t.key.userId, mContext.getResources());
Winson Chung66e86f92014-11-18 10:35:50 -0800174
175 if (cachedIcon == null) {
176 ActivityInfo info = ssp.getActivityInfo(
Winsone7f138c2015-10-22 16:15:21 -0700177 t.key.getComponent(), t.key.userId);
Winson Chung66e86f92014-11-18 10:35:50 -0800178 if (info != null) {
179 if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
Winson Chung296278a2015-12-17 12:09:02 -0500180 cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
Winson Chung66e86f92014-11-18 10:35:50 -0800181 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200182 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200183
Winson Chung66e86f92014-11-18 10:35:50 -0800184 if (cachedIcon == null) {
Winson Chung296278a2015-12-17 12:09:02 -0500185 cachedIcon = mDefaultIcon;
Winson Chung66e86f92014-11-18 10:35:50 -0800186 }
Jorim Jaggibdd4b202014-09-26 18:29:07 +0200187
Winson Chung66e86f92014-11-18 10:35:50 -0800188 // At this point, even if we can't load the icon, we will set the
189 // default icon.
Winson Chung296278a2015-12-17 12:09:02 -0500190 mIconCache.put(t.key, cachedIcon);
Winson Chung96d70412014-11-12 14:17:17 -0800191 }
Winson Chung66e86f92014-11-18 10:35:50 -0800192 // Load the thumbnail if it is stale or we haven't cached one yet
Winson21700932016-03-24 17:26:23 -0700193 if (cachedThumbnailData == null) {
Winson Chung66e86f92014-11-18 10:35:50 -0800194 if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
195 if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
Winson21700932016-03-24 17:26:23 -0700196 cachedThumbnailData = ssp.getTaskThumbnail(t.key.id);
Winson Chunga10370f2014-04-02 12:25:04 -0700197 }
Winson21700932016-03-24 17:26:23 -0700198
199 if (cachedThumbnailData.thumbnail == null) {
200 cachedThumbnailData.thumbnail = mDefaultThumbnail;
Winson Chung66e86f92014-11-18 10:35:50 -0800201 }
Winson21700932016-03-24 17:26:23 -0700202
Winson Chung66e86f92014-11-18 10:35:50 -0800203 // When svelte, we trim the memory to just the visible thumbnails when
204 // leaving, so don't thrash the cache as the user scrolls (just load
205 // them from scratch each time)
206 if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
Winson21700932016-03-24 17:26:23 -0700207 mThumbnailCache.put(t.key, cachedThumbnailData);
Winson Chung66e86f92014-11-18 10:35:50 -0800208 }
209 }
210 if (!mCancelled) {
211 // Notify that the task data has changed
212 final Drawable newIcon = cachedIcon;
Winson21700932016-03-24 17:26:23 -0700213 final ThumbnailData newThumbnailData = cachedThumbnailData;
Winson Chung66e86f92014-11-18 10:35:50 -0800214 mMainThreadHandler.post(new Runnable() {
215 @Override
216 public void run() {
Winson21700932016-03-24 17:26:23 -0700217 t.notifyTaskDataLoaded(newThumbnailData.thumbnail, newIcon,
218 newThumbnailData.thumbnailInfo);
Winson Chung66e86f92014-11-18 10:35:50 -0800219 }
220 });
221 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800222 }
223 }
224
225 // If there are no other items in the list, then just wait until something is added
226 if (!mCancelled && mLoadQueue.isEmpty()) {
227 synchronized(mLoadQueue) {
228 try {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700229 mWaitingOnLoadQueue = true;
Winson Chung303e1ff2014-03-07 15:06:19 -0800230 mLoadQueue.wait();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700231 mWaitingOnLoadQueue = false;
Winson Chung303e1ff2014-03-07 15:06:19 -0800232 } catch (InterruptedException ie) {
233 ie.printStackTrace();
234 }
235 }
236 }
237 }
238 }
239 }
240}
241
Winsone7f138c2015-10-22 16:15:21 -0700242/**
243 * Recents task loader
244 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800245public class RecentsTaskLoader {
Winsone7f138c2015-10-22 16:15:21 -0700246
Kenny Guya734fc12014-08-28 21:06:27 +0100247 private static final String TAG = "RecentsTaskLoader";
Winsone7f138c2015-10-22 16:15:21 -0700248 private static final boolean DEBUG = false;
Kenny Guya734fc12014-08-28 21:06:27 +0100249
Winsone7f138c2015-10-22 16:15:21 -0700250 // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
251 // for many tasks, which we use to get the activity labels and icons. Unlike the other caches
252 // below, this is per-package so we can't invalidate the items in the cache based on the last
253 // active time. Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
254 // package in the cache has been updated, so that we may remove it.
255 private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
Winson Chung296278a2015-12-17 12:09:02 -0500256 private final TaskKeyLruCache<Drawable> mIconCache;
Winson21700932016-03-24 17:26:23 -0700257 private final TaskKeyLruCache<ThumbnailData> mThumbnailCache;
Winsone7f138c2015-10-22 16:15:21 -0700258 private final TaskKeyLruCache<String> mActivityLabelCache;
259 private final TaskKeyLruCache<String> mContentDescriptionCache;
260 private final TaskResourceLoadQueue mLoadQueue;
261 private final BackgroundTaskLoader mLoader;
Winson Chung303e1ff2014-03-07 15:06:19 -0800262
Winsone7f138c2015-10-22 16:15:21 -0700263 private final int mMaxThumbnailCacheSize;
264 private final int mMaxIconCacheSize;
265 private int mNumVisibleTasksLoaded;
266 private int mNumVisibleThumbnailsLoaded;
Winson Chung303e1ff2014-03-07 15:06:19 -0800267
Winsone7f138c2015-10-22 16:15:21 -0700268 int mDefaultTaskBarBackgroundColor;
Winson Chung1af8eda2016-02-05 17:55:56 +0000269 int mDefaultTaskViewBackgroundColor;
Winson Chung296278a2015-12-17 12:09:02 -0500270 BitmapDrawable mDefaultIcon;
Winson Chung303e1ff2014-03-07 15:06:19 -0800271 Bitmap mDefaultThumbnail;
272
Winsona1b96b52016-03-14 18:58:43 -0700273 private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
274 new TaskKeyLruCache.EvictionCallback() {
275 @Override
276 public void onEntryEvicted(Task.TaskKey key) {
Winsona1ededd2016-03-25 12:23:12 -0700277 if (key != null) {
278 mActivityInfoCache.remove(key.getComponent());
279 }
Winsona1b96b52016-03-14 18:58:43 -0700280 }
281 };
282
Winsone7f138c2015-10-22 16:15:21 -0700283 public RecentsTaskLoader(Context context) {
284 Resources res = context.getResources();
285 mDefaultTaskBarBackgroundColor =
Winson Chung1af8eda2016-02-05 17:55:56 +0000286 context.getColor(R.color.recents_task_bar_default_background_color);
287 mDefaultTaskViewBackgroundColor =
288 context.getColor(R.color.recents_task_view_default_background_color);
Winsone7f138c2015-10-22 16:15:21 -0700289 mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
290 mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
Winsonc742f972015-11-12 11:32:21 -0800291 int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700292 mMaxIconCacheSize;
Winsonc742f972015-11-12 11:32:21 -0800293 int thumbnailCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
Winson Chung4d7b0922014-03-13 17:14:17 -0700294 mMaxThumbnailCacheSize;
295
Winson Chung7aceb9a2014-07-03 13:38:01 -0700296 // Create the default assets
Winsone7f138c2015-10-22 16:15:21 -0700297 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
298 icon.eraseColor(0);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700299 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
300 mDefaultThumbnail.setHasAlpha(false);
301 mDefaultThumbnail.eraseColor(0xFFffffff);
Winson Chung296278a2015-12-17 12:09:02 -0500302 mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700303
Winson Chunga10370f2014-04-02 12:25:04 -0700304 // Initialize the proxy, cache and loaders
Winsone7f138c2015-10-22 16:15:21 -0700305 int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
Winson Chung303e1ff2014-03-07 15:06:19 -0800306 mLoadQueue = new TaskResourceLoadQueue();
Winsona1b96b52016-03-14 18:58:43 -0700307 mIconCache = new TaskKeyLruCache<>(iconCacheSize, mClearActivityInfoOnEviction);
Winsone7f138c2015-10-22 16:15:21 -0700308 mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
Winsona1b96b52016-03-14 18:58:43 -0700309 mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
310 mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
311 mClearActivityInfoOnEviction);
Winsone7f138c2015-10-22 16:15:21 -0700312 mActivityInfoCache = new LruCache(numRecentTasks);
Winson Chung296278a2015-12-17 12:09:02 -0500313 mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache,
314 mDefaultThumbnail, mDefaultIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800315 }
316
Winson Chunga91c2932014-11-07 15:02:38 -0800317 /** Returns the size of the app icon cache. */
Winson Chung296278a2015-12-17 12:09:02 -0500318 public int getIconCacheSize() {
Winson Chunga91c2932014-11-07 15:02:38 -0800319 return mMaxIconCacheSize;
Winson Chungebfc6982014-08-26 12:25:34 -0700320 }
321
Winson Chung90d51362014-11-13 14:30:26 -0800322 /** Returns the size of the thumbnail cache. */
323 public int getThumbnailCacheSize() {
324 return mMaxThumbnailCacheSize;
325 }
326
327 /** Creates a new plan for loading the recent tasks. */
Winson Chunga91c2932014-11-07 15:02:38 -0800328 public RecentsTaskLoadPlan createLoadPlan(Context context) {
Winson53ec42c2015-10-28 15:55:35 -0700329 RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
Winson Chunga91c2932014-11-07 15:02:38 -0800330 return plan;
331 }
Winson Chungebfc6982014-08-26 12:25:34 -0700332
Winson Chung90d51362014-11-13 14:30:26 -0800333 /** Preloads recents tasks using the specified plan to store the output. */
Winson65c851e2016-01-20 12:43:35 -0800334 public void preloadTasks(RecentsTaskLoadPlan plan, int topTaskId, boolean isTopTaskHome) {
335 plan.preloadPlan(this, topTaskId, isTopTaskHome);
Winson Chunga91c2932014-11-07 15:02:38 -0800336 }
Winson Chungebfc6982014-08-26 12:25:34 -0700337
Winson Chung90d51362014-11-13 14:30:26 -0800338 /** Begins loading the heavy task data according to the specified options. */
Winson Chunga91c2932014-11-07 15:02:38 -0800339 public void loadTasks(Context context, RecentsTaskLoadPlan plan,
340 RecentsTaskLoadPlan.Options opts) {
341 if (opts == null) {
342 throw new RuntimeException("Requires load options");
Winson Chung303e1ff2014-03-07 15:06:19 -0800343 }
Winson Chung96d70412014-11-12 14:17:17 -0800344 plan.executePlan(opts, this, mLoadQueue);
Winson Chung90d51362014-11-13 14:30:26 -0800345 if (!opts.onlyLoadForCache) {
Winson Chunga91c2932014-11-07 15:02:38 -0800346 mNumVisibleTasksLoaded = opts.numVisibleTasks;
Winson Chung96d70412014-11-12 14:17:17 -0800347 mNumVisibleThumbnailsLoaded = opts.numVisibleTaskThumbnails;
Winson Chunga91c2932014-11-07 15:02:38 -0800348
Winson Chung90d51362014-11-13 14:30:26 -0800349 // Start the loader
350 mLoader.start(context);
351 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700352 }
353
Winson6d5b8b82016-01-26 17:15:45 -0800354 /**
355 * Acquires the task resource data directly from the cache, loading if necessary.
356 *
357 * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them
358 * in the cache and loading if necessary. Otherwise, do not
359 * load the thumbnail unless the icon also has to be loaded.
360 */
361 public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
Winson Chung296278a2015-12-17 12:09:02 -0500362 Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
Winson21700932016-03-24 17:26:23 -0700363 Bitmap thumbnail = null;
364 ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
Winson6d5b8b82016-01-26 17:15:45 -0800365 if (fetchAndInvalidateThumbnails) {
Winson21700932016-03-24 17:26:23 -0700366 ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
367 if (thumbnailData != null) {
368 thumbnail = thumbnailData.thumbnail;
369 thumbnailInfo = thumbnailData.thumbnailInfo;
370 }
371 } else {
372 thumbnail = mDefaultThumbnail;
Winson6d5b8b82016-01-26 17:15:45 -0800373 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800374
Winson Chungff88d7b2014-07-17 12:30:07 -0700375 // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
376 // use the default assets in their place until they load
Winson Chung296278a2015-12-17 12:09:02 -0500377 boolean requiresLoad = (icon == null) || (thumbnail == null);
378 icon = icon != null ? icon : mDefaultIcon;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700379 if (requiresLoad) {
Winson Chung93748a12014-07-13 17:43:31 -0700380 mLoadQueue.addTask(t);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700381 }
Winson21700932016-03-24 17:26:23 -0700382 t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon,
383 thumbnailInfo);
Winson Chung303e1ff2014-03-07 15:06:19 -0800384 }
385
Winson Chung4d7b0922014-03-13 17:14:17 -0700386 /** Releases the task resource data back into the pool. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800387 public void unloadTaskData(Task t) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700388 mLoadQueue.removeTask(t);
Winson Chung296278a2015-12-17 12:09:02 -0500389 t.notifyTaskDataUnloaded(null, mDefaultIcon);
Winson Chung303e1ff2014-03-07 15:06:19 -0800390 }
391
Winson Chung4d7b0922014-03-13 17:14:17 -0700392 /** Completely removes the resource data from the pool. */
Winson Chung5393dff2014-05-08 14:25:43 -0700393 public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
Winson Chung04dfe0d2014-03-14 14:06:29 -0700394 mLoadQueue.removeTask(t);
395 mThumbnailCache.remove(t.key);
Winson Chung296278a2015-12-17 12:09:02 -0500396 mIconCache.remove(t.key);
Winson6d5b8b82016-01-26 17:15:45 -0800397 mActivityLabelCache.remove(t.key);
398 mContentDescriptionCache.remove(t.key);
Winson Chung5393dff2014-05-08 14:25:43 -0700399 if (notifyTaskDataUnloaded) {
Winson Chung296278a2015-12-17 12:09:02 -0500400 t.notifyTaskDataUnloaded(null, mDefaultIcon);
Winson Chung5393dff2014-05-08 14:25:43 -0700401 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800402 }
403
Winson Chung9f49df92014-05-07 18:08:34 -0700404 /**
405 * Handles signals from the system, trimming memory when requested to prevent us from running
406 * out of memory.
407 */
Winson Chungf1fbd772014-06-24 18:06:58 -0700408 public void onTrimMemory(int level) {
Winson53ec42c2015-10-28 15:55:35 -0700409 RecentsConfiguration config = Recents.getConfiguration();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700410 switch (level) {
411 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
Winson Chung47c4c692014-03-17 10:17:11 -0700412 // Stop the loader immediately when the UI is no longer visible
413 stopLoader();
Winson Chung96d70412014-11-12 14:17:17 -0800414 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
415 mThumbnailCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
416 mMaxThumbnailCacheSize / 2));
417 } else if (config.svelteLevel == RecentsConfiguration.SVELTE_LIMIT_CACHE) {
418 mThumbnailCache.trimToSize(mNumVisibleThumbnailsLoaded);
419 } else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) {
420 mThumbnailCache.evictAll();
421 }
Winson Chung296278a2015-12-17 12:09:02 -0500422 mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
Jorim Jaggi81e0c842014-09-12 23:28:58 +0200423 mMaxIconCacheSize / 2));
Winson Chung04dfe0d2014-03-14 14:06:29 -0700424 break;
425 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
426 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
427 // We are leaving recents, so trim the data a bit
Winson Chunga91c2932014-11-07 15:02:38 -0800428 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
Winson Chung296278a2015-12-17 12:09:02 -0500429 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
Winsone7f138c2015-10-22 16:15:21 -0700430 mActivityInfoCache.trimToSize(Math.max(1,
431 ActivityManager.getMaxRecentTasksStatic() / 2));
Winson Chung04dfe0d2014-03-14 14:06:29 -0700432 break;
433 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
434 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
435 // We are going to be low on memory
Winson Chunga91c2932014-11-07 15:02:38 -0800436 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
Winson Chung296278a2015-12-17 12:09:02 -0500437 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
Winsone7f138c2015-10-22 16:15:21 -0700438 mActivityInfoCache.trimToSize(Math.max(1,
439 ActivityManager.getMaxRecentTasksStatic() / 4));
Winson Chung04dfe0d2014-03-14 14:06:29 -0700440 break;
441 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
442 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
443 // We are low on memory, so release everything
444 mThumbnailCache.evictAll();
Winson Chung296278a2015-12-17 12:09:02 -0500445 mIconCache.evictAll();
Winsone7f138c2015-10-22 16:15:21 -0700446 mActivityInfoCache.evictAll();
Winson Chunga4ccb862014-08-22 15:26:27 -0700447 // The cache is small, only clear the label cache when we are critical
448 mActivityLabelCache.evictAll();
Benjamin Franzb7a42fd2015-04-23 15:33:32 +0100449 mContentDescriptionCache.evictAll();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700450 break;
451 default:
452 break;
Winson Chung4d7b0922014-03-13 17:14:17 -0700453 }
454 }
Winsone7f138c2015-10-22 16:15:21 -0700455
456 /**
457 * Returns the cached task label if the task key is not expired, updating the cache if it is.
458 */
Winson Chung296278a2015-12-17 12:09:02 -0500459 String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
460 SystemServicesProxy ssp = Recents.getSystemServices();
461
Winsone7f138c2015-10-22 16:15:21 -0700462 // Return the task description label if it exists
463 if (td != null && td.getLabel() != null) {
464 return td.getLabel();
465 }
466 // Return the cached activity label if it exists
467 String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
468 if (label != null) {
469 return label;
470 }
471 // All short paths failed, load the label from the activity info and cache it
Winson Chung296278a2015-12-17 12:09:02 -0500472 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
Winsone7f138c2015-10-22 16:15:21 -0700473 if (activityInfo != null) {
Winsonc5ef63f2016-01-21 14:39:23 -0800474 label = ssp.getBadgedActivityLabel(activityInfo, taskKey.userId);
Winsone7f138c2015-10-22 16:15:21 -0700475 mActivityLabelCache.put(taskKey, label);
476 return label;
477 }
478 // If the activity info does not exist or fails to load, return an empty label for now,
479 // but do not cache it
480 return "";
481 }
482
483 /**
484 * Returns the cached task content description if the task key is not expired, updating the
485 * cache if it is.
486 */
Winsonc5ef63f2016-01-21 14:39:23 -0800487 String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
Winson Chung296278a2015-12-17 12:09:02 -0500488 SystemServicesProxy ssp = Recents.getSystemServices();
489
Winsone7f138c2015-10-22 16:15:21 -0700490 // Return the cached content description if it exists
491 String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
492 if (label != null) {
493 return label;
494 }
Winsone7f138c2015-10-22 16:15:21 -0700495
Winsonc5ef63f2016-01-21 14:39:23 -0800496 // All short paths failed, load the label from the activity info and cache it
497 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
498 if (activityInfo != null) {
499 label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res);
Winsone7f138c2015-10-22 16:15:21 -0700500 mContentDescriptionCache.put(taskKey, label);
501 return label;
502 }
503 // If the content description does not exist, return an empty label for now, but do not
504 // cache it
505 return "";
506 }
507
508 /**
509 * Returns the cached task icon if the task key is not expired, updating the cache if it is.
510 */
511 Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
Winson Chung296278a2015-12-17 12:09:02 -0500512 Resources res, boolean loadIfNotCached) {
513 SystemServicesProxy ssp = Recents.getSystemServices();
514
Winsone7f138c2015-10-22 16:15:21 -0700515 // Return the cached activity icon if it exists
Winson Chung296278a2015-12-17 12:09:02 -0500516 Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
Winsone7f138c2015-10-22 16:15:21 -0700517 if (icon != null) {
518 return icon;
519 }
520
521 if (loadIfNotCached) {
522 // Return and cache the task description icon if it exists
Winson Chung296278a2015-12-17 12:09:02 -0500523 icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
Winsone7f138c2015-10-22 16:15:21 -0700524 if (icon != null) {
Winson Chung296278a2015-12-17 12:09:02 -0500525 mIconCache.put(taskKey, icon);
Winsone7f138c2015-10-22 16:15:21 -0700526 return icon;
527 }
528
529 // Load the icon from the activity info and cache it
Winson Chung296278a2015-12-17 12:09:02 -0500530 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
Winsone7f138c2015-10-22 16:15:21 -0700531 if (activityInfo != null) {
Winson Chung296278a2015-12-17 12:09:02 -0500532 icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
Winsone7f138c2015-10-22 16:15:21 -0700533 if (icon != null) {
Winson Chung296278a2015-12-17 12:09:02 -0500534 mIconCache.put(taskKey, icon);
Winsone7f138c2015-10-22 16:15:21 -0700535 return icon;
536 }
537 }
538 }
539 // We couldn't load any icon
540 return null;
541 }
542
543 /**
544 * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
545 */
Winson Chung296278a2015-12-17 12:09:02 -0500546 Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
547 SystemServicesProxy ssp = Recents.getSystemServices();
548
Winsone7f138c2015-10-22 16:15:21 -0700549 // Return the cached thumbnail if it exists
Winson21700932016-03-24 17:26:23 -0700550 ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(taskKey);
551 if (thumbnailData != null) {
552 return thumbnailData.thumbnail;
Winsone7f138c2015-10-22 16:15:21 -0700553 }
554
555 if (loadIfNotCached) {
Winson53ec42c2015-10-28 15:55:35 -0700556 RecentsConfiguration config = Recents.getConfiguration();
Winsone7f138c2015-10-22 16:15:21 -0700557 if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
558 // Load the thumbnail from the system
Winson21700932016-03-24 17:26:23 -0700559 thumbnailData = ssp.getTaskThumbnail(taskKey.id);
560 if (thumbnailData.thumbnail != null) {
561 mThumbnailCache.put(taskKey, thumbnailData);
562 return thumbnailData.thumbnail;
Winsone7f138c2015-10-22 16:15:21 -0700563 }
564 }
565 }
566 // We couldn't load any thumbnail
567 return null;
568 }
569
570 /**
Winson Chung296278a2015-12-17 12:09:02 -0500571 * Returns the task's primary color if possible, defaulting to the default color if there is
572 * no specified primary color.
Winsone7f138c2015-10-22 16:15:21 -0700573 */
574 int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
575 if (td != null && td.getPrimaryColor() != 0) {
576 return td.getPrimaryColor();
577 }
578 return mDefaultTaskBarBackgroundColor;
579 }
580
581 /**
Winson Chung1af8eda2016-02-05 17:55:56 +0000582 * Returns the task's background color if possible.
583 */
584 int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
585 if (td != null && td.getBackgroundColor() != 0) {
586 return td.getBackgroundColor();
587 }
588 return mDefaultTaskViewBackgroundColor;
589 }
590
591 /**
Winsone7f138c2015-10-22 16:15:21 -0700592 * Returns the activity info for the given task key, retrieving one from the system if the
593 * task key is expired.
594 */
Winson8be16342016-02-09 11:53:27 -0800595 ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
Winson Chung296278a2015-12-17 12:09:02 -0500596 SystemServicesProxy ssp = Recents.getSystemServices();
Winsone7f138c2015-10-22 16:15:21 -0700597 ComponentName cn = taskKey.getComponent();
598 ActivityInfo activityInfo = mActivityInfoCache.get(cn);
599 if (activityInfo == null) {
600 activityInfo = ssp.getActivityInfo(cn, taskKey.userId);
Winsoneb1b65a2015-11-02 10:29:08 -0800601 if (cn == null || activityInfo == null) {
602 Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
603 activityInfo);
604 return null;
605 }
Winsone7f138c2015-10-22 16:15:21 -0700606 mActivityInfoCache.put(cn, activityInfo);
607 }
608 return activityInfo;
609 }
610
611 /**
612 * Stops the task loader and clears all queued, pending task loads.
613 */
614 private void stopLoader() {
615 mLoader.stop();
616 mLoadQueue.clearTasks();
617 }
618
619 /**** Event Bus Events ****/
620
621 public final void onBusEvent(PackagesChangedEvent event) {
622 // Remove all the cached activity infos for this package. The other caches do not need to
623 // be pruned at this time, as the TaskKey expiration checks will flush them next time their
624 // cached contents are requested
625 Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
626 for (ComponentName cn : activityInfoCache.keySet()) {
627 if (cn.getPackageName().equals(event.packageName)) {
628 if (DEBUG) {
629 Log.d(TAG, "Removing activity info from cache: " + cn);
630 }
631 mActivityInfoCache.remove(cn);
632 }
633 }
634 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800635}