Loading quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +12 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -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 Loading quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +11 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } } Loading Loading @@ -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); } /** Loading quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; } Loading quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt +45 −4 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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 Loading Loading @@ -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 Loading @@ -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() Loading Loading @@ -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> { Loading @@ -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>, Loading @@ -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}") Loading @@ -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 } } quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +93 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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)); Loading @@ -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); } } Loading @@ -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. */ Loading Loading @@ -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; } } Loading @@ -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 Loading
quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +12 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -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 Loading
quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +11 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } } Loading Loading @@ -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); } /** Loading
quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; } Loading
quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt +45 −4 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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 Loading Loading @@ -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 Loading @@ -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() Loading Loading @@ -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> { Loading @@ -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>, Loading @@ -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}") Loading @@ -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 } }
quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +93 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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)); Loading @@ -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); } } Loading @@ -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. */ Loading Loading @@ -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; } } Loading @@ -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