• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.os.Process.setThreadPriority;
20 
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.os.SystemClock;
24 import android.util.ArraySet;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.systemui.recents.misc.SystemServicesProxy;
29 import com.android.systemui.recents.model.Task.TaskCallbacks;
30 
31 import java.util.ArrayDeque;
32 import java.util.ArrayList;
33 
34 /**
35  * Loader class that loads full-resolution thumbnails when appropriate.
36  */
37 public class HighResThumbnailLoader implements TaskCallbacks {
38 
39     @GuardedBy("mLoadQueue")
40     private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
41     @GuardedBy("mLoadQueue")
42     private final ArraySet<Task> mLoadingTasks = new ArraySet<>();
43     @GuardedBy("mLoadQueue")
44     private boolean mLoaderIdling;
45 
46     private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
47     private final Thread mLoadThread;
48     private final Handler mMainThreadHandler;
49     private final SystemServicesProxy mSystemServicesProxy;
50     private boolean mLoading;
51     private boolean mVisible;
52     private boolean mFlingingFast;
53     private boolean mTaskLoadQueueIdle;
54 
HighResThumbnailLoader(SystemServicesProxy ssp, Looper looper)55     public HighResThumbnailLoader(SystemServicesProxy ssp, Looper looper) {
56         mMainThreadHandler = new Handler(looper);
57         mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
58         mLoadThread.start();
59         mSystemServicesProxy = ssp;
60     }
61 
setVisible(boolean visible)62     public void setVisible(boolean visible) {
63         mVisible = visible;
64         updateLoading();
65     }
66 
setFlingingFast(boolean flingingFast)67     public void setFlingingFast(boolean flingingFast) {
68         if (mFlingingFast == flingingFast) {
69             return;
70         }
71         mFlingingFast = flingingFast;
72         updateLoading();
73     }
74 
75     /**
76      * Sets whether the other task load queue is idling. Avoid double-loading bitmaps by not
77      * starting this queue until the other queue is idling.
78      */
setTaskLoadQueueIdle(boolean idle)79     public void setTaskLoadQueueIdle(boolean idle) {
80         mTaskLoadQueueIdle = idle;
81         updateLoading();
82     }
83 
84     @VisibleForTesting
isLoading()85     boolean isLoading() {
86         return mLoading;
87     }
88 
updateLoading()89     private void updateLoading() {
90         setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle);
91     }
92 
setLoading(boolean loading)93     private void setLoading(boolean loading) {
94         if (loading == mLoading) {
95             return;
96         }
97         synchronized (mLoadQueue) {
98             mLoading = loading;
99             if (!loading) {
100                 stopLoading();
101             } else {
102                 startLoading();
103             }
104         }
105     }
106 
107     @GuardedBy("mLoadQueue")
startLoading()108     private void startLoading() {
109         for (int i = mVisibleTasks.size() - 1; i >= 0; i--) {
110             Task t = mVisibleTasks.get(i);
111             if ((t.thumbnail == null || t.thumbnail.reducedResolution)
112                     && !mLoadQueue.contains(t) && !mLoadingTasks.contains(t)) {
113                 mLoadQueue.add(t);
114             }
115         }
116         mLoadQueue.notifyAll();
117     }
118 
119     @GuardedBy("mLoadQueue")
stopLoading()120     private void stopLoading() {
121         mLoadQueue.clear();
122         mLoadQueue.notifyAll();
123     }
124 
125     /**
126      * Needs to be called when a task becomes visible. Note that this is different from
127      * {@link TaskCallbacks#onTaskDataLoaded} as this method should only be called once when it
128      * becomes visible, whereas onTaskDataLoaded can be called multiple times whenever some data
129      * has been updated.
130      */
onTaskVisible(Task t)131     public void onTaskVisible(Task t) {
132         t.addCallback(this);
133         mVisibleTasks.add(t);
134         if ((t.thumbnail == null || t.thumbnail.reducedResolution) && mLoading) {
135             synchronized (mLoadQueue) {
136                 mLoadQueue.add(t);
137                 mLoadQueue.notifyAll();
138             }
139         }
140     }
141 
142     /**
143      * Needs to be called when a task becomes visible. See {@link #onTaskVisible} why this is
144      * different from {@link TaskCallbacks#onTaskDataUnloaded()}
145      */
onTaskInvisible(Task t)146     public void onTaskInvisible(Task t) {
147         t.removeCallback(this);
148         mVisibleTasks.remove(t);
149         synchronized (mLoadQueue) {
150             mLoadQueue.remove(t);
151         }
152     }
153 
154     @VisibleForTesting
waitForLoaderIdle()155     void waitForLoaderIdle() {
156         while (true) {
157             synchronized (mLoadQueue) {
158                 if (mLoadQueue.isEmpty() && mLoaderIdling) {
159                     return;
160                 }
161             }
162             SystemClock.sleep(100);
163         }
164     }
165 
166     @Override
onTaskDataLoaded(Task task, ThumbnailData thumbnailData)167     public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
168         if (thumbnailData != null && !thumbnailData.reducedResolution) {
169             synchronized (mLoadQueue) {
170                 mLoadQueue.remove(task);
171             }
172         }
173     }
174 
175     @Override
onTaskDataUnloaded()176     public void onTaskDataUnloaded() {
177     }
178 
179     @Override
onTaskStackIdChanged()180     public void onTaskStackIdChanged() {
181     }
182 
183     private final Runnable mLoader = new Runnable() {
184 
185         @Override
186         public void run() {
187             setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND + 1);
188             while (true) {
189                 Task next = null;
190                 synchronized (mLoadQueue) {
191                     if (!mLoading || mLoadQueue.isEmpty()) {
192                         try {
193                             mLoaderIdling = true;
194                             mLoadQueue.wait();
195                             mLoaderIdling = false;
196                         } catch (InterruptedException e) {
197                             // Don't care.
198                         }
199                     } else {
200                         next = mLoadQueue.poll();
201                         if (next != null) {
202                             mLoadingTasks.add(next);
203                         }
204                     }
205                 }
206                 if (next != null) {
207                     loadTask(next);
208                 }
209             }
210         }
211 
212         private void loadTask(Task t) {
213             ThumbnailData thumbnail = mSystemServicesProxy.getTaskThumbnail(t.key.id,
214                     false /* reducedResolution */);
215             mMainThreadHandler.post(() -> {
216                 synchronized (mLoadQueue) {
217                     mLoadingTasks.remove(t);
218                 }
219                 if (mVisibleTasks.contains(t)) {
220                     t.notifyTaskDataLoaded(thumbnail, t.icon);
221                 }
222             });
223         }
224     };
225 }
226