1 /* <lambda>null2 * 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.compatui.api 18 19 import android.content.Context 20 import android.content.res.Configuration 21 import android.graphics.PixelFormat 22 import android.graphics.Point 23 import android.os.Binder 24 import android.view.IWindow 25 import android.view.SurfaceControl 26 import android.view.SurfaceControlViewHost 27 import android.view.View 28 import android.view.WindowManager 29 import android.view.WindowlessWindowManager 30 import com.android.wm.shell.common.DisplayLayout 31 import com.android.wm.shell.common.SyncTransactionQueue 32 33 /** 34 * The component created after a {@link CompatUISpec} definition 35 */ 36 class CompatUIComponent( 37 private val spec: CompatUISpec, 38 private val id: String, 39 private var context: Context, 40 private val state: CompatUIState, 41 private var compatUIInfo: CompatUIInfo, 42 private val syncQueue: SyncTransactionQueue, 43 private var displayLayout: DisplayLayout? 44 ) : WindowlessWindowManager( 45 compatUIInfo.taskInfo.configuration, 46 /* rootSurface */ 47 null, 48 /* hostInputToken */ 49 null 50 ) { 51 52 private val tag 53 get() = "CompatUI {id = $id}" 54 55 private var leash: SurfaceControl? = null 56 57 private var layout: View? = null 58 59 /** 60 * Utility class for adding and releasing a View hierarchy for this [ ] to `mLeash`. 61 */ 62 protected var viewHost: SurfaceControlViewHost? = null 63 64 override fun setConfiguration(configuration: Configuration?) { 65 super.setConfiguration(configuration) 66 configuration?.let { 67 context = context.createConfigurationContext(it) 68 } 69 } 70 71 /** 72 * Invoked every time a new CompatUIInfo comes from core 73 * @param newInfo The new CompatUIInfo object 74 */ 75 fun update(newInfo: CompatUIInfo) { 76 updateComponentState(newInfo, state.stateForComponent(id)) 77 updateUI(state) 78 } 79 80 fun release() { 81 spec.log("$tag releasing.....") 82 // Implementation empty 83 // Hiding before releasing to avoid flickering when transitioning to the Home screen. 84 layout?.visibility = View.GONE 85 layout = null 86 spec.layout.viewReleaser() 87 spec.log("$tag layout releaser invoked!") 88 viewHost?.release() 89 viewHost = null 90 leash?.run { 91 val localLeash: SurfaceControl = this 92 syncQueue.runInSync { t: SurfaceControl.Transaction -> 93 t.remove( 94 localLeash 95 ) 96 } 97 leash = null 98 spec.log("$tag leash removed") 99 } 100 spec.log("$tag released") 101 } 102 103 override fun getParentSurface( 104 window: IWindow, 105 attrs: WindowManager.LayoutParams 106 ): SurfaceControl? { 107 val className = javaClass.simpleName 108 val builder = SurfaceControl.Builder() 109 .setContainerLayer() 110 .setName(className + "Leash") 111 .setHidden(false) 112 .setCallsite("$className#attachToParentSurface") 113 attachToParentSurface(builder) 114 leash = builder.build() 115 initSurface(leash) 116 return leash 117 } 118 119 fun attachToParentSurface(builder: SurfaceControl.Builder) { 120 compatUIInfo.listener?.attachChildSurfaceToTask(compatUIInfo.taskInfo.taskId, builder) 121 } 122 123 fun initLayout(newCompatUIInfo: CompatUIInfo) { 124 compatUIInfo = newCompatUIInfo 125 spec.log("$tag updating...") 126 check(viewHost == null) { "A UI has already been created with this window manager." } 127 val componentState: CompatUIComponentState? = state.stateForComponent(id) 128 spec.log("$tag state: $componentState") 129 // We inflate the layout 130 layout = spec.layout.viewBuilder(context, compatUIInfo, componentState) 131 spec.log("$tag layout: $layout") 132 viewHost = createSurfaceViewHost().apply { 133 spec.log("$tag adding view $layout to host $this") 134 setView(layout!!, getWindowLayoutParams()) 135 } 136 updateSurfacePosition() 137 } 138 139 /** Creates a [SurfaceControlViewHost] for this window manager. */ 140 fun createSurfaceViewHost(): SurfaceControlViewHost = 141 SurfaceControlViewHost(context, context.display, this, javaClass.simpleName) 142 143 fun relayout() { 144 spec.log("$tag relayout...") 145 viewHost?.run { 146 relayout(getWindowLayoutParams()) 147 updateSurfacePosition() 148 } 149 } 150 151 protected fun updateSurfacePosition() { 152 spec.log("$tag updateSurfacePosition on layout $layout") 153 layout?.let { 154 updateSurfacePosition( 155 spec.layout.positionFactory( 156 it, 157 compatUIInfo, 158 state.sharedState, 159 state.stateForComponent(id) 160 ) 161 ) 162 } 163 } 164 165 protected fun getWindowLayoutParams(width: Int, height: Int): WindowManager.LayoutParams { 166 // Cannot be wrap_content as this determines the actual window size 167 val winParams = 168 WindowManager.LayoutParams( 169 width, 170 height, 171 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, 172 spec.layout.layoutParamFlags, 173 PixelFormat.TRANSLUCENT 174 ) 175 winParams.token = Binder() 176 winParams.title = javaClass.simpleName + compatUIInfo.taskInfo.taskId 177 winParams.privateFlags = 178 winParams.privateFlags or (WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION 179 or WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) 180 spec.log("$tag getWindowLayoutParams $winParams") 181 return winParams 182 } 183 184 /** Gets the layout params. */ 185 protected fun getWindowLayoutParams(): WindowManager.LayoutParams = 186 layout?.run { 187 measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) 188 spec.log( 189 "$tag getWindowLayoutParams size: ${measuredWidth}x$measuredHeight" 190 ) 191 return getWindowLayoutParams(measuredWidth, measuredHeight) 192 } ?: WindowManager.LayoutParams() 193 194 protected fun updateSurfacePosition(position: Point) { 195 spec.log("$tag updateSurfacePosition on leash $leash") 196 leash?.run { 197 syncQueue.runInSync { t: SurfaceControl.Transaction -> 198 if (!isValid) { 199 spec.log("$tag The leash has been released.") 200 return@runInSync 201 } 202 spec.log("$tag settings position $position") 203 t.setPosition(this, position.x.toFloat(), position.y.toFloat()) 204 } 205 } 206 } 207 208 private fun updateComponentState( 209 newInfo: CompatUIInfo, 210 componentState: CompatUIComponentState? 211 ) { 212 spec.log("$tag component state updating.... $componentState") 213 compatUIInfo = newInfo 214 } 215 216 private fun updateUI(state: CompatUIState) { 217 spec.log("$tag updating ui") 218 setConfiguration(compatUIInfo.taskInfo.configuration) 219 val componentState: CompatUIComponentState? = state.stateForComponent(id) 220 layout?.run { 221 spec.log("$tag viewBinder execution...") 222 spec.layout.viewBinder(this, compatUIInfo, state.sharedState, componentState) 223 relayout() 224 } 225 } 226 227 private fun initSurface(leash: SurfaceControl?) { 228 syncQueue.runInSync { t: SurfaceControl.Transaction -> 229 if (leash == null || !leash.isValid) { 230 spec.log("$tag The leash has been released.") 231 return@runInSync 232 } 233 t.setLayer(leash, spec.layout.zOrder) 234 } 235 } 236 } 237