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 
17 package com.android.systemui.statusbar.phone
18 
19 import android.view.InsetsFlags
20 import android.view.ViewDebug
21 import android.view.WindowInsets.Type.InsetsType
22 import android.view.WindowInsetsController.Appearance
23 import android.view.WindowInsetsController.Behavior
24 import com.android.internal.statusbar.LetterboxDetails
25 import com.android.internal.view.AppearanceRegion
26 import com.android.systemui.Dumpable
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dump.DumpManager
29 import com.android.systemui.statusbar.SysuiStatusBarStateController
30 import java.io.PrintWriter
31 import javax.inject.Inject
32 
33 /**
34  * Top-level listener of system attributes changed. This class is __always the first__ one to be
35  * notified about changes.
36  *
37  * It is responsible for modifying any attributes if necessary, and then notifying the other
38  * downstream listeners.
39  */
40 @SysUISingleton
41 class SystemBarAttributesListener
42 @Inject
43 internal constructor(
44     private val centralSurfaces: CentralSurfaces,
45     private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
46     private val statusBarStateController: SysuiStatusBarStateController,
47     private val lightBarController: LightBarController,
48     dumpManager: DumpManager,
49 ) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
50 
51     private var lastLetterboxAppearance: LetterboxAppearance? = null
52     private var lastSystemBarAttributesParams: SystemBarAttributesParams? = null
53 
54     init {
55         dumpManager.registerCriticalDumpable(this)
56     }
57 
58     override fun onStatusBarBoundsChanged() {
59         val params = lastSystemBarAttributesParams
60         if (params != null && shouldUseLetterboxAppearance(params.letterboxesArray)) {
61             onSystemBarAttributesChanged(
62                 params.displayId,
63                 params.appearance,
64                 params.appearanceRegionsArray,
65                 params.navbarColorManagedByIme,
66                 params.behavior,
67                 params.requestedVisibleTypes,
68                 params.packageName,
69                 params.letterboxesArray)
70         }
71     }
72 
73     fun onSystemBarAttributesChanged(
74             displayId: Int,
75             @Appearance originalAppearance: Int,
76             originalAppearanceRegions: Array<AppearanceRegion>,
77             navbarColorManagedByIme: Boolean,
78             @Behavior behavior: Int,
79             @InsetsType requestedVisibleTypes: Int,
80             packageName: String,
81             letterboxDetails: Array<LetterboxDetails>
82     ) {
83         lastSystemBarAttributesParams =
84             SystemBarAttributesParams(
85                 displayId,
86                 originalAppearance,
87                 originalAppearanceRegions.toList(),
88                 navbarColorManagedByIme,
89                 behavior,
90                 requestedVisibleTypes,
91                 packageName,
92                 letterboxDetails.toList())
93 
94         val (appearance, appearanceRegions) =
95             modifyAppearanceIfNeeded(
96                 originalAppearance, originalAppearanceRegions, letterboxDetails)
97 
98         val barModeChanged = centralSurfaces.setAppearance(appearance)
99 
100         lightBarController.onStatusBarAppearanceChanged(
101             appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
102 
103         centralSurfaces.updateBubblesVisibility()
104         statusBarStateController.setSystemBarAttributes(
105             appearance, behavior, requestedVisibleTypes, packageName)
106     }
107 
108     private fun modifyAppearanceIfNeeded(
109         appearance: Int,
110         appearanceRegions: Array<AppearanceRegion>,
111         letterboxDetails: Array<LetterboxDetails>
112     ): Pair<Int, Array<AppearanceRegion>> =
113         if (shouldUseLetterboxAppearance(letterboxDetails)) {
114             val letterboxAppearance =
115                 letterboxAppearanceCalculator.getLetterboxAppearance(
116                     appearance, appearanceRegions, letterboxDetails)
117             lastLetterboxAppearance = letterboxAppearance
118             Pair(letterboxAppearance.appearance, letterboxAppearance.appearanceRegions)
119         } else {
120             lastLetterboxAppearance = null
121             Pair(appearance, appearanceRegions)
122         }
123 
124     private fun shouldUseLetterboxAppearance(letterboxDetails: Array<LetterboxDetails>) =
125         letterboxDetails.isNotEmpty()
126 
127     override fun dump(printWriter: PrintWriter, strings: Array<String>) {
128         printWriter.println("lastSystemBarAttributesParams: $lastSystemBarAttributesParams")
129         printWriter.println("lastLetterboxAppearance: $lastLetterboxAppearance")
130     }
131 }
132 
133 /**
134  * Keeps track of the parameters passed in
135  * [SystemBarAttributesListener.onSystemBarAttributesChanged].
136  */
137 private data class SystemBarAttributesParams(
138         val displayId: Int,
139         @Appearance val appearance: Int,
140         val appearanceRegions: List<AppearanceRegion>,
141         val navbarColorManagedByIme: Boolean,
142         @Behavior val behavior: Int,
143         @InsetsType val requestedVisibleTypes: Int,
144         val packageName: String,
145         val letterboxes: List<LetterboxDetails>,
146 ) {
147     val letterboxesArray = letterboxes.toTypedArray()
148     val appearanceRegionsArray = appearanceRegions.toTypedArray()
149     override fun toString(): String {
150         val appearanceToString =
151                 ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
152         return """SystemBarAttributesParams(
153             displayId=$displayId,
154             appearance=$appearanceToString,
155             appearanceRegions=$appearanceRegions,
156             navbarColorManagedByIme=$navbarColorManagedByIme,
157             behavior=$behavior,
158             requestedVisibleTypes=$requestedVisibleTypes,
159             packageName='$packageName',
160             letterboxes=$letterboxes,
161             letterboxesArray=${letterboxesArray.contentToString()},
162             appearanceRegionsArray=${appearanceRegionsArray.contentToString()}
163             )""".trimMargin()
164     }
165 }
166