blob: 7a1d0e83f62b0f0ca244f439ea1c23aaf5e82ff5 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep;
import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.util.SparseBooleanArray;
import com.android.launcher3.MainThreadExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.RecentTaskInfoCompat;
import com.android.systemui.shared.system.TaskDescriptionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* Manages the recent task list from the system, caching it as necessary.
*/
@TargetApi(Build.VERSION_CODES.P)
public class RecentTasksList extends TaskStackChangeListener {
private final KeyguardManagerCompat mKeyguardManager;
private final MainThreadExecutor mMainThreadExecutor;
// The list change id, increments as the task list changes in the system
private int mChangeId;
// The last change id when the list was last loaded completely, must be <= the list change id
private int mLastLoadedId;
// The last change id was loaded with keysOnly = true
private boolean mLastLoadHadKeysOnly;
ArrayList<Task> mTasks = new ArrayList<>();
public RecentTasksList(Context context) {
mMainThreadExecutor = new MainThreadExecutor();
mKeyguardManager = new KeyguardManagerCompat(context);
mChangeId = 1;
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
/**
* Fetches the task keys skipping any local cache.
*/
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
// Kick off task loading in the background
BACKGROUND_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
mMainThreadExecutor.execute(() -> callback.accept(tasks));
});
}
/**
* Asynchronously fetches the list of recent tasks, reusing cached list if available.
*
* @param loadKeysOnly Whether to load other associated task data, or just the key
* @param callback The callback to receive the list of recent tasks
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
final int requestLoadId = mChangeId;
Runnable resultCallback = callback == null
? () -> { }
: () -> callback.accept(copyOf(mTasks));
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
// The list is up to date, callback with the same list
mMainThreadExecutor.execute(resultCallback);
return requestLoadId;
}
// Kick off task loading in the background
BACKGROUND_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
mMainThreadExecutor.execute(() -> {
mTasks = tasks;
mLastLoadedId = requestLoadId;
mLastLoadHadKeysOnly = loadKeysOnly;
resultCallback.run();
});
});
return requestLoadId;
}
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
public synchronized boolean isTaskListValid(int changeId) {
return mChangeId == changeId;
}
@Override
public synchronized void onTaskStackChanged() {
mChangeId++;
}
@Override
public void onTaskRemoved(int taskId) {
for (int i = mTasks.size() - 1; i >= 0; i--) {
if (mTasks.get(i).key.id == taskId) {
mTasks.remove(i);
return;
}
}
}
@Override
public synchronized void onActivityPinned(String packageName, int userId, int taskId,
int stackId) {
mChangeId++;
}
@Override
public synchronized void onActivityUnpinned() {
mChangeId++;
}
/**
* Loads and creates a list of all the recent tasks.
*/
private ArrayList<Task> loadTasksInBackground(int numTasks,
boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
ArrayList<Task> allTasks = new ArrayList<>();
List<ActivityManager.RecentTaskInfo> rawTasks =
ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
SparseBooleanArray tmpLockedUsers = new SparseBooleanArray() {
@Override
public boolean get(int key) {
if (indexOfKey(key) < 0) {
// Fill the cached locked state as we fetch
put(key, mKeyguardManager.isDeviceLocked(key));
}
return super.get(key);
}
};
int taskCount = rawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask);
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
Task task;
if (!loadKeysOnly) {
ActivityManager.TaskDescription rawTd = t.getTaskDescription();
TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd);
boolean isLocked = tmpLockedUsers.get(t.getUserId());
task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(),
t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity());
} else {
task = new Task(taskKey);
}
allTasks.add(task);
}
return allTasks;
}
private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
ArrayList<Task> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
Task t = tasks.get(i);
newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
t.isLocked, t.taskDescription, t.topActivity));
}
return newTasks;
}
}