blob: 0b3c0e89457fec87044a77388ec88b5f8930bfbc [file]
/*
* 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.app.PendingIntent
import android.content.ComponentName
import android.content.Context
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
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.Mockito.`when`
import org.mockito.MockitoAnnotations
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
@Mock lateinit var pendingIntent: PendingIntent
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)
splitSelectStateController =
SplitSelectStateController(
context,
handler,
stateManager,
depthController,
statsLogManager,
systemUiProxy,
recentsModel
)
}
@Test
fun activeTasks_noMatchingTasks() {
val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle)
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(
nonMatchingComponent,
taskConsumer
)
verify(recentsModel).getTasks(capture())
}
// Send our mocked tasks
consumer.accept(tasks)
}
@Test
fun activeTasks_singleMatchingTask() {
val matchingPackage = "hotdog"
val matchingClass = "juice"
val matchingComponent =
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
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(
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())
}
// Send our mocked tasks
consumer.accept(tasks)
}
@Test
fun activeTasks_multipleMatchMostRecentTask() {
val matchingPackage = "hotdog"
val matchingClass = "juice"
val matchingComponent =
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
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(
matchingComponent,
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)
}
@Test
fun secondPendingIntentSet() {
val itemInfo = ItemInfo()
`when`(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle)
splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1)
splitSelectStateController.setSecondTask(pendingIntent)
assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
}
// Generate GroupTask with default userId.
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)
)
}
// 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)
)
}
}