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 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 62 public void setVisible(boolean visible) { 63 mVisible = visible; 64 updateLoading(); 65 } 66 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 */ 79 public void setTaskLoadQueueIdle(boolean idle) { 80 mTaskLoadQueueIdle = idle; 81 updateLoading(); 82 } 83 84 @VisibleForTesting 85 boolean isLoading() { 86 return mLoading; 87 } 88 89 private void updateLoading() { 90 setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle); 91 } 92 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") 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") 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 */ 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 */ 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 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 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 176 public void onTaskDataUnloaded() { 177 } 178 179 @Override 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