• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.systemui.shade
18 
19 import android.content.Context
20 import android.content.res.Resources
21 import android.view.LayoutInflater
22 import android.view.WindowManager
23 import android.view.WindowManager.LayoutParams
24 import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE
25 import android.window.WindowContext
26 import com.android.app.tracing.TrackGroupUtils.trackGroup
27 import com.android.systemui.CoreStartable
28 import com.android.systemui.common.ui.ConfigurationState
29 import com.android.systemui.common.ui.ConfigurationStateImpl
30 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
31 import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
32 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
33 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
34 import com.android.systemui.common.ui.view.ChoreographerUtils
35 import com.android.systemui.common.ui.view.ChoreographerUtilsImpl
36 import com.android.systemui.dagger.SysUISingleton
37 import com.android.systemui.dagger.qualifiers.Main
38 import com.android.systemui.log.LogBuffer
39 import com.android.systemui.log.LogBufferFactory
40 import com.android.systemui.res.R
41 import com.android.systemui.scene.ui.view.WindowRootView
42 import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
43 import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
44 import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
45 import com.android.systemui.shade.display.ShadeDisplayPolicyModule
46 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
47 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractorImpl
48 import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor
49 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
50 import com.android.systemui.statusbar.notification.stack.NotificationStackRebindingHider
51 import com.android.systemui.statusbar.notification.stack.NotificationStackRebindingHiderImpl
52 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
53 import com.android.systemui.statusbar.phone.ConfigurationForwarder
54 import com.android.systemui.statusbar.policy.ConfigurationController
55 import com.android.systemui.utils.windowmanager.WindowManagerProvider
56 import com.android.systemui.utils.windowmanager.WindowManagerUtils
57 import dagger.Module
58 import dagger.Provides
59 import dagger.multibindings.ClassKey
60 import dagger.multibindings.IntoMap
61 import javax.inject.Provider
62 import javax.inject.Qualifier
63 
64 /**
65  * Module responsible for managing display-specific components and resources for the notification
66  * shade window.
67  *
68  * This isolation is crucial because when the window transitions between displays, its associated
69  * context, resources, and display characteristics (like density and size) also change. If the shade
70  * window shared the same context as the rest of the system UI, it could lead to inconsistencies and
71  * errors due to incorrect display information.
72  *
73  * By using this dedicated module, we ensure the notification shade window always utilizes the
74  * correct display context and resources, regardless of the display it's on.
75  */
76 @Module(includes = [ShadeDisplayPolicyModule::class])
77 object ShadeDisplayAwareModule {
78 
79     /** Creates a new context for the shade window. */
80     @Provides
81     @ShadeDisplayAware
82     @SysUISingleton
provideShadeDisplayAwareContextnull83     fun provideShadeDisplayAwareContext(context: Context): Context {
84         return if (ShadeWindowGoesAround.isEnabled) {
85             context
86                 .createWindowContext(context.display, TYPE_NOTIFICATION_SHADE, /* options= */ null)
87                 .apply { setTheme(R.style.Theme_SystemUI) }
88         } else {
89             context
90         }
91     }
92 
93     @Provides
94     @ShadeDisplayAware
95     @SysUISingleton
provideShadeDisplayAwareWindowContextnull96     fun provideShadeDisplayAwareWindowContext(@ShadeDisplayAware context: Context): WindowContext {
97         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
98         // We rely on the fact context is a WindowContext as the API to reparent windows is only
99         // available there.
100         return (context as? WindowContext)
101             ?: error(
102                 "ShadeDisplayAware context must be a window context to allow window reparenting."
103             )
104     }
105 
106     @Provides
107     @ShadeDisplayAware
108     @SysUISingleton
provideShadeWindowLayoutParamsnull109     fun provideShadeWindowLayoutParams(@ShadeDisplayAware context: Context): LayoutParams {
110         return ShadeWindowLayoutParams.create(context)
111     }
112 
113     @Provides
114     @ShadeDisplayAware
115     @SysUISingleton
provideShadeWindowManagernull116     fun provideShadeWindowManager(
117         defaultWindowManager: WindowManager,
118         @ShadeDisplayAware context: Context,
119         windowManagerProvider: WindowManagerProvider
120     ): WindowManager {
121         return if (ShadeWindowGoesAround.isEnabled) {
122             windowManagerProvider.getWindowManager(context)
123         } else {
124             defaultWindowManager
125         }
126     }
127 
128     @Provides
129     @ShadeDisplayAware
130     @SysUISingleton
provideShadeDisplayAwareResourcesnull131     fun provideShadeDisplayAwareResources(@ShadeDisplayAware context: Context): Resources {
132         return context.resources
133     }
134 
135     @Provides
136     @ShadeDisplayAware
137     @SysUISingleton
providesDisplayAwareLayoutInflaternull138     fun providesDisplayAwareLayoutInflater(@ShadeDisplayAware context: Context): LayoutInflater {
139         return LayoutInflater.from(context)
140     }
141 
142     @Provides
143     @ShadeDisplayAware
144     @SysUISingleton
provideShadeWindowConfigurationControllernull145     fun provideShadeWindowConfigurationController(
146         @ShadeDisplayAware shadeContext: Context,
147         factory: ConfigurationControllerImpl.Factory,
148         @Main globalConfigController: ConfigurationController,
149     ): ConfigurationController {
150         return if (ShadeWindowGoesAround.isEnabled) {
151             factory.create(shadeContext)
152         } else {
153             globalConfigController
154         }
155     }
156 
157     @Provides
158     @ShadeDisplayAware
159     @SysUISingleton
provideShadeWindowConfigurationForwardernull160     fun provideShadeWindowConfigurationForwarder(
161         @ShadeDisplayAware shadeConfigurationController: ConfigurationController
162     ): ConfigurationForwarder {
163         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
164         return shadeConfigurationController
165     }
166 
167     @SysUISingleton
168     @Provides
169     @ShadeDisplayAware
provideShadeDisplayAwareConfigurationStatenull170     fun provideShadeDisplayAwareConfigurationState(
171         factory: ConfigurationStateImpl.Factory,
172         @ShadeDisplayAware configurationController: ConfigurationController,
173         @ShadeDisplayAware context: Context,
174         @Main configurationState: ConfigurationState,
175     ): ConfigurationState {
176         return if (ShadeWindowGoesAround.isEnabled) {
177             factory.create(context, configurationController)
178         } else {
179             configurationState
180         }
181     }
182 
183     @SysUISingleton
184     @Provides
185     @ShadeDisplayAware
provideShadeDisplayAwareConfigurationRepositorynull186     fun provideShadeDisplayAwareConfigurationRepository(
187         factory: ConfigurationRepositoryImpl.Factory,
188         @ShadeDisplayAware configurationController: ConfigurationController,
189         @ShadeDisplayAware context: Context,
190         @Main globalConfigurationRepository: ConfigurationRepository,
191     ): ConfigurationRepository {
192         return if (ShadeWindowGoesAround.isEnabled) {
193             factory.create(context, configurationController)
194         } else {
195             globalConfigurationRepository
196         }
197     }
198 
199     @SysUISingleton
200     @Provides
201     @ShadeDisplayAware
provideShadeAwareConfigurationInteractornull202     fun provideShadeAwareConfigurationInteractor(
203         @ShadeDisplayAware configurationRepository: ConfigurationRepository,
204         @Main configurationInteractor: ConfigurationInteractor,
205     ): ConfigurationInteractor {
206         return if (ShadeWindowGoesAround.isEnabled) {
207             ConfigurationInteractorImpl(configurationRepository)
208         } else {
209             configurationInteractor
210         }
211     }
212 
213     @SysUISingleton
214     @Provides
provideShadePositionRepositorynull215     fun provideShadePositionRepository(
216         impl: MutableShadeDisplaysRepository
217     ): ShadeDisplaysRepository {
218         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
219         return impl
220     }
221 
222     @SysUISingleton
223     @Provides
provideMutableShadePositionRepositorynull224     fun provideMutableShadePositionRepository(
225         impl: ShadeDisplaysRepositoryImpl
226     ): MutableShadeDisplaysRepository {
227         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
228         return impl
229     }
230 
231     @Provides
232     @SysUISingleton
provideShadeDialogContextInteractornull233     fun provideShadeDialogContextInteractor(
234         impl: ShadeDialogContextInteractorImpl
235     ): ShadeDialogContextInteractor = impl
236 
237     @Provides
238     @IntoMap
239     @ClassKey(ShadeDialogContextInteractor::class)
240     fun provideShadeDialogContextInteractorCoreStartable(
241         impl: Provider<ShadeDialogContextInteractorImpl>
242     ): CoreStartable {
243         return if (ShadeWindowGoesAround.isEnabled) {
244             impl.get()
245         } else {
246             CoreStartable.NOP
247         }
248     }
249 
250     @Provides
251     @IntoMap
252     @ClassKey(ShadePrimaryDisplayCommand::class)
provideShadePrimaryDisplayCommandnull253     fun provideShadePrimaryDisplayCommand(
254         impl: Provider<ShadePrimaryDisplayCommand>
255     ): CoreStartable {
256         return if (ShadeWindowGoesAround.isEnabled) {
257             impl.get()
258         } else {
259             CoreStartable.NOP
260         }
261     }
262 
263     /**
264      * Provided for making classes easier to test. In tests, a custom method to wait for the next
265      * frame can be easily provided.
266      */
provideChoreographerUtilsnull267     @Provides fun provideChoreographerUtils(): ChoreographerUtils = ChoreographerUtilsImpl
268 
269     @Provides
270     @ShadeOnDefaultDisplayWhenLocked
271     fun provideShadeOnDefaultDisplayWhenLocked(): Boolean = true
272 
273     /** Provides a [LogBuffer] for use by classes related to shade movement */
274     @Provides
275     @SysUISingleton
276     @ShadeDisplayLog
277     fun provideShadeDisplayLogLogBuffer(factory: LogBufferFactory): LogBuffer {
278         val logBufferName = "ShadeDisplayLog"
279         return factory.create(
280             logBufferName,
281             maxSize = 400,
282             alwaysLogToLogcat = true,
283             systraceTrackName = trackGroup("shade", logBufferName),
284         )
285     }
286 }
287 
288 /** Module that should be included only if the shade window [WindowRootView] is available. */
289 @Module
290 object ShadeDisplayAwareWithShadeWindowModule {
291     @Provides
292     @IntoMap
293     @ClassKey(ShadeDisplaysInteractor::class)
provideShadeDisplaysInteractornull294     fun provideShadeDisplaysInteractor(impl: Provider<ShadeDisplaysInteractor>): CoreStartable {
295         return if (ShadeWindowGoesAround.isEnabled) {
296             impl.get()
297         } else {
298             CoreStartable.NOP
299         }
300     }
301 
302     @Provides
303     @SysUISingleton
bindNotificationStackRebindingHidernull304     fun bindNotificationStackRebindingHider(
305         impl: NotificationStackRebindingHiderImpl
306     ): NotificationStackRebindingHider = impl
307 }
308 
309 /**
310  * Annotates the boolean value that defines whether the shade window should go back to the default
311  * display when the keyguard is visible.
312  *
313  * As of today (Dec 2024), This is a configuration parameter provided in the dagger graph as the
314  * final policy around keyguard display is still under discussion, and will be evaluated based on
315  * how well this solution behaves from the performance point of view.
316  */
317 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeOnDefaultDisplayWhenLocked
318 
319 /** A [com.android.systemui.log.LogBuffer] for changes to the shade display. */
320 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeDisplayLog
321