Refactoring OverviewCommandHelper (1/3)
- Updates pendingCommands to be a ConcurrentLinkedDeque and make the list safe across multiple threads.
- Introduces CommandStatus to clear only IDLE and COMPLETED commands.
- Adds CommandType enum to prevent adding an invalid command value into the queue and having an unexpected behavior.
- Log messages improved
Bug: 352046797
Bug: 351122926
Flag: EXEMPT bugfix.
Test: Manual.
Change-Id: I80705dca0be579e62cb9e2bd923808dd33c4d633
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 43960a1..6b1173a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
-import static com.android.quickstep.OverviewCommandHelper.TYPE_HIDE;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
@@ -40,6 +39,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewCommandHelper.CommandType;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
@@ -394,7 +394,7 @@
if (overviewCommandHelper == null) {
return;
}
- overviewCommandHelper.addCommand(TYPE_HIDE);
+ overviewCommandHelper.addCommand(CommandType.HIDE);
}
/**
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index f6b9e4e..56153a9 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -33,6 +33,8 @@
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.*
import com.android.launcher3.util.Executors
import com.android.launcher3.util.RunnableList
+import com.android.quickstep.OverviewCommandHelper.CommandInfo.CommandStatus
+import com.android.quickstep.OverviewCommandHelper.CommandType.*
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -40,6 +42,7 @@
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import java.io.PrintWriter
+import java.util.concurrent.ConcurrentLinkedDeque
/** Helper class to handle various atomic commands for switching between Overview. */
class OverviewCommandHelper(
@@ -47,7 +50,7 @@
private val overviewComponentObserver: OverviewComponentObserver,
private val taskAnimationManager: TaskAnimationManager
) {
- private val pendingCommands = mutableListOf<CommandInfo>()
+ private val commandQueue = ConcurrentLinkedDeque<CommandInfo>()
/**
* Index of the TaskView that should be focused when launching Overview. Persisted so that we do
@@ -64,22 +67,26 @@
*/
private var waitForToggleCommandComplete = false
+ private val activityInterface: BaseActivityInterface<*, *>
+ get() = overviewComponentObserver.activityInterface
+
+ private val visibleRecentsView: RecentsView<*, *>?
+ get() = activityInterface.getVisibleRecentsView<RecentsView<*, *>>()
+
/** Called when the command finishes execution. */
- private fun scheduleNextTask(command: CommandInfo) {
- if (pendingCommands.isEmpty()) {
- Log.d(TAG, "no pending commands to schedule")
- return
- }
- if (pendingCommands.first() !== command) {
+ private fun onCommandFinished(command: CommandInfo) {
+ command.status = CommandStatus.COMPLETED
+ if (commandQueue.first() !== command) {
Log.d(
TAG,
"next task not scheduled. First pending command type " +
- "is ${pendingCommands.first()} - command type is: $command"
+ "is ${commandQueue.first()} - command type is: $command"
)
return
}
- Log.d(TAG, "scheduleNextTask called: $command")
- pendingCommands.removeFirst()
+
+ Log.d(TAG, "command executed successfully! $command")
+ commandQueue.remove(command)
executeNext()
}
@@ -90,24 +97,21 @@
*/
@UiThread
private fun executeNext() {
- if (pendingCommands.isEmpty()) {
- Log.d(TAG, "executeNext - pendingCommands is empty")
- return
- }
- val command = pendingCommands.first()
- val result = executeCommand(command)
- Log.d(TAG, "executeNext command type: $command, result: $result")
- if (result) {
- scheduleNextTask(command)
- }
- }
+ val command: CommandInfo =
+ commandQueue.firstOrNull()
+ ?: run {
+ Log.d(TAG, "no pending commands to be executed.")
+ return
+ }
- @UiThread
- private fun addCommand(command: CommandInfo) {
- val wasEmpty = pendingCommands.isEmpty()
- pendingCommands.add(command)
- if (wasEmpty) {
- executeNext()
+ Log.d(TAG, "executing command: $command")
+ val result = executeCommand(command)
+
+ Log.d(TAG, "command executed: $command with result: $result")
+ if (result) {
+ onCommandFinished(command)
+ } else {
+ Log.d(TAG, "waiting for command callback: $command")
}
}
@@ -117,29 +121,29 @@
* dropped.
*/
@BinderThread
- fun addCommand(type: Int) {
- if (pendingCommands.size >= MAX_QUEUE_SIZE) {
- Log.d(
- TAG,
- "the pending command queue is full (${pendingCommands.size}). command not added: $type"
- )
+ fun addCommand(type: CommandType) {
+ if (commandQueue.size >= MAX_QUEUE_SIZE) {
+ Log.d(TAG, "commands queue is full ($commandQueue). command not added: $type")
return
}
- Log.d(TAG, "adding command type: $type")
+
val command = CommandInfo(type)
- Executors.MAIN_EXECUTOR.execute { addCommand(command) }
+ commandQueue.add(command)
+ Log.d(TAG, "command added: $command")
+
+ if (commandQueue.size == 1) {
+ Executors.MAIN_EXECUTOR.execute { executeNext() }
+ }
}
- @UiThread
+ fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME
+
+ /** Clear commands from the queue */
fun clearPendingCommands() {
- Log.d(TAG, "clearing pending commands - size: ${pendingCommands.size}")
- pendingCommands.clear()
+ Log.d(TAG, "clearing pending commands: $commandQueue")
+ commandQueue.clear()
}
- @UiThread
- fun canStartHomeSafely(): Boolean =
- pendingCommands.isEmpty() || pendingCommands.first().type == TYPE_HOME
-
private fun getNextTask(view: RecentsView<*, *>): TaskView? {
val runningTaskView = view.runningTaskView
@@ -166,7 +170,7 @@
if (callbackList != null) {
callbackList.add {
Log.d(TAG, "launching task callback: $command")
- scheduleNextTask(command)
+ onCommandFinished(command)
waitForToggleCommandComplete = false
}
Log.d(TAG, "launching task - waiting for callback: $command")
@@ -183,21 +187,18 @@
* is deferred until [.scheduleNextTask] is called
*/
private fun executeCommand(command: CommandInfo): Boolean {
- if (waitForToggleCommandComplete && command.type == TYPE_TOGGLE) {
+ command.status = CommandStatus.PROCESSING
+
+ if (waitForToggleCommandComplete && command.type == TOGGLE) {
Log.d(TAG, "executeCommand: $command - waiting for toggle command complete")
return true
}
- val activityInterface: BaseActivityInterface<*, *> =
- overviewComponentObserver.activityInterface
- val visibleRecentsView: RecentsView<*, *>? =
- activityInterface.getVisibleRecentsView<RecentsView<*, *>>()
- val createdRecentsView: RecentsView<*, *>?
-
- Log.d(TAG, "executeCommand: $command - visibleRecentsView: $visibleRecentsView")
- if (visibleRecentsView == null) {
+ var recentsView = visibleRecentsView
+ Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView")
+ if (recentsView == null) {
val activity = activityInterface.getCreatedContainer() as? RecentsViewContainer
- createdRecentsView = activity?.getOverviewPanel()
+ recentsView = activity?.getOverviewPanel()
val deviceProfile = activity?.getDeviceProfile()
val uiController = activityInterface.getTaskbarController()
val allowQuickSwitch =
@@ -207,22 +208,20 @@
(deviceProfile.isTablet || deviceProfile.isTwoPanels)
when (command.type) {
- TYPE_HIDE -> {
+ HIDE -> {
if (!allowQuickSwitch) return true
keyboardTaskFocusIndex = uiController!!.launchFocusedTask()
if (keyboardTaskFocusIndex == -1) return true
}
- TYPE_KEYBOARD_INPUT ->
+ KEYBOARD_INPUT ->
if (allowQuickSwitch) {
uiController!!.openQuickSwitchView()
return true
} else {
keyboardTaskFocusIndex = 0
}
- TYPE_HOME -> {
- ActiveGestureLog.INSTANCE.addLog(
- "OverviewCommandHelper.executeCommand(TYPE_HOME)"
- )
+ HOME -> {
+ ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)")
// Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
// we should still call it on main thread because launcher is waiting for
// ActivityTaskManager to resume it. Also calling startActivity() on bg thread
@@ -230,38 +229,37 @@
touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
return true
}
- TYPE_SHOW ->
- // When Recents is not currently visible, the command's type is
- // TYPE_SHOW
+ 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
- else -> {}
+ TOGGLE -> {}
}
} else {
- createdRecentsView = visibleRecentsView
- when (command.type) {
- TYPE_SHOW -> return true // already visible
- TYPE_KEYBOARD_INPUT,
- TYPE_HIDE -> {
- if (visibleRecentsView.isHandlingTouch) return true
-
- keyboardTaskFocusIndex = PagedView.INVALID_PAGE
- val currentPage = visibleRecentsView.nextPage
- val taskView = visibleRecentsView.getTaskViewAt(currentPage)
- return launchTask(visibleRecentsView, taskView, command)
+ return when (command.type) {
+ SHOW -> true // already visible
+ KEYBOARD_INPUT,
+ HIDE -> {
+ if (recentsView.isHandlingTouch) {
+ true
+ } else {
+ keyboardTaskFocusIndex = PagedView.INVALID_PAGE
+ val currentPage = recentsView.nextPage
+ val taskView = recentsView.getTaskViewAt(currentPage)
+ launchTask(recentsView, taskView, command)
+ }
}
- TYPE_TOGGLE ->
- return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), command)
- TYPE_HOME -> {
- visibleRecentsView.startHome()
- return true
+ TOGGLE -> launchTask(recentsView, getNextTask(recentsView), command)
+ HOME -> {
+ recentsView.startHome()
+ true
}
}
}
- createdRecentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex)
+ recentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex)
// Handle recents view focus when launching from home
val animatorListener: Animator.AnimatorListener =
object : AnimatorListenerAdapter() {
@@ -275,7 +273,7 @@
Log.d(TAG, "switching to Overview state - onAnimationEnd: $command")
super.onAnimationEnd(animation)
onRecentsViewFocusUpdated(command)
- scheduleNextTask(command)
+ onCommandFinished(command)
}
}
if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
@@ -326,7 +324,7 @@
command.removeListener(this)
activityInterface.getCreatedContainer() ?: return
- createdRecentsView?.onRecentsAnimationComplete()
+ recentsView?.onRecentsAnimationComplete()
}
}
@@ -365,17 +363,12 @@
command.removeListener(handler)
Trace.endAsyncSection(TRANSITION_NAME, 0)
onRecentsViewFocusUpdated(command)
- scheduleNextTask(command)
+ onCommandFinished(command)
}
private fun updateRecentsViewFocus(command: CommandInfo) {
- val recentsView: RecentsView<*, *> =
- overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
- if (
- command.type != TYPE_KEYBOARD_INPUT &&
- command.type != TYPE_HIDE &&
- command.type != TYPE_SHOW
- ) {
+ val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
+ if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) {
return
}
@@ -394,9 +387,8 @@
}
private fun onRecentsViewFocusUpdated(command: CommandInfo) {
- val recentsView: RecentsView<*, *> =
- overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
- if (command.type != TYPE_HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
+ val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
+ if (command.type != HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
return
}
recentsView.setKeyboardTaskFocusIndex(PagedView.INVALID_PAGE)
@@ -413,15 +405,13 @@
return true
}
- private fun logShowOverviewFrom(commandType: Int) {
- val activityInterface: BaseActivityInterface<*, *> =
- overviewComponentObserver.activityInterface
+ private fun logShowOverviewFrom(commandType: CommandType) {
val container = activityInterface.getCreatedContainer() as? RecentsViewContainer ?: return
val event =
when (commandType) {
- TYPE_SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
- TYPE_HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
- TYPE_TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
+ 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
else -> return
}
StatsLogManager.newInstance(container.asContext())
@@ -438,16 +428,17 @@
fun dump(pw: PrintWriter) {
pw.println("OverviewCommandHelper:")
- pw.println(" pendingCommands=${pendingCommands.size}")
- if (pendingCommands.isNotEmpty()) {
- pw.println(" pendingCommandType=${pendingCommands.first().type}")
+ pw.println(" pendingCommands=${commandQueue.size}")
+ if (commandQueue.isNotEmpty()) {
+ pw.println(" pendingCommandType=${commandQueue.first().type}")
}
- pw.println(" mKeyboardTaskFocusIndex=$keyboardTaskFocusIndex")
- pw.println(" mWaitForToggleCommandComplete=$waitForToggleCommandComplete")
+ pw.println(" keyboardTaskFocusIndex=$keyboardTaskFocusIndex")
+ pw.println(" waitForToggleCommandComplete=$waitForToggleCommandComplete")
}
private data class CommandInfo(
- val type: Int,
+ val type: CommandType,
+ var status: CommandStatus = CommandStatus.IDLE,
val createTime: Long = SystemClock.elapsedRealtime(),
private var animationCallbacks: RecentsAnimationCallbacks? = null
) {
@@ -462,23 +453,30 @@
fun removeListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener?) {
animationCallbacks?.removeListener(listener)
}
+
+ enum class CommandStatus {
+ IDLE,
+ PROCESSING,
+ COMPLETED
+ }
+ }
+
+ enum class CommandType {
+ SHOW,
+ KEYBOARD_INPUT,
+ HIDE,
+ TOGGLE, // Navigate to Overview
+ HOME, // Navigate to Home
}
companion object {
private const val TAG = "OverviewCommandHelper"
-
- const val TYPE_SHOW: Int = 1
- const val TYPE_KEYBOARD_INPUT: Int = 2
- const val TYPE_HIDE: Int = 3
- const val TYPE_TOGGLE: Int = 4
- const val TYPE_HOME: Int = 5
+ private const val TRANSITION_NAME = "Transition:toOverview"
/**
* Use case for needing a queue is double tapping recents button in 3 button nav. Size of 2
* should be enough. We'll toss in one more because we're kind hearted.
*/
private const val MAX_QUEUE_SIZE = 3
-
- private const val TRANSITION_NAME = "Transition:toOverview"
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2b5aa71..69d1c35 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -108,6 +108,7 @@
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.OverviewCommandHelper.CommandType;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
@@ -245,7 +246,7 @@
return;
}
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
+ tis.mOverviewCommandHelper.addCommand(CommandType.TOGGLE);
});
}
@@ -255,10 +256,9 @@
executeForTouchInteractionService(tis -> {
if (triggeredFromAltTab) {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- tis.mOverviewCommandHelper.addCommand(
- OverviewCommandHelper.TYPE_KEYBOARD_INPUT);
+ tis.mOverviewCommandHelper.addCommand(CommandType.KEYBOARD_INPUT);
} else {
- tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
+ tis.mOverviewCommandHelper.addCommand(CommandType.SHOW);
}
});
}
@@ -269,7 +269,7 @@
executeForTouchInteractionService(tis -> {
if (triggeredFromAltTab && !triggeredFromHomeKey) {
// onOverviewShownFromAltTab hides the overview and ends at the target app
- tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
+ tis.mOverviewCommandHelper.addCommand(CommandType.HIDE);
}
});
}
@@ -594,12 +594,12 @@
private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
@Override
public void onNavigateHome() {
- mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME);
+ mOverviewCommandHelper.addCommand(CommandType.HOME);
}
@Override
public void onToggleOverview() {
- mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
+ mOverviewCommandHelper.addCommand(CommandType.TOGGLE);
}
};
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 17a97fa..9284e13 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -46,6 +46,7 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewCommandHelper.CommandType;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -200,7 +201,7 @@
break;
case MotionEvent.ACTION_BUTTON_RELEASE:
if (isStashedTaskbarHovered) {
- mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME);
+ mOverviewCommandHelper.addCommand(CommandType.HOME);
}
break;
}