Merge "Make OverviewCommandHelper commands and OverviewCommandHelperTest display-aware" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
dee6dd8bb7
@@ -91,27 +91,33 @@ constructor(
|
||||
*/
|
||||
private var keyboardTaskFocusIndex = -1
|
||||
|
||||
// TODO (b/397942185): get per-display interface
|
||||
private val containerInterface: BaseContainerInterface<*, *>
|
||||
get() = overviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY)
|
||||
private fun getContainerInterface(displayId: Int) =
|
||||
overviewComponentObserver.getContainerInterface(displayId)
|
||||
|
||||
// TODO (b/397942185): get per-display RecentsView
|
||||
private val visibleRecentsView: RecentsView<*, *>?
|
||||
get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
|
||||
private fun getVisibleRecentsView(displayId: Int) =
|
||||
getContainerInterface(displayId).getVisibleRecentsView<RecentsView<*, *>>()
|
||||
|
||||
/**
|
||||
* Adds a command to be executed next, after all pending tasks are completed. Max commands that
|
||||
* can be queued is [.MAX_QUEUE_SIZE]. Requests after reaching that limit will be silently
|
||||
* dropped.
|
||||
*
|
||||
* @param type The type of the command
|
||||
* @param onDisplays The display to run the command on
|
||||
*/
|
||||
@BinderThread
|
||||
fun addCommand(type: CommandType): CommandInfo? {
|
||||
@JvmOverloads
|
||||
fun addCommand(
|
||||
type: CommandType,
|
||||
displayId: Int = DEFAULT_DISPLAY,
|
||||
isLastOfBatch: Boolean = true,
|
||||
): CommandInfo? {
|
||||
if (commandQueue.size >= MAX_QUEUE_SIZE) {
|
||||
Log.d(TAG, "command not added: $type - queue is full ($commandQueue).")
|
||||
return null
|
||||
}
|
||||
|
||||
val command = CommandInfo(type)
|
||||
val command = CommandInfo(type, displayId = displayId, isLastOfBatch = isLastOfBatch)
|
||||
commandQueue.add(command)
|
||||
Log.d(TAG, "command added: $command")
|
||||
|
||||
@@ -129,6 +135,35 @@ constructor(
|
||||
return command
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
fun addCommandsForDisplays(type: CommandType, displayIds: IntArray): CommandInfo? {
|
||||
if (displayIds.isEmpty()) return null
|
||||
var lastCommand: CommandInfo? = null
|
||||
displayIds.forEachIndexed({ i, displayId ->
|
||||
lastCommand = addCommand(type, displayId, i == displayIds.size - 1)
|
||||
})
|
||||
return lastCommand
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
fun addCommandsForAllDisplays(type: CommandType) =
|
||||
addCommandsForDisplays(
|
||||
type,
|
||||
recentsDisplayModel.activeDisplayResources
|
||||
.map { resource -> resource.displayId }
|
||||
.toIntArray(),
|
||||
)
|
||||
|
||||
@BinderThread
|
||||
fun addCommandsForDisplaysExcept(type: CommandType, excludedDisplayId: Int) =
|
||||
addCommandsForDisplays(
|
||||
type,
|
||||
recentsDisplayModel.activeDisplayResources
|
||||
.map { resource -> resource.displayId }
|
||||
.filter { displayId -> displayId != excludedDisplayId }
|
||||
.toIntArray(),
|
||||
)
|
||||
|
||||
fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME
|
||||
|
||||
/** Clear pending or completed commands from the queue */
|
||||
@@ -143,7 +178,7 @@ constructor(
|
||||
* completion (returns false).
|
||||
*/
|
||||
@UiThread
|
||||
private fun processNextCommand() =
|
||||
private fun processNextCommand(): Unit =
|
||||
traceSection("OverviewCommandHelper.processNextCommand") {
|
||||
val command: CommandInfo? = commandQueue.firstOrNull()
|
||||
if (command == null) {
|
||||
@@ -182,7 +217,7 @@ constructor(
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun executeCommand(command: CommandInfo, onCallbackResult: () -> Unit): Boolean {
|
||||
val recentsView = visibleRecentsView
|
||||
val recentsView = getVisibleRecentsView(command.displayId)
|
||||
Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView")
|
||||
return if (recentsView != null) {
|
||||
executeWhenRecentsIsVisible(command, recentsView, onCallbackResult)
|
||||
@@ -230,6 +265,7 @@ constructor(
|
||||
launchTask(recentsView, taskView, command, onCallbackResult)
|
||||
}
|
||||
}
|
||||
|
||||
TOGGLE -> {
|
||||
launchTask(
|
||||
recentsView,
|
||||
@@ -238,6 +274,7 @@ constructor(
|
||||
onCallbackResult,
|
||||
)
|
||||
}
|
||||
|
||||
HOME -> {
|
||||
recentsView.startHome()
|
||||
true
|
||||
@@ -294,6 +331,7 @@ constructor(
|
||||
command: CommandInfo,
|
||||
onCallbackResult: () -> Unit,
|
||||
): Boolean {
|
||||
val containerInterface = getContainerInterface(command.displayId)
|
||||
val recentsViewContainer = containerInterface.getCreatedContainer()
|
||||
val recentsView: RecentsView<*, *>? = recentsViewContainer?.getOverviewPanel()
|
||||
val deviceProfile = recentsViewContainer?.getDeviceProfile()
|
||||
@@ -335,6 +373,7 @@ constructor(
|
||||
|
||||
if (keyboardTaskFocusIndex == -1) return true
|
||||
}
|
||||
|
||||
KEYBOARD_INPUT ->
|
||||
if (uiController != null && deviceProfile?.isTablet == true) {
|
||||
if (
|
||||
@@ -348,6 +387,7 @@ constructor(
|
||||
} else {
|
||||
keyboardTaskFocusIndex = 0
|
||||
}
|
||||
|
||||
HOME -> {
|
||||
ActiveGestureProtoLogProxy.logExecuteHomeCommand()
|
||||
// Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
|
||||
@@ -357,12 +397,14 @@ constructor(
|
||||
touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
|
||||
return true
|
||||
}
|
||||
|
||||
SHOW ->
|
||||
// When Recents is not currently visible, the command's type is SHOW
|
||||
// when overview is triggered via the keyboard overview button or Action+Tab
|
||||
// keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
|
||||
// nav is TYPE_TOGGLE.
|
||||
keyboardTaskFocusIndex = 0
|
||||
|
||||
TOGGLE -> {}
|
||||
}
|
||||
|
||||
@@ -378,7 +420,7 @@ constructor(
|
||||
Log.d(TAG, "switching to Overview state - onAnimationStart: $command")
|
||||
super.onAnimationStart(animation)
|
||||
updateRecentsViewFocus(command)
|
||||
logShowOverviewFrom(command.type)
|
||||
logShowOverviewFrom(command)
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
@@ -402,7 +444,7 @@ constructor(
|
||||
|
||||
val gestureState =
|
||||
touchInteractionService.createGestureState(
|
||||
focusedDisplayId,
|
||||
command.displayId,
|
||||
GestureState.DEFAULT_STATE,
|
||||
GestureState.TrackpadGestureType.NONE,
|
||||
)
|
||||
@@ -432,7 +474,7 @@ constructor(
|
||||
}
|
||||
|
||||
updateRecentsViewFocus(command)
|
||||
logShowOverviewFrom(command.type)
|
||||
logShowOverviewFrom(command)
|
||||
containerInterface.runOnInitBackgroundStateUI {
|
||||
Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
|
||||
interactionHandler.onGestureEnded(
|
||||
@@ -456,12 +498,13 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val displayId = gestureState.displayId
|
||||
val taskAnimationManager =
|
||||
recentsDisplayModel.getTaskAnimationManager(displayId)
|
||||
recentsDisplayModel.getTaskAnimationManager(command.displayId)
|
||||
?: run {
|
||||
Log.e(TAG, "No TaskAnimationManager found for display $displayId")
|
||||
ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(displayId)
|
||||
Log.e(TAG, "No TaskAnimationManager found for display ${command.displayId}")
|
||||
ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(
|
||||
command.displayId
|
||||
)
|
||||
return false
|
||||
}
|
||||
if (taskAnimationManager.isRecentsAnimationRunning) {
|
||||
@@ -526,8 +569,13 @@ constructor(
|
||||
}
|
||||
|
||||
private fun updateRecentsViewFocus(command: CommandInfo) {
|
||||
val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
|
||||
if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) {
|
||||
val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
|
||||
if (
|
||||
command.type != KEYBOARD_INPUT &&
|
||||
command.type != HIDE &&
|
||||
command.type != SHOW &&
|
||||
command.type != TOGGLE
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -547,7 +595,7 @@ constructor(
|
||||
}
|
||||
|
||||
private fun onRecentsViewFocusUpdated(command: CommandInfo) {
|
||||
val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
|
||||
val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
|
||||
if (command.type != HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
|
||||
return
|
||||
}
|
||||
@@ -565,10 +613,11 @@ constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun logShowOverviewFrom(commandType: CommandType) {
|
||||
private fun logShowOverviewFrom(command: CommandInfo) {
|
||||
val containerInterface = getContainerInterface(command.displayId)
|
||||
val container = containerInterface.getCreatedContainer() ?: return
|
||||
val event =
|
||||
when (commandType) {
|
||||
when (command.type) {
|
||||
SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
|
||||
HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
|
||||
TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
|
||||
@@ -601,6 +650,8 @@ constructor(
|
||||
var status: CommandStatus = CommandStatus.IDLE,
|
||||
val createTime: Long = SystemClock.elapsedRealtime(),
|
||||
private var animationCallbacks: RecentsAnimationCallbacks? = null,
|
||||
val displayId: Int = DEFAULT_DISPLAY,
|
||||
val isLastOfBatch: Boolean = true,
|
||||
) {
|
||||
fun setAnimationCallbacks(recentsAnimationCallbacks: RecentsAnimationCallbacks) {
|
||||
this.animationCallbacks = recentsAnimationCallbacks
|
||||
|
||||
+80
-2
@@ -17,6 +17,7 @@
|
||||
package com.android.quickstep
|
||||
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.view.Display.DEFAULT_DISPLAY
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.Flags
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit
|
||||
@@ -25,6 +26,7 @@ import com.android.launcher3.util.rule.setFlags
|
||||
import com.android.quickstep.OverviewCommandHelper.CommandInfo
|
||||
import com.android.quickstep.OverviewCommandHelper.CommandInfo.CommandStatus
|
||||
import com.android.quickstep.OverviewCommandHelper.CommandType
|
||||
import com.android.quickstep.fallback.window.RecentsDisplayModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -41,9 +43,9 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@SmallTest
|
||||
@RunWith(LauncherMultivalentJUnit::class)
|
||||
@@ -57,18 +59,38 @@ class OverviewCommandHelperTest {
|
||||
|
||||
private var pendingCallbacksWithDelays = mutableListOf<Long>()
|
||||
|
||||
private val recentsDisplayModel: RecentsDisplayModel = mock()
|
||||
private val defaultDisplayResource: RecentsDisplayModel.RecentsDisplayResource = mock()
|
||||
private val secondaryDisplayResource: RecentsDisplayModel.RecentsDisplayResource = mock()
|
||||
private val executeCommandDisplayIds = mutableListOf<Int>()
|
||||
|
||||
private fun setupDefaultDisplay() {
|
||||
whenever(defaultDisplayResource.displayId).thenReturn(DEFAULT_DISPLAY)
|
||||
whenever(recentsDisplayModel.activeDisplayResources)
|
||||
.thenReturn(listOf(defaultDisplayResource))
|
||||
}
|
||||
|
||||
private fun setupMultipleDisplays() {
|
||||
whenever(defaultDisplayResource.displayId).thenReturn(DEFAULT_DISPLAY)
|
||||
whenever(secondaryDisplayResource.displayId).thenReturn(1)
|
||||
whenever(recentsDisplayModel.activeDisplayResources)
|
||||
.thenReturn(listOf(defaultDisplayResource, secondaryDisplayResource))
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Before
|
||||
fun setup() {
|
||||
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_OVERVIEW_COMMAND_HELPER_TIMEOUT)
|
||||
|
||||
setupDefaultDisplay()
|
||||
|
||||
sut =
|
||||
spy(
|
||||
OverviewCommandHelper(
|
||||
touchInteractionService = mock(),
|
||||
overviewComponentObserver = mock(),
|
||||
dispatcherProvider = TestDispatcherProvider(dispatcher),
|
||||
recentsDisplayModel = mock(),
|
||||
recentsDisplayModel = recentsDisplayModel,
|
||||
focusState = mock(),
|
||||
taskbarManager = mock(),
|
||||
)
|
||||
@@ -86,6 +108,8 @@ class OverviewCommandHelperTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
val commandInfo = invocation.arguments[0] as CommandInfo
|
||||
executeCommandDisplayIds.add(commandInfo.displayId)
|
||||
delayInMillis == null // if no callback to execute, returns success
|
||||
}
|
||||
.`when`(sut)
|
||||
@@ -175,7 +199,61 @@ class OverviewCommandHelperTest {
|
||||
assertThat(commandInfo2.status).isEqualTo(CommandStatus.COMPLETED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAllDisplaysCommandIsAdded_singleCommandProcessedForDefaultDisplay() =
|
||||
testScope.runTest {
|
||||
executeCommandDisplayIds.clear()
|
||||
// Add command to queue
|
||||
val commandInfo: CommandInfo = sut.addCommandsForAllDisplays(CommandType.HOME)!!
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
|
||||
runCurrent()
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
|
||||
assertThat(executeCommandDisplayIds).containsExactly(DEFAULT_DISPLAY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAllDisplaysCommandIsAdded_multipleCommandsProcessedForMultipleDisplays() =
|
||||
testScope.runTest {
|
||||
setupMultipleDisplays()
|
||||
executeCommandDisplayIds.clear()
|
||||
// Add command to queue
|
||||
val commandInfo: CommandInfo = sut.addCommandsForAllDisplays(CommandType.HOME)!!
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
|
||||
runCurrent()
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
|
||||
assertThat(executeCommandDisplayIds)
|
||||
.containsExactly(DEFAULT_DISPLAY, EXTERNAL_DISPLAY_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAllExceptDisplayCommandIsAdded_otherDisplayProcessed() =
|
||||
testScope.runTest {
|
||||
setupMultipleDisplays()
|
||||
executeCommandDisplayIds.clear()
|
||||
// Add command to queue
|
||||
val commandInfo: CommandInfo =
|
||||
sut.addCommandsForDisplaysExcept(CommandType.HOME, DEFAULT_DISPLAY)!!
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
|
||||
runCurrent()
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
|
||||
assertThat(executeCommandDisplayIds).containsExactly(EXTERNAL_DISPLAY_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenSingleDisplayCommandIsAdded_thatDisplayIsProcessed() =
|
||||
testScope.runTest {
|
||||
executeCommandDisplayIds.clear()
|
||||
val displayId = 5
|
||||
// Add command to queue
|
||||
val commandInfo: CommandInfo = sut.addCommand(CommandType.HOME, displayId)!!
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
|
||||
runCurrent()
|
||||
assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
|
||||
assertThat(executeCommandDisplayIds).containsExactly(displayId)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val QUEUE_TIMEOUT = 5001L
|
||||
const val EXTERNAL_DISPLAY_ID = 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user