From 52a8b7e24b6c25b43a993acbcd189e26e786bbbe Mon Sep 17 00:00:00 2001 From: Gustav Sennton Date: Fri, 12 Apr 2024 14:19:12 +0000 Subject: [PATCH] [Desktop Mode] Show indicators under Taskbar app icons for running apps In Desktop Mode we show running Desktop apps in the Taskbar. With this change we also show an indicator under the app icon for each such running app. Bug: 332504528 Test: manual Test: DesktopTaskbarRunningAppsControllerTest Flag: ACONFIG com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps DEVELOPMENT Change-Id: If0906dab8ad0bd8a78d93a4e99db47550e763bed --- quickstep/res/values/colors.xml | 1 + quickstep/res/values/dimens.xml | 3 ++ .../DesktopTaskbarRunningAppsController.kt | 7 +++ .../taskbar/TaskbarModelCallbacks.java | 13 ++++- .../taskbar/TaskbarRecentAppsController.java | 15 +++++- .../taskbar/TaskbarViewController.java | 10 ++++ ...DesktopTaskbarRunningAppsControllerTest.kt | 23 +++++++++ res/values/colors.xml | 2 + res/values/dimens.xml | 3 ++ src/com/android/launcher3/BubbleTextView.java | 50 +++++++++++++++++++ .../views/DoubleShadowBubbleTextView.java | 1 + 11 files changed, 124 insertions(+), 4 deletions(-) diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml index 1b5b0ee4db..14a916f68b 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -31,6 +31,7 @@ #99000000 #EBffffff #99000000 + #646464 #ffffff diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index dbf075cda3..b862d7c07d 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -353,6 +353,9 @@ 48dp 1dp 72dp + 4dp + 14dp + 2dp 12dp diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt index f665e214d7..06d25a21a7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt @@ -80,6 +80,13 @@ class DesktopTaskbarRunningAppsController( return newHotseatItemInfos.toTypedArray() } + override fun getRunningApps(): Set { + if (!isInDesktopMode) { + return emptySet() + } + return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() + } + @VisibleForTesting public override fun updateRunningApps(hotseatItems: SparseArray?) { if (!isInDesktopMode) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 6c84f806db..be87cfd8b7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -47,6 +47,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; /** @@ -233,15 +234,23 @@ public class TaskbarModelCallbacks implements } hotseatItemInfos = mControllers.taskbarRecentAppsController .updateHotseatItemInfos(hotseatItemInfos); + Set runningPackages = mControllers.taskbarRecentAppsController.getRunningApps(); if (mDeferUpdatesForSUW) { ItemInfo[] finalHotseatItemInfos = hotseatItemInfos; - mDeferredUpdates = () -> mContainer.updateHotseatItems(finalHotseatItemInfos); + mDeferredUpdates = () -> + commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages); } else { - mContainer.updateHotseatItems(hotseatItemInfos); + commitHotseatItemUpdates(hotseatItemInfos, runningPackages); } } + private void commitHotseatItemUpdates( + ItemInfo[] hotseatItemInfos, Set runningPackages) { + mContainer.updateHotseatItems(hotseatItemInfos); + mControllers.taskbarViewController.updateIconViewsRunningStates(runningPackages); + } + /** * This is used to defer UI updates after SUW builds the unstash animation. * @param defer if true, defers updates to the UI diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java index 8445cff0ee..9b84f1bf94 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar; +import static java.util.Collections.emptySet; + import android.util.SparseArray; import androidx.annotation.CallSuper; @@ -22,6 +24,8 @@ import androidx.annotation.CallSuper; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; +import java.util.Set; + /** * Base class for providing recent apps functionality */ @@ -43,7 +47,8 @@ public class TaskbarRecentAppsController { } /** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */ - protected void setApps(AppInfo[] apps) { } + protected void setApps(AppInfo[] apps) { + } /** * Indicates whether recent apps functionality is enabled, should return false except in @@ -59,5 +64,11 @@ public class TaskbarRecentAppsController { } /** Called to update the list of currently running apps, no-op except in desktop environment. */ - protected void updateRunningApps(SparseArray hotseatItems) { } + protected void updateRunningApps(SparseArray hotseatItems) { + } + + /** Returns the currently running apps, or an empty Set if outside of Desktop environment. */ + public Set getRunningApps() { + return emptySet(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 5d0eac3c40..e0b446e5a9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -71,6 +71,7 @@ import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.views.IconButtonView; import java.io.PrintWriter; +import java.util.Set; import java.util.function.Predicate; /** @@ -507,6 +508,15 @@ 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 runningPackages) { + for (View iconView : getIconViews()) { + if (iconView instanceof BubbleTextView btv) { + btv.updateRunningState(runningPackages.contains(btv.getTargetPackageName())); + } + } + } + /** * Defers any updates to the UI for the setup wizard animation. */ diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt index 93eefe22d7..2cfcf380fd 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt @@ -154,6 +154,29 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() { assertThat(newHotseatItems?.map { it.targetPackage }).isEqualTo(expectedPackages) } + @Test + fun getRunningApps_notInDesktopMode_returnsEmptySet() { + setInDesktopMode(false) + val runningTasks = + createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)) + whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks) + taskbarRunningAppsController.updateRunningApps(createSparseArray(emptyList())) + + assertThat(taskbarRunningAppsController.runningApps).isEqualTo(emptySet()) + } + + @Test + fun getRunningApps_inDesktopMode_returnsRunningApps() { + setInDesktopMode(true) + val runningTasks = + createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)) + whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks) + taskbarRunningAppsController.updateRunningApps(createSparseArray(emptyList())) + + assertThat(taskbarRunningAppsController.runningApps) + .isEqualTo(setOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)) + } + private fun createHotseatItemsFromPackageNames(packageNames: List): List { return packageNames.map { createTestAppInfo(packageName = it) } } diff --git a/res/values/colors.xml b/res/values/colors.xml index a620eb0ff4..dfe40fc30f 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -90,6 +90,8 @@ #D3E3FD #0842A0 + #000000 + #00668B #B5CAD7 #4BB6E8 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 31def0436b..1bf59e8477 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -394,6 +394,9 @@ 18dp 50dp 0dp + 0dp + 0dp + 0dp 0dp diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 83236d14ff..7f316e2cae 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -46,6 +46,7 @@ import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Property; +import android.util.Size; import android.util.TypedValue; import android.view.KeyEvent; import android.view.MotionEvent; @@ -182,6 +183,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private Animator mDotScaleAnim; private boolean mForceHideDot; + // These fields, related to showing running apps, are only used for Taskbar. + private final Size mRunningAppIndicatorSize; + private final int mRunningAppIndicatorTopMargin; + private final Paint mRunningAppIndicatorPaint; + private final Rect mRunningAppIconBounds = new Rect(); + private boolean mIsRunning; + @ViewDebug.ExportedProperty(category = "launcher") private boolean mStayPressed; @ViewDebug.ExportedProperty(category = "launcher") @@ -248,6 +256,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, defaultIconSize); a.recycle(); + mRunningAppIndicatorSize = new Size( + getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_width), + getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_height)); + mRunningAppIndicatorTopMargin = + getResources().getDimensionPixelSize( + R.dimen.taskbar_running_app_indicator_top_margin); + mRunningAppIndicatorPaint = new Paint(); + mRunningAppIndicatorPaint.setColor(getResources().getColor( + R.color.taskbar_running_app_indicator_color, context.getTheme())); + mLongPressHelper = new CheckLongPressHelper(this); mDotParams = new DotRenderer.DrawParams(); @@ -394,6 +412,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setDownloadStateContentDescription(info, info.getProgressLevel()); } + /** Updates whether the app this view represents is currently running. */ + @UiThread + public void updateRunningState(boolean isRunning) { + mIsRunning = isRunning; + } + protected void setItemInfo(ItemInfoWithIcon itemInfo) { setTag(itemInfo); } @@ -620,6 +644,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public void onDraw(Canvas canvas) { super.onDraw(canvas); drawDotIfNecessary(canvas); + drawRunningAppIndicatorIfNecessary(canvas); } /** @@ -640,6 +665,22 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } + /** Draws a line under the app icon if this is representing a running app in Desktop Mode. */ + protected void drawRunningAppIndicatorIfNecessary(Canvas canvas) { + if (!mIsRunning || mDisplay != DISPLAY_TASKBAR) { + return; + } + getIconBounds(mRunningAppIconBounds); + // TODO(b/333872717): update color, shape, and size of indicator + int indicatorTop = mRunningAppIconBounds.bottom + mRunningAppIndicatorTopMargin; + canvas.drawRect( + mRunningAppIconBounds.centerX() - mRunningAppIndicatorSize.getWidth() / 2, + indicatorTop, + mRunningAppIconBounds.centerX() + mRunningAppIndicatorSize.getWidth() / 2, + indicatorTop + mRunningAppIndicatorSize.getHeight(), + mRunningAppIndicatorPaint); + } + @Override public void setForceHideDot(boolean forceHideDot) { if (mForceHideDot == forceHideDot) { @@ -1230,4 +1271,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public boolean canShowLongPressPopup() { return getTag() instanceof ItemInfo && ShortcutUtil.supportsShortcuts((ItemInfo) getTag()); } + + /** Returns the package name of the app this icon represents. */ + public String getTargetPackageName() { + Object tag = getTag(); + if (tag instanceof ItemInfo itemInfo) { + return itemInfo.getTargetPackage(); + } + return null; + } } diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java index a309e6e95e..bc66a33b0c 100644 --- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java +++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java @@ -77,6 +77,7 @@ public class DoubleShadowBubbleTextView extends BubbleTextView { canvas.restore(); drawDotIfNecessary(canvas); + drawRunningAppIndicatorIfNecessary(canvas); } public static class ShadowInfo {