• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.annotation.SuppressLint
20 import android.content.ContentResolver
21 import android.os.Handler
22 import android.view.LayoutInflater
23 import android.view.ViewStub
24 import androidx.constraintlayout.motion.widget.MotionLayout
25 import com.android.compose.animation.scene.SceneKey
26 import com.android.keyguard.logging.ScrimLogger
27 import com.android.systemui.battery.BatteryMeterView
28 import com.android.systemui.battery.BatteryMeterViewController
29 import com.android.systemui.biometrics.AuthRippleView
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.dagger.qualifiers.Main
32 import com.android.systemui.flags.FeatureFlags
33 import com.android.systemui.keyguard.ui.view.KeyguardRootView
34 import com.android.systemui.privacy.OngoingPrivacyChip
35 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
36 import com.android.systemui.res.R
37 import com.android.systemui.scene.shared.flag.SceneContainerFlag
38 import com.android.systemui.scene.shared.model.SceneContainerConfig
39 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
40 import com.android.systemui.scene.ui.composable.Overlay
41 import com.android.systemui.scene.ui.composable.Scene
42 import com.android.systemui.scene.ui.view.SceneJankMonitor
43 import com.android.systemui.scene.ui.view.SceneWindowRootView
44 import com.android.systemui.scene.ui.view.WindowRootView
45 import com.android.systemui.scene.ui.view.WindowRootViewKeyEventHandler
46 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
47 import com.android.systemui.settings.UserTracker
48 import com.android.systemui.statusbar.LightRevealScrim
49 import com.android.systemui.statusbar.NotificationInsetsController
50 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
51 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
52 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
53 import com.android.systemui.statusbar.phone.StatusBarLocation
54 import com.android.systemui.statusbar.phone.StatusIconContainer
55 import com.android.systemui.statusbar.phone.TapAgainView
56 import com.android.systemui.statusbar.policy.BatteryController
57 import com.android.systemui.statusbar.policy.ConfigurationController
58 import com.android.systemui.tuner.TunerService
59 import dagger.Binds
60 import dagger.Module
61 import dagger.Provides
62 import javax.inject.Named
63 import javax.inject.Provider
64 
65 /** Module for providing views related to the shade. */
66 @Module
67 abstract class ShadeViewProviderModule {
68 
69     @Binds
70     @SysUISingleton
71     // TODO(b/277762009): Only allow this view's binder to inject the view.
72     abstract fun bindsNotificationScrollView(
73         notificationStackScrollLayout: NotificationStackScrollLayout
74     ): NotificationScrollView
75 
76     companion object {
77         const val SHADE_HEADER = "large_screen_shade_header"
78 
79         @SuppressLint("InflateParams") // Root views don't have parents.
80         @Provides
81         @SysUISingleton
82         fun providesWindowRootView(
83             @ShadeDisplayAware layoutInflater: LayoutInflater,
84             viewModelFactory: SceneContainerViewModel.Factory,
85             containerConfigProvider: Provider<SceneContainerConfig>,
86             scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
87             overlaysProvider: Provider<Set<@JvmSuppressWildcards Overlay>>,
88             layoutInsetController: NotificationInsetsController,
89             sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
90             qsSceneAdapter: Provider<QSSceneAdapter>,
91             sceneJankMonitorFactory: SceneJankMonitor.Factory,
92             windowRootViewKeyEventHandler: WindowRootViewKeyEventHandler,
93         ): WindowRootView {
94             return if (SceneContainerFlag.isEnabled) {
95                 checkNoSceneDuplicates(scenesProvider.get())
96                 val sceneWindowRootView =
97                     layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
98                 sceneWindowRootView.init(
99                     viewModelFactory = viewModelFactory,
100                     containerConfig = containerConfigProvider.get(),
101                     sharedNotificationContainer =
102                         sceneWindowRootView.requireViewById(R.id.shared_notification_container),
103                     scenes = scenesProvider.get(),
104                     overlays = overlaysProvider.get(),
105                     layoutInsetController = layoutInsetController,
106                     sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
107                     qsSceneAdapter = qsSceneAdapter,
108                     sceneJankMonitorFactory = sceneJankMonitorFactory,
109                     windowRootViewKeyEventHandler = windowRootViewKeyEventHandler,
110                 )
111                 sceneWindowRootView
112             } else {
113                 layoutInflater.inflate(R.layout.super_notification_shade, null)
114             }
115                 as WindowRootView?
116                 ?: throw IllegalStateException("Window root view could not be properly inflated")
117         }
118 
119         // TODO(b/277762009): Do something similar to
120         //  {@link StatusBarWindowModule.InternalWindowView} so that only
121         //  {@link NotificationShadeWindowViewController} can inject this view.
122         @Provides
123         @SysUISingleton
124         fun providesNotificationShadeWindowView(root: WindowRootView): NotificationShadeWindowView {
125             if (SceneContainerFlag.isEnabled) {
126                 return root.requireViewById(R.id.legacy_window_root)
127             }
128             return root as NotificationShadeWindowView?
129                 ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
130         }
131 
132         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
133         @Provides
134         @SysUISingleton
135         fun providesNotificationStackScrollLayout(
136             notificationShadeWindowView: NotificationShadeWindowView
137         ): NotificationStackScrollLayout {
138             return notificationShadeWindowView.requireViewById(R.id.notification_stack_scroller)
139         }
140 
141         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
142         @Provides
143         @SysUISingleton
144         fun providesNotificationPanelView(
145             notificationShadeWindowView: NotificationShadeWindowView
146         ): NotificationPanelView {
147             return notificationShadeWindowView.requireViewById(R.id.notification_panel)
148         }
149 
150         @Provides
151         @SysUISingleton
152         fun providesLightRevealScrim(
153             notificationShadeWindowView: NotificationShadeWindowView,
154             scrimLogger: ScrimLogger,
155         ): LightRevealScrim {
156             val scrim =
157                 notificationShadeWindowView.requireViewById<LightRevealScrim>(
158                     R.id.light_reveal_scrim
159                 )
160             scrim.scrimLogger = scrimLogger
161             return scrim
162         }
163 
164         @Provides
165         @SysUISingleton
166         fun providesKeyguardRootView(
167             notificationShadeWindowView: NotificationShadeWindowView
168         ): KeyguardRootView {
169             return notificationShadeWindowView.requireViewById(R.id.keyguard_root_view)
170         }
171 
172         @Provides
173         @SysUISingleton
174         fun providesSharedNotificationContainer(
175             notificationShadeWindowView: NotificationShadeWindowView
176         ): SharedNotificationContainer {
177             return notificationShadeWindowView.requireViewById(R.id.shared_notification_container)
178         }
179 
180         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
181         @Provides
182         @SysUISingleton
183         fun providesAuthRippleView(
184             notificationShadeWindowView: NotificationShadeWindowView
185         ): AuthRippleView? {
186             return notificationShadeWindowView.requireViewById(R.id.auth_ripple)
187         }
188 
189         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
190         @Provides
191         @SysUISingleton
192         fun providesTapAgainView(notificationPanelView: NotificationPanelView): TapAgainView {
193             return notificationPanelView.requireViewById(R.id.shade_falsing_tap_again)
194         }
195 
196         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
197         @Provides
198         @SysUISingleton
199         fun providesNotificationsQuickSettingsContainer(
200             notificationShadeWindowView: NotificationShadeWindowView
201         ): NotificationsQuickSettingsContainer {
202             return notificationShadeWindowView.requireViewById(R.id.notification_container_parent)
203         }
204 
205         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
206         @Provides
207         @SysUISingleton
208         @Named(SHADE_HEADER)
209         fun providesShadeHeaderView(
210             notificationShadeWindowView: NotificationShadeWindowView
211         ): MotionLayout {
212             val stub = notificationShadeWindowView.requireViewById<ViewStub>(R.id.qs_header_stub)
213             val layoutId = R.layout.combined_qs_header
214             stub.layoutResource = layoutId
215             return stub.inflate() as MotionLayout
216         }
217 
218         @Provides
219         @SysUISingleton
220         fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
221             return CombinedShadeHeadersConstraintManagerImpl
222         }
223 
224         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
225         @Provides
226         @SysUISingleton
227         @Named(SHADE_HEADER)
228         fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
229             return view.requireViewById(R.id.batteryRemainingIcon)
230         }
231 
232         @Provides
233         @SysUISingleton
234         @Named(SHADE_HEADER)
235         fun providesBatteryMeterViewController(
236             @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
237             userTracker: UserTracker,
238             @ShadeDisplayAware configurationController: ConfigurationController,
239             tunerService: TunerService,
240             @Main mainHandler: Handler,
241             contentResolver: ContentResolver,
242             featureFlags: FeatureFlags,
243             batteryController: BatteryController,
244         ): BatteryMeterViewController {
245             return BatteryMeterViewController(
246                 batteryMeterView,
247                 StatusBarLocation.QS,
248                 userTracker,
249                 configurationController,
250                 tunerService,
251                 mainHandler,
252                 contentResolver,
253                 featureFlags,
254                 batteryController,
255             )
256         }
257 
258         @Provides
259         @SysUISingleton
260         @Named(SHADE_HEADER)
261         fun providesOngoingPrivacyChip(
262             @Named(SHADE_HEADER) header: MotionLayout
263         ): OngoingPrivacyChip {
264             return header.requireViewById(R.id.privacy_chip)
265         }
266 
267         @Provides
268         @SysUISingleton
269         @Named(SHADE_HEADER)
270         fun providesStatusIconContainer(
271             @Named(SHADE_HEADER) header: MotionLayout
272         ): StatusIconContainer {
273             return header.requireViewById(R.id.statusIcons)
274         }
275 
276         private fun checkNoSceneDuplicates(scenes: Set<Scene>) {
277             val keys = mutableSetOf<SceneKey>()
278             val duplicates = mutableSetOf<SceneKey>()
279             scenes
280                 .map { it.key }
281                 .forEach { sceneKey ->
282                     if (keys.contains(sceneKey)) {
283                         duplicates.add(sceneKey)
284                     } else {
285                         keys.add(sceneKey)
286                     }
287                 }
288 
289             check(duplicates.isEmpty()) { "Duplicate scenes detected: $duplicates" }
290         }
291     }
292 }
293