diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 9147e4c54f..fecc43d0b2 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -284,3 +284,21 @@ 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" +} + +flag { + name: "enable_first_screen_broadcast_archiving_extras" + namespace: "launcher" + description: "adds Extras to first screen broadcast for archived apps" + bug: "322314760" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} 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/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java index eac4eaa130..911140aa2d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java @@ -5,9 +5,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK; @@ -16,6 +13,7 @@ import static com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_A import android.app.KeyguardManager; import com.android.launcher3.AbstractFloatingView; +import com.android.quickstep.util.SystemUiFlagUtils; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; @@ -26,19 +24,6 @@ import java.io.PrintWriter; */ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTaskbarController { - private static final long KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING - | SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED - | SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED - | SYSUI_STATE_WAKEFULNESS_MASK; - - // If any of these SysUi flags (via QuickstepContract) is set, the device to be considered - // locked. - public static final long MASK_ANY_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED - | SYSUI_STATE_DEVICE_DREAMING; - private final TaskbarActivityContext mContext; private long mKeyguardSysuiFlags; private boolean mBouncerShowing; @@ -55,7 +40,7 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas } public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags) { - long interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS; + long interestingKeyguardFlags = systemUiStateFlags & SystemUiFlagUtils.KEYGUARD_SYSUI_FLAGS; if (interestingKeyguardFlags == mKeyguardSysuiFlags) { // No change in keyguard relevant flags return; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ead1a8a441..cb9f24ae80 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -16,7 +16,6 @@ package com.android.launcher3.taskbar; import static com.android.app.animation.Interpolators.EMPHASIZED; -import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; @@ -51,6 +50,7 @@ import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; +import com.android.quickstep.util.SystemUiFlagUtils; import com.android.quickstep.views.RecentsView; import com.android.systemui.animation.ViewRootSync; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -343,8 +343,7 @@ public class TaskbarLauncherStateController { prevIsAwake && hasAnyFlag(FLAGS_LAUNCHER_ACTIVE)); } - boolean isDeviceLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED); - updateStateForFlag(FLAG_DEVICE_LOCKED, isDeviceLocked); + updateStateForFlag(FLAG_DEVICE_LOCKED, SystemUiFlagUtils.isLocked(systemUiStateFlags)); // Taskbar is hidden whenever the device is dreaming. The dreaming state includes the // interactive dreams, AoD, screen off. Since the SYSUI_STATE_DEVICE_DREAMING only kicks in 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/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 8fdb4601ab..995a6520bf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -25,7 +25,6 @@ import static com.android.internal.jank.InteractionJankMonitor.Configuration; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW; -import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.appendFlag; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; @@ -35,7 +34,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -66,6 +64,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.SystemUiFlagUtils; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -950,9 +949,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba && DisplayController.isTransientTaskbar(mActivity); updateStateForFlag(FLAG_STASHED_SYSUI, hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles); - boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED) - && !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY); - updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked); + updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, + SystemUiFlagUtils.isLocked(systemUiStateFlags)); mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING); mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java index fbd1b88693..efc747c725 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -153,6 +153,7 @@ public class BubbleDragController { @Override protected void onDragDismiss() { mBubblePinController.onDragEnd(); + mBubbleBarViewController.onBubbleDragEnd(); } @Override diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 463222dfd0..3199076547 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1614,7 +1614,8 @@ public abstract class AbsSwipeUpHandler recentsView.finishRecentsAnimation(true /* toRecents */, - false /* shouldPip */, null)); + public void onSystemUiFlagsChanged(@QuickStepContract.SystemUiStateFlags long lastSysUIFlags, + @QuickStepContract.SystemUiStateFlags long newSysUIFlags) { + long isShadeExpandedFlagMask = + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; + boolean wasExpanded = hasAnyFlag(lastSysUIFlags, isShadeExpandedFlagMask); + boolean isExpanded = hasAnyFlag(newSysUIFlags, isShadeExpandedFlagMask); + if (wasExpanded != isExpanded && isExpanded) { + // End live tile when expanding the notification panel for the first time from + // overview. + if (endLiveTile()) { + return; } } + + boolean wasLocked = SystemUiFlagUtils.isLocked(lastSysUIFlags); + boolean isLocked = SystemUiFlagUtils.isLocked(newSysUIFlags); + if (wasLocked != isLocked && isLocked) { + // Finish the running recents animation when locking the device. + finishRunningRecentsAnimation( + mController != null && mController.getFinishTargetIsLauncher()); + } + } + + private boolean hasAnyFlag(long flags, long flagMask) { + return (flags & flagMask) != 0; + } + + /** + * Switches the {@link RecentsView} to screenshot if in live tile mode. + * + * @return true iff the {@link RecentsView} was in live tile mode and was switched to screenshot + */ + public boolean endLiveTile() { + if (mLastGestureState == null) { + return false; + } + BaseContainerInterface containerInterface = mLastGestureState.getContainerInterface(); + if (!containerInterface.isInLiveTileMode() + || containerInterface.getCreatedContainer() == null) { + return false; + } + RecentsView recentsView = containerInterface.getCreatedContainer().getOverviewPanel(); + if (recentsView == null) { + return false; + } + recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation( + true /* toRecents */, false /* shouldPip */, null)); + return true; } public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) { diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 7aa99d9928..e9046b95a8 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -47,8 +47,6 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.wm.shell.Flags.enableBubblesLongPressNavHandle; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES; @@ -719,16 +717,7 @@ public class TouchInteractionService extends Service { SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); mOverviewComponentObserver.onSystemUiStateChanged(); mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); - - long isShadeExpandedFlag = - SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; - boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0; - boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0; - if (wasExpanded != isExpanded && isExpanded) { - // End live tile when expanding the notification panel for the first time from - // overview. - mTaskAnimationManager.endLiveTile(); - } + mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags); } } diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index b13e52d4fa..2d606f384a 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -267,6 +267,10 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { return mAppBounds; } + public Rect getSourceRectHint() { + return mSourceRectHint; + } + @Nullable public SurfaceControl getContentOverlay() { return mPipContentOverlay == null ? null : mPipContentOverlay.getLeash(); diff --git a/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt b/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt new file mode 100644 index 0000000000..5f4388c918 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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.quickstep.util + +import com.android.systemui.shared.system.QuickStepContract +import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags + +/** Util class for holding and checking [SystemUiStateFlags] masks. */ +object SystemUiFlagUtils { + const val KEYGUARD_SYSUI_FLAGS = + (QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING or + QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING or + QuickStepContract.SYSUI_STATE_DEVICE_DOZING or + QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED or + QuickStepContract.SYSUI_STATE_HOME_DISABLED or + QuickStepContract.SYSUI_STATE_BACK_DISABLED or + QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED or + QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK) + + // If any of these SysUi flags (via QuickstepContract) is set, the device to be considered + // locked. + private const val MASK_ANY_SYSUI_LOCKED = + (QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING or + QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING or + QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED or + QuickStepContract.SYSUI_STATE_DEVICE_DREAMING) + + /** + * Returns true iff the given [SystemUiStateFlags] imply that the device is considered locked. + */ + @JvmStatic + fun isLocked(@SystemUiStateFlags flags: Long): Boolean { + return hasAnyFlag(flags, MASK_ANY_SYSUI_LOCKED) && + !hasAnyFlag(flags, QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY) + } + + private fun hasAnyFlag(@SystemUiStateFlags flags: Long, flagMask: Long): Boolean { + return (flags and flagMask) != 0L + } +} diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index 604d072ad5..d9468c7038 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -278,7 +278,7 @@ public class OverviewActionsView extends FrameLayo boolean showSingleTaskActions = !mIsGroupedTask; boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair; Log.d(TAG, "updateActionButtonsVisibility() called: showSingleTaskActions = [" - + showSingleTaskActions + ", showGroupActions = [" + showGroupActions + "]"); + + showSingleTaskActions + "], showGroupActions = [" + showGroupActions + "]"); getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0); getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0); } diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java index a56d51e515..3d994e89a8 100644 --- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java +++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java @@ -18,13 +18,16 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON; +import static com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.FloatProperty; +import android.view.TouchDelegate; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; @@ -41,9 +44,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StateManager; -import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.states.StateAnimationConfig; - import com.android.quickstep.util.SplitSelectStateController; /** @@ -133,6 +134,28 @@ public class SplitInstructionsView extends LinearLayout { cancelTextView.setVisibility(VISIBLE); cancelTextView.setOnClickListener((v) -> exitSplitSelection()); instructionTextView.setText(R.string.toast_contextual_split_select_app); + + // After layout, expand touch target of cancel button to meet minimum a11y measurements. + post(() -> { + int minTouchSize = getResources() + .getDimensionPixelSize(settingslib_preferred_minimum_touch_target); + Rect r = new Rect(); + cancelTextView.getHitRect(r); + + if (r.width() < minTouchSize) { + // add 1 to ensure ceiling on int division + int expandAmount = (minTouchSize + 1 - r.width()) / 2; + r.left -= expandAmount; + r.right += expandAmount; + } + if (r.height() < minTouchSize) { + int expandAmount = (minTouchSize + 1 - r.height()) / 2; + r.top -= expandAmount; + r.bottom += expandAmount; + } + + setTouchDelegate(new TouchDelegate(r, cancelTextView)); + }); } // Set accessibility title, will be announced by a11y tools. 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 { diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java index fa10b61ad4..b7fd8be311 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java +++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java @@ -88,6 +88,8 @@ public class TaplOverviewIconTest extends AbstractLauncherUiTest { UiDevice.getInstance(getInstrumentation()).pressHome(); mLauncher.waitForLauncherInitialized(); } + + /** Clears all recent tasks */ + protected void clearAllRecentTasks() { + if (!mLauncher.getRecentTasks().isEmpty()) { + mLauncher.goHome().switchToOverview().dismissAllTasks(); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index e10893eea5..567a8bd1c0 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -23,6 +23,7 @@ import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT; import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; import android.graphics.Rect; +import android.util.Log; import android.view.KeyEvent; import androidx.annotation.NonNull; @@ -44,6 +45,7 @@ import java.util.stream.Collectors; * Common overview panel for both Launcher and fallback recents */ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { + private static final String TAG = "BaseOverview"; protected static final String TASK_RES_ID = "task"; private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile( "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0"); @@ -384,25 +386,31 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { protected boolean isActionsViewVisible() { if (!hasTasks() || isClearAllVisible()) { + Log.d(TAG, "Not expecting an actions bar: no tasks/'Clear all' is visible"); return false; } boolean isTablet = mLauncher.isTablet(); if (isTablet && mLauncher.isGridOnlyOverviewEnabled()) { + Log.d(TAG, "Not expecting an actions bar: device is tablet with grid-only Overview"); return false; } OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask(); if (task == null) { + Log.d(TAG, "Not expecting an actions bar: no current task"); return false; } // In tablets, if focused task is not in center, overview actions aren't visible. if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) { + Log.d(TAG, "Not expecting an actions bar: device is tablet and task is not centered"); return false; } if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) { + Log.d(TAG, "Not expecting an actions bar: device is phone and task is split"); // Overview actions aren't visible for split screen tasks, except for save app pair // button on tablets. return false; } + Log.d(TAG, "Expecting an actions bar"); return true; } @@ -447,10 +455,20 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } private void verifyActionsViewVisibility() { + // If no running tasks, no need to verify actions view visibility. + if (getTasks().isEmpty()) { + return; + } + + boolean isTablet = mLauncher.isTablet(); + OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask(); + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "want to assert overview actions view visibility")) { - boolean isTablet = mLauncher.isTablet(); - OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask(); + "want to assert overview actions view visibility=" + + isActionsViewVisible() + + ", focused task is " + + (task == null ? "null" : (task.isTaskSplit() ? "split" : "not split")) + )) { if (isActionsViewVisible()) { if (task.isTaskSplit()) {