1 /* <lambda>null2 * Copyright (C) 2019 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.customization.module 17 18 import android.app.Activity 19 import android.app.WallpaperColors 20 import android.app.WallpaperManager 21 import android.content.Context 22 import android.content.Intent 23 import android.content.res.Resources 24 import android.net.Uri 25 import androidx.activity.ComponentActivity 26 import androidx.lifecycle.DefaultLifecycleObserver 27 import androidx.lifecycle.LifecycleOwner 28 import androidx.lifecycle.ViewModelProvider 29 import com.android.customization.model.color.ColorCustomizationManager 30 import com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_PRESET 31 import com.android.customization.model.color.ThemedWallpaperColorResources 32 import com.android.customization.model.color.WallpaperColorResources 33 import com.android.customization.model.grid.GridOptionsManager 34 import com.android.customization.model.mode.DarkModeSnapshotRestorer 35 import com.android.customization.model.theme.OverlayManagerCompat 36 import com.android.customization.model.themedicon.ThemedIconSwitchProvider 37 import com.android.customization.model.themedicon.data.repository.ThemeIconRepository 38 import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor 39 import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer 40 import com.android.customization.module.logging.ThemesUserEventLogger 41 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor 42 import com.android.customization.picker.clock.domain.interactor.ClockPickerSnapshotRestorer 43 import com.android.customization.picker.clock.ui.view.ClockViewFactory 44 import com.android.customization.picker.clock.ui.view.ThemePickerClockViewFactory 45 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel 46 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel 47 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor 48 import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer 49 import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel 50 import com.android.customization.picker.grid.data.repository.GridRepositoryImpl 51 import com.android.customization.picker.grid.domain.interactor.GridInteractor 52 import com.android.customization.picker.grid.domain.interactor.GridSnapshotRestorer 53 import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel 54 import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer 55 import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel 56 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor 57 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer 58 import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel 59 import com.android.customization.picker.settings.ui.viewmodel.ColorContrastSectionViewModel 60 import com.android.systemui.shared.clocks.ClockRegistry 61 import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository 62 import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor 63 import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository 64 import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository 65 import com.android.wallpaper.config.BaseFlags 66 import com.android.wallpaper.module.CustomizationSections 67 import com.android.wallpaper.module.FragmentFactory 68 import com.android.wallpaper.module.NetworkStatusNotifier 69 import com.android.wallpaper.module.PackageStatusNotifier 70 import com.android.wallpaper.module.PartnerProvider 71 import com.android.wallpaper.module.WallpaperPicker2Injector 72 import com.android.wallpaper.module.WallpaperPreferences 73 import com.android.wallpaper.module.logging.UserEventLogger 74 import com.android.wallpaper.network.Requester 75 import com.android.wallpaper.picker.CustomizationPickerActivity 76 import com.android.wallpaper.picker.category.wrapper.WallpaperCategoryWrapper 77 import com.android.wallpaper.picker.customization.data.content.WallpaperClient 78 import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository 79 import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor 80 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher 81 import com.android.wallpaper.picker.di.modules.MainDispatcher 82 import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer 83 import com.android.wallpaper.system.UiModeManagerWrapper 84 import com.android.wallpaper.util.DisplayUtils 85 import dagger.Lazy 86 import javax.inject.Inject 87 import javax.inject.Singleton 88 import kotlinx.coroutines.CoroutineDispatcher 89 import kotlinx.coroutines.CoroutineScope 90 91 @Singleton 92 open class ThemePickerInjector 93 @Inject 94 constructor( 95 @MainDispatcher private val mainScope: CoroutineScope, 96 @BackgroundDispatcher private val bgScope: CoroutineScope, 97 @BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher, 98 private val colorContrastSectionViewModelFactory: Lazy<ColorContrastSectionViewModel.Factory>, 99 private val keyguardQuickAffordancePickerInteractor: 100 Lazy<KeyguardQuickAffordancePickerInteractor>, 101 private val keyguardQuickAffordanceSnapshotRestorer: 102 Lazy<KeyguardQuickAffordanceSnapshotRestorer>, 103 private val themesUserEventLogger: Lazy<ThemesUserEventLogger>, 104 private val colorPickerInteractor: Lazy<ColorPickerInteractor>, 105 private val colorPickerSnapshotRestorer: Lazy<ColorPickerSnapshotRestorer>, 106 private val clockRegistry: Lazy<ClockRegistry>, 107 private val secureSettingsRepository: Lazy<SecureSettingsRepository>, 108 private val systemSettingsRepository: Lazy<SystemSettingsRepository>, 109 private val clockPickerInteractor: Lazy<ClockPickerInteractor>, 110 private val clockPickerSnapshotRestorer: Lazy<ClockPickerSnapshotRestorer>, 111 displayUtils: Lazy<DisplayUtils>, 112 requester: Lazy<Requester>, 113 networkStatusNotifier: Lazy<NetworkStatusNotifier>, 114 partnerProvider: Lazy<PartnerProvider>, 115 val uiModeManager: Lazy<UiModeManagerWrapper>, 116 userEventLogger: Lazy<UserEventLogger>, 117 injectedWallpaperClient: Lazy<WallpaperClient>, 118 private val injectedWallpaperInteractor: Lazy<WallpaperInteractor>, 119 prefs: Lazy<WallpaperPreferences>, 120 wallpaperColorsRepository: Lazy<WallpaperColorsRepository>, 121 defaultWallpaperCategoryWrapper: Lazy<WallpaperCategoryWrapper>, 122 packageNotifier: Lazy<PackageStatusNotifier>, 123 ) : 124 WallpaperPicker2Injector( 125 mainScope, 126 displayUtils, 127 requester, 128 networkStatusNotifier, 129 partnerProvider, 130 uiModeManager, 131 userEventLogger, 132 injectedWallpaperClient, 133 injectedWallpaperInteractor, 134 prefs, 135 wallpaperColorsRepository, 136 defaultWallpaperCategoryWrapper, 137 packageNotifier, 138 ), 139 CustomizationInjector { 140 private var customizationSections: CustomizationSections? = null 141 private var keyguardQuickAffordancePickerViewModelFactory: 142 KeyguardQuickAffordancePickerViewModel.Factory? = 143 null 144 private var fragmentFactory: FragmentFactory? = null 145 private var notificationsSnapshotRestorer: NotificationsSnapshotRestorer? = null 146 private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null 147 private var clockViewFactory: ClockViewFactory? = null 148 private var notificationSettingsInteractor: NotificationSettingsInteractor? = null 149 private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null 150 private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null 151 private var colorCustomizationManager: ColorCustomizationManager? = null 152 private var darkModeSnapshotRestorer: DarkModeSnapshotRestorer? = null 153 private var themedIconSnapshotRestorer: ThemedIconSnapshotRestorer? = null 154 private var themedIconInteractor: ThemedIconInteractor? = null 155 private var clockSettingsViewModelFactory: ClockSettingsViewModel.Factory? = null 156 private var gridInteractor: GridInteractor? = null 157 private var gridSnapshotRestorer: GridSnapshotRestorer? = null 158 private var gridScreenViewModelFactory: GridScreenViewModel.Factory? = null 159 160 override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections { 161 val appContext = activity.applicationContext 162 val clockViewFactory = getClockViewFactory(activity) 163 val resources = activity.resources 164 return customizationSections 165 ?: DefaultCustomizationSections( 166 getColorPickerViewModelFactory(appContext), 167 getKeyguardQuickAffordancePickerViewModelFactory(appContext), 168 colorContrastSectionViewModelFactory.get(), 169 getNotificationSectionViewModelFactory(appContext), 170 getFlags(), 171 getClockCarouselViewModelFactory( 172 interactor = clockPickerInteractor.get(), 173 clockViewFactory = clockViewFactory, 174 resources = resources, 175 ), 176 clockViewFactory, 177 getThemedIconSnapshotRestorer(appContext), 178 getThemedIconInteractor(), 179 getGridInteractor(appContext), 180 colorPickerInteractor.get(), 181 getUserEventLogger(), 182 ) 183 .also { customizationSections = it } 184 } 185 186 override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent { 187 val intent = Intent() 188 intent.setClass(context, CustomizationPickerActivity::class.java) 189 intent.data = uri 190 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK 191 return intent 192 } 193 194 override fun getDownloadableIntentAction(): String? { 195 return null 196 } 197 198 @Synchronized 199 override fun getUserEventLogger(): ThemesUserEventLogger { 200 return themesUserEventLogger.get() 201 } 202 203 override fun getFragmentFactory(): FragmentFactory? { 204 return fragmentFactory ?: ThemePickerFragmentFactory().also { fragmentFactory } 205 } 206 207 override fun getSnapshotRestorers(context: Context): Map<Int, SnapshotRestorer> { 208 return super<WallpaperPicker2Injector>.getSnapshotRestorers(context).toMutableMap().apply { 209 this[KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER] = 210 keyguardQuickAffordanceSnapshotRestorer.get() 211 // TODO(b/285047815): Enable after adding wallpaper id for default static wallpaper 212 if (getFlags().isWallpaperRestorerEnabled()) { 213 this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context) 214 } 215 this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] = getNotificationsSnapshotRestorer(context) 216 this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context) 217 this[KEY_THEMED_ICON_SNAPSHOT_RESTORER] = getThemedIconSnapshotRestorer(context) 218 this[KEY_APP_GRID_SNAPSHOT_RESTORER] = getGridSnapshotRestorer(context) 219 this[KEY_COLOR_PICKER_SNAPSHOT_RESTORER] = colorPickerSnapshotRestorer.get() 220 this[KEY_CLOCKS_SNAPSHOT_RESTORER] = clockPickerSnapshotRestorer.get() 221 } 222 } 223 224 override fun getCustomizationPreferences(context: Context): CustomizationPreferences { 225 return getPreferences(context) as CustomizationPreferences 226 } 227 228 override fun getWallpaperInteractor(context: Context): WallpaperInteractor { 229 return injectedWallpaperInteractor.get() 230 } 231 232 override fun getKeyguardQuickAffordancePickerInteractor( 233 context: Context 234 ): KeyguardQuickAffordancePickerInteractor { 235 return keyguardQuickAffordancePickerInteractor.get() 236 } 237 238 fun getKeyguardQuickAffordancePickerViewModelFactory( 239 context: Context 240 ): KeyguardQuickAffordancePickerViewModel.Factory { 241 return keyguardQuickAffordancePickerViewModelFactory 242 ?: KeyguardQuickAffordancePickerViewModel.Factory( 243 context.applicationContext, 244 getKeyguardQuickAffordancePickerInteractor(context), 245 getWallpaperInteractor(context), 246 getCurrentWallpaperInfoFactory(context), 247 getUserEventLogger(), 248 ) 249 .also { keyguardQuickAffordancePickerViewModelFactory = it } 250 } 251 252 fun getNotificationSectionViewModelFactory( 253 context: Context 254 ): NotificationSectionViewModel.Factory { 255 return notificationSectionViewModelFactory 256 ?: NotificationSectionViewModel.Factory( 257 interactor = getNotificationsInteractor(context), 258 logger = getUserEventLogger(), 259 ) 260 .also { notificationSectionViewModelFactory = it } 261 } 262 263 private fun getNotificationsInteractor(context: Context): NotificationSettingsInteractor { 264 return notificationSettingsInteractor 265 ?: NotificationSettingsInteractor( 266 repository = 267 NotificationSettingsRepository( 268 backgroundScope = bgScope, 269 backgroundDispatcher = bgDispatcher, 270 secureSettingsRepository = secureSettingsRepository.get(), 271 systemSettingsRepository = systemSettingsRepository.get(), 272 ) 273 ) 274 .also { notificationSettingsInteractor = it } 275 } 276 277 private fun getNotificationsSnapshotRestorer(context: Context): NotificationsSnapshotRestorer { 278 return notificationsSnapshotRestorer 279 ?: NotificationsSnapshotRestorer( 280 interactor = getNotificationsInteractor(context = context), 281 backgroundScope = bgScope, 282 ) 283 .also { notificationsSnapshotRestorer = it } 284 } 285 286 override fun getClockCarouselViewModelFactory( 287 interactor: ClockPickerInteractor, 288 clockViewFactory: ClockViewFactory, 289 resources: Resources, 290 ): ClockCarouselViewModel.Factory { 291 return clockCarouselViewModelFactory 292 ?: ClockCarouselViewModel.Factory( 293 interactor, 294 bgDispatcher, 295 clockViewFactory, 296 resources, 297 getUserEventLogger(), 298 ) 299 .also { clockCarouselViewModelFactory = it } 300 } 301 302 override fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory { 303 return clockViewFactory 304 ?: ThemePickerClockViewFactory( 305 activity, 306 WallpaperManager.getInstance(activity.applicationContext), 307 clockRegistry.get(), 308 ) 309 .also { 310 clockViewFactory = it 311 activity.lifecycle.addObserver( 312 object : DefaultLifecycleObserver { 313 override fun onDestroy(owner: LifecycleOwner) { 314 super.onDestroy(owner) 315 if ((owner as Activity).isChangingConfigurations()) return 316 clockViewFactory?.onDestroy() 317 } 318 } 319 ) 320 } 321 } 322 323 override fun getWallpaperColorResources( 324 wallpaperColors: WallpaperColors, 325 context: Context, 326 ): WallpaperColorResources { 327 return ThemedWallpaperColorResources(wallpaperColors, secureSettingsRepository.get()) 328 } 329 330 override fun getColorPickerViewModelFactory(context: Context): ColorPickerViewModel.Factory { 331 return colorPickerViewModelFactory 332 ?: ColorPickerViewModel.Factory( 333 context.applicationContext, 334 colorPickerInteractor.get(), 335 getUserEventLogger(), 336 ) 337 .also { colorPickerViewModelFactory = it } 338 } 339 340 private fun getColorCustomizationManager(context: Context): ColorCustomizationManager { 341 return colorCustomizationManager 342 ?: ColorCustomizationManager.getInstance(context, OverlayManagerCompat(context)).also { 343 colorCustomizationManager = it 344 } 345 } 346 347 fun getDarkModeSnapshotRestorer(context: Context): DarkModeSnapshotRestorer { 348 val appContext = context.applicationContext 349 return darkModeSnapshotRestorer 350 ?: DarkModeSnapshotRestorer( 351 context = appContext, 352 manager = uiModeManager.get(), 353 backgroundDispatcher = bgDispatcher, 354 ) 355 .also { darkModeSnapshotRestorer = it } 356 } 357 358 protected fun getThemedIconSnapshotRestorer(context: Context): ThemedIconSnapshotRestorer { 359 val optionProvider = ThemedIconSwitchProvider.getInstance(context) 360 return themedIconSnapshotRestorer 361 ?: ThemedIconSnapshotRestorer( 362 isActivated = { optionProvider.isThemedIconEnabled }, 363 setActivated = { isActivated -> 364 optionProvider.isThemedIconEnabled = isActivated 365 }, 366 interactor = getThemedIconInteractor(), 367 ) 368 .also { themedIconSnapshotRestorer = it } 369 } 370 371 protected fun getThemedIconInteractor(): ThemedIconInteractor { 372 return themedIconInteractor 373 ?: ThemedIconInteractor(repository = ThemeIconRepository()).also { 374 themedIconInteractor = it 375 } 376 } 377 378 override fun getClockSettingsViewModelFactory( 379 context: Context, 380 wallpaperColorsRepository: WallpaperColorsRepository, 381 clockViewFactory: ClockViewFactory, 382 ): ClockSettingsViewModel.Factory { 383 return clockSettingsViewModelFactory 384 ?: ClockSettingsViewModel.Factory( 385 context.applicationContext, 386 clockPickerInteractor.get(), 387 colorPickerInteractor.get(), 388 getUserEventLogger(), 389 ) { clockId -> 390 clockId?.let { clockViewFactory.getController(clockId).config.isReactiveToTone } 391 ?: false 392 } 393 .also { clockSettingsViewModelFactory = it } 394 } 395 396 fun getGridScreenViewModelFactory(context: Context): ViewModelProvider.Factory { 397 return gridScreenViewModelFactory 398 ?: GridScreenViewModel.Factory( 399 context = context, 400 interactor = getGridInteractor(context), 401 ) 402 .also { gridScreenViewModelFactory = it } 403 } 404 405 fun getGridInteractor(context: Context): GridInteractor { 406 val appContext = context.applicationContext 407 return gridInteractor 408 ?: GridInteractor( 409 applicationScope = getApplicationCoroutineScope(), 410 repository = 411 GridRepositoryImpl( 412 applicationScope = getApplicationCoroutineScope(), 413 manager = GridOptionsManager.getInstance(context), 414 backgroundDispatcher = bgDispatcher, 415 isGridApplyButtonEnabled = 416 BaseFlags.get().isGridApplyButtonEnabled(appContext), 417 ), 418 snapshotRestorer = { getGridSnapshotRestorer(appContext) }, 419 ) 420 .also { gridInteractor = it } 421 } 422 423 private fun getGridSnapshotRestorer(context: Context): GridSnapshotRestorer { 424 return gridSnapshotRestorer 425 ?: GridSnapshotRestorer(interactor = getGridInteractor(context)).also { 426 gridSnapshotRestorer = it 427 } 428 } 429 430 override fun isCurrentSelectedColorPreset(context: Context): Boolean { 431 val colorManager = 432 ColorCustomizationManager.getInstance(context, OverlayManagerCompat(context)) 433 return COLOR_SOURCE_PRESET == colorManager.currentColorSource 434 } 435 436 companion object { 437 @JvmStatic 438 private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = 439 WallpaperPicker2Injector.MIN_SNAPSHOT_RESTORER_KEY 440 @JvmStatic 441 private val KEY_WALLPAPER_SNAPSHOT_RESTORER = KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER + 1 442 @JvmStatic 443 private val KEY_NOTIFICATIONS_SNAPSHOT_RESTORER = KEY_WALLPAPER_SNAPSHOT_RESTORER + 1 444 @JvmStatic 445 private val KEY_DARK_MODE_SNAPSHOT_RESTORER = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1 446 @JvmStatic 447 private val KEY_THEMED_ICON_SNAPSHOT_RESTORER = KEY_DARK_MODE_SNAPSHOT_RESTORER + 1 448 @JvmStatic 449 private val KEY_APP_GRID_SNAPSHOT_RESTORER = KEY_THEMED_ICON_SNAPSHOT_RESTORER + 1 450 @JvmStatic 451 private val KEY_COLOR_PICKER_SNAPSHOT_RESTORER = KEY_APP_GRID_SNAPSHOT_RESTORER + 1 452 @JvmStatic private val KEY_CLOCKS_SNAPSHOT_RESTORER = KEY_COLOR_PICKER_SNAPSHOT_RESTORER + 1 453 454 /** 455 * When this injector is overridden, this is the minimal value that should be used by 456 * restorers returns in [getSnapshotRestorers]. 457 * 458 * It should always be greater than the biggest restorer key. 459 */ 460 @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_CLOCKS_SNAPSHOT_RESTORER + 1 461 } 462 } 463