Merge changes from topic "taskbar-running-minimized" into main
* changes:
Maintain Running Tasks order in Desktop mode
Change running apps section to use GroupTask instead of AppInfo
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 2b0e169..365c5c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
-
import android.util.SparseArray;
import android.view.View;
@@ -29,7 +26,6 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -37,8 +33,6 @@
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.RecentsModel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -54,7 +48,7 @@
* Launcher model Callbacks for rendering taskbar.
*/
public class TaskbarModelCallbacks implements
- BgDataModel.Callbacks, LauncherBindableItemsContainer, RecentsModel.RunningTasksListener {
+ BgDataModel.Callbacks, LauncherBindableItemsContainer {
private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
private List<ItemInfo> mPredictedItems = Collections.emptyList();
@@ -68,8 +62,6 @@
// Used to defer any UI updates during the SUW unstash animation.
private boolean mDeferUpdatesForSUW;
private Runnable mDeferredUpdates;
- private final DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
- visible -> updateRunningApps();
public TaskbarModelCallbacks(
TaskbarActivityContext context, TaskbarView container) {
@@ -79,39 +71,6 @@
public void init(TaskbarControllers controllers) {
mControllers = controllers;
- if (mControllers.taskbarRecentAppsController.getCanShowRunningApps()) {
- RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this);
-
- if (shouldShowRunningAppsInDesktopMode()) {
- DesktopVisibilityController desktopVisibilityController =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- desktopVisibilityController.registerDesktopVisibilityListener(
- mDesktopVisibilityListener);
- }
- }
- }
- }
-
- /**
- * Unregisters listeners in this class.
- */
- public void unregisterListeners() {
- RecentsModel.INSTANCE.get(mContext).unregisterRunningTasksListener();
-
- if (shouldShowRunningAppsInDesktopMode()) {
- DesktopVisibilityController desktopVisibilityController =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- desktopVisibilityController.unregisterDesktopVisibilityListener(
- mDesktopVisibilityListener);
- }
- }
- }
-
- private boolean shouldShowRunningAppsInDesktopMode() {
- // TODO(b/335401172): unify DesktopMode checks in Launcher
- return enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps();
}
@Override
@@ -232,10 +191,12 @@
predictionNextIndex++;
}
}
- hotseatItemInfos = mControllers.taskbarRecentAppsController
- .updateHotseatItemInfos(hotseatItemInfos);
- Set<String> runningPackages = mControllers.taskbarRecentAppsController.getRunningApps();
- Set<String> minimizedPackages = mControllers.taskbarRecentAppsController.getMinimizedApps();
+
+ final TaskbarRecentAppsController recentAppsController =
+ mControllers.taskbarRecentAppsController;
+ hotseatItemInfos = recentAppsController.updateHotseatItemInfos(hotseatItemInfos);
+ Set<String> runningPackages = recentAppsController.getRunningAppPackages();
+ Set<String> minimizedPackages = recentAppsController.getMinimizedAppPackages();
if (mDeferUpdatesForSUW) {
ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
@@ -270,21 +231,11 @@
}
}
- @Override
- public void onRunningTasksChanged() {
- updateRunningApps();
- }
-
/** Called when there's a change in running apps to update the UI. */
public void commitRunningAppsToUI() {
commitItemsToUI();
}
- /** Call TaskbarRecentAppsController to update running apps with mHotseatItems. */
- public void updateRunningApps() {
- mControllers.taskbarRecentAppsController.updateRunningApps();
- }
-
@Override
public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy);
@@ -296,7 +247,6 @@
Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
Preconditions.assertUIThread();
mControllers.taskbarAllAppsController.setApps(apps, flags, packageUserKeytoUidMap);
- mControllers.taskbarRecentAppsController.setApps(apps);
}
protected void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index b1fc9cc..659f7c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -15,16 +15,13 @@
*/
package com.android.launcher3.taskbar
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
import androidx.annotation.VisibleForTesting
-import com.android.launcher3.Flags.enableRecentsInTaskbar
-import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
-import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.quickstep.RecentsModel
+import com.android.quickstep.util.DesktopTask
+import com.android.quickstep.util.GroupTask
import com.android.window.flags.Flags.enableDesktopWindowingMode
import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps
import java.io.PrintWriter
@@ -42,22 +39,22 @@
) : LoggableTaskbarController {
// TODO(b/335401172): unify DesktopMode checks in Launcher.
- val canShowRunningApps =
+ var canShowRunningApps =
enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()
-
- // TODO(b/343532825): Add a setting to disable Recents even when the flag is on.
- var isEnabled: Boolean = enableRecentsInTaskbar() || canShowRunningApps
@VisibleForTesting
- set(isEnabledFromTest){
+ set(isEnabledFromTest) {
field = isEnabledFromTest
}
// Initialized in init.
private lateinit var controllers: TaskbarControllers
- private var apps: Array<AppInfo>? = null
- private var allRunningDesktopAppInfos: List<AppInfo>? = null
- private var allMinimizedDesktopAppInfos: List<AppInfo>? = null
+ private var shownHotseatItems: List<ItemInfo> = emptyList()
+ private var allRecentTasks: List<GroupTask> = emptyList()
+ private var desktopTask: DesktopTask? = null
+ // TODO(next CL): actually read and show these
+ var shownTasks: List<GroupTask> = emptyList()
+ private set
private val desktopVisibilityController: DesktopVisibilityController?
get() = desktopVisibilityControllerProvider()
@@ -65,122 +62,139 @@
private val isInDesktopMode: Boolean
get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false
- val runningApps: Set<String>
+ val runningAppPackages: Set<String>
+ /**
+ * Returns the package names of apps that should be indicated as "running" to the user.
+ * Specifically, we return all the open tasks if we are in Desktop mode, else emptySet().
+ */
get() {
- if (!isEnabled || !isInDesktopMode) {
+ if (!canShowRunningApps || !isInDesktopMode) {
return emptySet()
}
- return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
+ val tasks = desktopTask?.tasks ?: return emptySet()
+ return tasks.map { task -> task.key.packageName }.toSet()
}
- val minimizedApps: Set<String>
+ val minimizedAppPackages: Set<String>
+ /**
+ * Returns the package names of apps that should be indicated as "minimized" to the user.
+ * Specifically, we return all the running packages where all the tasks in that package are
+ * minimized (not visible).
+ */
get() {
- if (!isInDesktopMode) {
+ if (!canShowRunningApps || !isInDesktopMode) {
return emptySet()
}
- return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet()
- ?: emptySet()
+ val desktopTasks = desktopTask?.tasks ?: return emptySet()
+ val packageToTasks = desktopTasks.groupBy { it.key.packageName }
+ return packageToTasks.filterValues { tasks -> tasks.all { !it.isVisible } }.keys
}
+ private val recentTasksChangedListener =
+ RecentsModel.RecentTasksChangedListener { reloadRecentTasksIfNeeded() }
+
+ // TODO(b/343291428): add TaskVisualsChangListener as well (for calendar/clock?)
+
+ // Used to keep track of the last requested task list ID, so that we do not request to load the
+ // tasks again if we have already requested it and the task list has not changed
+ private var taskListChangeId = -1
+
fun init(taskbarControllers: TaskbarControllers) {
controllers = taskbarControllers
+ recentsModel.registerRecentTasksChangedListener(recentTasksChangedListener)
+ reloadRecentTasksIfNeeded()
}
fun onDestroy() {
- apps = null
- }
-
- /** Stores the current [AppInfo] instances, no-op except in desktop environment. */
- fun setApps(apps: Array<AppInfo>?) {
- this.apps = apps
+ recentsModel.unregisterRecentTasksChangedListener()
}
/** Called to update hotseatItems, in order to de-dupe them from Recent/Running tasks later. */
- // TODO(next CL): add new section of Tasks instead of changing Hotseat items
fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
- if (!isEnabled || !isInDesktopMode) {
+ // Ignore predicted apps - we show running or recent apps instead.
+ val removePredictions = isInDesktopMode && canShowRunningApps
+ if (!removePredictions) {
+ shownHotseatItems = hotseatItems.filterNotNull()
+ onRecentsOrHotseatChanged()
return hotseatItems
}
- val newHotseatItemInfos =
+ shownHotseatItems =
hotseatItems
.filterNotNull()
- // Ignore predicted apps - we show running apps instead
.filter { itemInfo -> !itemInfo.isPredictedItem }
.toMutableList()
- val runningDesktopAppInfos =
- allRunningDesktopAppInfos?.let {
- getRunningDesktopAppInfosExceptHotseatApps(it, newHotseatItemInfos.toList())
+
+ onRecentsOrHotseatChanged()
+
+ return shownHotseatItems.toTypedArray()
+ }
+
+ private fun reloadRecentTasksIfNeeded() {
+ if (!recentsModel.isTaskListValid(taskListChangeId)) {
+ taskListChangeId =
+ recentsModel.getTasks { tasks ->
+ allRecentTasks = tasks
+ desktopTask = allRecentTasks.filterIsInstance<DesktopTask>().firstOrNull()
+ onRecentsOrHotseatChanged()
+ controllers.taskbarViewController.commitRunningAppsToUI()
+ }
+ }
+ }
+
+ private fun onRecentsOrHotseatChanged() {
+ shownTasks =
+ if (isInDesktopMode) {
+ computeShownRunningTasks()
+ } else {
+ computeShownRecentTasks()
}
- if (runningDesktopAppInfos != null) {
- newHotseatItemInfos.addAll(runningDesktopAppInfos)
- }
- return newHotseatItemInfos.toTypedArray()
}
- private fun getRunningDesktopAppInfosExceptHotseatApps(
- allRunningDesktopAppInfos: List<AppInfo>,
- hotseatItems: List<ItemInfo>
- ): List<ItemInfo> {
- val hotseatPackages = hotseatItems.map { it.targetPackage }
- return allRunningDesktopAppInfos
- .filter { appInfo -> !hotseatPackages.contains(appInfo.targetPackage) }
- .map { WorkspaceItemInfo(it) }
- }
-
- private fun getDesktopRunningTasks(): List<RunningTaskInfo> =
- recentsModel.runningTasks.filter { taskInfo: RunningTaskInfo ->
- taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
- }
-
- // TODO(b/335398876) fetch app icons from Tasks instead of AppInfos
- private fun getAppInfosFromRunningTasks(tasks: List<RunningTaskInfo>): List<AppInfo> {
- // Early return if apps is empty, since we then have no AppInfo to compare to
- if (apps == null) {
+ private fun computeShownRunningTasks(): List<GroupTask> {
+ if (!canShowRunningApps) {
return emptyList()
}
- val packageNames = tasks.map { it.realActivity?.packageName }.distinct().filterNotNull()
- return packageNames
- .map { packageName -> apps?.find { app -> packageName == app.targetPackage } }
- .filterNotNull()
+ val tasks = desktopTask?.tasks ?: emptyList()
+ // Kind of hacky, we wrap each single task in the Desktop as a GroupTask.
+ var desktopTaskAsList = tasks.map { GroupTask(it) }
+ // TODO(b/315344726 Multi-instance support): dedupe Tasks of the same package too.
+ desktopTaskAsList = dedupeHotseatTasks(desktopTaskAsList, shownHotseatItems)
+ val desktopPackages = desktopTaskAsList.map { it.packageNames }
+ // Remove any missing Tasks.
+ val newShownTasks = shownTasks.filter { it.packageNames in desktopPackages }.toMutableList()
+ val newShownPackages = newShownTasks.map { it.packageNames }
+ // Add any new Tasks, maintaining the order from previous shownTasks.
+ newShownTasks.addAll(desktopTaskAsList.filter { it.packageNames !in newShownPackages })
+ return newShownTasks.toList()
}
- /** Called to update the list of currently running apps, no-op except in desktop environment. */
- fun updateRunningApps() {
- if (!isEnabled || !isInDesktopMode) {
- return controllers.taskbarViewController.commitRunningAppsToUI()
+ private fun computeShownRecentTasks(): List<GroupTask> {
+ // TODO(next CL): implement Recents section
+ return emptyList()
+ }
+
+ private fun dedupeHotseatTasks(
+ groupTasks: List<GroupTask>,
+ shownHotseatItems: List<ItemInfo>
+ ): List<GroupTask> {
+ val hotseatPackages = shownHotseatItems.map { item -> item.targetPackage }
+ return groupTasks.filter { groupTask ->
+ groupTask.hasMultipleTasks() ||
+ !hotseatPackages.contains(groupTask.task1.key.packageName)
}
- val runningTasks = getDesktopRunningTasks()
- val runningAppInfo = getAppInfosFromRunningTasks(runningTasks)
- allRunningDesktopAppInfos = runningAppInfo
- updateMinimizedApps(runningTasks, runningAppInfo)
- controllers.taskbarViewController.commitRunningAppsToUI()
- }
-
- private fun updateMinimizedApps(
- runningTasks: List<RunningTaskInfo>,
- runningAppInfo: List<AppInfo>,
- ) {
- val allRunningAppTasks =
- runningAppInfo
- .mapNotNull { appInfo -> appInfo.targetPackage?.let { appInfo to it } }
- .associate { (appInfo, targetPackage) ->
- appInfo to
- runningTasks
- .filter { it.realActivity?.packageName == targetPackage }
- .map { it.taskId }
- }
- val minimizedTaskIds = runningTasks.associate { it.taskId to !it.isVisible }
- allMinimizedDesktopAppInfos =
- allRunningAppTasks
- .filterValues { taskIds -> taskIds.all { minimizedTaskIds[it] ?: false } }
- .keys
- .toList()
}
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println("$prefix TaskbarRecentAppsController:")
- pw.println("$prefix\tisEnabled=$isEnabled")
pw.println("$prefix\tcanShowRunningApps=$canShowRunningApps")
- // TODO(next CL): add more logs
+ pw.println("$prefix\tshownHotseatItems=${shownHotseatItems.map{item->item.targetPackage}}")
+ pw.println("$prefix\tallRecentTasks=${allRecentTasks.map { it.packageNames }}")
+ pw.println("$prefix\tdesktopTask=${desktopTask?.packageNames}")
+ pw.println("$prefix\tshownTasks=${shownTasks.map { it.packageNames }}")
+ pw.println("$prefix\trunningTasks=$runningAppPackages")
+ pw.println("$prefix\tminimizedTasks=$minimizedAppPackages")
}
+
+ private val GroupTask.packageNames: List<String>
+ get() = tasks.map { task -> task.key.packageName }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 55745b5..b03894de 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -224,7 +224,6 @@
}
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
- mModelCallbacks.unregisterListeners();
}
public boolean areIconsVisible() {
@@ -869,6 +868,11 @@
return mTaskbarView.isEventOverAnyItem(ev);
}
+ /** Called when there's a change in running apps to update the UI. */
+ public void commitRunningAppsToUI() {
+ mModelCallbacks.commitRunningAppsToUI();
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarViewController:");
@@ -889,14 +893,4 @@
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
- /** Called when there's a change in running apps to update the UI. */
- public void commitRunningAppsToUI() {
- mModelCallbacks.commitRunningAppsToUI();
- }
-
- /** Call TaskbarModelCallbacks to update running apps. */
- public void updateRunningApps() {
- mModelCallbacks.updateRunningApps();
- }
-
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index 668a87d..ac7dd06 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -33,7 +33,7 @@
val hasNavButtons = taskbarActivityContext.isThreeButtonNav
val hasRecents: Boolean
- get() = taskbarControllers.taskbarRecentAppsController.isEnabled
+ get() = taskbarControllers.taskbarRecentAppsController.shownTasks.isNotEmpty()
val hasDivider: Boolean
get() = enableTaskbarPinning() || hasRecents
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index b08a46f..66091d4 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -70,7 +70,8 @@
private TaskLoadResult mResultsBg = INVALID_RESULT;
private TaskLoadResult mResultsUi = INVALID_RESULT;
- private RecentsModel.RunningTasksListener mRunningTasksListener;
+ private @Nullable RecentsModel.RunningTasksListener mRunningTasksListener;
+ private @Nullable RecentsModel.RecentTasksChangedListener mRecentTasksChangedListener;
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
@@ -199,6 +200,9 @@
public void onRecentTasksChanged() {
invalidateLoadedTasks();
+ if (mRecentTasksChangedListener != null) {
+ mRecentTasksChangedListener.onRecentTasksChanged();
+ }
}
private synchronized void invalidateLoadedTasks() {
@@ -221,6 +225,21 @@
mRunningTasksListener = null;
}
+ /**
+ * Registers a listener for running tasks
+ */
+ public void registerRecentTasksChangedListener(
+ RecentsModel.RecentTasksChangedListener listener) {
+ mRecentTasksChangedListener = listener;
+ }
+
+ /**
+ * Removes the previously registered running tasks listener
+ */
+ public void unregisterRecentTasksChangedListener() {
+ mRecentTasksChangedListener = null;
+ }
+
private void initRunningTasks(ArrayList<ActivityManager.RunningTaskInfo> runningTasks) {
// Tasks are retrieved in order of most recently launched/used to least recently launched.
mRunningTasks = new ArrayList<>(runningTasks);
@@ -357,6 +376,7 @@
task.setLastSnapshotData(taskInfo);
task.positionInParent = taskInfo.positionInParent;
task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
+ task.isVisible = taskInfo.isVisible;
tasks.add(task);
}
return new DesktopTask(tasks);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 6eefe4a..b796951 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -42,6 +42,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.recents.data.RecentTasksDataSource;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
@@ -301,6 +302,8 @@
/**
* Registers a listener for running tasks
+ * TODO(b/343292503): Should we remove RunningTasksListener entirely if it's not needed?
+ * (Note that Desktop mode gets the running tasks by checking {@link DesktopTask#tasks}
*/
public void registerRunningTasksListener(RunningTasksListener listener) {
mTaskList.registerRunningTasksListener(listener);
@@ -314,6 +317,20 @@
}
/**
+ * Registers a listener for recent tasks
+ */
+ public void registerRecentTasksChangedListener(RecentTasksChangedListener listener) {
+ mTaskList.registerRecentTasksChangedListener(listener);
+ }
+
+ /**
+ * Removes the previously registered running tasks listener
+ */
+ public void unregisterRecentTasksChangedListener() {
+ mTaskList.unregisterRecentTasksChangedListener();
+ }
+
+ /**
* Gets the set of running tasks.
*/
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
@@ -379,4 +396,14 @@
*/
void onRunningTasksChanged();
}
+
+ /**
+ * Listener for receiving recent tasks changes
+ */
+ public interface RecentTasksChangedListener {
+ /**
+ * Called when there's a change to recent tasks
+ */
+ void onRecentTasksChanged();
+ }
}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index 104263a..5813708 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -16,24 +16,34 @@
package com.android.launcher3.taskbar
-import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.ComponentName
import android.content.Intent
import android.os.Process
import android.os.UserHandle
import android.testing.AndroidTestingRunner
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.quickstep.RecentsModel
+import com.android.quickstep.RecentsModel.RecentTasksChangedListener
+import com.android.quickstep.util.DesktopTask
+import com.android.quickstep.util.GroupTask
+import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@@ -45,173 +55,292 @@
@Mock private lateinit var mockDesktopVisibilityController: DesktopVisibilityController
private var nextTaskId: Int = 500
+ private var taskListChangeId: Int = 1
private lateinit var recentAppsController: TaskbarRecentAppsController
+ private lateinit var recentTasksChangedListener: RecentTasksChangedListener
private lateinit var userHandle: UserHandle
@Before
fun setUp() {
super.setup()
userHandle = Process.myUserHandle()
+
recentAppsController =
TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
recentAppsController.init(taskbarControllers)
- recentAppsController.isEnabled = true
- recentAppsController.setApps(
- ALL_APP_PACKAGES.map { createTestAppInfo(packageName = it) }.toTypedArray()
- )
+ recentAppsController.canShowRunningApps = true
+
+ val listenerCaptor = ArgumentCaptor.forClass(RecentTasksChangedListener::class.java)
+ verify(mockRecentsModel).registerRecentTasksChangedListener(listenerCaptor.capture())
+ recentTasksChangedListener = listenerCaptor.value
}
@Test
- fun updateHotseatItemInfos_notInDesktopMode_returnsExistingHotseatItems() {
- setInDesktopMode(false)
- val hotseatItems =
- createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
-
- assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
- .isEqualTo(hotseatItems.toTypedArray())
- }
-
- @Test
- fun updateHotseatItemInfos_notInDesktopMode_runningApps_returnsExistingHotseatItems() {
- setInDesktopMode(false)
- val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
- val hotseatItems = createHotseatItemsFromPackageNames(hotseatPackages)
- val runningTasks =
- createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
-
+ fun updateHotseatItemInfos_cantShowRunning_inDesktopMode_returnsAllHotseatItems() {
+ recentAppsController.canShowRunningApps = false
+ setInDesktopMode(true)
+ val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1)
val newHotseatItems =
- recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
-
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = hotseatPackages,
+ runningTaskPackages = emptyList(),
+ recentTaskPackages = emptyList()
+ )
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(hotseatPackages)
}
@Test
- fun updateHotseatItemInfos_noRunningApps_returnsExistingHotseatItems() {
+ fun updateHotseatItemInfos_canShowRunning_inDesktopMode_returnsNonPredictedHotseatItems() {
+ recentAppsController.canShowRunningApps = true
setInDesktopMode(true)
- val hotseatItems =
- createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
-
- assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
- .isEqualTo(hotseatItems.toTypedArray())
- }
-
- @Test
- fun updateHotseatItemInfos_returnsExistingHotseatItemsAndRunningApps() {
- setInDesktopMode(true)
- val hotseatItems =
- createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
- val runningTasks =
- createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
-
val newHotseatItems =
- recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
-
- val expectedPackages =
- listOf(
- HOTSEAT_PACKAGE_1,
- HOTSEAT_PACKAGE_2,
- RUNNING_APP_PACKAGE_1,
- RUNNING_APP_PACKAGE_2,
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
+ runningTaskPackages = emptyList(),
+ recentTaskPackages = emptyList()
)
+ val expectedPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(expectedPackages)
}
@Test
- fun updateHotseatItemInfos_runningAppIsHotseatItem_returnsDistinctItems() {
+ fun onRecentTasksChanged_cantShowRunning_inDesktopMode_shownTasks_returnsEmptyList() {
+ recentAppsController.canShowRunningApps = false
setInDesktopMode(true)
- val hotseatItems =
- createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
- val runningTasks =
- createDesktopTasksFromPackageNames(
- listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
- )
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
-
- val newHotseatItems =
- recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
-
- val expectedPackages =
- listOf(
- HOTSEAT_PACKAGE_1,
- HOTSEAT_PACKAGE_2,
- RUNNING_APP_PACKAGE_1,
- RUNNING_APP_PACKAGE_2,
- )
- assertThat(newHotseatItems.map { it?.targetPackage })
- .containsExactlyElementsIn(expectedPackages)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
+ runningTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2),
+ recentTaskPackages = emptyList()
+ )
+ assertThat(recentAppsController.shownTasks).isEmpty()
}
@Test
- fun getRunningApps_notInDesktopMode_returnsEmptySet() {
+ fun onRecentTasksChanged_inDesktopMode_noRunningApps_shownTasks_returnsEmptyList() {
+ setInDesktopMode(true)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = emptyList(),
+ recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
+ )
+ assertThat(recentAppsController.shownTasks).isEmpty()
+ }
+
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_shownTasks_returnsRunningTasks() {
+ setInDesktopMode(true)
+ val runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = runningTaskPackages,
+ recentTaskPackages = emptyList()
+ )
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ assertThat(shownPackages).containsExactlyElementsIn(runningTaskPackages)
+ }
+
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_runningAppIsHotseatItem_shownTasks_returnsDistinctItems() {
+ setInDesktopMode(true)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
+ runningTaskPackages =
+ listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2),
+ recentTaskPackages = emptyList()
+ )
+ val expectedPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ assertThat(shownPackages).containsExactlyElementsIn(expectedPackages)
+ }
+
+ @Test
+ fun onRecentTasksChanged_notInDesktopMode_getRunningApps_returnsEmptySet() {
setInDesktopMode(false)
- val runningTasks =
- createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
-
- assertThat(recentAppsController.runningApps).isEmpty()
- assertThat(recentAppsController.minimizedApps).isEmpty()
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2),
+ recentTaskPackages = emptyList()
+ )
+ assertThat(recentAppsController.runningAppPackages).isEmpty()
+ assertThat(recentAppsController.minimizedAppPackages).isEmpty()
}
@Test
- fun getRunningApps_inDesktopMode_returnsRunningApps() {
+ fun onRecentTasksChanged_inDesktopMode_getRunningApps_returnsAllDesktopTasks() {
setInDesktopMode(true)
- val runningTasks =
- createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
+ val runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = runningTaskPackages,
+ recentTaskPackages = emptyList()
+ )
+ assertThat(recentAppsController.runningAppPackages)
+ .containsExactlyElementsIn(runningTaskPackages)
+ assertThat(recentAppsController.minimizedAppPackages).isEmpty()
+ }
- assertThat(recentAppsController.runningApps)
- .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
- assertThat(recentAppsController.minimizedApps).isEmpty()
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_getRunningApps_includesHotseat() {
+ setInDesktopMode(true)
+ val runningTaskPackages =
+ listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
+ runningTaskPackages = runningTaskPackages,
+ recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
+ )
+ assertThat(recentAppsController.runningAppPackages)
+ .containsExactlyElementsIn(runningTaskPackages)
+ assertThat(recentAppsController.minimizedAppPackages).isEmpty()
}
@Test
fun getMinimizedApps_inDesktopMode_returnsAllAppsRunningAndInvisibleAppsMinimized() {
setInDesktopMode(true)
- val runningTasks =
- ArrayList(
- listOf(
- createDesktopTaskInfo(RUNNING_APP_PACKAGE_1) { isVisible = true },
- createDesktopTaskInfo(RUNNING_APP_PACKAGE_2) { isVisible = true },
- createDesktopTaskInfo(RUNNING_APP_PACKAGE_3) { isVisible = false },
- )
- )
- whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- recentAppsController.updateRunningApps()
+ val runningTaskPackages =
+ listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
+ val minimizedTaskIndices = setOf(2) // RUNNING_APP_PACKAGE_3
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = runningTaskPackages,
+ minimizedTaskIndices = minimizedTaskIndices,
+ recentTaskPackages = emptyList()
+ )
+ assertThat(recentAppsController.runningAppPackages)
+ .containsExactlyElementsIn(runningTaskPackages)
+ assertThat(recentAppsController.minimizedAppPackages).containsExactly(RUNNING_APP_PACKAGE_3)
+ }
- assertThat(recentAppsController.runningApps)
- .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
- assertThat(recentAppsController.minimizedApps).containsExactly(RUNNING_APP_PACKAGE_3)
+ @Test
+ fun getMinimizedApps_inDesktopMode_twoTasksSamePackageOneMinimizedReturnsNotMinimized() {
+ setInDesktopMode(true)
+ val runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_1)
+ val minimizedTaskIndices = setOf(1) // The second RUNNING_APP_PACKAGE_1 task.
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = runningTaskPackages,
+ minimizedTaskIndices = minimizedTaskIndices,
+ recentTaskPackages = emptyList()
+ )
+ assertThat(recentAppsController.runningAppPackages)
+ .containsExactlyElementsIn(runningTaskPackages.toSet())
+ assertThat(recentAppsController.minimizedAppPackages).isEmpty()
+ }
+
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_shownTasks_maintainsOrder() {
+ setInDesktopMode(true)
+ val originalOrder = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = originalOrder,
+ recentTaskPackages = emptyList()
+ )
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = listOf(RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_1),
+ recentTaskPackages = emptyList()
+ )
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ assertThat(shownPackages).isEqualTo(originalOrder)
+ }
+
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_addTask_shownTasks_maintainsOrder() {
+ setInDesktopMode(true)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2),
+ recentTaskPackages = emptyList()
+ )
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages =
+ listOf(RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_3),
+ recentTaskPackages = emptyList()
+ )
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ val expectedOrder =
+ listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
+ assertThat(shownPackages).isEqualTo(expectedOrder)
+ }
+
+ @Test
+ fun onRecentTasksChanged_inDesktopMode_removeTask_shownTasks_maintainsOrder() {
+ setInDesktopMode(true)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages =
+ listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3),
+ recentTaskPackages = emptyList()
+ )
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = listOf(RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_1),
+ recentTaskPackages = emptyList()
+ )
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ assertThat(shownPackages).isEqualTo(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
+ }
+
+ @Test
+ fun onRecentTasksChanged_enterDesktopMode_shownTasks_onlyIncludesRunningTasks() {
+ setInDesktopMode(false)
+ val runningTaskPackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ val recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = emptyList(),
+ runningTaskPackages = runningTaskPackages,
+ recentTaskPackages = recentTaskPackages
+ )
+ setInDesktopMode(true)
+ recentTasksChangedListener.onRecentTasksChanged()
+ val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
+ assertThat(shownPackages).containsExactlyElementsIn(runningTaskPackages)
+ }
+
+ private fun prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages: List<String>,
+ runningTaskPackages: List<String>,
+ minimizedTaskIndices: Set<Int> = emptySet(),
+ recentTaskPackages: List<String>,
+ ): Array<ItemInfo?> {
+ val hotseatItems = createHotseatItemsFromPackageNames(hotseatPackages)
+ val newHotseatItems =
+ recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
+ val runningTasks = createDesktopTask(runningTaskPackages, minimizedTaskIndices)
+ val recentTasks = createRecentTasksFromPackageNames(recentTaskPackages)
+ val allTasks =
+ ArrayList<GroupTask>().apply {
+ if (runningTasks != null) {
+ add(runningTasks)
+ }
+ addAll(recentTasks)
+ }
+ doAnswer {
+ val callback: Consumer<ArrayList<GroupTask>> = it.getArgument(0)
+ callback.accept(allTasks)
+ taskListChangeId
+ }
+ .whenever(mockRecentsModel)
+ .getTasks(any<Consumer<List<GroupTask>>>())
+ recentTasksChangedListener.onRecentTasksChanged()
+ return newHotseatItems
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
- return packageNames.map { createTestAppInfo(packageName = it) }
- }
-
- private fun createDesktopTasksFromPackageNames(
- packageNames: List<String>
- ): ArrayList<RunningTaskInfo> {
- return ArrayList(packageNames.map { createDesktopTaskInfo(packageName = it) })
- }
-
- private fun createDesktopTaskInfo(
- packageName: String,
- init: RunningTaskInfo.() -> Unit = { isVisible = true },
- ): RunningTaskInfo {
- return RunningTaskInfo().apply {
- taskId = nextTaskId++
- configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- realActivity = ComponentName(packageName, "TestActivity")
- init()
+ return packageNames.map {
+ createTestAppInfo(packageName = it).apply {
+ container =
+ if (it.startsWith("predicted")) {
+ CONTAINER_HOTSEAT_PREDICTION
+ } else {
+ CONTAINER_HOTSEAT
+ }
+ }
}
}
@@ -220,23 +349,54 @@
className: String = "testClassName"
) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
+ private fun createDesktopTask(
+ packageNames: List<String>,
+ minimizedTaskIndices: Set<Int>
+ ): DesktopTask? {
+ if (packageNames.isEmpty()) return null
+
+ return DesktopTask(
+ ArrayList(
+ packageNames.mapIndexed { index, packageName ->
+ createTask(packageName, index !in minimizedTaskIndices)
+ }
+ )
+ )
+ }
+
+ private fun createRecentTasksFromPackageNames(packageNames: List<String>): List<GroupTask> {
+ return packageNames.map { GroupTask(createTask(it)) }
+ }
+
+ private fun createTask(packageName: String, isVisible: Boolean = true): Task {
+ return Task(
+ Task.TaskKey(
+ nextTaskId++,
+ WINDOWING_MODE_FREEFORM,
+ Intent().apply { `package` = packageName },
+ ComponentName(packageName, "TestActivity"),
+ userHandle.identifier,
+ 0
+ )
+ )
+ .apply { this.isVisible = isVisible }
+ }
+
private fun setInDesktopMode(inDesktopMode: Boolean) {
whenever(mockDesktopVisibilityController.areDesktopTasksVisible()).thenReturn(inDesktopMode)
}
+ private val GroupTask.packageNames: List<String>
+ get() = tasks.map { task -> task.key.packageName }
+
private companion object {
const val HOTSEAT_PACKAGE_1 = "hotseat1"
const val HOTSEAT_PACKAGE_2 = "hotseat2"
+ const val PREDICTED_PACKAGE_1 = "predicted1"
const val RUNNING_APP_PACKAGE_1 = "running1"
const val RUNNING_APP_PACKAGE_2 = "running2"
const val RUNNING_APP_PACKAGE_3 = "running3"
- val ALL_APP_PACKAGES =
- listOf(
- HOTSEAT_PACKAGE_1,
- HOTSEAT_PACKAGE_2,
- RUNNING_APP_PACKAGE_1,
- RUNNING_APP_PACKAGE_2,
- RUNNING_APP_PACKAGE_3,
- )
+ const val RECENT_PACKAGE_1 = "recent1"
+ const val RECENT_PACKAGE_2 = "recent2"
}
}