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

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

Merge "Load and draw Recent/Running tasks that aren't in Hotseat" into main

parents a79bc15c c5995c8f
Loading
Loading
Loading
Loading
+12 −6
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.views.BubbleTextHolder;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LogUtils;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
@@ -181,7 +182,9 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im

    private DragView startInternalDrag(
            BubbleTextView btv, @Nullable DragPreviewProvider dragPreviewProvider) {
        float iconScale = btv.getIcon().getAnimatedScale();
        // TODO(b/344038728): null check is only necessary because Recents doesn't use
        //  FastBitmapDrawable
        float iconScale = btv.getIcon() == null ? 1f : btv.getIcon().getAnimatedScale();

        // Clear the pressed state if necessary
        btv.clearFocus();
@@ -248,7 +251,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
                dragLayerX + dragOffset.x,
                dragLayerY + dragOffset.y,
                (View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */,
                (ItemInfo) btv.getTag(),
                btv.getTag() instanceof ItemInfo itemInfo ? itemInfo : null,
                dragRect,
                scale * iconScale,
                scale,
@@ -288,7 +291,9 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
                initialDragViewScale,
                dragViewScaleOnDrop,
                scalePx);
        if (dragInfo != null) {
            dragView.setItemInfo(dragInfo);
        }
        mDragObject.dragComplete = false;

        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
@@ -301,7 +306,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im

        mDragObject.dragSource = source;
        mDragObject.dragInfo = dragInfo;
        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
        mDragObject.originalDragInfo =
                mDragObject.dragInfo != null ? mDragObject.dragInfo.makeShallowCopy() : null;

        if (mOptions.preDragCondition != null) {
            dragView.setHasDragOffset(mOptions.preDragCondition.getDragOffset().x != 0
@@ -431,8 +437,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
                                null, item.user));
            }
            intent.putExtra(Intent.EXTRA_USER, item.user);
        } else if (tag instanceof Task) {
            Task task = (Task) tag;
        } else if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
            Task task = groupTask.task1;
            clipDescription = new ClipDescription(task.titleDescription,
                    new String[] {
                            ClipDescription.MIMETYPE_APPLICATION_TASK
+11 −8
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ 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.util.GroupTask;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -130,7 +131,7 @@ public class TaskbarModelCallbacks implements
        final int itemCount = mContainer.getChildCount();
        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
            View item = mContainer.getChildAt(itemIdx);
            if (op.evaluate((ItemInfo) item.getTag(), item)) {
            if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) {
                return;
            }
        }
@@ -201,18 +202,20 @@ public class TaskbarModelCallbacks implements
        if (mDeferUpdatesForSUW) {
            ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
            mDeferredUpdates = () ->
                    commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages,
                    commitHotseatItemUpdates(finalHotseatItemInfos,
                            recentAppsController.getShownTasks(), runningPackages,
                            minimizedPackages);
        } else {
            commitHotseatItemUpdates(hotseatItemInfos, runningPackages, minimizedPackages);
            commitHotseatItemUpdates(hotseatItemInfos,
                    recentAppsController.getShownTasks(), runningPackages, minimizedPackages);
        }
    }

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

    /**
+2 −2
Original line number Diff line number Diff line
@@ -148,8 +148,8 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
            icon.clearFocus();
            return null;
        }
        ItemInfo item = (ItemInfo) icon.getTag();
        if (!ShortcutUtil.supportsShortcuts(item)) {
        // TODO(b/344657629) support GroupTask as well, for Taskbar Recent apps
        if (!(icon.getTag() instanceof ItemInfo item) || !ShortcutUtil.supportsShortcuts(item)) {
            return null;
        }

+45 −4
Original line number Diff line number Diff line
@@ -16,15 +16,19 @@
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.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.util.CancellableTask
import com.android.quickstep.RecentsModel
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.systemui.shared.recents.model.Task
import com.android.window.flags.Flags.enableDesktopWindowingMode
import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps
import java.io.PrintWriter
import java.util.function.Consumer

/**
 * Provides recent apps functionality, when the Taskbar Recent Apps section is enabled. Behavior:
@@ -46,13 +50,19 @@ class TaskbarRecentAppsController(
            field = isEnabledFromTest
        }

    // TODO(b/343532825): Add a setting to disable Recents even when the flag is on.
    var canShowRecentApps = enableRecentsInTaskbar()
        @VisibleForTesting
        set(isEnabledFromTest) {
            field = isEnabledFromTest
        }

    // Initialized in init.
    private lateinit var controllers: TaskbarControllers

    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

@@ -93,6 +103,8 @@ class TaskbarRecentAppsController(
    private val recentTasksChangedListener =
        RecentsModel.RecentTasksChangedListener { reloadRecentTasksIfNeeded() }

    private val iconLoadRequests: MutableSet<CancellableTask<*>> = HashSet()

    // 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
@@ -107,12 +119,15 @@ class TaskbarRecentAppsController(

    fun onDestroy() {
        recentsModel.unregisterRecentTasksChangedListener()
        iconLoadRequests.forEach { it.cancel() }
        iconLoadRequests.clear()
    }

    /** Called to update hotseatItems, in order to de-dupe them from Recent/Running tasks later. */
    fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
        // Ignore predicted apps - we show running or recent apps instead.
        val removePredictions = isInDesktopMode && canShowRunningApps
        val removePredictions =
            (isInDesktopMode && canShowRunningApps) || (!isInDesktopMode && canShowRecentApps)
        if (!removePredictions) {
            shownHotseatItems = hotseatItems.filterNotNull()
            onRecentsOrHotseatChanged()
@@ -148,6 +163,17 @@ class TaskbarRecentAppsController(
            } else {
                computeShownRecentTasks()
            }

        for (groupTask in shownTasks) {
            for (task in groupTask.tasks) {
                val callback =
                    Consumer<Task> { controllers.taskbarViewController.onTaskUpdated(it) }
                val cancellableTask = recentsModel.iconCache.updateIconInBackground(task, callback)
                if (cancellableTask != null) {
                    iconLoadRequests.add(cancellableTask)
                }
            }
        }
    }

    private fun computeShownRunningTasks(): List<GroupTask> {
@@ -169,9 +195,19 @@ class TaskbarRecentAppsController(
    }

    private fun computeShownRecentTasks(): List<GroupTask> {
        // TODO(next CL): implement Recents section
        if (!canShowRecentApps || allRecentTasks.isEmpty()) {
            return emptyList()
        }
        // Remove the current task.
        val allRecentTasks = allRecentTasks.subList(0, allRecentTasks.size - 1)
        // TODO(b/315344726 Multi-instance support): dedupe Tasks of the same package too
        var shownTasks = dedupeHotseatTasks(allRecentTasks, shownHotseatItems)
        if (shownTasks.size > MAX_RECENT_TASKS) {
            // Remove any tasks older than MAX_RECENT_TASKS.
            shownTasks = shownTasks.subList(shownTasks.size - MAX_RECENT_TASKS, shownTasks.size)
        }
        return shownTasks
    }

    private fun dedupeHotseatTasks(
        groupTasks: List<GroupTask>,
@@ -187,6 +223,7 @@ class TaskbarRecentAppsController(
    override fun dumpLogs(prefix: String, pw: PrintWriter) {
        pw.println("$prefix TaskbarRecentAppsController:")
        pw.println("$prefix\tcanShowRunningApps=$canShowRunningApps")
        pw.println("$prefix\tcanShowRecentApps=$canShowRecentApps")
        pw.println("$prefix\tshownHotseatItems=${shownHotseatItems.map{item->item.targetPackage}}")
        pw.println("$prefix\tallRecentTasks=${allRecentTasks.map { it.packageNames }}")
        pw.println("$prefix\tdesktopTask=${desktopTask?.packageNames}")
@@ -197,4 +234,8 @@ class TaskbarRecentAppsController(

    private val GroupTask.packageNames: List<String>
        get() = tasks.map { task -> task.key.packageName }

    private companion object {
        const val MAX_RECENT_TASKS = 2
    }
}
+93 −12
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_

import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.Flags.enableRecentsInTaskbar;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
@@ -30,6 +31,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.DisplayCutout;
@@ -67,7 +69,11 @@ import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconButtonView;
import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.util.AssistStateManager;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;

import java.util.List;
import java.util.function.Predicate;

/**
@@ -168,7 +174,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        mAllAppsButton.setForegroundTint(
                mActivityContext.getColor(R.color.all_apps_button_color));

        if (enableTaskbarPinning()) {
        if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
            mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate(
                    R.layout.taskbar_divider,
                    this, false);
@@ -308,9 +314,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
    /**
     * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
     */
    protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
    protected void updateHotseatItems(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
        int nextViewIndex = 0;
        int numViewsAnimated = 0;
        boolean addedDividerForRecents = false;

        if (mAllAppsButton != null) {
            removeView(mAllAppsButton);
@@ -321,8 +328,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        }
        removeView(mQsb);

        for (int i = 0; i < hotseatItemInfos.length; i++) {
            ItemInfo hotseatItemInfo = hotseatItemInfos[i];
        // Add Hotseat icons.
        for (ItemInfo hotseatItemInfo : hotseatItemInfos) {
            if (hotseatItemInfo == null) {
                continue;
            }
@@ -388,11 +395,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
            }

            // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
            if (hotseatView instanceof BubbleTextView
                    && hotseatItemInfo instanceof WorkspaceItemInfo) {
                BubbleTextView btv = (BubbleTextView) hotseatView;
                WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) hotseatItemInfo;

            if (hotseatView instanceof BubbleTextView btv
                    && hotseatItemInfo instanceof WorkspaceItemInfo workspaceInfo) {
                boolean animate = btv.shouldAnimateIconChange((WorkspaceItemInfo) hotseatItemInfo);
                btv.applyFromWorkspaceItem(workspaceInfo, animate, numViewsAnimated);
                if (animate) {
@@ -405,6 +409,67 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
            }
            nextViewIndex++;
        }

        if (mTaskbarDivider != null && !recentTasks.isEmpty()) {
            addView(mTaskbarDivider, nextViewIndex++);
            addedDividerForRecents = true;
        }

        // Add Recent/Running icons.
        for (GroupTask task : recentTasks) {
            // Replace any Recent views with the appropriate type if it's not already that type.
            final int expectedLayoutResId;
            boolean isCollection = false;
            if (task.hasMultipleTasks()) {
                if (task instanceof DesktopTask) {
                    // TODO(b/316004172): use Desktop tile layout.
                    expectedLayoutResId = -1;
                } else {
                    // TODO(b/343289567): use R.layout.app_pair_icon
                    expectedLayoutResId = -1;
                }
                isCollection = true;
            } else {
                expectedLayoutResId = R.layout.taskbar_app_icon;
            }

            View recentIcon = null;
            while (nextViewIndex < getChildCount()) {
                recentIcon = getChildAt(nextViewIndex);

                // see if the view can be reused
                if ((recentIcon.getSourceLayoutResId() != expectedLayoutResId)
                        || (isCollection && (recentIcon.getTag() != task))) {
                    removeAndRecycle(recentIcon);
                    recentIcon = null;
                } else {
                    // View found
                    break;
                }
            }

            if (recentIcon == null) {
                if (isCollection) {
                    // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
                    continue;
                }

                recentIcon = inflate(expectedLayoutResId);
                LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
                recentIcon.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
                addView(recentIcon, nextViewIndex, lp);
            }

            if (recentIcon instanceof BubbleTextView btv) {
                applyGroupTaskToBubbleTextView(btv, task);
            }
            setClickAndLongClickListenersForIcon(recentIcon);
            if (enableCursorHoverStates()) {
                setHoverListenerForIcon(recentIcon);
            }
            nextViewIndex++;
        }

        // Remove remaining views
        while (nextViewIndex < getChildCount()) {
            removeAndRecycle(getChildAt(nextViewIndex));
@@ -413,8 +478,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        if (mAllAppsButton != null) {
            addView(mAllAppsButton, mIsRtl ? getChildCount() : 0);

            // if only all apps button present, don't include divider view.
            if (mTaskbarDivider != null && getChildCount() > 1) {
            // If there are no recent tasks, add divider after All Apps (unless it's the only view).
            if (!addedDividerForRecents && mTaskbarDivider != null && getChildCount() > 1) {
                addView(mTaskbarDivider, mIsRtl ? (getChildCount() - 1) : 1);
            }
        }
@@ -425,6 +490,20 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        }
    }

    /** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */
    public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) {
        // TODO(b/343289567): support app pairs.
        Task task1 = groupTask.task1;
        // TODO(b/344038728): use FastBitmapDrawable instead of Drawable, to get disabled state
        //  while dragging.
        Drawable taskIcon = groupTask.task1.icon;
        if (taskIcon != null) {
            taskIcon = taskIcon.getConstantState().newDrawable().mutate();
        }
        btv.applyIconAndLabel(taskIcon, task1.titleDescription);
        btv.setTag(groupTask);
    }

    /**
     * Sets OnClickListener and OnLongClickListener for the given view.
     */
@@ -677,7 +756,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        // map over all the shortcuts on the taskbar
        for (int i = 0; i < getChildCount(); i++) {
            View item = getChildAt(i);
            if (op.evaluate((ItemInfo) item.getTag(), item)) {
            // TODO(b/344657629): Support GroupTask as well for notification dots/popup
            if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) {
                return;
            }
        }
@@ -694,6 +774,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
                View item = getChildAt(i);
                if (!(item.getTag() instanceof ItemInfo)) {
                    // Should only happen for All Apps button.
                    // Will also happen for Recent/Running app icons. (Which have GroupTask as tags)
                    continue;
                }
                ItemInfo info = (ItemInfo) item.getTag();
Loading