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

Commit f3108c59 authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Taskbar running apps: show one icon per task + tap opens that task

Before this CL we would show only one Taskbar icon per app, making it
impossible (through the taskbar) to switch between several windows of
the same app.
With this CL we add one icon per task instead, making it possible to
bring each task to the front individually.

Flag: com.android.window.flags.enable_desktop_windowing_taskbar_running_apps
Bug: 351118893
Bug: 349790805
Bug: 351156858
Test: Started several Chrome windows -> taskbar has one icon per window

Change-Id: Ia692977effceb9ce339906bf6ca24d73e19d8769
parent a00c81c1
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.TaskItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
@@ -1132,6 +1133,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
                mControllers.uiController.onTaskbarIconLaunched(api);
                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
            }
        } else if (tag instanceof TaskItemInfo info) {
            UI_HELPER_EXECUTOR.execute(() ->
                    SystemUiProxy.INSTANCE.get(this).showDesktopApp(info.getTaskId()));
            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
                    /* stash= */ true);
        } else if (tag instanceof WorkspaceItemInfo) {
            // Tapping a launchable icon on Taskbar
            WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
+7 −7
Original line number Diff line number Diff line
@@ -196,26 +196,26 @@ public class TaskbarModelCallbacks implements
        final TaskbarRecentAppsController recentAppsController =
                mControllers.taskbarRecentAppsController;
        hotseatItemInfos = recentAppsController.updateHotseatItemInfos(hotseatItemInfos);
        Set<String> runningPackages = recentAppsController.getRunningAppPackages();
        Set<String> minimizedPackages = recentAppsController.getMinimizedAppPackages();
        Set<Integer> runningTaskIds = recentAppsController.getRunningTaskIds();
        Set<Integer> minimizedTaskIds = recentAppsController.getMinimizedTaskIds();

        if (mDeferUpdatesForSUW) {
            ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
            mDeferredUpdates = () ->
                    commitHotseatItemUpdates(finalHotseatItemInfos,
                            recentAppsController.getShownTasks(), runningPackages,
                            minimizedPackages);
                            recentAppsController.getShownTasks(), runningTaskIds,
                            minimizedTaskIds);
        } else {
            commitHotseatItemUpdates(hotseatItemInfos,
                    recentAppsController.getShownTasks(), runningPackages, minimizedPackages);
                    recentAppsController.getShownTasks(), runningTaskIds, minimizedTaskIds);
        }
    }

    private void commitHotseatItemUpdates(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks,
            Set<String> runningPackages, Set<String> minimizedPackages) {
            Set<Integer> runningTaskIds, Set<Integer> minimizedTaskIds) {
        mContainer.updateHotseatItems(hotseatItemInfos, recentTasks);
        mControllers.taskbarViewController.updateIconViewsRunningStates(
                runningPackages, minimizedPackages);
                runningTaskIds, minimizedTaskIds);
    }

    /**
+84 −30
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.launcher3.taskbar
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.util.CancellableTask
@@ -58,9 +60,13 @@ class TaskbarRecentAppsController(
    // Initialized in init.
    private lateinit var controllers: TaskbarControllers

    private var shownHotseatItems: List<ItemInfo> = emptyList()
    var shownHotseatItems: List<ItemInfo> = emptyList()
        private set

    private var allRecentTasks: List<GroupTask> = emptyList()
    private var desktopTask: DesktopTask? = null
    // Keeps track of the order in which running tasks appear.
    private var orderedRunningTaskIds = emptyList<Int>()
    var shownTasks: List<GroupTask> = emptyList()
        private set

@@ -70,9 +76,9 @@ class TaskbarRecentAppsController(
    private val isInDesktopMode: Boolean
        get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false

    val runningAppPackages: Set<String>
    val runningTaskIds: Set<Int>
        /**
         * Returns the package names of apps that should be indicated as "running" to the user.
         * Returns the task IDs 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() {
@@ -80,22 +86,19 @@ class TaskbarRecentAppsController(
                return emptySet()
            }
            val tasks = desktopTask?.tasks ?: return emptySet()
            return tasks.map { task -> task.key.packageName }.toSet()
            return tasks.map { task -> task.key.id }.toSet()
        }

    val minimizedAppPackages: Set<String>
    val minimizedTaskIds: Set<Int>
        /**
         * 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).
         * Returns the task IDs for the tasks that should be indicated as "minimized" to the user.
         */
        get() {
            if (!canShowRunningApps || !isInDesktopMode) {
                return emptySet()
            }
            val desktopTasks = desktopTask?.tasks ?: return emptySet()
            val packageToTasks = desktopTasks.groupBy { it.key.packageName }
            return packageToTasks.filterValues { tasks -> tasks.all { !it.isVisible } }.keys
            return desktopTasks.filter { !it.isVisible }.map { task -> task.key.id }.toSet()
        }

    private val recentTasksChangedListener =
@@ -137,25 +140,39 @@ class TaskbarRecentAppsController(
                .filter { itemInfo -> !itemInfo.isPredictedItem }
                .toMutableList()

        if (isInDesktopMode && canShowRunningApps) {
            shownHotseatItems =
                updateHotseatItemsFromRunningTasks(
                    getOrderedAndWrappedDesktopTasks(),
                    shownHotseatItems
                )
        }

        onRecentsOrHotseatChanged()

        return shownHotseatItems.toTypedArray()
    }

    private fun getOrderedAndWrappedDesktopTasks(): List<GroupTask> {
        val tasks = desktopTask?.tasks ?: emptyList()
        // Kind of hacky, we wrap each single task in the Desktop as a GroupTask.
        val orderFromId = orderedRunningTaskIds.withIndex().associate { (index, id) -> id to index }
        val sortedTasks = tasks.sortedWith(compareBy(nullsLast()) { orderFromId[it.key.id] })
        return sortedTasks.map { GroupTask(it) }
    }

    private fun reloadRecentTasksIfNeeded() {
        if (!recentsModel.isTaskListValid(taskListChangeId)) {
            taskListChangeId =
                recentsModel.getTasks { tasks ->
                    allRecentTasks = tasks
                    val oldRunningPackages = runningAppPackages
                    val oldMinimizedPackages = minimizedAppPackages
                    val oldRunningTaskdIds = runningTaskIds
                    val oldMinimizedTaskIds = minimizedTaskIds
                    desktopTask = allRecentTasks.filterIsInstance<DesktopTask>().firstOrNull()
                    val runningPackagesChanged = oldRunningPackages != runningAppPackages
                    val minimizedPackagessChanged = oldMinimizedPackages != minimizedAppPackages
                    val runningTasksChanged = oldRunningTaskdIds != runningTaskIds
                    val minimizedTasksChanged = oldMinimizedTaskIds != minimizedTaskIds
                    if (
                        onRecentsOrHotseatChanged() ||
                            runningPackagesChanged ||
                            minimizedPackagessChanged
                        onRecentsOrHotseatChanged() || runningTasksChanged || minimizedTasksChanged
                    ) {
                        controllers.taskbarViewController.commitRunningAppsToUI()
                    }
@@ -170,6 +187,7 @@ class TaskbarRecentAppsController(
     */
    private fun onRecentsOrHotseatChanged(): Boolean {
        val oldShownTasks = shownTasks
        orderedRunningTaskIds = updateOrderedRunningTaskIds()
        shownTasks =
            if (isInDesktopMode) {
                computeShownRunningTasks()
@@ -201,22 +219,39 @@ class TaskbarRecentAppsController(
        return shownTasksChanged
    }

    private fun updateOrderedRunningTaskIds(): MutableList<Int> {
        val desktopTaskAsList = getOrderedAndWrappedDesktopTasks()
        val desktopTaskIds = desktopTaskAsList.map { it.task1.key.id }
        var newOrder =
            orderedRunningTaskIds
                .filter { it in desktopTaskIds } // Only keep the tasks that are still running
                .toMutableList()
        // Add new tasks not already listed
        newOrder.addAll(desktopTaskIds.filter { it !in newOrder })
        return newOrder
    }

    private fun computeShownRunningTasks(): List<GroupTask> {
        if (!canShowRunningApps) {
            return emptyList()
        }
        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 }
        val desktopTaskAsList = getOrderedAndWrappedDesktopTasks()
        val desktopTaskIds = desktopTaskAsList.map { it.task1.key.id }
        val shownTaskIds = shownTasks.map { it.task1.key.id }
        // TODO(b/315344726 Multi-instance support): only show one icon per package once we support
        //  taskbar multi-instance menus
        val shownHotseatItemTaskIds =
            shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
        // Remove any newly-missing Tasks, and actual group-tasks
        val newShownTasks =
            shownTasks
                .filter { !it.hasMultipleTasks() }
                .filter { it.task1.key.id in desktopTaskIds }
                .toMutableList()
        // Add any new Tasks, maintaining the order from previous shownTasks.
        newShownTasks.addAll(desktopTaskAsList.filter { it.packageNames !in newShownPackages })
        return newShownTasks.toList()
        newShownTasks.addAll(desktopTaskAsList.filter { it.task1.key.id !in shownTaskIds })
        // Remove any tasks already covered by Hotseat icons
        return newShownTasks.filter { it.task1.key.id !in shownHotseatItemTaskIds }
    }

    private fun computeShownRecentTasks(): List<GroupTask> {
@@ -245,6 +280,25 @@ class TaskbarRecentAppsController(
        }
    }

    /**
     * Returns the hotseat items updated so that any item that points to a package with a running
     * task also references that task.
     */
    private fun updateHotseatItemsFromRunningTasks(
        groupTasks: List<GroupTask>,
        shownHotseatItems: List<ItemInfo>
    ): List<ItemInfo> =
        shownHotseatItems.map { itemInfo ->
            if (itemInfo is TaskItemInfo) {
                itemInfo
            } else {
                val foundTask =
                    groupTasks.find { task -> task.task1.key.packageName == itemInfo.targetPackage }
                        ?: return@map itemInfo
                TaskItemInfo(foundTask.task1.key.id, itemInfo as WorkspaceItemInfo)
            }
        }

    override fun dumpLogs(prefix: String, pw: PrintWriter) {
        pw.println("$prefix TaskbarRecentAppsController:")
        pw.println("$prefix\tcanShowRunningApps=$canShowRunningApps")
@@ -253,8 +307,8 @@ class TaskbarRecentAppsController(
        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")
        pw.println("$prefix\trunningTaskIds=$runningTaskIds")
        pw.println("$prefix\tminimizedTaskIds=$minimizedTaskIds")
    }

    private val GroupTask.packageNames: List<String>
+15 −11
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.TaskItemInfo;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
@@ -515,35 +516,38 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
        return mTaskbarView.getTaskbarDividerView();
    }

    /** Updates which icons are marked as running given the Set of currently running packages. */
    public void updateIconViewsRunningStates(Set<String> runningPackages,
            Set<String> minimizedPackages) {
    /**
     * Updates which icons are marked as running or minimized given the Sets of currently running
     * and minimized tasks.
     */
    public void updateIconViewsRunningStates(Set<Integer> runningTaskIds,
            Set<Integer> minimizedTaskIds) {
        for (View iconView : getIconViews()) {
            if (iconView instanceof BubbleTextView btv) {
                btv.updateRunningState(
                        getRunningAppState(btv, runningPackages, minimizedPackages));
                        getRunningAppState(btv, runningTaskIds, minimizedTaskIds));
            }
        }
    }

    private BubbleTextView.RunningAppState getRunningAppState(
            BubbleTextView btv,
            Set<String> runningPackages,
            Set<String> minimizedPackages) {
            Set<Integer> runningTaskIds,
            Set<Integer> minimizedTaskIds) {
        Object tag = btv.getTag();
        if (tag instanceof ItemInfo itemInfo) {
            if (minimizedPackages.contains(itemInfo.getTargetPackage())) {
        if (tag instanceof TaskItemInfo itemInfo) {
            if (minimizedTaskIds.contains(itemInfo.getTaskId())) {
                return BubbleTextView.RunningAppState.MINIMIZED;
            }
            if (runningPackages.contains(itemInfo.getTargetPackage())) {
            if (runningTaskIds.contains(itemInfo.getTaskId())) {
                return BubbleTextView.RunningAppState.RUNNING;
            }
        }
        if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
            if (minimizedPackages.contains(groupTask.task1.key.getPackageName())) {
            if (minimizedTaskIds.contains(groupTask.task1.key.id)) {
                return BubbleTextView.RunningAppState.MINIMIZED;
            }
            if (runningPackages.contains(groupTask.task1.key.getPackageName())) {
            if (runningTaskIds.contains(groupTask.task1.key.id)) {
                return BubbleTextView.RunningAppState.RUNNING;
            }
        }
+247 −135

File changed.

Preview size limit exceeded, changes collapsed.

Loading