blob: 6fef8a24fff3ac921224ca2f07b0ce56df30ce88 [file] [log] [blame]
Winson Chunga91c2932014-11-07 15:02:38 -08001/*
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;
Winson59e18722016-02-16 09:28:54 -080021import android.content.pm.ActivityInfo;
Winson8be16342016-02-09 11:53:27 -080022import android.content.pm.ApplicationInfo;
Rubin Xuefb844d2015-12-08 20:57:24 +000023import android.content.pm.UserInfo;
Winson Chunga91c2932014-11-07 15:02:38 -080024import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.drawable.Drawable;
27import android.os.UserHandle;
Rubin Xuefb844d2015-12-08 20:57:24 +000028import android.os.UserManager;
29import android.util.ArraySet;
Winson65c851e2016-01-20 12:43:35 -080030import android.util.SparseArray;
31import android.util.SparseIntArray;
Winsonc0d70582016-01-29 10:24:39 -080032
Winson250608a2015-11-24 15:00:31 -080033import com.android.systemui.Prefs;
Winson55003902016-01-12 12:00:37 -080034import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070035import com.android.systemui.recents.Recents;
Winson Chunga91c2932014-11-07 15:02:38 -080036import com.android.systemui.recents.RecentsConfiguration;
Winson6e6bd8772016-01-25 10:41:40 -080037import com.android.systemui.recents.RecentsDebugFlags;
Winson Chunga91c2932014-11-07 15:02:38 -080038import com.android.systemui.recents.misc.SystemServicesProxy;
39
40import java.util.ArrayList;
41import java.util.Collections;
Winson49df4202016-01-25 17:33:34 -080042import java.util.Formatter;
Winson Chunga91c2932014-11-07 15:02:38 -080043import java.util.List;
44
45
46/**
47 * This class stores the loading state as it goes through multiple stages of loading:
Winson Chung6ac8bd62015-01-07 16:38:35 -080048 * 1) preloadRawTasks() will load the raw set of recents tasks from the system
49 * 2) preloadPlan() will construct a new task stack with all metadata and only icons and
50 * thumbnails that are currently in the cache
51 * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load
52 * options specified, such that we can transition into the Recents activity seamlessly
Winson Chunga91c2932014-11-07 15:02:38 -080053 */
54public class RecentsTaskLoadPlan {
Winson53ec42c2015-10-28 15:55:35 -070055
Winson250608a2015-11-24 15:00:31 -080056 private static int MIN_NUM_TASKS = 5;
Winson0c1a9b82015-12-03 10:59:07 -080057 private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
58 6 /* hrs */;
Winson Chunga91c2932014-11-07 15:02:38 -080059
60 /** The set of conditions to load tasks. */
61 public static class Options {
62 public int runningTaskId = -1;
63 public boolean loadIcons = true;
64 public boolean loadThumbnails = true;
Winson Chung90d51362014-11-13 14:30:26 -080065 public boolean onlyLoadForCache = false;
Winson Chung0eae5572014-12-11 11:04:19 -080066 public boolean onlyLoadPausedActivities = false;
Winson Chunga91c2932014-11-07 15:02:38 -080067 public int numVisibleTasks = 0;
68 public int numVisibleTaskThumbnails = 0;
69 }
70
71 Context mContext;
Winson Chunga91c2932014-11-07 15:02:38 -080072
73 List<ActivityManager.RecentTaskInfo> mRawTasks;
Winson147ecaf2015-09-16 16:49:55 -070074 TaskStack mStack;
Rubin Xuefb844d2015-12-08 20:57:24 +000075 ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
Winson Chunga91c2932014-11-07 15:02:38 -080076
77 /** Package level ctor */
Winson53ec42c2015-10-28 15:55:35 -070078 RecentsTaskLoadPlan(Context context) {
Winson Chunga91c2932014-11-07 15:02:38 -080079 mContext = context;
Winson Chunga91c2932014-11-07 15:02:38 -080080 }
81
Rubin Xuefb844d2015-12-08 20:57:24 +000082 private void updateCurrentQuietProfilesCache(int currentUserId) {
83 mCurrentQuietProfiles.clear();
84
85 if (currentUserId == UserHandle.USER_CURRENT) {
86 currentUserId = ActivityManager.getCurrentUser();
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
Winson Chunga91c2932014-11-07 15:02:38 -0800100 /**
Winson65c851e2016-01-20 12:43:35 -0800101 * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
Winson53ec42c2015-10-28 15:55:35 -0700102 * to most-recent order.
Winson Chunga91c2932014-11-07 15:02:38 -0800103 */
104 public synchronized void preloadRawTasks(boolean isTopTaskHome) {
Rubin Xuefb844d2015-12-08 20:57:24 +0000105 int currentUserId = UserHandle.USER_CURRENT;
106 updateCurrentQuietProfilesCache(currentUserId);
Winsone7f138c2015-10-22 16:15:21 -0700107 SystemServicesProxy ssp = Recents.getSystemServices();
108 mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
Rubin Xuefb844d2015-12-08 20:57:24 +0000109 currentUserId, isTopTaskHome, mCurrentQuietProfiles);
110
Winson53ec42c2015-10-28 15:55:35 -0700111 // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
Winson Chunga91c2932014-11-07 15:02:38 -0800112 Collections.reverse(mRawTasks);
Winson Chunga91c2932014-11-07 15:02:38 -0800113 }
114
115 /**
Winson65c851e2016-01-20 12:43:35 -0800116 * Preloads the list of recent tasks from the system. After this call, the TaskStack will
Winson Chunga91c2932014-11-07 15:02:38 -0800117 * have a list of all the recent tasks with their metadata, not including icons or
118 * thumbnails which were not cached and have to be loaded.
Winson53ec42c2015-10-28 15:55:35 -0700119 *
120 * The tasks will be ordered by:
121 * - least-recent to most-recent stack tasks
122 * - least-recent to most-recent freeform tasks
Winson Chunga91c2932014-11-07 15:02:38 -0800123 */
Winson65c851e2016-01-20 12:43:35 -0800124 public synchronized void preloadPlan(RecentsTaskLoader loader, int topTaskId,
125 boolean isTopTaskHome) {
Winson Chunga91c2932014-11-07 15:02:38 -0800126 Resources res = mContext.getResources();
Winson Chung509d0d02015-12-16 15:43:12 -0500127 ArrayList<Task> allTasks = new ArrayList<>();
Winson Chunga91c2932014-11-07 15:02:38 -0800128 if (mRawTasks == null) {
129 preloadRawTasks(isTopTaskHome);
130 }
Winson250608a2015-11-24 15:00:31 -0800131
Winson65c851e2016-01-20 12:43:35 -0800132 SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
133 SparseIntArray affiliatedTaskCounts = new SparseIntArray();
Winson55003902016-01-12 12:00:37 -0800134 String dismissDescFormat = mContext.getString(
135 R.string.accessibility_recents_item_will_be_dismissed);
Winson49df4202016-01-25 17:33:34 -0800136 Formatter dismissDescFormatter = new Formatter();
Winson250608a2015-11-24 15:00:31 -0800137 long lastStackActiveTime = Prefs.getLong(mContext,
138 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
Winson6e6bd8772016-01-25 10:41:40 -0800139 if (RecentsDebugFlags.Static.EnableMockTasks) {
140 lastStackActiveTime = 0;
141 }
Winson250608a2015-11-24 15:00:31 -0800142 long newLastStackActiveTime = -1;
Winsondf1020c2016-02-01 10:05:10 -0800143 long prevLastActiveTime = lastStackActiveTime;
Winson Chunga91c2932014-11-07 15:02:38 -0800144 int taskCount = mRawTasks.size();
145 for (int i = 0; i < taskCount; i++) {
146 ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
147
Winsondf1020c2016-02-01 10:05:10 -0800148 /*
149 * Affiliated tasks are returned in a specific order from ActivityManager but without a
150 * lastActiveTime since it hasn't yet been started. However, we later sort the task list
151 * by lastActiveTime, which rearranges the tasks. For now, we need to workaround this
152 * by updating the lastActiveTime of this task to the lastActiveTime of the task it is
153 * affiliated with, in the same order that we encounter it in the original list (just
154 * its index in the task group for the task it is affiliated with).
155 *
156 * If the parent task is not available, then we will use the last active time of the
157 * previous task as a base point (since the task itself may not have an active time)
158 * for the entire affiliated group.
159 */
Winson65c851e2016-01-20 12:43:35 -0800160 if (t.persistentId != t.affiliatedTaskId) {
Winsondf1020c2016-02-01 10:05:10 -0800161 Task.TaskKey parentTask = affiliatedTasks.get(t.affiliatedTaskId);
162 long parentTaskLastActiveTime = parentTask != null
163 ? parentTask.lastActiveTime
164 : prevLastActiveTime;
165 t.lastActiveTime = parentTaskLastActiveTime +
Winson65c851e2016-01-20 12:43:35 -0800166 affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
167 }
168
Winson Chunga91c2932014-11-07 15:02:38 -0800169 // Compose the task key
Winson Chungd16c5652015-01-26 16:11:07 -0800170 Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
171 t.userId, t.firstActiveTime, t.lastActiveTime);
Winson Chunga91c2932014-11-07 15:02:38 -0800172
Winson250608a2015-11-24 15:00:31 -0800173 // This task is only shown in the stack if it statisfies the historical time or min
174 // number of tasks constraints. Freeform tasks are also always shown.
Winson Chungead5c0f2015-12-14 11:18:57 -0500175 boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
Winson Chung296278a2015-12-17 12:09:02 -0500176 boolean isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
177 (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)));
Winson65c851e2016-01-20 12:43:35 -0800178 boolean isLaunchTarget = taskKey.id == topTaskId;
Winson Chungead5c0f2015-12-14 11:18:57 -0500179 if (isStackTask && newLastStackActiveTime < 0) {
180 newLastStackActiveTime = t.lastActiveTime;
Winson250608a2015-11-24 15:00:31 -0800181 }
182
Winson Chung296278a2015-12-17 12:09:02 -0500183 // Load the title, icon, and color
Winson59e18722016-02-16 09:28:54 -0800184 ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
Winson Chung296278a2015-12-17 12:09:02 -0500185 String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
Winsonc5ef63f2016-01-21 14:39:23 -0800186 String contentDescription = loader.getAndUpdateContentDescription(taskKey, res);
Winson49df4202016-01-25 17:33:34 -0800187 String dismissDescription = dismissDescFormatter.format(dismissDescFormat,
188 contentDescription).toString();
Winson Chung296278a2015-12-17 12:09:02 -0500189 Drawable icon = isStackTask
190 ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
Winson250608a2015-11-24 15:00:31 -0800191 : null;
Winson Chung296278a2015-12-17 12:09:02 -0500192 Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
Winsone7f138c2015-10-22 16:15:21 -0700193 int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
Winson Chung1af8eda2016-02-05 17:55:56 +0000194 int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
Winson59e18722016-02-16 09:28:54 -0800195 boolean isSystemApp = (info != null) &&
196 ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
Winson Chunga91c2932014-11-07 15:02:38 -0800197
Winson Chunga91c2932014-11-07 15:02:38 -0800198 // Add the task to the stack
Winson Chung296278a2015-12-17 12:09:02 -0500199 Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
Winson55003902016-01-12 12:00:37 -0800200 thumbnail, title, contentDescription, dismissDescription, activityColor,
Winson931845f2016-02-24 19:38:41 -0800201 backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.isDockable,
202 t.bounds, t.taskDescription);
Winson Chunga91c2932014-11-07 15:02:38 -0800203
Winson Chung509d0d02015-12-16 15:43:12 -0500204 allTasks.add(task);
Winson65c851e2016-01-20 12:43:35 -0800205 affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
206 affiliatedTasks.put(taskKey.id, taskKey);
Winsondf1020c2016-02-01 10:05:10 -0800207
208 prevLastActiveTime = t.lastActiveTime;
Winson Chungd16c5652015-01-26 16:11:07 -0800209 }
Winson Chungead5c0f2015-12-14 11:18:57 -0500210 if (newLastStackActiveTime != -1) {
Winson250608a2015-11-24 15:00:31 -0800211 Prefs.putLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
212 newLastStackActiveTime);
213 }
Winson Chungd16c5652015-01-26 16:11:07 -0800214
215 // Initialize the stacks
Winson147ecaf2015-09-16 16:49:55 -0700216 mStack = new TaskStack();
Winson Chung06266772015-12-11 10:24:21 -0500217 mStack.setTasks(allTasks, false /* notifyStackChanges */);
Winson35f30502015-09-28 11:24:36 -0700218 mStack.createAffiliatedGroupings(mContext);
Winson Chunga91c2932014-11-07 15:02:38 -0800219 }
220
221 /**
222 * Called to apply the actual loading based on the specified conditions.
223 */
Winsone7f138c2015-10-22 16:15:21 -0700224 public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
Winson Chung96d70412014-11-12 14:17:17 -0800225 TaskResourceLoadQueue loadQueue) {
Winson53ec42c2015-10-28 15:55:35 -0700226 RecentsConfiguration config = Recents.getConfiguration();
Winson Chunga91c2932014-11-07 15:02:38 -0800227 Resources res = mContext.getResources();
228
229 // Iterate through each of the tasks and load them according to the load conditions.
Winson250608a2015-11-24 15:00:31 -0800230 ArrayList<Task> tasks = mStack.getStackTasks();
Winson147ecaf2015-09-16 16:49:55 -0700231 int taskCount = tasks.size();
232 for (int i = 0; i < taskCount; i++) {
Winson147ecaf2015-09-16 16:49:55 -0700233 Task task = tasks.get(i);
234 Task.TaskKey taskKey = task.key;
Winson Chunga91c2932014-11-07 15:02:38 -0800235
Winson147ecaf2015-09-16 16:49:55 -0700236 boolean isRunningTask = (task.key.id == opts.runningTaskId);
237 boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
238 boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
239
240 // If requested, skip the running task
241 if (opts.onlyLoadPausedActivities && isRunningTask) {
242 continue;
243 }
244
245 if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
Winson Chung296278a2015-12-17 12:09:02 -0500246 if (task.icon == null) {
247 task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
248 res, true);
Winson Chunga91c2932014-11-07 15:02:38 -0800249 }
Winson147ecaf2015-09-16 16:49:55 -0700250 }
251 if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
252 if (task.thumbnail == null || isRunningTask) {
Winson53ec42c2015-10-28 15:55:35 -0700253 if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
Winson Chung296278a2015-12-17 12:09:02 -0500254 task.thumbnail = loader.getAndUpdateThumbnail(taskKey, true);
Winson53ec42c2015-10-28 15:55:35 -0700255 } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
Winson147ecaf2015-09-16 16:49:55 -0700256 loadQueue.addTask(task);
Winson Chung96d70412014-11-12 14:17:17 -0800257 }
Winson Chunga91c2932014-11-07 15:02:38 -0800258 }
Winson147ecaf2015-09-16 16:49:55 -0700259 }
Winson Chunga91c2932014-11-07 15:02:38 -0800260 }
261 }
262
263 /**
Winson147ecaf2015-09-16 16:49:55 -0700264 * Returns the TaskStack from the preloaded list of recent tasks.
Winson Chunga91c2932014-11-07 15:02:38 -0800265 */
Winson147ecaf2015-09-16 16:49:55 -0700266 public TaskStack getTaskStack() {
267 return mStack;
Winson Chungd16c5652015-01-26 16:11:07 -0800268 }
269
270 /** Returns whether there are any tasks in any stacks. */
271 public boolean hasTasks() {
Winson147ecaf2015-09-16 16:49:55 -0700272 if (mStack != null) {
Winson4b057c62016-01-12 15:01:52 -0800273 return mStack.getTaskCount() > 0;
Winson Chungd16c5652015-01-26 16:11:07 -0800274 }
275 return false;
Winson Chunga91c2932014-11-07 15:02:38 -0800276 }
Winson250608a2015-11-24 15:00:31 -0800277
278 /**
279 * Returns whether this task is considered a task to be shown in the history.
280 */
281 private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
282 return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
283 }
Winson Chunga91c2932014-11-07 15:02:38 -0800284}