Call into shell for recent tasks
- No change in behavior from today until groups are actually returned from the shell Bug: 202740477 Test: atest RecentTasksListTest Change-Id: I4ac7b472ce2e0a3b2574dc6d8f4c1761a0ad993a Merged-In: I4ac7b472ce2e0a3b2574dc6d8f4c1761a0ad993a
This commit is contained in:
@@ -22,35 +22,33 @@ import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.os.RemoteException;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.systemui.shared.recents.model.GroupTask;
|
||||
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.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
|
||||
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.R)
|
||||
public class RecentTasksList extends TaskStackChangeListener {
|
||||
public class RecentTasksList {
|
||||
|
||||
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
|
||||
|
||||
private final KeyguardManagerCompat mKeyguardManager;
|
||||
private final LooperExecutor mMainThreadExecutor;
|
||||
private final ActivityManagerWrapper mActivityManagerWrapper;
|
||||
private final SystemUiProxy mSysUiProxy;
|
||||
|
||||
// The list change id, increments as the task list changes in the system
|
||||
private int mChangeId;
|
||||
@@ -62,12 +60,17 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
private TaskLoadResult mResultsUi = INVALID_RESULT;
|
||||
|
||||
public RecentTasksList(LooperExecutor mainThreadExecutor,
|
||||
KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
|
||||
KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
|
||||
mMainThreadExecutor = mainThreadExecutor;
|
||||
mKeyguardManager = keyguardManager;
|
||||
mChangeId = 1;
|
||||
mActivityManagerWrapper = activityManagerWrapper;
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
|
||||
mSysUiProxy = sysUiProxy;
|
||||
sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
|
||||
@Override
|
||||
public void onRecentTasksChanged() throws RemoteException {
|
||||
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -78,10 +81,11 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
/**
|
||||
* Fetches the task keys skipping any local cache.
|
||||
*/
|
||||
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
|
||||
public void getTaskKeys(int numTasks, Consumer<ArrayList<GroupTask>> callback) {
|
||||
// Kick off task loading in the background
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
ArrayList<Task> tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */);
|
||||
ArrayList<GroupTask> tasks = loadTasksInBackground(numTasks, -1,
|
||||
true /* loadKeysOnly */);
|
||||
mMainThreadExecutor.execute(() -> callback.accept(tasks));
|
||||
});
|
||||
}
|
||||
@@ -93,14 +97,15 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
* @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) {
|
||||
public synchronized int getTasks(boolean loadKeysOnly,
|
||||
Consumer<ArrayList<GroupTask>> callback) {
|
||||
final int requestLoadId = mChangeId;
|
||||
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
|
||||
// The list is up to date, send the callback on the next frame,
|
||||
// so that requestID can be returned first.
|
||||
if (callback != null) {
|
||||
// Copy synchronously as the changeId might change by next frame
|
||||
ArrayList<Task> result = copyOf(mResultsUi);
|
||||
ArrayList<GroupTask> result = copyOf(mResultsUi);
|
||||
mMainThreadExecutor.post(() -> {
|
||||
callback.accept(result);
|
||||
});
|
||||
@@ -120,7 +125,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
mLoadingTasksInBackground = false;
|
||||
mResultsUi = loadResult;
|
||||
if (callback != null) {
|
||||
ArrayList<Task> result = copyOf(mResultsUi);
|
||||
ArrayList<GroupTask> result = copyOf(mResultsUi);
|
||||
callback.accept(result);
|
||||
}
|
||||
});
|
||||
@@ -136,35 +141,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
return mChangeId == changeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentTaskListUpdated() {
|
||||
// In some cases immediately after booting, the tasks in the system recent task list may be
|
||||
// loaded, but not in the active task hierarchy in the system. These tasks are displayed in
|
||||
// overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
|
||||
// callback (those are for changes to the active tasks), but the task list is still updated,
|
||||
// so we should also invalidate the change id to ensure we load a new list instead of
|
||||
// reusing a stale list.
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(int taskId) {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityUnpinned() {
|
||||
public void onRecentTasksChanged() {
|
||||
invalidateLoadedTasks();
|
||||
}
|
||||
|
||||
@@ -180,8 +157,8 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
@VisibleForTesting
|
||||
TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
|
||||
int currentUserId = Process.myUserHandle().getIdentifier();
|
||||
List<ActivityManager.RecentTaskInfo> rawTasks =
|
||||
mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
|
||||
ArrayList<GroupedRecentTaskInfo> rawTasks =
|
||||
mSysUiProxy.getRecentTasks(numTasks, currentUserId);
|
||||
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
|
||||
Collections.reverse(rawTasks);
|
||||
|
||||
@@ -197,45 +174,53 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
};
|
||||
|
||||
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
|
||||
for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
|
||||
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
|
||||
Task task;
|
||||
if (!loadKeysOnly) {
|
||||
boolean isLocked = tmpLockedUsers.get(taskKey.userId);
|
||||
task = Task.from(taskKey, rawTask, isLocked);
|
||||
} else {
|
||||
task = new Task(taskKey);
|
||||
for (GroupedRecentTaskInfo rawTask : rawTasks) {
|
||||
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.mTaskInfo1;
|
||||
ActivityManager.RecentTaskInfo taskInfo2 = rawTask.mTaskInfo2;
|
||||
Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
|
||||
Task task1 = loadKeysOnly
|
||||
? new Task(task1Key)
|
||||
: Task.from(task1Key, taskInfo1,
|
||||
tmpLockedUsers.get(task1Key.userId) /* isLocked */);
|
||||
task1.setLastSnapshotData(taskInfo1);
|
||||
Task task2 = null;
|
||||
if (taskInfo2 != null) {
|
||||
Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
|
||||
task2 = loadKeysOnly
|
||||
? new Task(task2Key)
|
||||
: Task.from(task2Key, taskInfo2,
|
||||
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
|
||||
task2.setLastSnapshotData(taskInfo2);
|
||||
}
|
||||
task.setLastSnapshotData(rawTask);
|
||||
allTasks.add(task);
|
||||
allTasks.add(new GroupTask(task1, task2));
|
||||
}
|
||||
|
||||
return allTasks;
|
||||
}
|
||||
|
||||
private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
|
||||
ArrayList<Task> newTasks = new ArrayList<>();
|
||||
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
|
||||
ArrayList<GroupTask> newTasks = new ArrayList<>();
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
newTasks.add(new Task(tasks.get(i)));
|
||||
newTasks.add(new GroupTask(tasks.get(i)));
|
||||
}
|
||||
return newTasks;
|
||||
}
|
||||
|
||||
private static class TaskLoadResult extends ArrayList<Task> {
|
||||
private static class TaskLoadResult extends ArrayList<GroupTask> {
|
||||
|
||||
final int mId;
|
||||
final int mRequestId;
|
||||
|
||||
// If the result was loaded with keysOnly = true
|
||||
final boolean mKeysOnly;
|
||||
|
||||
TaskLoadResult(int id, boolean keysOnly, int size) {
|
||||
TaskLoadResult(int requestId, boolean keysOnly, int size) {
|
||||
super(size);
|
||||
mId = id;
|
||||
mRequestId = requestId;
|
||||
mKeysOnly = keysOnly;
|
||||
}
|
||||
|
||||
boolean isValidForRequest(int requestId, boolean loadKeysOnly) {
|
||||
return mId == requestId && (!mKeysOnly || loadKeysOnly);
|
||||
return mRequestId == requestId && (!mKeysOnly || loadKeysOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.launcher3.icons.IconProvider.IconChangeListener;
|
||||
import com.android.launcher3.util.Executors.SimpleThreadFactory;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.systemui.shared.recents.model.GroupTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -70,7 +71,7 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
|
||||
private RecentsModel(Context context) {
|
||||
mContext = context;
|
||||
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
|
||||
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
|
||||
new KeyguardManagerCompat(context), SystemUiProxy.INSTANCE.get(context));
|
||||
|
||||
IconProvider iconProvider = new IconProvider(context);
|
||||
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
|
||||
@@ -95,7 +96,7 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
|
||||
* always called on the UI thread.
|
||||
* @return the request id associated with this call.
|
||||
*/
|
||||
public int getTasks(Consumer<ArrayList<Task>> callback) {
|
||||
public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
|
||||
return mTaskList.getTasks(false /* loadKeysOnly */, callback);
|
||||
}
|
||||
|
||||
@@ -120,9 +121,9 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
|
||||
* @param callback Receives true if task is removed, false otherwise
|
||||
*/
|
||||
public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
|
||||
mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
|
||||
for (Task task : tasks) {
|
||||
if (task.key.id == taskId) {
|
||||
mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
|
||||
for (GroupTask group : taskGroups) {
|
||||
if (group.containsTask(taskId)) {
|
||||
callback.accept(false);
|
||||
return;
|
||||
}
|
||||
@@ -148,14 +149,15 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
|
||||
ActivityManager.RunningTaskInfo runningTask =
|
||||
ActivityManagerWrapper.getInstance().getRunningTask();
|
||||
int runningTaskId = runningTask != null ? runningTask.id : -1;
|
||||
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
|
||||
for (Task task : tasks) {
|
||||
if (task.key.id == runningTaskId) {
|
||||
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
|
||||
for (GroupTask group : taskGroups) {
|
||||
if (group.containsTask(runningTaskId)) {
|
||||
// Skip the running task, it's not going to have an up-to-date snapshot by the
|
||||
// time the user next enters overview
|
||||
continue;
|
||||
}
|
||||
mThumbnailCache.updateThumbnailInCache(task);
|
||||
mThumbnailCache.updateThumbnailInCache(group.task1);
|
||||
mThumbnailCache.updateThumbnailInCache(group.task2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
@@ -47,6 +49,9 @@ import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionContro
|
||||
import com.android.wm.shell.onehanded.IOneHanded;
|
||||
import com.android.wm.shell.pip.IPip;
|
||||
import com.android.wm.shell.pip.IPipAnimationListener;
|
||||
import com.android.wm.shell.recents.IRecentTasks;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreen;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||
import com.android.wm.shell.startingsurface.IStartingWindow;
|
||||
@@ -54,6 +59,7 @@ import com.android.wm.shell.startingsurface.IStartingWindowListener;
|
||||
import com.android.wm.shell.transition.IShellTransitions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Holds the reference to SystemUI.
|
||||
@@ -72,6 +78,7 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
private IOneHanded mOneHanded;
|
||||
private IShellTransitions mShellTransitions;
|
||||
private IStartingWindow mStartingWindow;
|
||||
private IRecentTasks mRecentTasks;
|
||||
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
|
||||
MAIN_EXECUTOR.execute(() -> clearProxy());
|
||||
};
|
||||
@@ -82,6 +89,7 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
private ISplitScreenListener mPendingSplitScreenListener;
|
||||
private IStartingWindowListener mPendingStartingWindowListener;
|
||||
private ISmartspaceCallback mPendingSmartspaceCallback;
|
||||
private IRecentTasksListener mPendingRecentTasksListener;
|
||||
private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
|
||||
|
||||
// Used to dedupe calls to SystemUI
|
||||
@@ -135,7 +143,7 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
|
||||
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
|
||||
IOneHanded oneHanded, IShellTransitions shellTransitions,
|
||||
IStartingWindow startingWindow,
|
||||
IStartingWindow startingWindow, IRecentTasks recentTasks,
|
||||
ISmartspaceTransitionController smartSpaceTransitionController) {
|
||||
unlinkToDeath();
|
||||
mSystemUiProxy = proxy;
|
||||
@@ -145,6 +153,7 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
mShellTransitions = shellTransitions;
|
||||
mStartingWindow = startingWindow;
|
||||
mSmartspaceTransitionController = smartSpaceTransitionController;
|
||||
mRecentTasks = recentTasks;
|
||||
linkToDeath();
|
||||
// re-attach the listeners once missing due to setProxy has not been initialized yet.
|
||||
if (mPendingPipAnimationListener != null && mPip != null) {
|
||||
@@ -167,6 +176,10 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
registerRemoteTransition(mPendingRemoteTransitions.get(i));
|
||||
}
|
||||
mPendingRemoteTransitions.clear();
|
||||
if (mPendingRecentTasksListener != null && mRecentTasks != null) {
|
||||
registerRecentTasksListener(mPendingRecentTasksListener);
|
||||
mPendingRecentTasksListener = null;
|
||||
}
|
||||
|
||||
if (mPendingSetNavButtonAlpha != null) {
|
||||
mPendingSetNavButtonAlpha.run();
|
||||
@@ -175,7 +188,7 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
}
|
||||
|
||||
public void clearProxy() {
|
||||
setProxy(null, null, null, null, null, null, null);
|
||||
setProxy(null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
// TODO(141886704): Find a way to remove this
|
||||
@@ -745,7 +758,6 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// SmartSpace transitions
|
||||
//
|
||||
@@ -761,4 +773,43 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
mPendingSmartspaceCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Recents
|
||||
//
|
||||
|
||||
public void registerRecentTasksListener(IRecentTasksListener listener) {
|
||||
if (mRecentTasks != null) {
|
||||
try {
|
||||
mRecentTasks.registerRecentTasksListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call registerRecentTasksListener", e);
|
||||
}
|
||||
} else {
|
||||
mPendingRecentTasksListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterRecentTasksListener(IRecentTasksListener listener) {
|
||||
if (mRecentTasks != null) {
|
||||
try {
|
||||
mRecentTasks.unregisterRecentTasksListener(listener);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call unregisterRecentTasksListener");
|
||||
}
|
||||
}
|
||||
mPendingRecentTasksListener = null;
|
||||
}
|
||||
|
||||
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
|
||||
if (mRecentTasks != null) {
|
||||
try {
|
||||
return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
|
||||
RECENT_IGNORE_UNAVAILABLE, userId)));
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call getRecentTasks", e);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,9 @@ public class TaskThumbnailCache {
|
||||
* Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
|
||||
*/
|
||||
public void updateThumbnailInCache(Task task) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
Preconditions.assertUIThread();
|
||||
// Fetch the thumbnail for this task and put it in the cache
|
||||
if (task.thumbnail == null) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TI
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.quickstep.GestureState.DEFAULT_STATE;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
|
||||
@@ -112,6 +113,7 @@ import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionContro
|
||||
import com.android.systemui.shared.tracing.ProtoTraceable;
|
||||
import com.android.wm.shell.onehanded.IOneHanded;
|
||||
import com.android.wm.shell.pip.IPip;
|
||||
import com.android.wm.shell.recents.IRecentTasks;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreen;
|
||||
import com.android.wm.shell.startingsurface.IStartingWindow;
|
||||
import com.android.wm.shell.transition.IShellTransitions;
|
||||
@@ -170,9 +172,11 @@ public class TouchInteractionService extends Service
|
||||
ISmartspaceTransitionController smartspaceTransitionController =
|
||||
ISmartspaceTransitionController.Stub.asInterface(
|
||||
bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
|
||||
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
|
||||
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
|
||||
splitscreen, onehanded, shellTransitions, startingWindow,
|
||||
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
|
||||
smartspaceTransitionController);
|
||||
TouchInteractionService.this.initInputMonitor();
|
||||
preloadOverview(true /* fromInit */);
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.GroupTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
|
||||
@@ -153,29 +154,31 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyLoadPlan(ArrayList<Task> tasks) {
|
||||
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
|
||||
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
|
||||
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
|
||||
// track the index of the next task appropriately, as if we are switching on any other app.
|
||||
// TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
|
||||
int runningTaskId = getTaskIdsForRunningTaskView()[0];
|
||||
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !tasks.isEmpty()) {
|
||||
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !taskGroups.isEmpty()) {
|
||||
// Check if the task list has running task
|
||||
boolean found = false;
|
||||
for (Task t : tasks) {
|
||||
if (t.key.id == runningTaskId) {
|
||||
for (GroupTask group : taskGroups) {
|
||||
if (group.containsTask(runningTaskId)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ArrayList<Task> newList = new ArrayList<>(tasks.size() + 1);
|
||||
newList.addAll(tasks);
|
||||
newList.add(Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false));
|
||||
tasks = newList;
|
||||
ArrayList<GroupTask> newList = new ArrayList<>(taskGroups.size() + 1);
|
||||
newList.addAll(taskGroups);
|
||||
newList.add(new GroupTask(
|
||||
Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false),
|
||||
null));
|
||||
taskGroups = newList;
|
||||
}
|
||||
}
|
||||
super.applyLoadPlan(tasks);
|
||||
super.applyLoadPlan(taskGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -167,6 +167,7 @@ import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.VibratorWrapper;
|
||||
import com.android.systemui.plugins.ResourceProvider;
|
||||
import com.android.systemui.shared.recents.model.GroupTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
@@ -1296,13 +1297,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
updateGridProperties();
|
||||
}
|
||||
|
||||
protected void applyLoadPlan(ArrayList<Task> tasks) {
|
||||
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
|
||||
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
if (taskGroups == null || taskGroups.isEmpty()) {
|
||||
removeTasksViewsAndClearAllButton();
|
||||
onTaskStackUpdated();
|
||||
return;
|
||||
@@ -1324,10 +1325,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
LauncherSplitScreenListener.INSTANCE.getNoCreate().getPersistentSplitIds();
|
||||
int requiredGroupTaskViews = splitTaskIds.length / 2;
|
||||
|
||||
// TODO(b/202740477): Update once grouped tasks are returned
|
||||
// Subtract half the number of split tasks and not total number because we've already
|
||||
// added a GroupedTaskView when swipe up gesture happens.
|
||||
// This will need to change if we start showing GroupedTaskViews during swipe up from home
|
||||
int requiredTaskViewCount = tasks.size() - requiredGroupTaskViews;
|
||||
int requiredTaskViewCount = taskGroups.size() - requiredGroupTaskViews;
|
||||
|
||||
if (getTaskViewCount() != requiredTaskViewCount) {
|
||||
if (indexOfChild(mClearAllButton) != -1) {
|
||||
@@ -1360,11 +1362,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
+ " runningTaskViewId: " + mRunningTaskViewId
|
||||
+ " forTaskView: " + getTaskViewFromTaskViewId(mRunningTaskViewId));
|
||||
|
||||
for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = tasks.size() - 1;
|
||||
for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = taskGroups.size() - 1;
|
||||
taskViewIndex >= 0;
|
||||
taskViewIndex--, taskDataIndex--) {
|
||||
final int pageIndex = requiredTaskViewCount - taskViewIndex - 1;
|
||||
final Task task = tasks.get(taskDataIndex);
|
||||
// TODO(b/202740477): Temporary assumption, to be updated once groups are actually used
|
||||
final Task task = taskGroups.get(taskDataIndex).task1;
|
||||
final TaskView taskView = (TaskView) getChildAt(pageIndex);
|
||||
if (taskView instanceof GroupedTaskView) {
|
||||
Task leftTop;
|
||||
@@ -1372,11 +1375,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
if (task.key.id == splitTaskIds[0]) {
|
||||
leftTop = task;
|
||||
taskDataIndex--;
|
||||
rightBottom = tasks.get(taskDataIndex);
|
||||
rightBottom = taskGroups.get(taskDataIndex).task1;
|
||||
} else {
|
||||
rightBottom = task;
|
||||
taskDataIndex--;
|
||||
leftTop = tasks.get(taskDataIndex);
|
||||
leftTop = taskGroups.get(taskDataIndex).task1;
|
||||
}
|
||||
((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState,
|
||||
mSplitBoundsConfig);
|
||||
|
||||
@@ -30,70 +30,78 @@ import android.app.ActivityManager;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.systemui.shared.recents.model.GroupTask;
|
||||
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.wm.shell.util.GroupedRecentTaskInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SmallTest
|
||||
public class RecentTasksListTest {
|
||||
|
||||
private ActivityManagerWrapper mockActivityManagerWrapper;
|
||||
@Mock
|
||||
private SystemUiProxy mockSystemUiProxy;
|
||||
|
||||
// Class under test
|
||||
private RecentTasksList mRecentTasksList;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
|
||||
KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
|
||||
mockActivityManagerWrapper = mock(ActivityManagerWrapper.class);
|
||||
mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
|
||||
mockActivityManagerWrapper);
|
||||
mockSystemUiProxy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTaskRemoved_doesNotFetchTasks() {
|
||||
mRecentTasksList.onTaskRemoved(0);
|
||||
verify(mockActivityManagerWrapper, times(0))
|
||||
.getRecentTasks(anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTaskStackChanged_doesNotFetchTasks() {
|
||||
mRecentTasksList.onTaskStackChanged();
|
||||
verify(mockActivityManagerWrapper, times(0))
|
||||
public void onRecentTasksChanged_doesNotFetchTasks() {
|
||||
mRecentTasksList.onRecentTasksChanged();
|
||||
verify(mockSystemUiProxy, times(0))
|
||||
.getRecentTasks(anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
|
||||
ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(recentTaskInfo));
|
||||
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
|
||||
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo());
|
||||
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true);
|
||||
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
|
||||
true);
|
||||
|
||||
assertEquals(1, taskList.size());
|
||||
assertNull(taskList.get(0).taskDescription.getLabel());
|
||||
assertNull(taskList.get(0).task1.taskDescription.getLabel());
|
||||
assertNull(taskList.get(0).task2.taskDescription.getLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() {
|
||||
String taskDescription = "Wheeee!";
|
||||
ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||
recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription);
|
||||
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(recentTaskInfo));
|
||||
ActivityManager.RecentTaskInfo task1 = new ActivityManager.RecentTaskInfo();
|
||||
task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
|
||||
ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
|
||||
task2.taskDescription = new ActivityManager.TaskDescription();
|
||||
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
|
||||
task1, task2);
|
||||
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
|
||||
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false);
|
||||
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
|
||||
false);
|
||||
|
||||
assertEquals(1, taskList.size());
|
||||
assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
|
||||
assertEquals(taskDescription, taskList.get(0).task1.taskDescription.getLabel());
|
||||
assertNull(taskList.get(0).task2.taskDescription.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user