diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 9147e4c54f..22fe82b0ec 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -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" +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 0de0550016..61877f0afa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -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); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index f9ddc3db2f..58c5e835c9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -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, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 35e1c7baa9..2b0e1699cc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -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()) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java deleted file mode 100644 index 606ba5b633..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java +++ /dev/null @@ -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 getRunningApps() { - return emptySet(); - } - - /** Returns the set of apps whose tasks are all minimized. */ - public Set getMinimizedApps() { - return emptySet(); - } -} diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt similarity index 62% rename from quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt rename to quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt index d4bef28bf8..0946caf986 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt @@ -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? = null private var allRunningDesktopAppInfos: List? = null @@ -55,22 +62,40 @@ class DesktopTaskbarRunningAppsController( private val isInDesktopMode: Boolean get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false - override fun onDestroy() { - super.onDestroy() + val runningApps: Set + get() { + if (!isEnabled || !isInDesktopMode) { + return emptySet() + } + return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() + } + + val minimizedApps: Set + 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?) { + /** Stores the current [AppInfo] instances, no-op except in desktop environment. */ + fun setApps(apps: Array?) { this.apps = apps } - override fun isEnabled() = true - - @VisibleForTesting - public override fun updateHotseatItemInfos(hotseatItems: Array): Array { - 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): Array { + if (!isEnabled || !isInDesktopMode) { return hotseatItems } val newHotseatItemInfos = @@ -89,55 +114,6 @@ class DesktopTaskbarRunningAppsController( return newHotseatItemInfos.toTypedArray() } - override fun getRunningApps(): Set { - if (!isInDesktopMode) { - return emptySet() - } - return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() - } - - override fun getMinimizedApps(): Set { - 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, - runningAppInfo: List, - ) { - 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, hotseatItems: List @@ -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 SparseArray.toList(): List = valueIterator().asSequence().toList() + private fun updateMinimizedApps( + runningTasks: List, + runningAppInfo: List, + ) { + 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 } } diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt similarity index 82% rename from quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt rename to quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt index 5b567101b6..104263af5b 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt @@ -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): List {