Loading core/java/android/window/DesktopExperienceFlags.java +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public enum DesktopExperienceFlags { false), ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF( Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false), ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false), ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, false), ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays, false), Loading libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +31 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.wm.shell.shared.desktopmode; import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE; import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement; import static com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper.enableBubbleToFullscreen; import android.annotation.NonNull; Loading Loading @@ -224,7 +226,7 @@ public class DesktopModeStatus { /** * Return {@code true} if the current device can host desktop sessions on its internal display. */ public static boolean canInternalDisplayHostDesktops(@NonNull Context context) { private static boolean canInternalDisplayHostDesktops(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops); } Loading Loading @@ -268,6 +270,29 @@ public class DesktopModeStatus { && canShowDesktopModeDevOption(context); } /** * Check to see if a display should have desktop mode enabled or not. Internal * and external displays have separate logic. */ public static boolean isDesktopModeSupportedOnDisplay(Context context, Display display) { if (!canEnterDesktopMode(context)) { return false; } if (display.getType() == Display.TYPE_INTERNAL) { return canInternalDisplayHostDesktops(context); } // TODO (b/395014779): Change this to use WM API if ((display.getType() == Display.TYPE_EXTERNAL || display.getType() == Display.TYPE_OVERLAY) && enableDisplayContentModeManagement()) { final WindowManager wm = context.getSystemService(WindowManager.class); return wm != null && wm.shouldShowSystemDecors(display.getDisplayId()); } return false; } /** * Returns whether the multiple desktops feature is enabled for this device (both backend and * frontend implementations). Loading Loading @@ -341,8 +366,11 @@ public class DesktopModeStatus { if (!enforceDeviceRestrictions()) { return true; } final boolean desktopModeSupported = isDesktopModeSupported(context) && canInternalDisplayHostDesktops(context); // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a // requirement. final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue() ? isDesktopModeSupported(context) : (isDesktopModeSupported(context) && canInternalDisplayHostDesktops(context)); final boolean desktopModeSupportedByDevOptions = Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported(context); Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +16 −4 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel; import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier; Loading Loading @@ -1021,6 +1022,7 @@ public abstract class WMShellModule { Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, Loading @@ -1044,10 +1046,10 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy, desktopTilingDecorViewModel, appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy, desktopTilingDecorViewModel, multiDisplayDragMoveIndicatorController)); } Loading @@ -1073,6 +1075,16 @@ public abstract class WMShellModule { return new MultiDisplayDragMoveIndicatorSurface.Factory(context); } @WMSingleton @Provides static AppHandleAndHeaderVisibilityHelper provideAppHandleAndHeaderVisibilityHelper( @NonNull Context context, @NonNull DisplayController displayController, @NonNull DesktopModeCompatPolicy desktopModeCompatPolicy) { return new AppHandleAndHeaderVisibilityHelper(context, displayController, desktopModeCompatPolicy); } @WMSingleton @Provides static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader( Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +8 −30 Original line number Diff line number Diff line Loading @@ -17,11 +17,9 @@ package com.android.wm.shell.windowdecor; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_HOVER_ENTER; Loading Loading @@ -79,7 +77,6 @@ import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewRootImpl; import android.view.WindowManager; import android.window.DesktopModeFlags; import android.window.TaskSnapshot; import android.window.WindowContainerToken; Loading Loading @@ -121,7 +118,6 @@ import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.desktopmode.DesktopTasksLimiter; import com.android.wm.shell.desktopmode.DesktopUserRepositories; import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt; Loading @@ -146,6 +142,7 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; Loading Loading @@ -207,6 +204,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter; private final AppHandleEducationController mAppHandleEducationController; private final AppToWebEducationController mAppToWebEducationController; private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper; private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory; private boolean mTransitionDragActive; Loading Loading @@ -294,6 +292,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, Loading Loading @@ -338,6 +337,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), Loading Loading @@ -386,6 +386,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, Loading Loading @@ -431,6 +432,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksLimiter = desktopTasksLimiter; mAppHandleEducationController = appHandleEducationController; mAppToWebEducationController = appToWebEducationController; mAppHandleAndHeaderVisibilityHelper = appHandleAndHeaderVisibilityHelper; mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository; mActivityOrientationChangeHandler = activityOrientationChangeHandler; mAssistContentRequester = assistContentRequester; Loading Loading @@ -528,6 +530,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public void setSplitScreenController(SplitScreenController splitScreenController) { mSplitScreenController = splitScreenController; mAppHandleAndHeaderVisibilityHelper.setSplitScreenController(splitScreenController); } @Override Loading Loading @@ -1724,32 +1727,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (mDisplayController.getDisplay(taskInfo.displayId) == null) { // If DisplayController doesn't have it tracked, it could be a private/managed display. return false; } if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; if (mSplitScreenController != null && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) { return false; } if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { return false; } final boolean isOnLargeScreen = mDisplayController.getDisplay(taskInfo.displayId).getMinSizeDimensionDp() >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; if (!DesktopModeStatus.canEnterDesktopMode(mContext) && DesktopModeStatus.overridesShowAppHandle(mContext) && !isOnLargeScreen) { // Devices with multiple screens may enable the app handle but it should not show on // small screens return false; } return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(mContext) && !DesktopWallpaperActivity.isWallpaperTask(taskInfo) && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop(); return mAppHandleAndHeaderVisibilityHelper.shouldShowAppHandleOrHeader(taskInfo); } private void createWindowDecoration( Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.wm.shell.windowdecor.common import android.app.ActivityManager import android.app.WindowConfiguration import android.content.Context import android.view.WindowManager import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.splitscreen.SplitScreenController /** * Resolves whether, given a task and its associated display that it is currently on, to show the * app handle/header or not. */ class AppHandleAndHeaderVisibilityHelper ( private val context: Context, private val displayController: DisplayController, private val desktopModeCompatPolicy: DesktopModeCompatPolicy ) { var splitScreenController: SplitScreenController? = null /** * Returns, given a task's attribute and its display attribute, whether the app * handle/header should show or not for this task. */ fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean { if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) { return allowedForTask(taskInfo) } return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId) } private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean { // TODO (b/382023296): Remove once we no longer rely on // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay if (displayController.getDisplay(taskInfo.displayId) == null) { // If DisplayController doesn't have it tracked, it could be a private/managed display. return false } if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) return true if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) { return false } if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { return false } // TODO (b/382023296): Remove once we no longer rely on // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay val isOnLargeScreen = displayController.getDisplay(taskInfo.displayId).minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP if (!DesktopModeStatus.canEnterDesktopMode(context) && DesktopModeStatus.overridesShowAppHandle(context) && !isOnLargeScreen ) { // Devices with multiple screens may enable the app handle but it should not show on // small screens return false } return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && !isWallpaperTask(taskInfo) && taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED && taskInfo.activityType == WindowConfiguration.ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop } private fun allowedForDisplay(displayId: Int): Boolean { // If DisplayController doesn't have it tracked, it could be a private/managed display. val display = displayController.getDisplay(displayId) if (display == null) return false if (DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)) { return true } // If on default display and on Large Screen (unfolded), show app handle return DesktopModeStatus.overridesShowAppHandle(context) && display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP } } No newline at end of file Loading
core/java/android/window/DesktopExperienceFlags.java +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public enum DesktopExperienceFlags { false), ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF( Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false), ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false), ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, false), ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays, false), Loading
libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +31 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.wm.shell.shared.desktopmode; import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE; import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement; import static com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper.enableBubbleToFullscreen; import android.annotation.NonNull; Loading Loading @@ -224,7 +226,7 @@ public class DesktopModeStatus { /** * Return {@code true} if the current device can host desktop sessions on its internal display. */ public static boolean canInternalDisplayHostDesktops(@NonNull Context context) { private static boolean canInternalDisplayHostDesktops(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops); } Loading Loading @@ -268,6 +270,29 @@ public class DesktopModeStatus { && canShowDesktopModeDevOption(context); } /** * Check to see if a display should have desktop mode enabled or not. Internal * and external displays have separate logic. */ public static boolean isDesktopModeSupportedOnDisplay(Context context, Display display) { if (!canEnterDesktopMode(context)) { return false; } if (display.getType() == Display.TYPE_INTERNAL) { return canInternalDisplayHostDesktops(context); } // TODO (b/395014779): Change this to use WM API if ((display.getType() == Display.TYPE_EXTERNAL || display.getType() == Display.TYPE_OVERLAY) && enableDisplayContentModeManagement()) { final WindowManager wm = context.getSystemService(WindowManager.class); return wm != null && wm.shouldShowSystemDecors(display.getDisplayId()); } return false; } /** * Returns whether the multiple desktops feature is enabled for this device (both backend and * frontend implementations). Loading Loading @@ -341,8 +366,11 @@ public class DesktopModeStatus { if (!enforceDeviceRestrictions()) { return true; } final boolean desktopModeSupported = isDesktopModeSupported(context) && canInternalDisplayHostDesktops(context); // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a // requirement. final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue() ? isDesktopModeSupported(context) : (isDesktopModeSupported(context) && canInternalDisplayHostDesktops(context)); final boolean desktopModeSupportedByDevOptions = Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported(context); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +16 −4 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel; import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier; Loading Loading @@ -1021,6 +1022,7 @@ public abstract class WMShellModule { Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, Loading @@ -1044,10 +1046,10 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy, desktopTilingDecorViewModel, appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy, desktopTilingDecorViewModel, multiDisplayDragMoveIndicatorController)); } Loading @@ -1073,6 +1075,16 @@ public abstract class WMShellModule { return new MultiDisplayDragMoveIndicatorSurface.Factory(context); } @WMSingleton @Provides static AppHandleAndHeaderVisibilityHelper provideAppHandleAndHeaderVisibilityHelper( @NonNull Context context, @NonNull DisplayController displayController, @NonNull DesktopModeCompatPolicy desktopModeCompatPolicy) { return new AppHandleAndHeaderVisibilityHelper(context, displayController, desktopModeCompatPolicy); } @WMSingleton @Provides static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader( Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +8 −30 Original line number Diff line number Diff line Loading @@ -17,11 +17,9 @@ package com.android.wm.shell.windowdecor; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_HOVER_ENTER; Loading Loading @@ -79,7 +77,6 @@ import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewRootImpl; import android.view.WindowManager; import android.window.DesktopModeFlags; import android.window.TaskSnapshot; import android.window.WindowContainerToken; Loading Loading @@ -121,7 +118,6 @@ import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.desktopmode.DesktopTasksLimiter; import com.android.wm.shell.desktopmode.DesktopUserRepositories; import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction; import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt; Loading @@ -146,6 +142,7 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; import com.android.wm.shell.windowdecor.common.AppHandleAndHeaderVisibilityHelper; import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; Loading Loading @@ -207,6 +204,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter; private final AppHandleEducationController mAppHandleEducationController; private final AppToWebEducationController mAppToWebEducationController; private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper; private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory; private boolean mTransitionDragActive; Loading Loading @@ -294,6 +292,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, Loading Loading @@ -338,6 +337,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), Loading Loading @@ -386,6 +386,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, Loading Loading @@ -431,6 +432,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksLimiter = desktopTasksLimiter; mAppHandleEducationController = appHandleEducationController; mAppToWebEducationController = appToWebEducationController; mAppHandleAndHeaderVisibilityHelper = appHandleAndHeaderVisibilityHelper; mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository; mActivityOrientationChangeHandler = activityOrientationChangeHandler; mAssistContentRequester = assistContentRequester; Loading Loading @@ -528,6 +530,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public void setSplitScreenController(SplitScreenController splitScreenController) { mSplitScreenController = splitScreenController; mAppHandleAndHeaderVisibilityHelper.setSplitScreenController(splitScreenController); } @Override Loading Loading @@ -1724,32 +1727,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (mDisplayController.getDisplay(taskInfo.displayId) == null) { // If DisplayController doesn't have it tracked, it could be a private/managed display. return false; } if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; if (mSplitScreenController != null && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) { return false; } if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { return false; } final boolean isOnLargeScreen = mDisplayController.getDisplay(taskInfo.displayId).getMinSizeDimensionDp() >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; if (!DesktopModeStatus.canEnterDesktopMode(mContext) && DesktopModeStatus.overridesShowAppHandle(mContext) && !isOnLargeScreen) { // Devices with multiple screens may enable the app handle but it should not show on // small screens return false; } return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(mContext) && !DesktopWallpaperActivity.isWallpaperTask(taskInfo) && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop(); return mAppHandleAndHeaderVisibilityHelper.shouldShowAppHandleOrHeader(taskInfo); } private void createWindowDecoration( Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.wm.shell.windowdecor.common import android.app.ActivityManager import android.app.WindowConfiguration import android.content.Context import android.view.WindowManager import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.splitscreen.SplitScreenController /** * Resolves whether, given a task and its associated display that it is currently on, to show the * app handle/header or not. */ class AppHandleAndHeaderVisibilityHelper ( private val context: Context, private val displayController: DisplayController, private val desktopModeCompatPolicy: DesktopModeCompatPolicy ) { var splitScreenController: SplitScreenController? = null /** * Returns, given a task's attribute and its display attribute, whether the app * handle/header should show or not for this task. */ fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean { if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) { return allowedForTask(taskInfo) } return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId) } private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean { // TODO (b/382023296): Remove once we no longer rely on // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay if (displayController.getDisplay(taskInfo.displayId) == null) { // If DisplayController doesn't have it tracked, it could be a private/managed display. return false } if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) return true if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) { return false } if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { return false } // TODO (b/382023296): Remove once we no longer rely on // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay val isOnLargeScreen = displayController.getDisplay(taskInfo.displayId).minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP if (!DesktopModeStatus.canEnterDesktopMode(context) && DesktopModeStatus.overridesShowAppHandle(context) && !isOnLargeScreen ) { // Devices with multiple screens may enable the app handle but it should not show on // small screens return false } return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && !isWallpaperTask(taskInfo) && taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED && taskInfo.activityType == WindowConfiguration.ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop } private fun allowedForDisplay(displayId: Int): Boolean { // If DisplayController doesn't have it tracked, it could be a private/managed display. val display = displayController.getDisplay(displayId) if (display == null) return false if (DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)) { return true } // If on default display and on Large Screen (unfolded), show app handle return DesktopModeStatus.overridesShowAppHandle(context) && display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP } } No newline at end of file