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:
Winson Chung
2021-10-16 22:49:07 -07:00
parent c29f5441da
commit f097e628dc
8 changed files with 178 additions and 119 deletions
@@ -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());
}
}