diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 3080f040ac..79dd633b21 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -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> callback) { + public void getTaskKeys(int numTasks, Consumer> callback) { // Kick off task loading in the background UI_HELPER_EXECUTOR.execute(() -> { - ArrayList tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */); + ArrayList 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> callback) { + public synchronized int getTasks(boolean loadKeysOnly, + Consumer> 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 result = copyOf(mResultsUi); + ArrayList 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 result = copyOf(mResultsUi); + ArrayList 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 rawTasks = - mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId); + ArrayList 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 copyOf(ArrayList tasks) { - ArrayList newTasks = new ArrayList<>(); + private ArrayList copyOf(ArrayList tasks) { + ArrayList 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 { + private static class TaskLoadResult extends ArrayList { - 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); } } } \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index 1e82c8cbfa..71153c64fa 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -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> callback) { + public int getTasks(Consumer> 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 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); } }); } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index d9319a9b00..9cd206a48f 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -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 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 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<>(); + } } diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index a8a0219594..eaa43cfd9c 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -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) { diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index ecc4b2b07d..a0b8abdd06 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -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 */); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 7e8b83e0c9..48315d0c8f 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -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 tasks) { + protected void applyLoadPlan(ArrayList 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 newList = new ArrayList<>(tasks.size() + 1); - newList.addAll(tasks); - newList.add(Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false)); - tasks = newList; + ArrayList 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 diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 1a3bfa9958..48f8619e77 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -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 tasks) { + protected void applyLoadPlan(ArrayList 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= 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(Collections.singletonList(recentTaskInfos))); - List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true); + List 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 taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false); + List 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()); } }