• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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