• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.quickstep;
17 
18 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
19 
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
22 
23 import android.annotation.TargetApi;
24 import android.app.ActivityManager;
25 import android.app.KeyguardManager;
26 import android.content.ComponentCallbacks2;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.os.Build;
30 import android.os.Process;
31 import android.os.UserHandle;
32 
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.launcher3.icons.IconProvider;
36 import com.android.launcher3.icons.IconProvider.IconChangeListener;
37 import com.android.launcher3.util.Executors.SimpleThreadFactory;
38 import com.android.launcher3.util.MainThreadInitializedObject;
39 import com.android.quickstep.util.GroupTask;
40 import com.android.quickstep.util.TaskVisualsChangeListener;
41 import com.android.systemui.shared.recents.model.Task;
42 import com.android.systemui.shared.recents.model.ThumbnailData;
43 import com.android.systemui.shared.system.ActivityManagerWrapper;
44 import com.android.systemui.shared.system.TaskStackChangeListener;
45 import com.android.systemui.shared.system.TaskStackChangeListeners;
46 
47 import java.io.PrintWriter;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.concurrent.Executor;
51 import java.util.concurrent.Executors;
52 import java.util.function.Consumer;
53 import java.util.function.Predicate;
54 
55 /**
56  * Singleton class to load and manage recents model.
57  */
58 @TargetApi(Build.VERSION_CODES.O)
59 public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
60         TaskVisualsChangeListener {
61 
62     // We do not need any synchronization for this variable as its only written on UI thread.
63     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
64             new MainThreadInitializedObject<>(RecentsModel::new);
65 
66     private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
67             new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
68 
69     private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
70     private final Context mContext;
71 
72     private final RecentTasksList mTaskList;
73     private final TaskIconCache mIconCache;
74     private final TaskThumbnailCache mThumbnailCache;
75 
RecentsModel(Context context)76     private RecentsModel(Context context) {
77         mContext = context;
78         mTaskList = new RecentTasksList(MAIN_EXECUTOR,
79                 context.getSystemService(KeyguardManager.class),
80                 SystemUiProxy.INSTANCE.get(context));
81 
82         IconProvider iconProvider = new IconProvider(context);
83         mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
84         mIconCache.registerTaskVisualsChangeListener(this);
85         mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
86 
87         TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
88         iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
89     }
90 
getIconCache()91     public TaskIconCache getIconCache() {
92         return mIconCache;
93     }
94 
getThumbnailCache()95     public TaskThumbnailCache getThumbnailCache() {
96         return mThumbnailCache;
97     }
98 
99     /**
100      * Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
101      * at the end of the list.
102      *
103      * @param callback The callback to receive the task plan once its complete or null. This is
104      *                always called on the UI thread.
105      * @return the request id associated with this call.
106      */
getTasks(Consumer<ArrayList<GroupTask>> callback)107     public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
108         return mTaskList.getTasks(false /* loadKeysOnly */, callback,
109                 RecentsFilterState.DEFAULT_FILTER);
110     }
111 
112 
113     /**
114      * Fetches the list of recent tasks, based on a filter
115      *
116      * @param callback The callback to receive the task plan once its complete or null. This is
117      *                always called on the UI thread.
118      * @param filter  Returns true if a GroupTask should be included into the list passed into
119      *                callback.
120      * @return the request id associated with this call.
121      */
getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter)122     public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
123         return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
124     }
125 
126     /**
127      * @return Whether the provided {@param changeId} is the latest recent tasks list id.
128      */
isTaskListValid(int changeId)129     public boolean isTaskListValid(int changeId) {
130         return mTaskList.isTaskListValid(changeId);
131     }
132 
133     /**
134      * @return Whether the task list is currently updating in the background
135      */
136     @VisibleForTesting
isLoadingTasksInBackground()137     public boolean isLoadingTasksInBackground() {
138         return mTaskList.isLoadingTasksInBackground();
139     }
140 
141     /**
142      * Checks if a task has been removed or not.
143      *
144      * @param callback Receives true if task is removed, false otherwise
145      * @param filter Returns true if GroupTask should be in the list of considerations
146      */
isTaskRemoved(int taskId, Consumer<Boolean> callback, Predicate<GroupTask> filter)147     public void isTaskRemoved(int taskId, Consumer<Boolean> callback, Predicate<GroupTask> filter) {
148         mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
149             for (GroupTask group : taskGroups) {
150                 if (group.containsTask(taskId)) {
151                     callback.accept(false);
152                     return;
153                 }
154             }
155             callback.accept(true);
156         }, filter);
157     }
158 
159     @Override
onTaskStackChangedBackground()160     public void onTaskStackChangedBackground() {
161         if (!mThumbnailCache.isPreloadingEnabled()) {
162             // Skip if we aren't preloading
163             return;
164         }
165 
166         int currentUserId = Process.myUserHandle().getIdentifier();
167         if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
168             // Skip if we are not the current user
169             return;
170         }
171 
172         // Keep the cache up to date with the latest thumbnails
173         ActivityManager.RunningTaskInfo runningTask =
174                 ActivityManagerWrapper.getInstance().getRunningTask();
175         int runningTaskId = runningTask != null ? runningTask.id : -1;
176         mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
177             for (GroupTask group : taskGroups) {
178                 if (group.containsTask(runningTaskId)) {
179                     // Skip the running task, it's not going to have an up-to-date snapshot by the
180                     // time the user next enters overview
181                     continue;
182                 }
183                 mThumbnailCache.updateThumbnailInCache(group.task1);
184                 mThumbnailCache.updateThumbnailInCache(group.task2);
185             }
186         });
187     }
188 
189     @Override
onTaskSnapshotChanged(int taskId, ThumbnailData snapshot)190     public boolean onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
191         mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
192 
193         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
194             Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
195             if (task != null) {
196                 task.thumbnail = snapshot;
197             }
198         }
199         return true;
200     }
201 
202     @Override
onTaskRemoved(int taskId)203     public void onTaskRemoved(int taskId) {
204         Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, new Intent(), null, 0, 0);
205         mThumbnailCache.remove(stubKey);
206         mIconCache.onTaskRemoved(stubKey);
207     }
208 
onTrimMemory(int level)209     public void onTrimMemory(int level) {
210         if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
211             mThumbnailCache.getHighResLoadingState().setVisible(false);
212         }
213         if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
214             // Clear everything once we reach a low-mem situation
215             mThumbnailCache.clear();
216             mIconCache.clearCache();
217         }
218     }
219 
220     @Override
onAppIconChanged(String packageName, UserHandle user)221     public void onAppIconChanged(String packageName, UserHandle user) {
222         mIconCache.invalidateCacheEntries(packageName, user);
223         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
224             mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
225         }
226     }
227 
228     @Override
onTaskIconChanged(int taskId)229     public void onTaskIconChanged(int taskId) {
230         for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
231             listener.onTaskIconChanged(taskId);
232         }
233     }
234 
235     @Override
onSystemIconStateChanged(String iconState)236     public void onSystemIconStateChanged(String iconState) {
237         mIconCache.clearCache();
238     }
239 
240     /**
241      * Adds a listener for visuals changes
242      */
addThumbnailChangeListener(TaskVisualsChangeListener listener)243     public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
244         mThumbnailChangeListeners.add(listener);
245     }
246 
247     /**
248      * Removes a previously added listener
249      */
removeThumbnailChangeListener(TaskVisualsChangeListener listener)250     public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
251         mThumbnailChangeListeners.remove(listener);
252     }
253 
dump(String prefix, PrintWriter writer)254     public void dump(String prefix, PrintWriter writer) {
255         writer.println(prefix + "RecentsModel:");
256         mTaskList.dump("  ", writer);
257     }
258 
259     /**
260      * Registers a listener for running tasks
261      */
registerRunningTasksListener(RunningTasksListener listener)262     public void registerRunningTasksListener(RunningTasksListener listener) {
263         mTaskList.registerRunningTasksListener(listener);
264     }
265 
266     /**
267      * Removes the previously registered running tasks listener
268      */
unregisterRunningTasksListener()269     public void unregisterRunningTasksListener() {
270         mTaskList.unregisterRunningTasksListener();
271     }
272 
273     /**
274      * Gets the set of running tasks.
275      */
getRunningTasks()276     public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
277         return mTaskList.getRunningTasks();
278     }
279 
280     /**
281      * Listener for receiving running tasks changes
282      */
283     public interface RunningTasksListener {
284         /**
285          * Called when there's a change to running tasks
286          */
onRunningTasksChanged()287         void onRunningTasksChanged();
288     }
289 }
290