Refactor SplitSelectStateController to add testing

Bug: 266482558
Change-Id: I2d62a3a310043710d66a26bc533df2309084b81e
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3dbe6c8..2251c34 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -235,7 +235,8 @@
         RecentsView overviewPanel = getOverviewPanel();
         mSplitSelectStateController =
                 new SplitSelectStateController(this, mHandler, getStateManager(),
-                        getDepthController(), getStatsLogManager());
+                        getDepthController(), getStatsLogManager(),
+                        SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this));
         overviewPanel.init(mActionsView, mSplitSelectStateController);
         mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
                 mSplitSelectStateController);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index f26189c..3f8da56 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -134,7 +134,8 @@
 
         SplitSelectStateController controller =
                 new SplitSelectStateController(this, mHandler, getStateManager(),
-                        /* depthController */ null, getStatsLogManager());
+                         null /* depthController */, getStatsLogManager(),
+                        SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this));
         mDragLayer.recreateControllers();
         mFallbackRecentsView.init(mActionsView, controller);
 
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 51afb5b..d1100c7 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -107,14 +107,15 @@
     private FloatingTaskView mFirstFloatingTaskView;
 
     public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
-            DepthController depthController, StatsLogManager statsLogManager) {
+            DepthController depthController, StatsLogManager statsLogManager,
+            SystemUiProxy systemUiProxy, RecentsModel recentsModel) {
         mContext = context;
         mHandler = handler;
         mStatsLogManager = statsLogManager;
-        mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
+        mSystemUiProxy = systemUiProxy;
         mStateManager = stateManager;
         mDepthController = depthController;
-        mRecentTasksModel = RecentsModel.INSTANCE.get(context);
+        mRecentTasksModel = recentsModel;
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
new file mode 100644
index 0000000..9db0368
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -0,0 +1,212 @@
+/*
+ *  Copyright (C) 2023 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package com.android.quickstep.util
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Handler
+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.SplitConfigurationOptions
+import com.android.launcher3.util.withArgCaptor
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.SystemUiProxy
+import com.android.systemui.shared.recents.model.Task
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.ArrayList
+import java.util.function.Consumer
+
+
+@RunWith(AndroidJUnit4::class)
+class SplitSelectStateControllerTest {
+
+    @Mock lateinit var systemUiProxy: SystemUiProxy
+    @Mock lateinit var depthController: DepthController
+    @Mock lateinit var statsLogManager: StatsLogManager
+    @Mock lateinit var stateManager: StateManager<LauncherState>
+    @Mock lateinit var handler: Handler
+    @Mock lateinit var context: Context
+    @Mock lateinit var recentsModel: RecentsModel
+
+    lateinit var splitSelectStateController: SplitSelectStateController
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        splitSelectStateController = SplitSelectStateController(context, handler,
+                stateManager, depthController, statsLogManager, systemUiProxy, recentsModel)
+    }
+
+    @Test
+    fun activeTasks_noMatchingTasks() {
+        val groupTask1 = generateGroupTask(
+                ComponentName("pomegranate", "juice"),
+                ComponentName("pumpkin", "pie"))
+        val groupTask2 = generateGroupTask(
+                ComponentName("hotdog", "juice"),
+                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(
+                    ComponentName("no", "match"), taskConsumer)
+            verify(recentsModel).getTasks(capture())
+        }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_singleMatchingTask() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        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> {
+            assertEquals("ComponentName package mismatched",
+                    it.key.baseIntent.component.packageName, matchingPackage)
+            assertEquals("ComponentName class mismatched",
+                    it.key.baseIntent.component.className, matchingClass)
+            assertEquals(it, groupTask1.task1)
+        }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+            splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    ComponentName(matchingPackage, matchingClass), taskConsumer)
+            verify(recentsModel).getTasks(capture())
+        }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_multipleMatchMostRecentTask() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        val groupTask1 = generateGroupTask(
+                ComponentName(matchingPackage, matchingClass),
+                ComponentName("pumpkin", "pie"))
+        val groupTask2 = generateGroupTask(
+                ComponentName("pomegranate", "juice"),
+                ComponentName(matchingPackage, matchingClass))
+        val tasks: ArrayList<GroupTask> = ArrayList()
+        tasks.add(groupTask2)
+        tasks.add(groupTask1)
+
+        // 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(it, groupTask2.task2)
+        }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+            splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    ComponentName(matchingPackage, matchingClass), taskConsumer)
+            verify(recentsModel).getTasks(capture())
+        }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun setInitialApp_withTaskId() {
+        splitSelectStateController.setInitialTaskSelect(null /*intent*/,
+                -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/, 10 /*alreadyRunningTask*/)
+        assertTrue(splitSelectStateController.isSplitSelectActive)
+    }
+
+    @Test
+    fun setInitialApp_withIntent() {
+        splitSelectStateController.setInitialTaskSelect(Intent() /*intent*/,
+                -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/, -1 /*alreadyRunningTask*/)
+        assertTrue(splitSelectStateController.isSplitSelectActive)
+    }
+
+    @Test
+    fun resetAfterInitial() {
+        splitSelectStateController.setInitialTaskSelect(Intent() /*intent*/,
+                -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/,
+                -1)
+        splitSelectStateController.resetState()
+        assertFalse(splitSelectStateController.isSplitSelectActive)
+    }
+
+    private fun generateGroupTask(task1ComponentName: ComponentName,
+                                  task2ComponentName: ComponentName): GroupTask {
+        val task1 = Task()
+        var taskInfo = ActivityManager.RunningTaskInfo()
+        var intent = Intent()
+        intent.component = task1ComponentName
+        taskInfo.baseIntent = intent
+        task1.key = Task.TaskKey(taskInfo)
+
+        val task2 = Task()
+        taskInfo = ActivityManager.RunningTaskInfo()
+        intent = Intent()
+        intent.component = task2ComponentName
+        taskInfo.baseIntent = intent
+        task2.key = Task.TaskKey(taskInfo)
+        return GroupTask(task1, task2, SplitConfigurationOptions.SplitBounds(
+                Rect(), Rect(), -1, -1
+        ))
+    }
+}
\ No newline at end of file