Merge "Call into shell for recent tasks" into sc-v2-dev
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 3080f04..79dd633 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -22,35 +22,33 @@
 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 @@
     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 @@
     /**
      * 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 @@
      * @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 @@
                 mLoadingTasksInBackground = false;
                 mResultsUi = loadResult;
                 if (callback != null) {
-                    ArrayList<Task> result = copyOf(mResultsUi);
+                    ArrayList<GroupTask> result = copyOf(mResultsUi);
                     callback.accept(result);
                 }
             });
@@ -136,35 +141,7 @@
         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 @@
     @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 @@
         };
 
         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);
         }
     }
 }
\ 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 1e82c8c..71153c6 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.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 @@
     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 @@
      *                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 @@
      * @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 @@
         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 541af76..a875b69 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;
@@ -48,6 +50,9 @@
 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;
@@ -55,6 +60,7 @@
 import com.android.wm.shell.transition.IShellTransitions;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Holds the reference to SystemUI.
@@ -73,6 +79,7 @@
     private IOneHanded mOneHanded;
     private IShellTransitions mShellTransitions;
     private IStartingWindow mStartingWindow;
+    private IRecentTasks mRecentTasks;
     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
         MAIN_EXECUTOR.execute(() -> clearProxy());
     };
@@ -83,6 +90,7 @@
     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
@@ -147,7 +155,7 @@
 
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
             IOneHanded oneHanded, IShellTransitions shellTransitions,
-            IStartingWindow startingWindow,
+            IStartingWindow startingWindow, IRecentTasks recentTasks,
             ISmartspaceTransitionController smartSpaceTransitionController) {
         unlinkToDeath();
         mSystemUiProxy = proxy;
@@ -157,6 +165,7 @@
         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) {
@@ -179,6 +188,10 @@
             registerRemoteTransition(mPendingRemoteTransitions.get(i));
         }
         mPendingRemoteTransitions.clear();
+        if (mPendingRecentTasksListener != null && mRecentTasks != null) {
+            registerRecentTasksListener(mPendingRecentTasksListener);
+            mPendingRecentTasksListener = null;
+        }
 
         if (mPendingSetNavButtonAlpha != null) {
             mPendingSetNavButtonAlpha.run();
@@ -187,7 +200,7 @@
     }
 
     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
@@ -759,7 +772,6 @@
         }
     }
 
-
     //
     // SmartSpace transitions
     //
@@ -775,4 +787,43 @@
             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<>();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index a8a0219..eaa43cf 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -104,6 +104,9 @@
      * 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 b56c1b7..c037ac4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -26,6 +26,7 @@
 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;
@@ -113,6 +114,7 @@
 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;
@@ -171,9 +173,11 @@
             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 7e8b83e..48315d0 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.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 @@
     }
 
     @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
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 96c5561..b5238c6 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.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 @@
         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 @@
                 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 @@
                 + " 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 @@
                 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);
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 79ddf7a..6b2d5ed 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -30,70 +30,78 @@
 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());
     }
 }