Update desktop mode to identify minimized tasks

When a task is minimised, it becomes invisible but is still running.
WMShell now signals to the launcher when a task's visibility changes.
The task bar takes the visibility into account to know if a running task
is, in fact, minimised.

Test: atest NexusLauncherTests:DesktopTaskbarRunningAppsControllerTest
Flag: com.android.window.flags.enable_desktop_windowing_taskbar_running_apps
Bug: 333872717
Change-Id: Iaff6b1240d354bb3c4de8e4884948acf9bf40112
This commit is contained in:
Pierre Barbier de Reuille
2024-05-17 13:24:22 +01:00
parent 656f0e8d13
commit f60dd5471c
8 changed files with 145 additions and 38 deletions

View File

@@ -358,6 +358,9 @@
<dimen name="taskbar_running_app_indicator_height">4dp</dimen>
<dimen name="taskbar_running_app_indicator_width">14dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">2dp</dimen>
<dimen name="taskbar_minimized_app_indicator_height">2dp</dimen>
<dimen name="taskbar_minimized_app_indicator_width">12dp</dimen>
<dimen name="taskbar_minimized_app_indicator_top_margin">2dp</dimen>
<!-- Transient taskbar -->
<dimen name="transient_taskbar_padding">12dp</dimen>

View File

@@ -47,6 +47,7 @@ class DesktopTaskbarRunningAppsController(
private var apps: Array<AppInfo>? = null
private var allRunningDesktopAppInfos: List<AppInfo>? = null
private var allMinimizedDesktopAppInfos: List<AppInfo>? = null
private val desktopVisibilityController: DesktopVisibilityController?
get() = desktopVisibilityControllerProvider()
@@ -95,6 +96,13 @@ class DesktopTaskbarRunningAppsController(
return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
}
override fun getMinimizedApps(): Set<String> {
if (!isInDesktopMode) {
return emptySet()
}
return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
}
@VisibleForTesting
public override fun updateRunningApps() {
if (!isInDesktopMode) {
@@ -102,10 +110,34 @@ class DesktopTaskbarRunningAppsController(
mControllers.taskbarViewController.commitRunningAppsToUI()
return
}
allRunningDesktopAppInfos = getRunningDesktopAppInfos()
val runningTasks = getDesktopRunningTasks()
val runningAppInfo = getAppInfosFromRunningTasks(runningTasks)
allRunningDesktopAppInfos = runningAppInfo
updateMinimizedApps(runningTasks, runningAppInfo)
mControllers.taskbarViewController.commitRunningAppsToUI()
}
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 }
}
val minimizedTaskIds = runningTasks.associate { it.taskId to !it.isVisible }
allMinimizedDesktopAppInfos =
allRunningAppTasks
.filterValues { taskIds -> taskIds.all { minimizedTaskIds[it] ?: false } }
.keys
.toList()
}
private fun getRunningDesktopAppInfosExceptHotseatApps(
allRunningDesktopAppInfos: List<AppInfo>,
hotseatItems: List<ItemInfo>
@@ -116,15 +148,10 @@ class DesktopTaskbarRunningAppsController(
.map { WorkspaceItemInfo(it) }
}
private fun getRunningDesktopAppInfos(): List<AppInfo> {
return getAppInfosFromRunningTasks(
recentsModel.runningTasks
.filter { taskInfo: RunningTaskInfo ->
taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
}
.toList()
)
}
private fun getDesktopRunningTasks(): List<RunningTaskInfo> =
recentsModel.runningTasks.filter { taskInfo: RunningTaskInfo ->
taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
}
// TODO(b/335398876) fetch app icons from Tasks instead of AppInfos
private fun getAppInfosFromRunningTasks(tasks: List<RunningTaskInfo>): List<AppInfo> {
@@ -138,9 +165,10 @@ class DesktopTaskbarRunningAppsController(
.filterNotNull()
}
private fun <E> SparseArray<E>.toList(): List<E> {
return valueIterator().asSequence().toList()
}
private fun getAppInfosFromRunningTask(task: RunningTaskInfo): AppInfo? =
apps?.firstOrNull { it.targetPackage == task.realActivity?.packageName }
private fun <E> SparseArray<E>.toList(): List<E> = valueIterator().asSequence().toList()
companion object {
private const val TAG = "TabletDesktopTaskbarRunningAppsController"

View File

@@ -68,7 +68,7 @@ public class TaskbarModelCallbacks implements
// Used to defer any UI updates during the SUW unstash animation.
private boolean mDeferUpdatesForSUW;
private Runnable mDeferredUpdates;
private DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
private final DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
visible -> updateRunningApps();
public TaskbarModelCallbacks(
@@ -235,20 +235,23 @@ public class TaskbarModelCallbacks implements
hotseatItemInfos = mControllers.taskbarRecentAppsController
.updateHotseatItemInfos(hotseatItemInfos);
Set<String> runningPackages = mControllers.taskbarRecentAppsController.getRunningApps();
Set<String> minimizedPackages = mControllers.taskbarRecentAppsController.getMinimizedApps();
if (mDeferUpdatesForSUW) {
ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
mDeferredUpdates = () ->
commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages);
commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages,
minimizedPackages);
} else {
commitHotseatItemUpdates(hotseatItemInfos, runningPackages);
commitHotseatItemUpdates(hotseatItemInfos, runningPackages, minimizedPackages);
}
}
private void commitHotseatItemUpdates(
ItemInfo[] hotseatItemInfos, Set<String> runningPackages) {
private void commitHotseatItemUpdates(ItemInfo[] hotseatItemInfos, Set<String> runningPackages,
Set<String> minimizedPackages) {
mContainer.updateHotseatItems(hotseatItemInfos);
mControllers.taskbarViewController.updateIconViewsRunningStates(runningPackages);
mControllers.taskbarViewController.updateIconViewsRunningStates(runningPackages,
minimizedPackages);
}
/**

View File

@@ -69,4 +69,9 @@ public class TaskbarRecentAppsController {
public Set<String> getRunningApps() {
return emptySet();
}
/** Returns the set of apps whose tasks are all minimized. */
public Set<String> getMinimizedApps() {
return emptySet();
}
}

View File

@@ -510,14 +510,30 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
/** Updates which icons are marked as running given the Set of currently running packages. */
public void updateIconViewsRunningStates(Set<String> runningPackages) {
public void updateIconViewsRunningStates(Set<String> runningPackages,
Set<String> minimizedPackages) {
for (View iconView : getIconViews()) {
if (iconView instanceof BubbleTextView btv) {
btv.updateRunningState(runningPackages.contains(btv.getTargetPackageName()));
btv.updateRunningState(
getRunningAppState(btv.getTargetPackageName(), runningPackages,
minimizedPackages));
}
}
}
private BubbleTextView.RunningAppState getRunningAppState(
String packageName,
Set<String> runningPackages,
Set<String> minimizedPackages) {
if (minimizedPackages.contains(packageName)) {
return BubbleTextView.RunningAppState.MINIMIZED;
}
if (runningPackages.contains(packageName)) {
return BubbleTextView.RunningAppState.RUNNING;
}
return BubbleTextView.RunningAppState.NOT_RUNNING;
}
/**
* Defers any updates to the UI for the setup wizard animation.
*/

View File

@@ -86,7 +86,8 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(hotseatPackages)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(hotseatPackages)
}
@Test
@@ -119,7 +120,8 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(expectedPackages)
}
@Test
@@ -144,7 +146,8 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(expectedPackages)
}
@Test
@@ -155,7 +158,8 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps).isEqualTo(emptySet<String>())
assertThat(taskbarRunningAppsController.runningApps).isEmpty()
assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
}
@Test
@@ -167,7 +171,28 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
.isEqualTo(setOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
.containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
}
@Test
fun getMinimizedApps_inDesktopMode_returnsAllAppsRunningAndInvisibleAppsMinimized() {
setInDesktopMode(true)
val runningTasks =
ArrayList(
listOf(
createDesktopTaskInfo(RUNNING_APP_PACKAGE_1) { isVisible = true },
createDesktopTaskInfo(RUNNING_APP_PACKAGE_2) { isVisible = true },
createDesktopTaskInfo(RUNNING_APP_PACKAGE_3) { isVisible = false },
)
)
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
.containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
assertThat(taskbarRunningAppsController.minimizedApps)
.containsExactly(RUNNING_APP_PACKAGE_3)
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
@@ -180,11 +205,15 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
return ArrayList(packageNames.map { createDesktopTaskInfo(packageName = it) })
}
private fun createDesktopTaskInfo(packageName: String): RunningTaskInfo {
private fun createDesktopTaskInfo(
packageName: String,
init: RunningTaskInfo.() -> Unit = { isVisible = true },
): RunningTaskInfo {
return RunningTaskInfo().apply {
taskId = nextTaskId++
configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
realActivity = ComponentName(packageName, "TestActivity")
init()
}
}

View File

@@ -416,6 +416,9 @@
<dimen name="taskbar_running_app_indicator_height">0dp</dimen>
<dimen name="taskbar_running_app_indicator_width">0dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">0dp</dimen>
<dimen name="taskbar_minimized_app_indicator_height">0dp</dimen>
<dimen name="taskbar_minimized_app_indicator_width">0dp</dimen>
<dimen name="taskbar_minimized_app_indicator_top_margin">0dp</dimen>
<!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="transient_taskbar_padding">0dp</dimen>

View File

@@ -186,9 +186,20 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
// These fields, related to showing running apps, are only used for Taskbar.
private final Size mRunningAppIndicatorSize;
private final int mRunningAppIndicatorTopMargin;
private final Size mMinimizedAppIndicatorSize;
private final int mMinimizedAppIndicatorTopMargin;
private final Paint mRunningAppIndicatorPaint;
private final Rect mRunningAppIconBounds = new Rect();
private boolean mIsRunning;
private RunningAppState mRunningAppState;
/**
* Various options for the running state of an app.
*/
public enum RunningAppState {
NOT_RUNNING,
RUNNING,
MINIMIZED,
}
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mStayPressed;
@@ -259,9 +270,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
mRunningAppIndicatorSize = new Size(
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_width),
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_height));
mMinimizedAppIndicatorSize = new Size(
getResources().getDimensionPixelSize(R.dimen.taskbar_minimized_app_indicator_width),
getResources().getDimensionPixelSize(
R.dimen.taskbar_minimized_app_indicator_height));
mRunningAppIndicatorTopMargin =
getResources().getDimensionPixelSize(
R.dimen.taskbar_running_app_indicator_top_margin);
mMinimizedAppIndicatorTopMargin =
getResources().getDimensionPixelSize(
R.dimen.taskbar_minimized_app_indicator_top_margin);
mRunningAppIndicatorPaint = new Paint();
mRunningAppIndicatorPaint.setColor(getResources().getColor(
R.color.taskbar_running_app_indicator_color, context.getTheme()));
@@ -414,8 +432,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
/** Updates whether the app this view represents is currently running. */
@UiThread
public void updateRunningState(boolean isRunning) {
mIsRunning = isRunning;
public void updateRunningState(RunningAppState runningAppState) {
mRunningAppState = runningAppState;
}
protected void setItemInfo(ItemInfoWithIcon itemInfo) {
@@ -667,18 +685,20 @@ 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) {
if (mRunningAppState == RunningAppState.NOT_RUNNING || 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);
boolean isMinimized = mRunningAppState == RunningAppState.MINIMIZED;
int indicatorTop =
mRunningAppIconBounds.bottom + (isMinimized ? mMinimizedAppIndicatorTopMargin
: mRunningAppIndicatorTopMargin);
final Size indicatorSize =
isMinimized ? mMinimizedAppIndicatorSize : mRunningAppIndicatorSize;
canvas.drawRect(mRunningAppIconBounds.centerX() - indicatorSize.getWidth() / 2,
indicatorTop, mRunningAppIconBounds.centerX() + indicatorSize.getWidth() / 2,
indicatorTop + indicatorSize.getHeight(), mRunningAppIndicatorPaint);
}
@Override