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