1 /* 2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.wm.shell.windowdecor.common 18 19 import android.app.ActivityManager 20 import android.app.WindowConfiguration 21 import android.content.Context 22 import android.view.WindowManager 23 import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY 24 import com.android.wm.shell.common.DisplayController 25 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask 26 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper 27 import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy 28 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus 29 import com.android.wm.shell.splitscreen.SplitScreenController 30 31 /** 32 * Resolves whether, given a task and its associated display that it is currently on, to show the 33 * app handle/header or not. 34 */ 35 class AppHandleAndHeaderVisibilityHelper ( 36 private val context: Context, 37 private val displayController: DisplayController, 38 private val desktopModeCompatPolicy: DesktopModeCompatPolicy 39 ) { 40 var splitScreenController: SplitScreenController? = null 41 42 /** 43 * Returns, given a task's attribute and its display attribute, whether the app 44 * handle/header should show or not for this task. 45 */ shouldShowAppHandleOrHeadernull46 fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean { 47 if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) { 48 return allowedForTask(taskInfo) 49 } 50 return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId) 51 } 52 allowedForTasknull53 private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean { 54 // TODO (b/382023296): Remove once we no longer rely on 55 // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay 56 val display = displayController.getDisplay(taskInfo.displayId) 57 if (display == null) { 58 // If DisplayController doesn't have it tracked, it could be a private/managed display. 59 return false 60 } 61 if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) return true 62 if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) { 63 return false 64 } 65 66 if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) { 67 return false 68 } 69 70 // TODO (b/382023296): Remove once we no longer rely on 71 // Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay 72 val isOnLargeScreen = 73 display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP 74 if (!DesktopModeStatus.canEnterDesktopMode(context) 75 && DesktopModeStatus.overridesShowAppHandle(context) 76 && !isOnLargeScreen 77 ) { 78 // Devices with multiple screens may enable the app handle but it should not show on 79 // small screens 80 return false 81 } 82 if (BubbleAnythingFlagHelper.enableBubbleToFullscreen() 83 && !DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display) 84 ) { 85 // TODO(b/388853233): enable handles for split tasks once drag to bubble is enabled 86 if (taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_FULLSCREEN) { 87 return false 88 } 89 } 90 return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) 91 && !isWallpaperTask(taskInfo) 92 && taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED 93 && taskInfo.activityType == WindowConfiguration.ACTIVITY_TYPE_STANDARD 94 && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop 95 } 96 allowedForDisplaynull97 private fun allowedForDisplay(displayId: Int): Boolean { 98 // If DisplayController doesn't have it tracked, it could be a private/managed display. 99 val display = displayController.getDisplay(displayId) 100 if (display == null) return false 101 102 if (DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)) { 103 return true 104 } 105 // If on default display and on Large Screen (unfolded), show app handle 106 return DesktopModeStatus.overridesShowAppHandle(context) 107 && display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP 108 } 109 }