• 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.content.ComponentCallbacks2;
26 import android.content.Context;
27 import android.os.Build;
28 import android.os.Process;
29 import android.os.UserHandle;
30 
31 import androidx.annotation.VisibleForTesting;
32 
33 import com.android.launcher3.icons.IconProvider;
34 import com.android.launcher3.icons.IconProvider.IconChangeListener;
35 import com.android.launcher3.util.Executors.SimpleThreadFactory;
36 import com.android.launcher3.util.MainThreadInitializedObject;
37 import com.android.systemui.shared.recents.model.Task;
38 import com.android.systemui.shared.recents.model.ThumbnailData;
39 import com.android.systemui.shared.system.ActivityManagerWrapper;
40 import com.android.systemui.shared.system.KeyguardManagerCompat;
41 import com.android.systemui.shared.system.TaskStackChangeListener;
42 import com.android.systemui.shared.system.TaskStackChangeListeners;
43 
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.concurrent.Executor;
47 import java.util.concurrent.Executors;
48 import java.util.function.Consumer;
49 
50 /**
51  * Singleton class to load and manage recents model.
52  */
53 @TargetApi(Build.VERSION_CODES.O)
54 public class RecentsModel extends TaskStackChangeListener implements IconChangeListener {
55 
56     // We do not need any synchronization for this variable as its only written on UI thread.
57     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
58             new MainThreadInitializedObject<>(RecentsModel::new);
59 
60     private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
61             new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
62 
63     private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
64     private final Context mContext;
65 
66     private final RecentTasksList mTaskList;
67     private final TaskIconCache mIconCache;
68     private final TaskThumbnailCache mThumbnailCache;
69 
RecentsModel(Context context)70     private RecentsModel(Context context) {
71         mContext = context;
72         mTaskList = new RecentTasksList(MAIN_EXECUTOR,
73                 new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
74 
75         IconProvider iconProvider = new IconProvider(context);
76         mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
77         mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
78 
79         TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
80         iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
81     }
82 
getIconCache()83     public TaskIconCache getIconCache() {
84         return mIconCache;
85     }
86 
getThumbnailCache()87     public TaskThumbnailCache getThumbnailCache() {
88         return mThumbnailCache;
89     }
90 
91     /**
92      * Fetches the list of recent tasks.
93      *
94      * @param callback The callback to receive the task plan once its complete or null. This is
95      *                always called on the UI thread.
96      * @return the request id associated with this call.
97      */
getTasks(Consumer<ArrayList<Task>> callback)98     public int getTasks(Consumer<ArrayList<Task>> callback) {
99         return mTaskList.getTasks(false /* loadKeysOnly */, callback);
100     }
101 
102     /**
103      * @return Whether the provided {@param changeId} is the latest recent tasks list id.
104      */
isTaskListValid(int changeId)105     public boolean isTaskListValid(int changeId) {
106         return mTaskList.isTaskListValid(changeId);
107     }
108 
109     /**
110      * @return Whether the task list is currently updating in the background
111      */
112     @VisibleForTesting
isLoadingTasksInBackground()113     public boolean isLoadingTasksInBackground() {
114         return mTaskList.isLoadingTasksInBackground();
115     }
116 
117     /**
118      * Checks if a task has been removed or not.
119      *
120      * @param callback Receives true if task is removed, false otherwise
121      */
isTaskRemoved(int taskId, Consumer<Boolean> callback)122     public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
123         mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
124             for (Task task : tasks) {
125                 if (task.key.id == taskId) {
126                     callback.accept(false);
127                     return;
128                 }
129             }
130             callback.accept(true);
131         });
132     }
133 
134     @Override
onTaskStackChangedBackground()135     public void onTaskStackChangedBackground() {
136         if (!mThumbnailCache.isPreloadingEnabled()) {
137             // Skip if we aren't preloading
138             return;
139         }
140 
141         int currentUserId = Process.myUserHandle().getIdentifier();
142         if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
143             // Skip if we are not the current user
144             return;
145         }
146 
147         // Keep the cache up to date with the latest thumbnails
148         ActivityManager.RunningTaskInfo runningTask =
149                 ActivityManagerWrapper.getInstance().getRunningTask();
150         int runningTaskId = runningTask != null ? runningTask.id : -1;
151         mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
152             for (Task task : tasks) {
153                 if (task.key.id == runningTaskId) {
154                     // Skip the running task, it's not going to have an up-to-date snapshot by the
155                     // time the user next enters overview
156                     continue;
157                 }
158                 mThumbnailCache.updateThumbnailInCache(task);
159             }
160         });
161     }
162 
163     @Override
onTaskSnapshotChanged(int taskId, ThumbnailData snapshot)164     public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
165         mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
166 
167         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
168             Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
169             if (task != null) {
170                 task.thumbnail = snapshot;
171             }
172         }
173     }
174 
175     @Override
onTaskRemoved(int taskId)176     public void onTaskRemoved(int taskId) {
177         Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
178         mThumbnailCache.remove(stubKey);
179         mIconCache.onTaskRemoved(stubKey);
180     }
181 
onTrimMemory(int level)182     public void onTrimMemory(int level) {
183         if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
184             mThumbnailCache.getHighResLoadingState().setVisible(false);
185         }
186         if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
187             // Clear everything once we reach a low-mem situation
188             mThumbnailCache.clear();
189             mIconCache.clearCache();
190         }
191     }
192 
193     @Override
onAppIconChanged(String packageName, UserHandle user)194     public void onAppIconChanged(String packageName, UserHandle user) {
195         mIconCache.invalidateCacheEntries(packageName, user);
196         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
197             mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
198         }
199     }
200 
201     @Override
onSystemIconStateChanged(String iconState)202     public void onSystemIconStateChanged(String iconState) {
203         mIconCache.clearCache();
204     }
205 
206     /**
207      * Adds a listener for visuals changes
208      */
addThumbnailChangeListener(TaskVisualsChangeListener listener)209     public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
210         mThumbnailChangeListeners.add(listener);
211     }
212 
213     /**
214      * Removes a previously added listener
215      */
removeThumbnailChangeListener(TaskVisualsChangeListener listener)216     public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
217         mThumbnailChangeListeners.remove(listener);
218     }
219 
220     /**
221      * Listener for receiving various task properties changes
222      */
223     public interface TaskVisualsChangeListener {
224 
225         /**
226          * Called whn the task thumbnail changes
227          */
onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData)228         Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
229 
230         /**
231          * Called when the icon for a task changes
232          */
onTaskIconChanged(String pkg, UserHandle user)233         void onTaskIconChanged(String pkg, UserHandle user);
234     }
235 }
236