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.shared.recents.model; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 20 21 import android.app.ActivityManager; 22 import android.app.KeyguardManager; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.pm.ActivityInfo; 26 import android.content.pm.ApplicationInfo; 27 import android.content.res.Resources; 28 import android.graphics.drawable.Drawable; 29 import android.util.SparseBooleanArray; 30 31 import com.android.systemui.shared.recents.model.Task.TaskKey; 32 import com.android.systemui.shared.system.ActivityManagerWrapper; 33 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 38 39 /** 40 * This class stores the loading state as it goes through multiple stages of loading: 41 * 1) preloadRawTasks() will load the raw set of recents tasks from the system 42 * 2) preloadPlan() will construct a new task stack with all metadata and only icons and 43 * thumbnails that are currently in the cache 44 * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load 45 * options specified, such that we can transition into the Recents activity seamlessly 46 */ 47 public class RecentsTaskLoadPlan { 48 49 /** The set of conditions to preload tasks. */ 50 public static class PreloadOptions { 51 public boolean loadTitles = true; 52 } 53 54 /** The set of conditions to load tasks. */ 55 public static class Options { 56 public int runningTaskId = -1; 57 public boolean loadIcons = true; 58 public boolean loadThumbnails = false; 59 public boolean onlyLoadForCache = false; 60 public boolean onlyLoadPausedActivities = false; 61 public int numVisibleTasks = 0; 62 public int numVisibleTaskThumbnails = 0; 63 } 64 65 private final Context mContext; 66 private final KeyguardManager mKeyguardManager; 67 68 private List<ActivityManager.RecentTaskInfo> mRawTasks; 69 private TaskStack mStack; 70 71 private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray(); 72 RecentsTaskLoadPlan(Context context)73 public RecentsTaskLoadPlan(Context context) { 74 mContext = context; 75 mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); 76 } 77 78 /** 79 * Preloads the list of recent tasks from the system. After this call, the TaskStack will 80 * have a list of all the recent tasks with their metadata, not including icons or 81 * thumbnails which were not cached and have to be loaded. 82 * 83 * The tasks will be ordered by: 84 * - least-recent to most-recent stack tasks 85 * 86 * Note: Do not lock, since this can be calling back to the loader, which separately also drives 87 * this call (callers should synchronize on the loader before making this call). 88 */ preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId, int currentUserId)89 public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId, 90 int currentUserId) { 91 Resources res = mContext.getResources(); 92 ArrayList<Task> allTasks = new ArrayList<>(); 93 if (mRawTasks == null) { 94 mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks( 95 ActivityManager.getMaxRecentTasksStatic(), currentUserId); 96 97 // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it 98 Collections.reverse(mRawTasks); 99 } 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 final ComponentName sourceComponent = t.origActivity != null 107 // Activity alias if there is one 108 ? t.origActivity 109 // The real activity if there is no alias (or the target if there is one) 110 : t.realActivity; 111 final int windowingMode = t.configuration.windowConfiguration.getWindowingMode(); 112 TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent, 113 sourceComponent, t.userId, t.lastActiveTime); 114 115 boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM; 116 boolean isStackTask = !isFreeformTask; 117 boolean isLaunchTarget = taskKey.id == runningTaskId; 118 119 ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); 120 if (info == null) { 121 continue; 122 } 123 124 // Load the title, icon, and color 125 String title = opts.loadTitles 126 ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription) 127 : ""; 128 String titleDescription = opts.loadTitles 129 ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription) 130 : ""; 131 Drawable icon = isStackTask 132 ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false) 133 : null; 134 ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey, 135 false /* loadIfNotCached */, false /* storeInCache */); 136 int activityColor = loader.getActivityPrimaryColor(t.taskDescription); 137 int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription); 138 boolean isSystemApp = (info != null) && 139 ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); 140 141 // TODO: Refactor to not do this every preload 142 if (mTmpLockedUsers.indexOfKey(t.userId) < 0) { 143 mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId)); 144 } 145 boolean isLocked = mTmpLockedUsers.get(t.userId); 146 147 // Add the task to the stack 148 Task task = new Task(taskKey, icon, 149 thumbnail, title, titleDescription, activityColor, backgroundColor, 150 isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow, 151 t.taskDescription, t.resizeMode, t.topActivity, isLocked); 152 153 allTasks.add(task); 154 } 155 156 // Initialize the stacks 157 mStack = new TaskStack(); 158 mStack.setTasks(allTasks, false /* notifyStackChanges */); 159 } 160 161 /** 162 * Called to apply the actual loading based on the specified conditions. 163 * 164 * Note: Do not lock, since this can be calling back to the loader, which separately also drives 165 * this call (callers should synchronize on the loader before making this call). 166 */ executePlan(Options opts, RecentsTaskLoader loader)167 public void executePlan(Options opts, RecentsTaskLoader loader) { 168 Resources res = mContext.getResources(); 169 170 // Iterate through each of the tasks and load them according to the load conditions. 171 ArrayList<Task> tasks = mStack.getTasks(); 172 int taskCount = tasks.size(); 173 for (int i = 0; i < taskCount; i++) { 174 Task task = tasks.get(i); 175 TaskKey taskKey = task.key; 176 177 boolean isRunningTask = (task.key.id == opts.runningTaskId); 178 boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks); 179 boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails); 180 181 // If requested, skip the running task 182 if (opts.onlyLoadPausedActivities && isRunningTask) { 183 continue; 184 } 185 186 if (opts.loadIcons && (isRunningTask || isVisibleTask)) { 187 if (task.icon == null) { 188 task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, 189 true); 190 } 191 } 192 if (opts.loadThumbnails && isVisibleThumbnail) { 193 task.thumbnail = loader.getAndUpdateThumbnail(taskKey, 194 true /* loadIfNotCached */, true /* storeInCache */); 195 } 196 } 197 } 198 199 /** 200 * Returns the TaskStack from the preloaded list of recent tasks. 201 */ getTaskStack()202 public TaskStack getTaskStack() { 203 return mStack; 204 } 205 206 /** Returns whether there are any tasks in any stacks. */ hasTasks()207 public boolean hasTasks() { 208 if (mStack != null) { 209 return mStack.getTaskCount() > 0; 210 } 211 return false; 212 } 213 } 214