1 /* 2 * Copyright (C) 2024 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 18 19 import android.app.ActivityManager.RunningTaskInfo 20 import android.content.Context 21 import android.graphics.PixelFormat 22 import android.graphics.Point 23 import android.view.SurfaceControl 24 import android.view.SurfaceControlViewHost 25 import android.view.WindowInsets.Type.systemBars 26 import android.view.WindowManager 27 import android.view.WindowlessWindowManager 28 import android.window.DesktopModeFlags 29 import android.window.TaskConstants 30 import android.window.TaskSnapshot 31 import androidx.compose.ui.graphics.toArgb 32 import com.android.internal.annotations.VisibleForTesting 33 import com.android.wm.shell.RootTaskDisplayAreaOrganizer 34 import com.android.wm.shell.common.DisplayController 35 import com.android.wm.shell.desktopmode.DesktopUserRepositories 36 import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer 37 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer 38 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer 39 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer 40 import com.android.wm.shell.windowdecor.common.DecorThemeUtil 41 import java.util.function.Supplier 42 43 /** 44 * Implementation of [ManageWindowsViewContainer] meant to be used in desktop header and app 45 * handle. 46 */ 47 class DesktopHeaderManageWindowsMenu( 48 private val callerTaskInfo: RunningTaskInfo, 49 private val x: Int, 50 private val y: Int, 51 private val displayController: DisplayController, 52 private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer, 53 context: Context, 54 private val desktopUserRepositories: DesktopUserRepositories, 55 private val surfaceControlBuilderSupplier: Supplier<SurfaceControl.Builder>, 56 private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>, 57 snapshotList: List<Pair<Int, TaskSnapshot?>>, 58 onIconClickListener: ((Int) -> Unit), 59 onOutsideClickListener: (() -> Unit) 60 ) : ManageWindowsViewContainer( 61 context, 62 DecorThemeUtil(context).getColorScheme(callerTaskInfo).background.toArgb() 63 ) { 64 @VisibleForTesting 65 var menuViewContainer: AdditionalViewContainer? = null 66 67 init { 68 createMenu(snapshotList, onIconClickListener, onOutsideClickListener) 69 menuView.rootView.pivotX = 0f 70 menuView.rootView.pivotY = 0f 71 animateOpen() 72 } 73 addToContainernull74 override fun addToContainer(menuView: ManageWindowsView) { 75 val menuPosition = Point(x, y) 76 val flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or 77 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 78 val desktopRepository = desktopUserRepositories.getProfile(callerTaskInfo.userId) 79 menuViewContainer = if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue 80 && desktopRepository.isTaskInFullImmersiveState(callerTaskInfo.taskId)) { 81 // Use system view container so that forcibly shown system bars take effect in 82 // immersive. 83 createAsSystemViewContainer(menuPosition, flags) 84 } else { 85 createAsViewHostContainer(menuPosition, flags) 86 } 87 } 88 createAsSystemViewContainernull89 private fun createAsSystemViewContainer(position: Point, flags: Int): AdditionalViewContainer { 90 return AdditionalSystemViewContainer( 91 windowManagerWrapper = WindowManagerWrapper( 92 context.getSystemService(WindowManager::class.java) 93 ), 94 taskId = callerTaskInfo.taskId, 95 x = position.x, 96 y = position.y, 97 width = menuView.menuWidth, 98 height = menuView.menuHeight, 99 flags = flags, 100 forciblyShownTypes = systemBars(), 101 view = menuView.rootView 102 ) 103 } 104 createAsViewHostContainernull105 private fun createAsViewHostContainer(position: Point, flags: Int): AdditionalViewContainer { 106 val builder = surfaceControlBuilderSupplier.get() 107 rootTdaOrganizer.attachToDisplayArea(callerTaskInfo.displayId, builder) 108 val leash = builder 109 .setName("Manage Windows Menu") 110 .setContainerLayer() 111 .build() 112 val lp = WindowManager.LayoutParams( 113 menuView.menuWidth, 114 menuView.menuHeight, 115 WindowManager.LayoutParams.TYPE_APPLICATION, 116 flags, 117 PixelFormat.TRANSPARENT 118 ) 119 val windowManager = WindowlessWindowManager( 120 callerTaskInfo.configuration, 121 leash, 122 null // HostInputToken 123 ) 124 val viewHost = SurfaceControlViewHost( 125 context, 126 displayController.getDisplay(callerTaskInfo.displayId), windowManager, 127 "MaximizeMenu" 128 ) 129 menuView.let { viewHost.setView(it.rootView, lp) } 130 val t = surfaceControlTransactionSupplier.get() 131 t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU) 132 .setPosition(leash, position.x.toFloat(), position.y.toFloat()) 133 .show(leash) 134 t.apply() 135 return AdditionalViewHostViewContainer( 136 leash, 137 viewHost, 138 surfaceControlTransactionSupplier 139 ) 140 } 141 removeFromContainernull142 override fun removeFromContainer() { 143 menuViewContainer?.releaseView() 144 } 145 } 146