• 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.quickstep;
18 
19 import android.annotation.TargetApi;
20 import android.app.ActivityManager;
21 import android.content.Context;
22 import android.os.Build;
23 import android.os.Process;
24 import android.util.SparseBooleanArray;
25 import com.android.launcher3.MainThreadExecutor;
26 import com.android.systemui.shared.recents.model.Task;
27 import com.android.systemui.shared.system.ActivityManagerWrapper;
28 import com.android.systemui.shared.system.BackgroundExecutor;
29 import com.android.systemui.shared.system.KeyguardManagerCompat;
30 import com.android.systemui.shared.system.RecentTaskInfoCompat;
31 import com.android.systemui.shared.system.TaskDescriptionCompat;
32 import com.android.systemui.shared.system.TaskStackChangeListener;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.function.Consumer;
37 
38 /**
39  * Manages the recent task list from the system, caching it as necessary.
40  */
41 @TargetApi(Build.VERSION_CODES.P)
42 public class RecentTasksList extends TaskStackChangeListener {
43 
44     private final KeyguardManagerCompat mKeyguardManager;
45     private final MainThreadExecutor mMainThreadExecutor;
46     private final BackgroundExecutor mBgThreadExecutor;
47 
48     // The list change id, increments as the task list changes in the system
49     private int mChangeId;
50     // The last change id when the list was last loaded completely, must be <= the list change id
51     private int mLastLoadedId;
52     // The last change id was loaded with keysOnly  = true
53     private boolean mLastLoadHadKeysOnly;
54 
55     ArrayList<Task> mTasks = new ArrayList<>();
56 
RecentTasksList(Context context)57     public RecentTasksList(Context context) {
58         mMainThreadExecutor = new MainThreadExecutor();
59         mBgThreadExecutor = BackgroundExecutor.get();
60         mKeyguardManager = new KeyguardManagerCompat(context);
61         mChangeId = 1;
62         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
63     }
64 
65     /**
66      * Fetches the task keys skipping any local cache.
67      */
getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback)68     public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
69         // Kick off task loading in the background
70         mBgThreadExecutor.submit(() -> {
71             ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
72             mMainThreadExecutor.execute(() -> callback.accept(tasks));
73         });
74     }
75 
76     /**
77      * Asynchronously fetches the list of recent tasks, reusing cached list if available.
78      *
79      * @param loadKeysOnly Whether to load other associated task data, or just the key
80      * @param callback The callback to receive the list of recent tasks
81      * @return The change id of the current task list
82      */
getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback)83     public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
84         final int requestLoadId = mChangeId;
85         Runnable resultCallback = callback == null
86                 ? () -> { }
87                 : () -> callback.accept(copyOf(mTasks));
88 
89         if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
90             // The list is up to date, callback with the same list
91             mMainThreadExecutor.execute(resultCallback);
92             return requestLoadId;
93         }
94 
95         // Kick off task loading in the background
96         mBgThreadExecutor.submit(() -> {
97             ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
98 
99             mMainThreadExecutor.execute(() -> {
100                 mTasks = tasks;
101                 mLastLoadedId = requestLoadId;
102                 mLastLoadHadKeysOnly = loadKeysOnly;
103                 resultCallback.run();
104             });
105         });
106 
107         return requestLoadId;
108     }
109 
110     /**
111      * @return Whether the provided {@param changeId} is the latest recent tasks list id.
112      */
isTaskListValid(int changeId)113     public synchronized boolean isTaskListValid(int changeId) {
114         return mChangeId == changeId;
115     }
116 
117     @Override
onTaskStackChanged()118     public synchronized void onTaskStackChanged() {
119         mChangeId++;
120     }
121 
122     @Override
onTaskRemoved(int taskId)123     public void onTaskRemoved(int taskId) {
124         for (int i = mTasks.size() - 1; i >= 0; i--) {
125             if (mTasks.get(i).key.id == taskId) {
126                 mTasks.remove(i);
127                 return;
128             }
129         }
130     }
131 
132     @Override
onActivityPinned(String packageName, int userId, int taskId, int stackId)133     public synchronized void onActivityPinned(String packageName, int userId, int taskId,
134             int stackId) {
135         mChangeId++;
136     }
137 
138     @Override
onActivityUnpinned()139     public synchronized void onActivityUnpinned() {
140         mChangeId++;
141     }
142 
143     /**
144      * Loads and creates a list of all the recent tasks.
145      */
loadTasksInBackground(int numTasks, boolean loadKeysOnly)146     private ArrayList<Task> loadTasksInBackground(int numTasks,
147             boolean loadKeysOnly) {
148         int currentUserId = Process.myUserHandle().getIdentifier();
149         ArrayList<Task> allTasks = new ArrayList<>();
150         List<ActivityManager.RecentTaskInfo> rawTasks =
151                 ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
152         // The raw tasks are given in most-recent to least-recent order, we need to reverse it
153         Collections.reverse(rawTasks);
154 
155         SparseBooleanArray tmpLockedUsers = new SparseBooleanArray() {
156             @Override
157             public boolean get(int key) {
158                 if (indexOfKey(key) < 0) {
159                     // Fill the cached locked state as we fetch
160                     put(key, mKeyguardManager.isDeviceLocked(key));
161                 }
162                 return super.get(key);
163             }
164         };
165 
166         int taskCount = rawTasks.size();
167         for (int i = 0; i < taskCount; i++) {
168             ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
169             RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask);
170             Task.TaskKey taskKey = new Task.TaskKey(rawTask);
171             Task task;
172             if (!loadKeysOnly) {
173                 ActivityManager.TaskDescription rawTd = t.getTaskDescription();
174                 TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd);
175                 boolean isLocked = tmpLockedUsers.get(t.getUserId());
176                 task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(),
177                         t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity());
178             } else {
179                 task = new Task(taskKey);
180             }
181             allTasks.add(task);
182         }
183 
184         return allTasks;
185     }
186 
copyOf(ArrayList<Task> tasks)187     private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
188         ArrayList<Task> newTasks = new ArrayList<>();
189         for (int i = 0; i < tasks.size(); i++) {
190             Task t = tasks.get(i);
191             newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
192                     t.isLocked, t.taskDescription, t.topActivity));
193         }
194         return newTasks;
195     }
196 }