• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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