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
This commit is contained in:
Tony Wickham
2024-06-21 21:00:01 +00:00
committed by Android (Google) Code Review
7 changed files with 456 additions and 291 deletions
@@ -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.BgDataModel.FixedContainerItems;
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.ItemInfoMatcher;
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 @@ import java.util.function.Predicate;
* 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 @@ public class TaskbarModelCallbacks implements
// 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 class TaskbarModelCallbacks implements
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 @@ public class TaskbarModelCallbacks implements
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 @@ public class TaskbarModelCallbacks implements
}
}
@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 @@ public class TaskbarModelCallbacks implements
Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
Preconditions.assertUIThread();
mControllers.taskbarAllAppsController.setApps(apps, flags, packageUserKeytoUidMap);
mControllers.taskbarRecentAppsController.setApps(apps);
}
protected void dumpLogs(String prefix, PrintWriter pw) {
@@ -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 @@ class TaskbarRecentAppsController(
) : 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 @@ class TaskbarRecentAppsController(
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 }
}
@@ -224,7 +224,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
mModelCallbacks.unregisterListeners();
}
public boolean areIconsVisible() {
@@ -869,6 +868,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
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 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
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();
}
}
@@ -33,7 +33,7 @@ class TaskbarFeatureEvaluator(
val hasNavButtons = taskbarActivityContext.isThreeButtonNav
val hasRecents: Boolean
get() = taskbarControllers.taskbarRecentAppsController.isEnabled
get() = taskbarControllers.taskbarRecentAppsController.shownTasks.isNotEmpty()
val hasDivider: Boolean
get() = enableTaskbarPinning() || hasRecents
@@ -70,7 +70,8 @@ public class RecentTasksList {
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 class RecentTasksList {
public void onRecentTasksChanged() {
invalidateLoadedTasks();
if (mRecentTasksChangedListener != null) {
mRecentTasksChangedListener.onRecentTasksChanged();
}
}
private synchronized void invalidateLoadedTasks() {
@@ -221,6 +225,21 @@ public class RecentTasksList {
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 @@ public class RecentTasksList {
task.setLastSnapshotData(taskInfo);
task.positionInParent = taskInfo.positionInParent;
task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
task.isVisible = taskInfo.isVisible;
tasks.add(task);
}
return new DesktopTask(tasks);
@@ -42,6 +42,7 @@ import com.android.launcher3.util.Executors.SimpleThreadFactory;
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 @@ public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
/**
* 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);
@@ -313,6 +316,20 @@ public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
mTaskList.unregisterRunningTasksListener();
}
/**
* 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.
*/
@@ -379,4 +396,14 @@ public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
*/
void onRunningTasksChanged();
}
/**
* Listener for receiving recent tasks changes
*/
public interface RecentTasksChangedListener {
/**
* Called when there's a change to recent tasks
*/
void onRecentTasksChanged();
}
}