Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a79bc15c authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

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
parents 59e8952f 7c7c9052
Loading
Loading
Loading
Loading
+7 −57
Original line number Diff line number Diff line
@@ -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) {
+103 −89
Original line number Diff line number Diff line
@@ -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,11 +39,8 @@ 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) {
            field = isEnabledFromTest
@@ -55,9 +49,12 @@ class TaskbarRecentAppsController(
    // 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())
            }
        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) }
        onRecentsOrHotseatChanged()

        return shownHotseatItems.toTypedArray()
    }

    private fun getDesktopRunningTasks(): List<RunningTaskInfo> =
        recentsModel.runningTasks.filter { taskInfo: RunningTaskInfo ->
            taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
    private fun reloadRecentTasksIfNeeded() {
        if (!recentsModel.isTaskListValid(taskListChangeId)) {
            taskListChangeId =
                recentsModel.getTasks { tasks ->
                    allRecentTasks = tasks
                    desktopTask = allRecentTasks.filterIsInstance<DesktopTask>().firstOrNull()
                    onRecentsOrHotseatChanged()
                    controllers.taskbarViewController.commitRunningAppsToUI()
                }
        }
    }

    // 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) {
            return emptyList()
    private fun onRecentsOrHotseatChanged() {
        shownTasks =
            if (isInDesktopMode) {
                computeShownRunningTasks()
            } else {
                computeShownRecentTasks()
            }
        val packageNames = tasks.map { it.realActivity?.packageName }.distinct().filterNotNull()
        return packageNames
            .map { packageName -> apps?.find { app -> packageName == app.targetPackage } }
            .filterNotNull()
    }

    /** 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 computeShownRunningTasks(): List<GroupTask> {
        if (!canShowRunningApps) {
            return emptyList()
        }
        val runningTasks = getDesktopRunningTasks()
        val runningAppInfo = getAppInfosFromRunningTasks(runningTasks)
        allRunningDesktopAppInfos = runningAppInfo
        updateMinimizedApps(runningTasks, runningAppInfo)
        controllers.taskbarViewController.commitRunningAppsToUI()
        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()
    }

    private fun computeShownRecentTasks(): List<GroupTask> {
        // TODO(next CL): implement Recents section
        return emptyList()
    }

    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 }
    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 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 }
}
+5 −11
Original line number Diff line number Diff line
@@ -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();
    }

}
+1 −1
Original line number Diff line number Diff line
@@ -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
+21 −1
Original line number Diff line number Diff line
@@ -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);
Loading