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 17 package com.android.systemui.recents.model; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.graphics.Bitmap; 23 import android.graphics.drawable.Drawable; 24 import android.os.UserHandle; 25 import android.util.Log; 26 import com.android.systemui.recents.RecentsConfiguration; 27 import com.android.systemui.recents.misc.SystemServicesProxy; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.List; 33 34 35 /** 36 * This class stores the loading state as it goes through multiple stages of loading: 37 * - preloadRawTasks() will load the raw set of recents tasks from the system 38 * - preloadPlan() will construct a new task stack with all metadata and only icons and thumbnails 39 * that are currently in the cache 40 * - executePlan() will actually load and fill in the icons and thumbnails according to the load 41 * options specified, such that we can transition into the Recents activity seamlessly 42 */ 43 public class RecentsTaskLoadPlan { 44 static String TAG = "RecentsTaskLoadPlan"; 45 static boolean DEBUG = false; 46 47 /** The set of conditions to load tasks. */ 48 public static class Options { 49 public int runningTaskId = -1; 50 public boolean loadIcons = true; 51 public boolean loadThumbnails = true; 52 public boolean onlyLoadForCache = false; 53 public boolean onlyLoadPausedActivities = false; 54 public int numVisibleTasks = 0; 55 public int numVisibleTaskThumbnails = 0; 56 } 57 58 Context mContext; 59 RecentsConfiguration mConfig; 60 SystemServicesProxy mSystemServicesProxy; 61 62 List<ActivityManager.RecentTaskInfo> mRawTasks; 63 TaskStack mStack; 64 HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache = 65 new HashMap<Task.ComponentNameKey, ActivityInfoHandle>(); 66 67 /** Package level ctor */ RecentsTaskLoadPlan(Context context, RecentsConfiguration config, SystemServicesProxy ssp)68 RecentsTaskLoadPlan(Context context, RecentsConfiguration config, SystemServicesProxy ssp) { 69 mContext = context; 70 mConfig = config; 71 mSystemServicesProxy = ssp; 72 } 73 74 /** 75 * An optimization to preload the raw list of tasks. 76 */ preloadRawTasks(boolean isTopTaskHome)77 public synchronized void preloadRawTasks(boolean isTopTaskHome) { 78 mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad, 79 UserHandle.CURRENT.getIdentifier(), isTopTaskHome); 80 Collections.reverse(mRawTasks); 81 82 if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size()); 83 } 84 85 /** 86 * Preloads the list of recent tasks from the system. After this call, the TaskStack will 87 * have a list of all the recent tasks with their metadata, not including icons or 88 * thumbnails which were not cached and have to be loaded. 89 */ preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome)90 synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) { 91 if (DEBUG) Log.d(TAG, "preloadPlan"); 92 93 mActivityInfoCache.clear(); 94 mStack = new TaskStack(); 95 96 Resources res = mContext.getResources(); 97 ArrayList<Task> loadedTasks = new ArrayList<Task>(); 98 if (mRawTasks == null) { 99 preloadRawTasks(isTopTaskHome); 100 } 101 int taskCount = mRawTasks.size(); 102 for (int i = 0; i < taskCount; i++) { 103 ActivityManager.RecentTaskInfo t = mRawTasks.get(i); 104 105 // Compose the task key 106 Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId, 107 t.firstActiveTime, t.lastActiveTime); 108 109 // Get an existing activity info handle if possible 110 Task.ComponentNameKey cnKey = taskKey.getComponentNameKey(); 111 ActivityInfoHandle infoHandle; 112 boolean hadCachedActivityInfo = false; 113 if (mActivityInfoCache.containsKey(cnKey)) { 114 infoHandle = mActivityInfoCache.get(cnKey); 115 hadCachedActivityInfo = true; 116 } else { 117 infoHandle = new ActivityInfoHandle(); 118 } 119 120 // Load the label, icon, and color 121 String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription, 122 mSystemServicesProxy, infoHandle); 123 Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, 124 mSystemServicesProxy, res, infoHandle, false); 125 int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig); 126 127 // Update the activity info cache 128 if (!hadCachedActivityInfo && infoHandle.info != null) { 129 mActivityInfoCache.put(cnKey, infoHandle); 130 } 131 132 Bitmap icon = t.taskDescription != null 133 ? t.taskDescription.getInMemoryIcon() 134 : null; 135 String iconFilename = t.taskDescription != null 136 ? t.taskDescription.getIconFilename() 137 : null; 138 139 // Add the task to the stack 140 Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID), 141 t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, activityIcon, 142 activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon, 143 iconFilename); 144 task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false); 145 if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail); 146 loadedTasks.add(task); 147 } 148 mStack.setTasks(loadedTasks); 149 mStack.createAffiliatedGroupings(mConfig); 150 151 // Assertion 152 if (mStack.getTaskCount() != mRawTasks.size()) { 153 throw new RuntimeException("Loading failed"); 154 } 155 } 156 157 /** 158 * Called to apply the actual loading based on the specified conditions. 159 */ executePlan(Options opts, RecentsTaskLoader loader, TaskResourceLoadQueue loadQueue)160 synchronized void executePlan(Options opts, RecentsTaskLoader loader, 161 TaskResourceLoadQueue loadQueue) { 162 if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks + 163 ", # thumbnails: " + opts.numVisibleTaskThumbnails + 164 ", running task id: " + opts.runningTaskId); 165 166 Resources res = mContext.getResources(); 167 168 // Iterate through each of the tasks and load them according to the load conditions. 169 ArrayList<Task> tasks = mStack.getTasks(); 170 int taskCount = tasks.size(); 171 for (int i = 0; i < taskCount; i++) { 172 ActivityManager.RecentTaskInfo t = mRawTasks.get(i); 173 Task task = tasks.get(i); 174 Task.TaskKey taskKey = task.key; 175 176 // Get an existing activity info handle if possible 177 Task.ComponentNameKey cnKey = taskKey.getComponentNameKey(); 178 ActivityInfoHandle infoHandle; 179 boolean hadCachedActivityInfo = false; 180 if (mActivityInfoCache.containsKey(cnKey)) { 181 infoHandle = mActivityInfoCache.get(cnKey); 182 hadCachedActivityInfo = true; 183 } else { 184 infoHandle = new ActivityInfoHandle(); 185 } 186 187 boolean isRunningTask = (task.key.id == opts.runningTaskId); 188 boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks); 189 boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails); 190 191 // If requested, skip the running task 192 if (opts.onlyLoadPausedActivities && isRunningTask) { 193 continue; 194 } 195 196 if (opts.loadIcons && (isRunningTask || isVisibleTask)) { 197 if (task.activityIcon == null) { 198 if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey); 199 task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, 200 mSystemServicesProxy, res, infoHandle, true); 201 } 202 } 203 if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) { 204 if (task.thumbnail == null || isRunningTask) { 205 if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey); 206 if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) { 207 task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, 208 true); 209 } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) { 210 loadQueue.addTask(task); 211 } 212 } 213 } 214 215 // Update the activity info cache 216 if (!hadCachedActivityInfo && infoHandle.info != null) { 217 mActivityInfoCache.put(cnKey, infoHandle); 218 } 219 } 220 } 221 222 /** 223 * Composes and returns a TaskStack from the preloaded list of recent tasks. 224 */ getTaskStack()225 public TaskStack getTaskStack() { 226 return mStack; 227 } 228 229 /** 230 * Composes and returns a SpaceNode from the preloaded list of recent tasks. 231 */ getSpaceNode()232 public SpaceNode getSpaceNode() { 233 SpaceNode node = new SpaceNode(); 234 node.setStack(mStack); 235 return node; 236 } 237 } 238