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.Context; 21import android.content.pm.ActivityInfo; 22import android.content.pm.ApplicationInfo; 23import android.content.pm.UserInfo; 24import android.content.res.Resources; 25import android.graphics.Bitmap; 26import android.graphics.drawable.Drawable; 27import android.os.UserHandle; 28import android.os.UserManager; 29import android.util.ArraySet; 30import android.util.SparseArray; 31import android.util.SparseIntArray; 32 33import com.android.systemui.Prefs; 34import com.android.systemui.R; 35import com.android.systemui.recents.Recents; 36import com.android.systemui.recents.RecentsConfiguration; 37import com.android.systemui.recents.RecentsDebugFlags; 38import com.android.systemui.recents.misc.SystemServicesProxy; 39 40import java.util.ArrayList; 41import java.util.Collections; 42import java.util.List; 43 44 45/** 46 * This class stores the loading state as it goes through multiple stages of loading: 47 * 1) preloadRawTasks() will load the raw set of recents tasks from the system 48 * 2) preloadPlan() will construct a new task stack with all metadata and only icons and 49 * thumbnails that are currently in the cache 50 * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load 51 * options specified, such that we can transition into the Recents activity seamlessly 52 */ 53public class RecentsTaskLoadPlan { 54 55 private static int MIN_NUM_TASKS = 5; 56 private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ * 57 6 /* hrs */; 58 59 /** The set of conditions to load tasks. */ 60 public static class Options { 61 public int runningTaskId = -1; 62 public boolean loadIcons = true; 63 public boolean loadThumbnails = true; 64 public boolean onlyLoadForCache = false; 65 public boolean onlyLoadPausedActivities = false; 66 public int numVisibleTasks = 0; 67 public int numVisibleTaskThumbnails = 0; 68 } 69 70 Context mContext; 71 72 List<ActivityManager.RecentTaskInfo> mRawTasks; 73 TaskStack mStack; 74 ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>(); 75 76 /** Package level ctor */ 77 RecentsTaskLoadPlan(Context context) { 78 mContext = context; 79 } 80 81 private void updateCurrentQuietProfilesCache(int currentUserId) { 82 mCurrentQuietProfiles.clear(); 83 84 if (currentUserId == UserHandle.USER_CURRENT) { 85 currentUserId = ActivityManager.getCurrentUser(); 86 } 87 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 88 List<UserInfo> profiles = userManager.getProfiles(currentUserId); 89 if (profiles != null) { 90 for (int i = 0; i < profiles.size(); i++) { 91 UserInfo user = profiles.get(i); 92 if (user.isManagedProfile() && user.isQuietModeEnabled()) { 93 mCurrentQuietProfiles.add(user.id); 94 } 95 } 96 } 97 } 98 99 /** 100 * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent 101 * to most-recent order. 102 */ 103 public synchronized void preloadRawTasks(boolean includeFrontMostExcludedTask) { 104 int currentUserId = UserHandle.USER_CURRENT; 105 updateCurrentQuietProfilesCache(currentUserId); 106 SystemServicesProxy ssp = Recents.getSystemServices(); 107 mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), 108 currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles); 109 110 // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it 111 Collections.reverse(mRawTasks); 112 } 113 114 /** 115 * Preloads the list of recent tasks from the system. After this call, the TaskStack will 116 * have a list of all the recent tasks with their metadata, not including icons or 117 * thumbnails which were not cached and have to be loaded. 118 * 119 * The tasks will be ordered by: 120 * - least-recent to most-recent stack tasks 121 * - least-recent to most-recent freeform tasks 122 */ 123 public synchronized void preloadPlan(RecentsTaskLoader loader, int runningTaskId, 124 boolean includeFrontMostExcludedTask) { 125 Resources res = mContext.getResources(); 126 ArrayList<Task> allTasks = new ArrayList<>(); 127 if (mRawTasks == null) { 128 preloadRawTasks(includeFrontMostExcludedTask); 129 } 130 131 SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>(); 132 SparseIntArray affiliatedTaskCounts = new SparseIntArray(); 133 String dismissDescFormat = mContext.getString( 134 R.string.accessibility_recents_item_will_be_dismissed); 135 String appInfoDescFormat = mContext.getString( 136 R.string.accessibility_recents_item_open_app_info); 137 long lastStackActiveTime = Prefs.getLong(mContext, 138 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0); 139 if (RecentsDebugFlags.Static.EnableMockTasks) { 140 lastStackActiveTime = 0; 141 } 142 long newLastStackActiveTime = -1; 143 int taskCount = mRawTasks.size(); 144 for (int i = 0; i < taskCount; i++) { 145 ActivityManager.RecentTaskInfo t = mRawTasks.get(i); 146 147 // Compose the task key 148 Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent, 149 t.userId, t.firstActiveTime, t.lastActiveTime); 150 151 // This task is only shown in the stack if it statisfies the historical time or min 152 // number of tasks constraints. Freeform tasks are also always shown. 153 boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId); 154 boolean isStackTask = isFreeformTask || !isHistoricalTask(t) || 155 (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)); 156 boolean isLaunchTarget = taskKey.id == runningTaskId; 157 158 // The last stack active time is the baseline for which we show visible tasks. Since 159 // the system will store all the tasks, we don't want to show the tasks prior to the 160 // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy 161 // the other stack-task constraints. 162 if (isStackTask && newLastStackActiveTime < 0) { 163 newLastStackActiveTime = t.lastActiveTime; 164 } 165 166 // Load the title, icon, and color 167 ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); 168 String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription); 169 String titleDescription = loader.getAndUpdateContentDescription(taskKey, res); 170 String dismissDescription = String.format(dismissDescFormat, titleDescription); 171 String appInfoDescription = String.format(appInfoDescFormat, titleDescription); 172 Drawable icon = isStackTask 173 ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false) 174 : null; 175 Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false /* loadIfNotCached */); 176 int activityColor = loader.getActivityPrimaryColor(t.taskDescription); 177 int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription); 178 boolean isSystemApp = (info != null) && 179 ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); 180 181 // Add the task to the stack 182 Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon, 183 thumbnail, title, titleDescription, dismissDescription, appInfoDescription, 184 activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp, 185 t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity); 186 187 allTasks.add(task); 188 affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1); 189 affiliatedTasks.put(taskKey.id, taskKey); 190 } 191 if (newLastStackActiveTime != -1) { 192 Prefs.putLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 193 newLastStackActiveTime); 194 } 195 196 // Initialize the stacks 197 mStack = new TaskStack(); 198 mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */); 199 } 200 201 /** 202 * Called to apply the actual loading based on the specified conditions. 203 */ 204 public synchronized void executePlan(Options opts, RecentsTaskLoader loader, 205 TaskResourceLoadQueue loadQueue) { 206 RecentsConfiguration config = Recents.getConfiguration(); 207 Resources res = mContext.getResources(); 208 209 // Iterate through each of the tasks and load them according to the load conditions. 210 ArrayList<Task> tasks = mStack.getStackTasks(); 211 int taskCount = tasks.size(); 212 for (int i = 0; i < taskCount; i++) { 213 Task task = tasks.get(i); 214 Task.TaskKey taskKey = task.key; 215 216 boolean isRunningTask = (task.key.id == opts.runningTaskId); 217 boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks); 218 boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails); 219 220 // If requested, skip the running task 221 if (opts.onlyLoadPausedActivities && isRunningTask) { 222 continue; 223 } 224 225 if (opts.loadIcons && (isRunningTask || isVisibleTask)) { 226 if (task.icon == null) { 227 task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res, 228 true); 229 } 230 } 231 if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) { 232 if (task.thumbnail == null || isRunningTask) { 233 if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) { 234 task.thumbnail = loader.getAndUpdateThumbnail(taskKey, 235 true /* loadIfNotCached */); 236 } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) { 237 loadQueue.addTask(task); 238 } 239 } 240 } 241 } 242 } 243 244 /** 245 * Returns the TaskStack from the preloaded list of recent tasks. 246 */ 247 public TaskStack getTaskStack() { 248 return mStack; 249 } 250 251 /** Returns whether there are any tasks in any stacks. */ 252 public boolean hasTasks() { 253 if (mStack != null) { 254 return mStack.getTaskCount() > 0; 255 } 256 return false; 257 } 258 259 /** 260 * Returns whether this task is too old to be shown. 261 */ 262 private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) { 263 return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME); 264 } 265} 266