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