• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.model
17 
18 import android.util.Log
19 import android.view.Display
20 import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
21 import com.android.systemui.Dumpable
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dump.DumpManager
24 import com.android.systemui.model.SysUiState.SysUiStateCallback
25 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
26 import com.android.systemui.shared.system.QuickStepContract
27 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
28 import dagger.assisted.Assisted
29 import dagger.assisted.AssistedFactory
30 import dagger.assisted.AssistedInject
31 import dalvik.annotation.optimization.NeverCompile
32 import java.io.PrintWriter
33 import java.lang.Long.bitCount
34 import javax.inject.Inject
35 
36 /** Contains sysUi state flags and notifies registered listeners whenever changes happen. */
37 interface SysUiState : Dumpable {
38     /**
39      * Add listener to be notified of changes made to SysUI state.
40      *
41      * The callback will also be called as part of this function.
42      */
addCallbacknull43     fun addCallback(callback: SysUiStateCallback)
44 
45     /** Removes a callback for state changes. */
46     fun removeCallback(callback: SysUiStateCallback)
47 
48     /** Returns whether a flag is enabled in this state. */
49     fun isFlagEnabled(@SystemUiStateFlags flag: Long): Boolean {
50         return (flags and flag) != 0L
51     }
52 
53     /** Returns the current sysui state flags. */
54     val flags: Long
55 
56     /** Methods to this call can be chained together before calling [commitUpdate]. */
setFlagnull57     fun setFlag(@SystemUiStateFlags flag: Long, enabled: Boolean): SysUiState
58 
59     /** Call to save all the flags updated from [setFlag]. */
60     @Deprecated("Each SysUIState instance is now display specific. Just use commitUpdate()")
61     fun commitUpdate(displayId: Int)
62 
63     /** Call to save all the flags updated from [setFlag]. */
64     fun commitUpdate()
65 
66     /** Callback to be notified whenever system UI state flags are changed. */
67     fun interface SysUiStateCallback {
68 
69         /** To be called when any SysUiStateFlag gets updated for a specific [displayId]. */
70         fun onSystemUiStateChanged(@SystemUiStateFlags sysUiFlags: Long, displayId: Int)
71     }
72 
73     /**
74      * Destroys an instance. It shouldn't be used anymore afterwards.
75      *
76      * This is mainly used to clean up instances associated with displays that are removed.
77      */
destroynull78     fun destroy()
79 
80     /** Initializes the state after construction. */
81     fun start()
82 
83     /** The display ID this instances is associated with */
84     val displayId: Int
85 
86     companion object {
87         const val DEBUG: Boolean = false
88     }
89 }
90 
91 private const val TAG = "SysUIState"
92 
93 open class SysUiStateImpl
94 @AssistedInject
95 constructor(
96     @Assisted override val displayId: Int,
97     private val sceneContainerPlugin: SceneContainerPlugin?,
98     private val dumpManager: DumpManager,
99     private val stateDispatcher: SysUIStateDispatcher,
100 ) : SysUiState {
101 
102     private val debugName
103         get() = "SysUiStateImpl-ForDisplay=$displayId"
104 
startnull105     override fun start() {
106         dumpManager.registerNormalDumpable(debugName, this)
107     }
108 
109     /** Returns the current sysui state flags. */
110     @get:SystemUiStateFlags
111     @SystemUiStateFlags
112     override val flags: Long
113         get() = _flags
114 
115     private var _flags: Long = 0
116     private val stateChange = StateChange()
117 
118     /**
119      * Add listener to be notified of changes made to SysUI state. The callback will also be called
120      * as part of this function.
121      *
122      * Note that the listener would receive updates for all displays.
123      */
addCallbacknull124     override fun addCallback(callback: SysUiStateCallback) {
125         stateDispatcher.registerListener(callback)
126         callback.onSystemUiStateChanged(flags, displayId)
127     }
128 
129     /** Callback will no longer receive events on state change */
removeCallbacknull130     override fun removeCallback(callback: SysUiStateCallback) {
131         stateDispatcher.unregisterListener(callback)
132     }
133 
134     /** Methods to this call can be chained together before calling [.commitUpdate]. */
setFlagnull135     override fun setFlag(@SystemUiStateFlags flag: Long, enabled: Boolean): SysUiState {
136         if (ShadeWindowGoesAround.isEnabled && bitCount(flag) > 1) {
137             error("Flags should be a single bit.")
138         }
139         val toSet = flagWithOptionalOverrides(flag, enabled, displayId, sceneContainerPlugin)
140         stateChange.setFlag(flag, toSet)
141         return this
142     }
143 
144     @Deprecated(
145         "Each SysUIState instance is now display specific. Just use commitUpdate.",
146         ReplaceWith("commitUpdate()"),
147     )
commitUpdatenull148     override fun commitUpdate(displayId: Int) {
149         commitUpdate()
150     }
151 
commitUpdatenull152     override fun commitUpdate() {
153         val newState = stateChange.applyTo(flags)
154         notifyAndSetSystemUiStateChanged(newState, flags)
155         stateChange.clear()
156     }
157 
158     /** Notify all those who are registered that the state has changed. */
notifyAndSetSystemUiStateChangednull159     private fun notifyAndSetSystemUiStateChanged(newFlags: Long, oldFlags: Long) {
160         if (SysUiState.DEBUG) {
161             Log.d(TAG, "SysUiState changed for displayId=$displayId: old=$oldFlags new=$newFlags")
162         }
163         if (newFlags != oldFlags) {
164             _flags = newFlags
165             stateDispatcher.dispatchSysUIStateChange(newFlags, displayId)
166         }
167     }
168 
169     @NeverCompile
dumpnull170     override fun dump(pw: PrintWriter, args: Array<String>) {
171         pw.println("SysUiState state:")
172         pw.print("  mSysUiStateFlags=")
173         pw.println(flags)
174         pw.println("    " + QuickStepContract.getSystemUiStateString(flags))
175         pw.print("    backGestureDisabled=")
176         pw.println(QuickStepContract.isBackGestureDisabled(flags, false /* forTrackpad */))
177         pw.print("    assistantGestureDisabled=")
178         pw.println(QuickStepContract.isAssistantGestureDisabled(flags))
179         pw.print("    pendingStateChanges=")
180         pw.println(stateChange.toString())
181     }
182 
destroynull183     override fun destroy() {
184         dumpManager.unregisterDumpable(debugName)
185     }
186 
187     @AssistedFactory
188     interface Factory {
189         /** Creates a new instance of [SysUiStateImpl] for a given [displayId]. */
createnull190         fun create(displayId: Int): SysUiStateImpl
191     }
192 
193     companion object {
194         private val TAG: String = SysUiState::class.java.simpleName
195     }
196 }
197 
198 /** Returns the flag value taking into account [SceneContainerPlugin] potential overrides. */
flagWithOptionalOverridesnull199 fun flagWithOptionalOverrides(
200     flag: Long,
201     enabled: Boolean,
202     displayId: Int,
203     sceneContainerPlugin: SceneContainerPlugin?,
204 ): Boolean {
205     var toSet = enabled
206     val overrideOrNull = sceneContainerPlugin?.flagValueOverride(flag = flag, displayId = displayId)
207     if (overrideOrNull != null && toSet != overrideOrNull) {
208         if (SysUiState.DEBUG) {
209             Log.d(
210                 TAG,
211                 "setFlag for flag $flag and value $toSet overridden to " +
212                     "$overrideOrNull by scene container plugin",
213             )
214         }
215 
216         toSet = overrideOrNull
217     }
218     return toSet
219 }
220 
221 /** Creates and destroy instances of [SysUiState] */
222 @SysUISingleton
223 class SysUIStateInstanceProvider
224 @Inject
225 constructor(
226     private val factory: SysUiStateImpl.Factory,
227     private val overrideFactory: SysUIStateOverride.Factory,
228 ) : PerDisplayInstanceProviderWithTeardown<SysUiState> {
createInstancenull229     override fun createInstance(displayId: Int): SysUiState {
230         return if (displayId == Display.DEFAULT_DISPLAY) {
231                 factory.create(displayId)
232             } else {
233                 overrideFactory.create(displayId)
234             }
235             .apply { start() }
236     }
237 
destroyInstancenull238     override fun destroyInstance(instance: SysUiState) {
239         instance.destroy()
240     }
241 }
242