Merge changes I8411fb83,I5b757558 into main

* changes:
  Move Desktop running apps support into TaskbarRecentAppsController
  Add flag for Recents in Taskbar
This commit is contained in:
Treehugger Robot
2024-06-11 02:16:56 +00:00
committed by Android (Google) Code Review
7 changed files with 123 additions and 197 deletions
+7
View File
@@ -284,3 +284,10 @@ flag {
description: "Enables folders in all apps"
bug: "341582436"
}
flag {
name: "enable_recents_in_taskbar"
namespace: "launcher"
description: "Replace hybrid hotseat app predictions with strictly Recent Apps"
bug: "315354060"
}
@@ -41,8 +41,6 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
import static com.android.wm.shell.Flags.enableTinyTaskbar;
import android.animation.AnimatorSet;
@@ -304,7 +302,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new VoiceInteractionWindowController(this),
new TaskbarTranslationController(this),
new TaskbarSpringOnStashController(this),
createTaskbarRecentAppsController(),
new TaskbarRecentAppsController(
RecentsModel.INSTANCE.get(this),
LauncherActivityInterface.INSTANCE::getDesktopVisibilityController),
TaskbarEduTooltipController.newInstance(this),
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this, () ->
@@ -314,16 +314,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mLauncherPrefs = LauncherPrefs.get(this);
}
private TaskbarRecentAppsController createTaskbarRecentAppsController() {
// TODO(b/335401172): unify DesktopMode checks in Launcher
if (enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()) {
return new DesktopTaskbarRunningAppsController(
RecentsModel.INSTANCE.get(this),
LauncherActivityInterface.INSTANCE::getDesktopVisibilityController);
}
return TaskbarRecentAppsController.DEFAULT;
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile launcherDp) {
applyDeviceProfile(launcherDp);
@@ -180,8 +180,9 @@ public class TaskbarControllers {
taskbarUnfoldAnimationController, taskbarKeyguardController,
stashedHandleViewController, taskbarStashController,
taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
voiceInteractionWindowController, taskbarTranslationController,
taskbarEduTooltipController, keyboardQuickSwitchController, taskbarPinningController
voiceInteractionWindowController, taskbarRecentAppsController,
taskbarTranslationController, taskbarEduTooltipController,
keyboardQuickSwitchController, taskbarPinningController,
};
mBackgroundRendererControllers = new BackgroundRendererController[] {
taskbarDragLayerController, taskbarScrimViewController,
@@ -79,7 +79,7 @@ public class TaskbarModelCallbacks implements
public void init(TaskbarControllers controllers) {
mControllers = controllers;
if (mControllers.taskbarRecentAppsController.isEnabled()) {
if (mControllers.taskbarRecentAppsController.getCanShowRunningApps()) {
RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this);
if (shouldShowRunningAppsInDesktopMode()) {
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.taskbar;
import static java.util.Collections.emptySet;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
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
*/
public class TaskbarRecentAppsController {
public static final TaskbarRecentAppsController DEFAULT = new TaskbarRecentAppsController();
// Initialized in init.
protected TaskbarControllers mControllers;
@CallSuper
protected void init(TaskbarControllers taskbarControllers) {
mControllers = taskbarControllers;
}
@CallSuper
protected void onDestroy() {
mControllers = null;
}
/** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */
protected void setApps(AppInfo[] apps) {
}
/**
* Indicates whether recent apps functionality is enabled, should return false except in
* desktop environment.
*/
protected boolean isEnabled() {
return false;
}
/** Called to update hotseatItems, no-op except in desktop environment. */
protected ItemInfo[] updateHotseatItemInfos(@NonNull ItemInfo[] hotseatItems) {
return hotseatItems;
}
/** Called to update the list of currently running apps, no-op except in desktop environment. */
protected void updateRunningApps() {}
/** Returns the currently running apps, or an empty Set if outside of Desktop environment. */
public Set<String> getRunningApps() {
return emptySet();
}
/** Returns the set of apps whose tasks are all minimized. */
public Set<String> getMinimizedApps() {
return emptySet();
}
}
@@ -13,37 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.taskbar
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration
import android.util.Log
import android.util.SparseArray
import androidx.annotation.VisibleForTesting
import androidx.core.util.valueIterator
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.quickstep.RecentsModel
import kotlin.collections.filterNotNull
import com.android.window.flags.Flags.enableDesktopWindowingMode
import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps
import java.io.PrintWriter
/**
* Shows running apps when in Desktop Mode.
*
* Users can enter and exit Desktop Mode at run-time, meaning this class falls back to the default
* recent-apps behaviour when outside of Desktop Mode.
*
* This class should only be used if
* [com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps] is enabled.
* Provides recent apps functionality, when the Taskbar Recent Apps section is enabled. Behavior:
* - When in Fullscreen mode: show the N most recent Tasks
* - When in Desktop Mode: show the currently running (open) Tasks
*/
class DesktopTaskbarRunningAppsController(
class TaskbarRecentAppsController(
private val recentsModel: RecentsModel,
// Pass a provider here instead of the actual DesktopVisibilityController instance since that
// instance might not be available when this constructor is called.
private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?,
) : TaskbarRecentAppsController() {
) : LoggableTaskbarController {
// TODO(b/335401172): unify DesktopMode checks in Launcher.
val canShowRunningApps =
enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()
// TODO(b/343532825): Add a setting to disable Recents even when the flag is on.
@VisibleForTesting
var isEnabled = enableRecentsInTaskbar() || canShowRunningApps
// Initialized in init.
private lateinit var controllers: TaskbarControllers
private var apps: Array<AppInfo>? = null
private var allRunningDesktopAppInfos: List<AppInfo>? = null
@@ -55,22 +62,40 @@ class DesktopTaskbarRunningAppsController(
private val isInDesktopMode: Boolean
get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false
override fun onDestroy() {
super.onDestroy()
val runningApps: Set<String>
get() {
if (!isEnabled || !isInDesktopMode) {
return emptySet()
}
return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
}
val minimizedApps: Set<String>
get() {
if (!isInDesktopMode) {
return emptySet()
}
return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet()
?: emptySet()
}
fun init(taskbarControllers: TaskbarControllers) {
controllers = taskbarControllers
}
fun onDestroy() {
apps = null
}
@VisibleForTesting
public override fun setApps(apps: Array<AppInfo>?) {
/** Stores the current [AppInfo] instances, no-op except in desktop environment. */
fun setApps(apps: Array<AppInfo>?) {
this.apps = apps
}
override fun isEnabled() = true
@VisibleForTesting
public override fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
if (!isInDesktopMode) {
Log.d(TAG, "updateHotseatItemInfos: not in Desktop Mode")
/** Called to update hotseatItems, in order to de-dupe them from Recent/Running tasks later. */
// TODO(next CL): add new section of Tasks instead of changing Hotseat items
fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
if (!isEnabled || !isInDesktopMode) {
return hotseatItems
}
val newHotseatItemInfos =
@@ -89,55 +114,6 @@ class DesktopTaskbarRunningAppsController(
return newHotseatItemInfos.toTypedArray()
}
override fun getRunningApps(): Set<String> {
if (!isInDesktopMode) {
return emptySet()
}
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) {
Log.d(TAG, "updateRunningApps: not in Desktop Mode")
mControllers.taskbarViewController.commitRunningAppsToUI()
return
}
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>
@@ -165,12 +141,43 @@ class DesktopTaskbarRunningAppsController(
.filterNotNull()
}
private fun getAppInfosFromRunningTask(task: RunningTaskInfo): AppInfo? =
apps?.firstOrNull { it.targetPackage == task.realActivity?.packageName }
/** Called to update the list of currently running apps, no-op except in desktop environment. */
fun updateRunningApps() {
if (!isEnabled || !isInDesktopMode) {
return controllers.taskbarViewController.commitRunningAppsToUI()
}
val runningTasks = getDesktopRunningTasks()
val runningAppInfo = getAppInfosFromRunningTasks(runningTasks)
allRunningDesktopAppInfos = runningAppInfo
updateMinimizedApps(runningTasks, runningAppInfo)
controllers.taskbarViewController.commitRunningAppsToUI()
}
private fun <E> SparseArray<E>.toList(): List<E> = valueIterator().asSequence().toList()
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()
}
companion object {
private const val TAG = "TabletDesktopTaskbarRunningAppsController"
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println("$prefix TaskbarRecentAppsController:")
pw.println("$prefix\tisEnabled=$isEnabled")
pw.println("$prefix\tcanShowRunningApps=$canShowRunningApps")
// TODO(next CL): add more logs
}
}
@@ -37,7 +37,7 @@ import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
@get:Rule val mockitoRule = MockitoJUnit.rule()
@@ -46,19 +46,18 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
private var nextTaskId: Int = 500
private lateinit var taskbarRunningAppsController: DesktopTaskbarRunningAppsController
private lateinit var recentAppsController: TaskbarRecentAppsController
private lateinit var userHandle: UserHandle
@Before
fun setUp() {
super.setup()
userHandle = Process.myUserHandle()
taskbarRunningAppsController =
DesktopTaskbarRunningAppsController(mockRecentsModel) {
mockDesktopVisibilityController
}
taskbarRunningAppsController.init(taskbarControllers)
taskbarRunningAppsController.setApps(
recentAppsController =
TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
recentAppsController.init(taskbarControllers)
recentAppsController.isEnabled = true
recentAppsController.setApps(
ALL_APP_PACKAGES.map { createTestAppInfo(packageName = it) }.toTypedArray()
)
}
@@ -69,7 +68,7 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val hotseatItems =
createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
.isEqualTo(hotseatItems.toTypedArray())
}
@@ -81,10 +80,10 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(hotseatPackages)
@@ -96,7 +95,7 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val hotseatItems =
createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
.isEqualTo(hotseatItems.toTypedArray())
}
@@ -108,10 +107,10 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
val expectedPackages =
listOf(
@@ -134,10 +133,10 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
)
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
val expectedPackages =
listOf(
@@ -156,10 +155,10 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps).isEmpty()
assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
assertThat(recentAppsController.runningApps).isEmpty()
assertThat(recentAppsController.minimizedApps).isEmpty()
}
@Test
@@ -168,11 +167,11 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
assertThat(recentAppsController.runningApps)
.containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
assertThat(recentAppsController.minimizedApps).isEmpty()
}
@Test
@@ -187,12 +186,11 @@ class DesktopTaskbarRunningAppsControllerTest : TaskbarBaseTestCase() {
)
)
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
recentAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
assertThat(recentAppsController.runningApps)
.containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
assertThat(taskbarRunningAppsController.minimizedApps)
.containsExactly(RUNNING_APP_PACKAGE_3)
assertThat(recentAppsController.minimizedApps).containsExactly(RUNNING_APP_PACKAGE_3)
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {