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