Merge "Preload expensive objects before user is unlocked." into tm-qpr-dev
diff --git a/OWNERS b/OWNERS
index 962d63a..dd9fbf9 100644
--- a/OWNERS
+++ b/OWNERS
@@ -36,4 +36,4 @@
 xuqiu@google.com
 
 per-file FeatureFlags.java, globs = set noparent
-per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
+per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index ab52adb..dde7d94 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -88,6 +88,7 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -105,7 +106,6 @@
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
-import java.util.function.Consumer;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -877,8 +877,9 @@
      * (potentially breaking a split pair).
      */
     private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) {
+        ComponentKey componentToBeLaunched = new ComponentKey(info.getTargetComponent(), info.user);
         recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                info.getTargetComponent(),
+                componentToBeLaunched,
                 foundTask -> {
                     if (foundTask != null) {
                         TaskView foundTaskView =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 3479255..b552e9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -30,16 +30,15 @@
 
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
-import com.android.systemui.shared.recents.model.Task;
 
 import java.io.PrintWriter;
-import java.util.function.Consumer;
 
 /**
  * Base class for providing different taskbar UI
@@ -189,8 +188,12 @@
         if (recentsView == null) {
             return;
         }
+
+        ComponentKey componentToBeStaged = new ComponentKey(
+                splitSelectSource.itemInfo.getTargetComponent(),
+                splitSelectSource.itemInfo.user);
         recentsView.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                splitSelectSource.intent.getComponent(),
+                componentToBeStaged,
                 foundTask -> {
                     splitSelectSource.alreadyRunningTaskId = foundTask == null
                             ? INVALID_TASK_ID
@@ -206,8 +209,9 @@
      */
     public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
         RecentsView recents = getRecentsView();
+        ComponentKey secondAppComponent = new ComponentKey(info.getTargetComponent(), info.user);
         recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                info.getTargetComponent(),
+                secondAppComponent,
                 foundTask -> {
                     if (foundTask != null) {
                         TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 951a9b6..93baf5b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -136,6 +136,7 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.NavigationMode;
@@ -574,10 +575,13 @@
     @Override
     public void startSplitSelection(SplitSelectSource splitSelectSource) {
         RecentsView recentsView = getOverviewPanel();
+        ComponentKey componentToBeStaged = new ComponentKey(
+                splitSelectSource.itemInfo.getTargetComponent(),
+                splitSelectSource.itemInfo.user);
         // Check if there is already an instance of this app running, if so, initiate the split
         // using that.
         mSplitSelectStateController.findLastActiveTaskAndRunCallback(
-                splitSelectSource.intent.getComponent(),
+                componentToBeStaged,
                 foundTask -> {
                     splitSelectSource.alreadyRunningTaskId = foundTask == null
                             ? INVALID_TASK_ID
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d1100c7..5214f7c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -29,7 +29,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -58,6 +57,7 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.RecentsModel;
@@ -162,7 +162,7 @@
      * Used in various task-switching or splitscreen operations when we need to check if there is a
      * currently running Task of a certain type and use the most recent one.
      */
-    public void findLastActiveTaskAndRunCallback(ComponentName componentName,
+    public void findLastActiveTaskAndRunCallback(ComponentKey componentKey,
             Consumer<Task> callback) {
         mRecentTasksModel.getTasks(taskGroups -> {
             Task lastActiveTask = null;
@@ -170,12 +170,12 @@
             for (int i = taskGroups.size() - 1; i >= 0; i--) {
                 GroupTask groupTask = taskGroups.get(i);
                 Task task1 = groupTask.task1;
-                if (isInstanceOfComponent(task1, componentName)) {
+                if (isInstanceOfComponent(task1, componentKey)) {
                     lastActiveTask = task1;
                     break;
                 }
                 Task task2 = groupTask.task2;
-                if (isInstanceOfComponent(task2, componentName)) {
+                if (isInstanceOfComponent(task2, componentKey)) {
                     lastActiveTask = task2;
                     break;
                 }
@@ -189,13 +189,14 @@
      * Checks if a given Task is the most recently-active Task of type componentName. Used for
      * selecting already-running Tasks for splitscreen.
      */
-    public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
+    public boolean isInstanceOfComponent(@Nullable Task task, ComponentKey componentKey) {
         // Exclude the task that is already staged
         if (task == null || task.key.id == mInitialTaskId) {
             return false;
         }
 
-        return task.key.baseIntent.getComponent().equals(componentName);
+        return task.key.baseIntent.getComponent().equals(componentKey.componentName)
+                && task.key.userId == componentKey.user.getIdentifier();
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 5abdc93..512df8e 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -23,12 +23,14 @@
 import android.content.Intent
 import android.graphics.Rect
 import android.os.Handler
+import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.launcher3.LauncherState
 import com.android.launcher3.logging.StatsLogManager
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.statehandlers.DepthController
 import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.launcher3.util.withArgCaptor
 import com.android.quickstep.RecentsModel
@@ -60,6 +62,9 @@
 
     lateinit var splitSelectStateController: SplitSelectStateController
 
+    private val primaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId)
+    private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10)
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -77,6 +82,7 @@
 
     @Test
     fun activeTasks_noMatchingTasks() {
+        val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName("pomegranate", "juice"),
@@ -100,7 +106,7 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName("no", "match"),
+                    nonMatchingComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -114,6 +120,8 @@
     fun activeTasks_singleMatchingTask() {
         val matchingPackage = "hotdog"
         val matchingClass = "juice"
+        val matchingComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName(matchingPackage, matchingClass),
@@ -149,7 +157,100 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName(matchingPackage, matchingClass),
+                    matchingComponent,
+                    taskConsumer
+                )
+                verify(recentsModel).getTasks(capture())
+            }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_skipTaskWithDifferentUser() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        val nonPrimaryUserComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
+        val groupTask1 =
+            generateGroupTask(
+                ComponentName(matchingPackage, matchingClass),
+                ComponentName("pomegranate", "juice")
+            )
+        val groupTask2 =
+            generateGroupTask(
+                ComponentName("pumpkin", "pie"),
+                ComponentName("personal", "computer")
+            )
+        val tasks: ArrayList<GroupTask> = ArrayList()
+        tasks.add(groupTask1)
+        tasks.add(groupTask2)
+
+        // Assertions happen in the callback we get from what we pass into
+        // #findLastActiveTaskAndRunCallback
+        val taskConsumer =
+            Consumer<Task> { assertNull("No tasks should have matched", it /*task*/) }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer =
+            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+                splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    nonPrimaryUserComponent,
+                    taskConsumer
+                )
+                verify(recentsModel).getTasks(capture())
+            }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_findTaskAsNonPrimaryUser() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        val nonPrimaryUserComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
+        val groupTask1 =
+            generateGroupTask(
+                ComponentName(matchingPackage, matchingClass),
+                nonPrimaryUserHandle,
+                ComponentName("pomegranate", "juice"),
+                nonPrimaryUserHandle
+            )
+        val groupTask2 =
+            generateGroupTask(
+                ComponentName("pumpkin", "pie"),
+                ComponentName("personal", "computer")
+            )
+        val tasks: ArrayList<GroupTask> = ArrayList()
+        tasks.add(groupTask1)
+        tasks.add(groupTask2)
+
+        // Assertions happen in the callback we get from what we pass into
+        // #findLastActiveTaskAndRunCallback
+        val taskConsumer =
+            Consumer<Task> {
+                assertEquals(
+                    "ComponentName package mismatched",
+                    it.key.baseIntent.component.packageName,
+                    matchingPackage
+                )
+                assertEquals(
+                    "ComponentName class mismatched",
+                    it.key.baseIntent.component.className,
+                    matchingClass
+                )
+                assertEquals("userId mismatched", it.key.userId, nonPrimaryUserHandle.identifier)
+                assertEquals(it, groupTask1.task1)
+            }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer =
+            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+                splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    nonPrimaryUserComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -163,6 +264,8 @@
     fun activeTasks_multipleMatchMostRecentTask() {
         val matchingPackage = "hotdog"
         val matchingClass = "juice"
+        val matchingComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName(matchingPackage, matchingClass),
@@ -198,7 +301,7 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName(matchingPackage, matchingClass),
+                    matchingComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -245,6 +348,7 @@
         assertFalse(splitSelectStateController.isSplitSelectActive)
     }
 
+    // Generate GroupTask with default userId.
     private fun generateGroupTask(
         task1ComponentName: ComponentName,
         task2ComponentName: ComponentName
@@ -268,4 +372,34 @@
             SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
         )
     }
+
+    // Generate GroupTask with custom user handles.
+    private fun generateGroupTask(
+        task1ComponentName: ComponentName,
+        userHandle1: UserHandle,
+        task2ComponentName: ComponentName,
+        userHandle2: UserHandle
+    ): GroupTask {
+        val task1 = Task()
+        var taskInfo = ActivityManager.RunningTaskInfo()
+        // Apply custom userHandle1
+        taskInfo.userId = userHandle1.identifier
+        var intent = Intent()
+        intent.component = task1ComponentName
+        taskInfo.baseIntent = intent
+        task1.key = Task.TaskKey(taskInfo)
+        val task2 = Task()
+        taskInfo = ActivityManager.RunningTaskInfo()
+        // Apply custom userHandle2
+        taskInfo.userId = userHandle2.identifier
+        intent = Intent()
+        intent.component = task2ComponentName
+        taskInfo.baseIntent = intent
+        task2.key = Task.TaskKey(taskInfo)
+        return GroupTask(
+            task1,
+            task2,
+            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
+        )
+    }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index af24334..b1159cd 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -247,10 +247,6 @@
             270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
             "Enable option to launch search results using the new view container transitions");
 
-    public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = getReleaseFlag(270394225,
-            "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
-            "Use 2 rows of app predictions in All Apps search zero-state");
-
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
             270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
             "Enable option to show keyboard when going to all-apps");
@@ -354,10 +350,6 @@
             "Enable the ability to generate monochromatic icons, if it is not provided by the app"
     );
 
-    public static final BooleanFlag ENABLE_DREAM_TRANSITION = getDebugFlag(270396364,
-            "ENABLE_DREAM_TRANSITION", true,
-            "Enable the launcher transition when the device enters a dream");
-
     public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(270396268,
             "ENABLE_TASKBAR_EDU_TOOLTIP", true,
             "Enable the tooltip version of the Taskbar education flow.");
@@ -384,10 +376,6 @@
             "ENABLE_KEYBOARD_QUICK_SWITCH", false,
             "Enables keyboard quick switching");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_DEFAULT_DATE_REMOVED = getDebugFlag(269761613,
-            "ENABLE_SMARTSPACE_DEFAULT_DATE_REMOVED", false,
-            "Enables remove smartspace default date");
-
     public static class BooleanFlag {
 
         private final boolean mCurrentValue;