RecentsTaskLoader.java revision ffa2ec664479bff6b4b61d4c349d9db2cb37ca16
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.model;
18
19import android.app.ActivityManager;
20import android.content.ComponentCallbacks2;
21import android.content.Context;
22import android.content.pm.ActivityInfo;
23import android.content.res.Resources;
24import android.graphics.Bitmap;
25import android.graphics.drawable.BitmapDrawable;
26import android.graphics.drawable.Drawable;
27import android.os.Handler;
28import android.os.HandlerThread;
29import android.os.UserHandle;
30import android.util.Pair;
31import com.android.systemui.recents.Constants;
32import com.android.systemui.recents.RecentsConfiguration;
33import com.android.systemui.recents.misc.Console;
34import com.android.systemui.recents.misc.SystemServicesProxy;
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.List;
39import java.util.concurrent.ConcurrentHashMap;
40import java.util.concurrent.ConcurrentLinkedQueue;
41
42
43/** A bitmap load queue */
44class TaskResourceLoadQueue {
45    ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
46    ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
47            new ConcurrentHashMap<Task.TaskKey, Boolean>();
48
49    static final Boolean sFalse = new Boolean(false);
50
51    /** Adds a new task to the load queue */
52    void addTask(Task t, boolean forceLoad) {
53        if (Console.Enabled) {
54            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
55        }
56        if (!mQueue.contains(t)) {
57            mQueue.add(t);
58        }
59        if (forceLoad) {
60            mForceLoadSet.put(t.key, new Boolean(true));
61        }
62        synchronized(this) {
63            notifyAll();
64        }
65    }
66
67    /**
68     * Retrieves the next task from the load queue, as well as whether we want that task to be
69     * force reloaded.
70     */
71    Pair<Task, Boolean> nextTask() {
72        if (Console.Enabled) {
73            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
74        }
75        Task task = mQueue.poll();
76        Boolean forceLoadTask = null;
77        if (task != null) {
78            forceLoadTask = mForceLoadSet.remove(task.key);
79        }
80        if (forceLoadTask == null) {
81            forceLoadTask = sFalse;
82        }
83        return new Pair<Task, Boolean>(task, forceLoadTask);
84    }
85
86    /** Removes a task from the load queue */
87    void removeTask(Task t) {
88        if (Console.Enabled) {
89            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
90        }
91        mQueue.remove(t);
92        mForceLoadSet.remove(t.key);
93    }
94
95    /** Clears all the tasks from the load queue */
96    void clearTasks() {
97        if (Console.Enabled) {
98            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
99        }
100        mQueue.clear();
101        mForceLoadSet.clear();
102    }
103
104    /** Returns whether the load queue is empty */
105    boolean isEmpty() {
106        return mQueue.isEmpty();
107    }
108}
109
110/* Task resource loader */
111class TaskResourceLoader implements Runnable {
112    Context mContext;
113    HandlerThread mLoadThread;
114    Handler mLoadThreadHandler;
115    Handler mMainThreadHandler;
116
117    SystemServicesProxy mSystemServicesProxy;
118    TaskResourceLoadQueue mLoadQueue;
119    DrawableLruCache mApplicationIconCache;
120    BitmapLruCache mThumbnailCache;
121    Bitmap mDefaultThumbnail;
122
123    boolean mCancelled;
124    boolean mWaitingOnLoadQueue;
125
126    /** Constructor, creates a new loading thread that loads task resources in the background */
127    public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
128                              DrawableLruCache applicationIconCache,
129                              BitmapLruCache thumbnailCache,
130                              Bitmap defaultThumbnail) {
131        mLoadQueue = loadQueue;
132        mApplicationIconCache = applicationIconCache;
133        mThumbnailCache = thumbnailCache;
134        mDefaultThumbnail = defaultThumbnail;
135        mMainThreadHandler = new Handler();
136        mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
137        mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
138        mLoadThread.start();
139        mLoadThreadHandler = new Handler(mLoadThread.getLooper());
140        mLoadThreadHandler.post(this);
141    }
142
143    /** Restarts the loader thread */
144    void start(Context context) {
145        if (Console.Enabled) {
146            Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|start]");
147        }
148        mContext = context;
149        mCancelled = false;
150        mSystemServicesProxy = new SystemServicesProxy(context);
151        // Notify the load thread to start loading
152        synchronized(mLoadThread) {
153            mLoadThread.notifyAll();
154        }
155    }
156
157    /** Requests the loader thread to stop after the current iteration */
158    void stop() {
159        if (Console.Enabled) {
160            Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|stop]");
161        }
162        // Mark as cancelled for the thread to pick up
163        mCancelled = true;
164        mSystemServicesProxy = null;
165        // If we are waiting for the load queue for more tasks, then we can just reset the
166        // Context now, since nothing is using it
167        if (mWaitingOnLoadQueue) {
168            mContext = null;
169        }
170    }
171
172    @Override
173    public void run() {
174        while (true) {
175            if (Console.Enabled) {
176                Console.log(Constants.Log.App.TaskDataLoader,
177                        "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
178            }
179            if (mCancelled) {
180                if (Console.Enabled) {
181                    Console.log(Constants.Log.App.TaskDataLoader,
182                            "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
183                }
184                // We have to unset the context here, since the background thread may be using it
185                // when we call stop()
186                mContext = null;
187                // If we are cancelled, then wait until we are started again
188                synchronized(mLoadThread) {
189                    try {
190                        if (Console.Enabled) {
191                            Console.log(Constants.Log.App.TaskDataLoader,
192                                    "[TaskResourceLoader|waitOnLoadThreadCancelled]");
193                        }
194                        mLoadThread.wait();
195                    } catch (InterruptedException ie) {
196                        ie.printStackTrace();
197                    }
198                }
199            } else {
200                SystemServicesProxy ssp = mSystemServicesProxy;
201
202                // Load the next item from the queue
203                Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
204                final Task t = nextTaskData.first;
205                final boolean forceLoadTask = nextTaskData.second;
206                if (t != null) {
207                    Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
208                    Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
209                    if (Console.Enabled) {
210                        Console.log(Constants.Log.App.TaskDataLoader,
211                                "  [TaskResourceLoader|load]",
212                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
213                                        " forceLoad: " + forceLoadTask);
214                    }
215                    // Load the application icon
216                    if (loadIcon == null || forceLoadTask) {
217                        ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
218                                t.userId);
219                        Drawable icon = ssp.getActivityIcon(info, t.userId);
220                        if (!mCancelled) {
221                            if (icon != null) {
222                                if (Console.Enabled) {
223                                    Console.log(Constants.Log.App.TaskDataLoader,
224                                            "    [TaskResourceLoader|loadIcon]", icon);
225                                }
226                                loadIcon = icon;
227                                mApplicationIconCache.put(t.key, icon);
228                            }
229                        }
230                    }
231                    // Load the thumbnail
232                    if (loadThumbnail == null || forceLoadTask) {
233                        Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
234                        if (!mCancelled) {
235                            if (thumbnail != null) {
236                                if (Console.Enabled) {
237                                    Console.log(Constants.Log.App.TaskDataLoader,
238                                            "    [TaskResourceLoader|loadThumbnail]", thumbnail);
239                                }
240                                thumbnail.setHasAlpha(false);
241                                loadThumbnail = thumbnail;
242                            } else {
243                                loadThumbnail = mDefaultThumbnail;
244                                Console.logError(mContext,
245                                        "Failed to load task top thumbnail for: " +
246                                                t.key.baseIntent.getComponent().getPackageName());
247                            }
248                            // We put the default thumbnail in the cache anyways
249                            mThumbnailCache.put(t.key, loadThumbnail);
250                        }
251                    }
252                    if (!mCancelled) {
253                        // Notify that the task data has changed
254                        final Drawable newIcon = loadIcon;
255                        final Bitmap newThumbnail = loadThumbnail;
256                        mMainThreadHandler.post(new Runnable() {
257                            @Override
258                            public void run() {
259                                t.notifyTaskDataLoaded(newThumbnail, newIcon);
260                            }
261                        });
262                    }
263                }
264
265                // If there are no other items in the list, then just wait until something is added
266                if (!mCancelled && mLoadQueue.isEmpty()) {
267                    synchronized(mLoadQueue) {
268                        try {
269                            if (Console.Enabled) {
270                                Console.log(Constants.Log.App.TaskDataLoader,
271                                        "[TaskResourceLoader|waitOnLoadQueue]");
272                            }
273                            mWaitingOnLoadQueue = true;
274                            mLoadQueue.wait();
275                            mWaitingOnLoadQueue = false;
276                        } catch (InterruptedException ie) {
277                            ie.printStackTrace();
278                        }
279                    }
280                }
281            }
282        }
283    }
284}
285
286/* Recents task loader
287 * NOTE: We should not hold any references to a Context from a static instance */
288public class RecentsTaskLoader {
289    static RecentsTaskLoader sInstance;
290
291    SystemServicesProxy mSystemServicesProxy;
292    DrawableLruCache mApplicationIconCache;
293    BitmapLruCache mThumbnailCache;
294    TaskResourceLoadQueue mLoadQueue;
295    TaskResourceLoader mLoader;
296
297    RecentsPackageMonitor mPackageMonitor;
298
299    int mMaxThumbnailCacheSize;
300    int mMaxIconCacheSize;
301
302    BitmapDrawable mDefaultApplicationIcon;
303    Bitmap mDefaultThumbnail;
304    Bitmap mLoadingThumbnail;
305
306    /** Private Constructor */
307    private RecentsTaskLoader(Context context) {
308        // Calculate the cache sizes, we just use a reasonable number here similar to those
309        // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
310        // for icons.
311        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
312        mMaxThumbnailCacheSize = maxMemory / 8;
313        mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
314        int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
315                mMaxIconCacheSize;
316        int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
317                mMaxThumbnailCacheSize;
318
319        if (Console.Enabled) {
320            Console.log(Constants.Log.App.TaskDataLoader,
321                    "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
322                    " iconCache: " + iconCacheSize);
323        }
324
325        // Create the default assets
326        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
327        icon.eraseColor(0x00000000);
328        mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
329        mDefaultThumbnail.setHasAlpha(false);
330        mDefaultThumbnail.eraseColor(0xFFffffff);
331        mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
332        mLoadingThumbnail.setHasAlpha(false);
333        mLoadingThumbnail.eraseColor(0xFFffffff);
334        mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
335
336        // Initialize the proxy, cache and loaders
337        mSystemServicesProxy = new SystemServicesProxy(context);
338        mPackageMonitor = new RecentsPackageMonitor();
339        mLoadQueue = new TaskResourceLoadQueue();
340        mApplicationIconCache = new DrawableLruCache(iconCacheSize);
341        mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
342        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
343                mDefaultThumbnail);
344
345        if (Console.Enabled) {
346            Console.log(Constants.Log.App.TaskDataLoader,
347                    "[RecentsTaskLoader|defaultBitmaps]",
348                    "icon: " + mDefaultApplicationIcon +
349                    " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
350        }
351    }
352
353    /** Initializes the recents task loader */
354    public static RecentsTaskLoader initialize(Context context) {
355        if (sInstance == null) {
356            sInstance = new RecentsTaskLoader(context);
357        }
358        return sInstance;
359    }
360
361    /** Returns the current recents task loader */
362    public static RecentsTaskLoader getInstance() {
363        return sInstance;
364    }
365
366    /** Returns the system services proxy */
367    public SystemServicesProxy getSystemServicesProxy() {
368        return mSystemServicesProxy;
369    }
370
371    private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
372        long t1 = System.currentTimeMillis();
373
374        List<ActivityManager.RecentTaskInfo> tasks =
375                ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
376        Collections.reverse(tasks);
377        if (Console.Enabled) {
378            Console.log(Constants.Log.App.TimeSystemCalls,
379                    "[RecentsTaskLoader|getRecentTasks]",
380                    "" + (System.currentTimeMillis() - t1) + "ms");
381            Console.log(Constants.Log.App.TaskDataLoader,
382                    "[RecentsTaskLoader|tasks]", "" + tasks.size());
383        }
384
385        return tasks;
386    }
387
388    /** Reload the set of recent tasks */
389    public SpaceNode reload(Context context, int preloadCount) {
390        long t1 = System.currentTimeMillis();
391
392        if (Console.Enabled) {
393            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
394        }
395        RecentsConfiguration config = RecentsConfiguration.getInstance();
396        Resources res = context.getResources();
397        ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
398        TaskStack stack = new TaskStack();
399        SpaceNode root = new SpaceNode(context);
400        root.setStack(stack);
401
402        // Get the recent tasks
403        SystemServicesProxy ssp = mSystemServicesProxy;
404        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
405
406        // Add each task to the task stack
407        t1 = System.currentTimeMillis();
408        int taskCount = tasks.size();
409        for (int i = 0; i < taskCount; i++) {
410            ActivityManager.RecentTaskInfo t = tasks.get(i);
411            ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
412            if (info == null) continue;
413
414            ActivityManager.TaskDescription av = t.taskDescription;
415            String activityLabel = null;
416            Drawable activityIcon = null;
417            int activityColor = config.taskBarViewDefaultBackgroundColor;
418            if (av != null) {
419                activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info));
420                activityIcon = (av.getIcon() != null) ?
421                        ssp.getBadgedIcon(new BitmapDrawable(res, av.getIcon()), t.userId) : null;
422                if (av.getPrimaryColor() != 0) {
423                    activityColor = av.getPrimaryColor();
424                }
425            } else {
426                activityLabel = ssp.getActivityLabel(info);
427            }
428            boolean isForemostTask = (i == (taskCount - 1));
429
430            // Create a new task
431            Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
432                    activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
433
434            // Preload the specified number of apps
435            if (i >= (taskCount - preloadCount)) {
436                if (Console.Enabled) {
437                    Console.log(Constants.Log.App.TaskDataLoader,
438                            "[RecentsTaskLoader|preloadTask]",
439                            "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
440                }
441
442                // Load the icon from the cache if possible
443                task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
444                if (task.applicationIcon == null) {
445                    if (isForemostTask) {
446                        // We force loading the application icon for the foremost task
447                        task.applicationIcon = ssp.getActivityIcon(info, task.userId);
448                        if (task.applicationIcon != null) {
449                            mApplicationIconCache.put(task.key, task.applicationIcon);
450                        } else {
451                            task.applicationIcon = mDefaultApplicationIcon;
452                        }
453                    } else {
454                        // Either the task has updated, or we haven't cached any information for the
455                        // task, so reload it
456                        tasksToForceLoad.add(task);
457                    }
458                }
459
460                // Load the thumbnail (if possible and not the foremost task, from the cache)
461                task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
462                if (task.thumbnail == null) {
463                    if (Console.Enabled) {
464                        Console.log(Constants.Log.App.TaskDataLoader,
465                                "[RecentsTaskLoader|loadingTaskThumbnail]");
466                    }
467                    if (isForemostTask) {
468                        // We force loading the thumbnail icon for the foremost task
469                        task.thumbnail = ssp.getTaskThumbnail(task.key.id);
470                        if (task.thumbnail != null) {
471                            task.thumbnail.setHasAlpha(false);
472                        } else {
473                            task.thumbnail = mDefaultThumbnail;
474                        }
475                        mThumbnailCache.put(task.key, task.thumbnail);
476                    } else {
477                        // Either the task has updated, or we haven't cached any information for the
478                        // task, so reload it
479                        tasksToForceLoad.add(task);
480                    }
481                }
482            }
483
484            // Add the task to the stack
485            if (Console.Enabled) {
486                Console.log(Constants.Log.App.TaskDataLoader,
487                        "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
488            }
489            stack.addTask(task);
490        }
491        if (Console.Enabled) {
492            Console.log(Constants.Log.App.TimeSystemCalls,
493                    "[RecentsTaskLoader|getAllTaskTopThumbnail]",
494                    "" + (System.currentTimeMillis() - t1) + "ms");
495        }
496
497        // Simulate the groupings that we describe
498        stack.createSimulatedAffiliatedGroupings();
499
500        // Start the task loader
501        mLoader.start(context);
502
503        // Add all the tasks that we are force/re-loading
504        for (Task t : tasksToForceLoad) {
505            mLoadQueue.addTask(t, true);
506        }
507
508        // Update the package monitor with the list of packages to listen for
509        mPackageMonitor.setTasks(tasks);
510
511        return root;
512    }
513
514    /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
515    public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) {
516        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
517        TaskStack stack = new TaskStack();
518
519        int taskCount = tasks.size();
520        for (int i = 0; i < taskCount; i++) {
521            ActivityManager.RecentTaskInfo t = tasks.get(i);
522            ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
523            if (info == null) continue;
524
525            stack.addTask(new Task(t.persistentId, true, t.baseIntent, null, null, 0, 0,
526                    t.firstActiveTime, t.lastActiveTime));
527        }
528        stack.createSimulatedAffiliatedGroupings();
529        return stack;
530    }
531
532    /** Acquires the task resource data directly from the pool. */
533    public void loadTaskData(Task t) {
534        Drawable applicationIcon = mApplicationIconCache.get(t.key);
535        Bitmap thumbnail = mThumbnailCache.get(t.key);
536
537        if (Console.Enabled) {
538            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
539                    t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail +
540                            " thumbnailCacheSize: " + mThumbnailCache.size());
541        }
542
543        boolean requiresLoad = false;
544        if (applicationIcon == null) {
545            applicationIcon = mDefaultApplicationIcon;
546            requiresLoad = true;
547        }
548        if (thumbnail == null) {
549            thumbnail = mLoadingThumbnail;
550            requiresLoad = true;
551        }
552        if (requiresLoad) {
553            mLoadQueue.addTask(t, false);
554        }
555        t.notifyTaskDataLoaded(thumbnail, applicationIcon);
556    }
557
558    /** Releases the task resource data back into the pool. */
559    public void unloadTaskData(Task t) {
560        if (Console.Enabled) {
561            Console.log(Constants.Log.App.TaskDataLoader,
562                    "[RecentsTaskLoader|unloadTask]", t +
563                    " thumbnailCacheSize: " + mThumbnailCache.size());
564        }
565
566        mLoadQueue.removeTask(t);
567        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
568    }
569
570    /** Completely removes the resource data from the pool. */
571    public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
572        if (Console.Enabled) {
573            Console.log(Constants.Log.App.TaskDataLoader,
574                    "[RecentsTaskLoader|deleteTask]", t);
575        }
576
577        mLoadQueue.removeTask(t);
578        mThumbnailCache.remove(t.key);
579        mApplicationIconCache.remove(t.key);
580        if (notifyTaskDataUnloaded) {
581            t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
582        }
583    }
584
585    /** Stops the task loader and clears all pending tasks */
586    void stopLoader() {
587        if (Console.Enabled) {
588            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
589        }
590        mLoader.stop();
591        mLoadQueue.clearTasks();
592    }
593
594    /** Registers any broadcast receivers. */
595    public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
596        // Register the broadcast receiver to handle messages related to packages being added/removed
597        mPackageMonitor.register(context, cb);
598    }
599
600    /** Unregisters any broadcast receivers. */
601    public void unregisterReceivers() {
602        mPackageMonitor.unregister();
603    }
604
605    /**
606     * Handles signals from the system, trimming memory when requested to prevent us from running
607     * out of memory.
608     */
609    public void onTrimMemory(int level) {
610        if (Console.Enabled) {
611            Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
612                    Console.trimMemoryLevelToString(level));
613        }
614
615        switch (level) {
616            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
617                // Stop the loader immediately when the UI is no longer visible
618                stopLoader();
619                break;
620            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
621            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
622                // We are leaving recents, so trim the data a bit
623                mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
624                mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
625                break;
626            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
627            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
628                // We are going to be low on memory
629                mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
630                mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
631                break;
632            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
633            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
634                // We are low on memory, so release everything
635                mThumbnailCache.evictAll();
636                mApplicationIconCache.evictAll();
637                break;
638            default:
639                break;
640        }
641    }
642}
643