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