1 /*
2  * Copyright (C) 2022 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 package com.android.systemui.decor
17 
18 import android.annotation.IdRes
19 import android.annotation.NonNull
20 import android.content.Context
21 import android.view.Surface
22 import android.view.View
23 import android.view.ViewGroup
24 import com.android.systemui.RegionInterceptingFrameLayout
25 import java.io.PrintWriter
26 
27 class OverlayWindow(private val context: Context) {
28 
29     val rootView = RegionInterceptingFrameLayout(context) as ViewGroup
30     private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>()
31 
32     val viewIds: List<Int>
33         get() = viewProviderMap.keys.toList()
34 
35     fun addDecorProvider(
36         decorProvider: DecorProvider,
37         @Surface.Rotation rotation: Int,
38         tintColor: Int
39     ) {
40         val view = decorProvider.inflateView(context, rootView, rotation, tintColor)
41         viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider)
42     }
43 
44     fun getView(@IdRes id: Int): View? {
45         val pair = viewProviderMap[id]
46         return pair?.first
47     }
48 
49     fun removeView(@IdRes id: Int) {
50         val view = getView(id)
51         if (view != null) {
52             rootView.removeView(view)
53             viewProviderMap.remove(id)
54         }
55     }
56 
57     /**
58      * Remove views which does not been found in expectExistViewIds
59      */
60     fun removeRedundantViews(expectExistViewIds: IntArray?) {
61         viewIds.forEach {
62             if (expectExistViewIds == null || !(expectExistViewIds.contains(it))) {
63                 removeView(it)
64             }
65         }
66     }
67 
68     /**
69      * Check that newProviders is the same list with viewProviderMap.
70      */
71     fun hasSameProviders(newProviders: List<DecorProvider>): Boolean {
72         return (newProviders.size == viewProviderMap.size) &&
73             newProviders.all { getView(it.viewId) != null }
74     }
75 
76     /**
77      * Apply new configuration info into views.
78      * @param filterIds target view ids. Apply to all if null.
79      * @param rotation current or new rotation direction.
80      * @param displayUniqueId new displayUniqueId if any.
81      */
82     fun onReloadResAndMeasure(
83         filterIds: Array<Int>? = null,
84         reloadToken: Int,
85         @Surface.Rotation rotation: Int,
86         tintColor: Int,
87         displayUniqueId: String? = null
88     ) {
89         filterIds?.forEach { id ->
90             viewProviderMap[id]?.let {
91                 it.second.onReloadResAndMeasure(
92                     view = it.first,
93                     reloadToken = reloadToken,
94                     rotation = rotation,
95                     tintColor = tintColor,
96                     displayUniqueId = displayUniqueId
97                 )
98             }
99         } ?: run {
100             viewProviderMap.values.forEach {
101                 it.second.onReloadResAndMeasure(
102                     view = it.first,
103                     reloadToken = reloadToken,
104                     rotation = rotation,
105                     tintColor = tintColor,
106                     displayUniqueId = displayUniqueId
107                 )
108             }
109         }
110     }
111 
112     fun dump(@NonNull pw: PrintWriter, name: String) {
113         pw.println("  $name=")
114         pw.println("    rootView=$rootView")
115         for (i in 0 until rootView.childCount) {
116             val child = rootView.getChildAt(i)
117             val provider = viewProviderMap[child.id]?.second
118             pw.println("    child[$i]=$child $provider")
119         }
120     }
121 }
122