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