diff options
10 files changed, 186 insertions, 43 deletions
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java index cf582176a9f7..b4ff8e70ec36 100644 --- a/core/java/android/window/DesktopExperienceFlags.java +++ b/core/java/android/window/DesktopExperienceFlags.java @@ -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), diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index 2e33253b5e09..ed5e0c608675 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -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; @@ -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); } @@ -269,6 +271,29 @@ public class DesktopModeStatus { } /** + * 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). */ @@ -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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 6b7f311daa7c..32bdf3221bbe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -167,6 +167,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; @@ -1002,6 +1003,7 @@ public abstract class WMShellModule { Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, AppToWebEducationController appToWebEducationController, + AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, @@ -1025,10 +1027,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)); } @@ -1056,6 +1058,16 @@ public abstract class WMShellModule { @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( @NonNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 7ef1a93cbe45..ffb81f8c33e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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, @@ -338,6 +337,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, + appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), @@ -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, @@ -431,6 +432,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksLimiter = desktopTasksLimiter; mAppHandleEducationController = appHandleEducationController; mAppToWebEducationController = appToWebEducationController; + mAppHandleAndHeaderVisibilityHelper = appHandleAndHeaderVisibilityHelper; mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository; mActivityOrientationChangeHandler = activityOrientationChangeHandler; mAssistContentRequester = assistContentRequester; @@ -528,6 +530,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public void setSplitScreenController(SplitScreenController splitScreenController) { mSplitScreenController = splitScreenController; + mAppHandleAndHeaderVisibilityHelper.setSplitScreenController(splitScreenController); } @Override @@ -1717,32 +1720,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( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt new file mode 100644 index 000000000000..39ccf5bd03a7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt @@ -0,0 +1,100 @@ +/* + * 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 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt index fb62ba75e056..edf91fe62e7d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt @@ -234,14 +234,25 @@ class DesktopModeStatusTest : ShellTestCase() { assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() } + @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) @Test fun isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsFalse() { doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) - doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops)) + doReturn(false).whenever(mockResources) + .getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops)) assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() } + @EnableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) + @Test + fun isPDDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) + doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops)) + + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue() + } + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index e89a122595d5..d69509faf4ec 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -115,7 +115,8 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest .spyStatic(DragPositioningCallbackUtility::class.java) .startMocking() - doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(Mockito.any()) } + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(Mockito.any(), + Mockito.any()) } doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(Mockito.any()) } doReturn(false).`when` { DesktopModeStatus.overridesShowAppHandle(Mockito.any()) } @@ -394,7 +395,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN) - doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(any()) } + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(any(), any()) } setUpMockDecorationsForTasks(task) onTaskOpening(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index 81dfaed56b6f..a1f40fdefee9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -79,6 +79,7 @@ import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.StubTransaction import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener +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 @@ -174,6 +175,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener protected lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel protected lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy + protected lateinit var appHandleAndHeaderVisibilityHelper: AppHandleAndHeaderVisibilityHelper fun setUpCommon() { spyContext = spy(mContext) @@ -185,9 +187,13 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository) whenever(mockDisplayController.getDisplayContext(any())).thenReturn(spyContext) whenever(mockDisplayController.getDisplay(any())).thenReturn(display) + whenever(display.type).thenReturn(Display.TYPE_INTERNAL) whenever(mockDesktopUserRepositories.getProfile(anyInt())) .thenReturn(mockDesktopRepository) desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext) + appHandleAndHeaderVisibilityHelper = + AppHandleAndHeaderVisibilityHelper(spyContext, mockDisplayController, + desktopModeCompatPolicy) desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, testShellExecutor, @@ -222,6 +228,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { Optional.of(mockTasksLimiter), mockAppHandleEducationController, mockAppToWebEducationController, + appHandleAndHeaderVisibilityHelper, mockCaptionHandleRepository, Optional.of(mockActivityOrientationChangeHandler), mockTaskPositionerFactory, diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java index dc42b32967e2..d91fca9e2816 100644 --- a/services/core/java/com/android/server/wm/DesktopModeHelper.java +++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.Flags.enableConnectedDisplaysWallpaper; +import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE; import android.annotation.NonNull; import android.content.Context; @@ -66,7 +67,7 @@ public final class DesktopModeHelper { * Return {@code true} if the current device can hosts desktop sessions on its internal display. */ @VisibleForTesting - static boolean canInternalDisplayHostDesktops(@NonNull Context context) { + private static boolean canInternalDisplayHostDesktops(@NonNull Context context) { return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops); } @@ -83,8 +84,11 @@ public final class DesktopModeHelper { if (!shouldEnforceDeviceRestrictions()) { 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() && isDesktopModeDevOptionsSupported(context); diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java index 1e91bedb5c18..43755ea3165e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java @@ -181,6 +181,7 @@ public class DesktopModeHelperTest { assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue(); } + @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE) @Test public void isDeviceEligibleForDesktopMode_configDEModeOffAndIntDispHostsDesktop_returnsFalse() { doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); |