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