• 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 com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
19 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
20 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
21 
22 import android.annotation.TargetApi;
23 import android.app.ActivityManager;
24 import android.content.ComponentCallbacks2;
25 import android.content.Context;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.HandlerThread;
29 import android.os.Process;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import com.android.launcher3.util.MainThreadInitializedObject;
34 import com.android.systemui.shared.recents.ISystemUiProxy;
35 import com.android.systemui.shared.recents.model.Task;
36 import com.android.systemui.shared.recents.model.ThumbnailData;
37 import com.android.systemui.shared.system.ActivityManagerWrapper;
38 import com.android.systemui.shared.system.QuickStepContract;
39 import com.android.systemui.shared.system.TaskStackChangeListener;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.function.Consumer;
44 
45 /**
46  * Singleton class to load and manage recents model.
47  */
48 @TargetApi(Build.VERSION_CODES.O)
49 public class RecentsModel extends TaskStackChangeListener {
50 
51     private static final String TAG = "RecentsModel";
52 
53     // We do not need any synchronization for this variable as its only written on UI thread.
54     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
55             new MainThreadInitializedObject<>(c -> new RecentsModel(c));
56 
57     private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
58     private final Context mContext;
59 
60     private ISystemUiProxy mSystemUiProxy;
61 
62     private final RecentTasksList mTaskList;
63     private final TaskIconCache mIconCache;
64     private final TaskThumbnailCache mThumbnailCache;
65 
RecentsModel(Context context)66     private RecentsModel(Context context) {
67         mContext = context;
68         HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
69                 Process.THREAD_PRIORITY_BACKGROUND);
70         loaderThread.start();
71         mTaskList = new RecentTasksList(context);
72         mIconCache = new TaskIconCache(context, loaderThread.getLooper());
73         mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
74         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
75     }
76 
getIconCache()77     public TaskIconCache getIconCache() {
78         return mIconCache;
79     }
80 
getThumbnailCache()81     public TaskThumbnailCache getThumbnailCache() {
82         return mThumbnailCache;
83     }
84 
85     /**
86      * Fetches the list of recent tasks.
87      *
88      * @param callback The callback to receive the task plan once its complete or null. This is
89      *                always called on the UI thread.
90      * @return the request id associated with this call.
91      */
getTasks(Consumer<ArrayList<Task>> callback)92     public int getTasks(Consumer<ArrayList<Task>> callback) {
93         return mTaskList.getTasks(false /* loadKeysOnly */, callback);
94     }
95 
96     /**
97      * @return The task id of the running task, or -1 if there is no current running task.
98      */
getRunningTaskId()99     public static int getRunningTaskId() {
100         ActivityManager.RunningTaskInfo runningTask =
101                 ActivityManagerWrapper.getInstance().getRunningTask();
102         return runningTask != null ? runningTask.id : -1;
103     }
104 
105     /**
106      * @return Whether the provided {@param changeId} is the latest recent tasks list id.
107      */
isTaskListValid(int changeId)108     public boolean isTaskListValid(int changeId) {
109         return mTaskList.isTaskListValid(changeId);
110     }
111 
112     /**
113      * Finds and returns the task key associated with the given task id.
114      *
115      * @param callback The callback to receive the task key if it is found or null. This is always
116      *                 called on the UI thread.
117      */
findTaskWithId(int taskId, Consumer<Task.TaskKey> callback)118     public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
119         mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
120             for (Task task : tasks) {
121                 if (task.key.id == taskId) {
122                     callback.accept(task.key);
123                     return;
124                 }
125             }
126             callback.accept(null);
127         });
128     }
129 
130     @Override
onTaskStackChangedBackground()131     public void onTaskStackChangedBackground() {
132         if (!mThumbnailCache.isPreloadingEnabled()) {
133             // Skip if we aren't preloading
134             return;
135         }
136 
137         int currentUserId = Process.myUserHandle().getIdentifier();
138         if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
139             // Skip if we are not the current user
140             return;
141         }
142 
143         // Keep the cache up to date with the latest thumbnails
144         int runningTaskId = RecentsModel.getRunningTaskId();
145         mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
146             for (Task task : tasks) {
147                 if (task.key.id == runningTaskId) {
148                     // Skip the running task, it's not going to have an up-to-date snapshot by the
149                     // time the user next enters overview
150                     continue;
151                 }
152                 mThumbnailCache.updateThumbnailInCache(task);
153             }
154         });
155     }
156 
157     @Override
onTaskSnapshotChanged(int taskId, ThumbnailData snapshot)158     public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
159         mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
160 
161         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
162             Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
163             if (task != null) {
164                 task.thumbnail = snapshot;
165             }
166         }
167     }
168 
169     @Override
onTaskRemoved(int taskId)170     public void onTaskRemoved(int taskId) {
171         Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
172         mThumbnailCache.remove(dummyKey);
173     }
174 
setSystemUiProxy(ISystemUiProxy systemUiProxy)175     public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
176         mSystemUiProxy = systemUiProxy;
177     }
178 
getSystemUiProxy()179     public ISystemUiProxy getSystemUiProxy() {
180         return mSystemUiProxy;
181     }
182 
onTrimMemory(int level)183     public void onTrimMemory(int level) {
184         if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
185             mThumbnailCache.getHighResLoadingState().setVisible(false);
186         }
187         if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
188             // Clear everything once we reach a low-mem situation
189             mThumbnailCache.clear();
190             mIconCache.clear();
191         }
192     }
193 
onOverviewShown(boolean fromHome, String tag)194     public void onOverviewShown(boolean fromHome, String tag) {
195         if (mSystemUiProxy == null) {
196             return;
197         }
198         try {
199             mSystemUiProxy.onOverviewShown(fromHome);
200         } catch (RemoteException e) {
201             Log.w(tag,
202                     "Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
203                             + ": ", e);
204         }
205     }
206 
addThumbnailChangeListener(TaskThumbnailChangeListener listener)207     public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
208         mThumbnailChangeListeners.add(listener);
209     }
210 
removeThumbnailChangeListener(TaskThumbnailChangeListener listener)211     public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
212         mThumbnailChangeListeners.remove(listener);
213     }
214 
215     public interface TaskThumbnailChangeListener {
216 
onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData)217         Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
218     }
219 }
220