blob: 4988c6b26a84854550a82c7eaa5fb31cde9fce0d [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.customization.module
import android.app.UiModeManager
import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.TextUtils
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import com.android.customization.model.color.ColorCustomizationManager
import com.android.customization.model.color.ColorOptionsProvider
import com.android.customization.model.grid.GridOptionsManager
import com.android.customization.model.grid.data.repository.GridRepositoryImpl
import com.android.customization.model.grid.domain.interactor.GridInteractor
import com.android.customization.model.grid.domain.interactor.GridSnapshotRestorer
import com.android.customization.model.grid.ui.viewmodel.GridScreenViewModel
import com.android.customization.model.mode.DarkModeSnapshotRestorer
import com.android.customization.model.theme.OverlayManagerCompat
import com.android.customization.model.theme.ThemeBundleProvider
import com.android.customization.model.theme.ThemeManager
import com.android.customization.model.themedicon.ThemedIconSwitchProvider
import com.android.customization.model.themedicon.data.repository.ThemeIconRepository
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor
import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer
import com.android.customization.picker.clock.data.repository.ClockPickerRepositoryImpl
import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.domain.interactor.ClockPickerSnapshotRestorer
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
import com.android.customization.picker.notifications.data.repository.NotificationsRepository
import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer
import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel
import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer
import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
import com.android.wallpaper.model.LiveWallpaperInfo
import com.android.wallpaper.model.WallpaperColorsViewModel
import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.module.FragmentFactory
import com.android.wallpaper.module.UserEventLogger
import com.android.wallpaper.module.WallpaperPicker2Injector
import com.android.wallpaper.module.WallpaperPreferences
import com.android.wallpaper.picker.CustomizationPickerActivity
import com.android.wallpaper.picker.ImagePreviewFragment
import com.android.wallpaper.picker.LivePreviewFragment
import com.android.wallpaper.picker.PreviewFragment
import com.android.wallpaper.picker.customization.data.content.WallpaperClientImpl
import com.android.wallpaper.picker.customization.data.repository.WallpaperRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
import com.android.wallpaper.util.ScreenSizeCalculator
import kotlinx.coroutines.Dispatchers
open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInjector {
private var customizationSections: CustomizationSections? = null
private var userEventLogger: UserEventLogger? = null
private var prefs: WallpaperPreferences? = null
private var wallpaperInteractor: WallpaperInteractor? = null
private var keyguardQuickAffordancePickerInteractor: KeyguardQuickAffordancePickerInteractor? =
null
private var keyguardQuickAffordancePickerViewModelFactory:
KeyguardQuickAffordancePickerViewModel.Factory? =
null
private var customizationProviderClient: CustomizationProviderClient? = null
private var fragmentFactory: FragmentFactory? = null
private var keyguardQuickAffordanceSnapshotRestorer: KeyguardQuickAffordanceSnapshotRestorer? =
null
private var notificationsSnapshotRestorer: NotificationsSnapshotRestorer? = null
private var clockPickerInteractor: ClockPickerInteractor? = null
private var clockSectionViewModel: ClockSectionViewModel? = null
private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null
private var clockViewFactories: MutableMap<Int, ClockViewFactory> = HashMap()
private var clockPickerSnapshotRestorer: ClockPickerSnapshotRestorer? = null
private var notificationsInteractor: NotificationsInteractor? = null
private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
private var colorPickerInteractor: ColorPickerInteractor? = null
private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null
private var colorPickerSnapshotRestorer: ColorPickerSnapshotRestorer? = null
private var colorCustomizationManager: ColorCustomizationManager? = null
private var darkModeSnapshotRestorer: DarkModeSnapshotRestorer? = null
private var themedIconSnapshotRestorer: ThemedIconSnapshotRestorer? = null
private var themedIconInteractor: ThemedIconInteractor? = null
private var clockSettingsViewModelFactory: ClockSettingsViewModel.Factory? = null
private var gridInteractor: GridInteractor? = null
private var gridSnapshotRestorer: GridSnapshotRestorer? = null
private var gridScreenViewModelFactory: GridScreenViewModel.Factory? = null
private var clockRegistryProvider: ClockRegistryProvider? = null
override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections {
return customizationSections
?: DefaultCustomizationSections(
getColorPickerViewModelFactory(
context = activity,
wallpaperColorsViewModel = getWallpaperColorsViewModel(),
),
getKeyguardQuickAffordancePickerInteractor(activity),
getKeyguardQuickAffordancePickerViewModelFactory(activity),
NotificationSectionViewModel.Factory(
interactor = getNotificationsInteractor(activity),
),
getFlags(),
getClockCarouselViewModelFactory(
getClockPickerInteractor(activity.applicationContext),
),
getClockViewFactory(activity),
getDarkModeSnapshotRestorer(activity),
getThemedIconSnapshotRestorer(activity),
getThemedIconInteractor(),
)
.also { customizationSections = it }
}
override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent {
val intent = Intent()
intent.setClass(context, CustomizationPickerActivity::class.java)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
return intent
}
override fun getDownloadableIntentAction(): String? {
return null
}
override fun getPreviewFragment(
context: Context,
wallpaperInfo: WallpaperInfo,
mode: Int,
viewAsHome: Boolean,
viewFullScreen: Boolean,
testingModeEnabled: Boolean
): Fragment {
return if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment()
else
ImagePreviewFragment().apply {
arguments =
Bundle().apply {
putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
}
}
}
@Synchronized
override fun getUserEventLogger(context: Context): ThemesUserEventLogger {
return if (userEventLogger != null) userEventLogger as ThemesUserEventLogger
else StatsLogUserEventLogger(context.applicationContext).also { userEventLogger = it }
}
@Synchronized
override fun getPreferences(context: Context): WallpaperPreferences {
return prefs
?: DefaultCustomizationPreferences(context.applicationContext).also { prefs = it }
}
override fun getFragmentFactory(): FragmentFactory? {
return fragmentFactory ?: ThemePickerFragmentFactory().also { fragmentFactory }
}
override fun getSnapshotRestorers(
context: Context,
): Map<Int, SnapshotRestorer> {
return super<WallpaperPicker2Injector>.getSnapshotRestorers(context).toMutableMap().apply {
this[KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER] =
getKeyguardQuickAffordanceSnapshotRestorer(context)
this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context)
this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] = getNotificationsSnapshotRestorer(context)
this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context)
this[KEY_THEMED_ICON_SNAPSHOT_RESTORER] = getThemedIconSnapshotRestorer(context)
this[KEY_APP_GRID_SNAPSHOT_RESTORER] = getGridSnapshotRestorer(context)
this[KEY_COLOR_PICKER_SNAPSHOT_RESTORER] =
getColorPickerSnapshotRestorer(context, getWallpaperColorsViewModel())
this[KEY_CLOCKS_SNAPSHOT_RESTORER] = getClockPickerSnapshotRestorer(context)
}
}
override fun getCustomizationPreferences(context: Context): CustomizationPreferences {
return getPreferences(context) as CustomizationPreferences
}
override fun getThemeManager(
provider: ThemeBundleProvider,
activity: FragmentActivity,
overlayManagerCompat: OverlayManagerCompat,
logger: ThemesUserEventLogger
): ThemeManager {
return ThemeManager(provider, activity, overlayManagerCompat, logger)
}
override fun getWallpaperInteractor(context: Context): WallpaperInteractor {
val appContext = context.applicationContext
return wallpaperInteractor
?: WallpaperInteractor(
repository =
WallpaperRepository(
scope = getApplicationCoroutineScope(),
client =
WallpaperClientImpl(
context = appContext,
infoFactory = getCurrentWallpaperInfoFactory(context),
wallpaperManager = WallpaperManager.getInstance(context)
),
wallpaperPreferences = getPreferences(context = appContext),
backgroundDispatcher = Dispatchers.IO,
),
shouldHandleReload = {
TextUtils.equals(
getColorCustomizationManager(appContext).currentColorSource,
ColorOptionsProvider.COLOR_SOURCE_PRESET
)
}
)
.also { wallpaperInteractor = it }
}
override fun getKeyguardQuickAffordancePickerInteractor(
context: Context
): KeyguardQuickAffordancePickerInteractor {
return keyguardQuickAffordancePickerInteractor
?: getKeyguardQuickAffordancePickerInteractorImpl(context).also {
keyguardQuickAffordancePickerInteractor = it
}
}
fun getKeyguardQuickAffordancePickerViewModelFactory(
context: Context
): KeyguardQuickAffordancePickerViewModel.Factory {
return keyguardQuickAffordancePickerViewModelFactory
?: KeyguardQuickAffordancePickerViewModel.Factory(
context.applicationContext,
getKeyguardQuickAffordancePickerInteractor(context),
getWallpaperInteractor(context),
getCurrentWallpaperInfoFactory(context),
)
.also { keyguardQuickAffordancePickerViewModelFactory = it }
}
private fun getKeyguardQuickAffordancePickerInteractorImpl(
context: Context
): KeyguardQuickAffordancePickerInteractor {
val client = getKeyguardQuickAffordancePickerProviderClient(context)
val appContext = context.applicationContext
return KeyguardQuickAffordancePickerInteractor(
KeyguardQuickAffordancePickerRepository(client, Dispatchers.IO),
client
) {
getKeyguardQuickAffordanceSnapshotRestorer(appContext)
}
}
private fun getKeyguardQuickAffordancePickerProviderClient(
context: Context
): CustomizationProviderClient {
return customizationProviderClient
?: CustomizationProviderClientImpl(context.applicationContext, Dispatchers.IO).also {
customizationProviderClient = it
}
}
private fun getKeyguardQuickAffordanceSnapshotRestorer(
context: Context
): KeyguardQuickAffordanceSnapshotRestorer {
return keyguardQuickAffordanceSnapshotRestorer
?: KeyguardQuickAffordanceSnapshotRestorer(
getKeyguardQuickAffordancePickerInteractor(context),
getKeyguardQuickAffordancePickerProviderClient(context)
)
.also { keyguardQuickAffordanceSnapshotRestorer = it }
}
fun getNotificationSectionViewModelFactory(
context: Context,
): NotificationSectionViewModel.Factory {
return notificationSectionViewModelFactory
?: NotificationSectionViewModel.Factory(
interactor = getNotificationsInteractor(context),
)
.also { notificationSectionViewModelFactory = it }
}
private fun getNotificationsInteractor(
context: Context,
): NotificationsInteractor {
val appContext = context.applicationContext
return notificationsInteractor
?: NotificationsInteractor(
repository =
NotificationsRepository(
scope = getApplicationCoroutineScope(),
backgroundDispatcher = Dispatchers.IO,
secureSettingsRepository = getSecureSettingsRepository(context),
),
snapshotRestorer = { getNotificationsSnapshotRestorer(appContext) },
)
.also { notificationsInteractor = it }
}
private fun getNotificationsSnapshotRestorer(context: Context): NotificationsSnapshotRestorer {
return notificationsSnapshotRestorer
?: NotificationsSnapshotRestorer(
interactor =
getNotificationsInteractor(
context = context,
),
)
.also { notificationsSnapshotRestorer = it }
}
override fun getClockRegistry(context: Context): ClockRegistry {
return (clockRegistryProvider
?: ClockRegistryProvider(
context = context.applicationContext,
coroutineScope = getApplicationCoroutineScope(),
mainDispatcher = Dispatchers.Main,
backgroundDispatcher = Dispatchers.IO,
)
.also { clockRegistryProvider = it })
.get()
}
override fun getClockPickerInteractor(
context: Context,
): ClockPickerInteractor {
val appContext = context.applicationContext
return clockPickerInteractor
?: ClockPickerInteractor(
repository =
ClockPickerRepositoryImpl(
secureSettingsRepository = getSecureSettingsRepository(appContext),
registry = getClockRegistry(appContext),
scope = getApplicationCoroutineScope(),
mainDispatcher = Dispatchers.Main,
),
snapshotRestorer = { getClockPickerSnapshotRestorer(appContext) },
)
.also { clockPickerInteractor = it }
}
override fun getClockSectionViewModel(
context: Context,
): ClockSectionViewModel {
return clockSectionViewModel
?: ClockSectionViewModel(
context.applicationContext,
getClockPickerInteractor(context.applicationContext)
)
.also { clockSectionViewModel = it }
}
override fun getClockCarouselViewModelFactory(
interactor: ClockPickerInteractor,
): ClockCarouselViewModel.Factory {
return clockCarouselViewModelFactory
?: ClockCarouselViewModel.Factory(interactor, Dispatchers.IO).also {
clockCarouselViewModelFactory = it
}
}
override fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory {
val activityHashCode = activity.hashCode()
return clockViewFactories[activityHashCode]
?: ClockViewFactory(
activity.applicationContext,
ScreenSizeCalculator.getInstance()
.getScreenSize(activity.windowManager.defaultDisplay),
WallpaperManager.getInstance(activity.applicationContext),
getClockRegistry(activity.applicationContext),
)
.also {
clockViewFactories[activityHashCode] = it
activity.lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
clockViewFactories[activityHashCode]?.onDestroy()
clockViewFactories.remove(activityHashCode)
}
}
)
}
}
private fun getClockPickerSnapshotRestorer(
context: Context,
): ClockPickerSnapshotRestorer {
return clockPickerSnapshotRestorer
?: ClockPickerSnapshotRestorer(getClockPickerInteractor(context)).also {
clockPickerSnapshotRestorer = it
}
}
override fun getColorPickerInteractor(
context: Context,
wallpaperColorsViewModel: WallpaperColorsViewModel,
): ColorPickerInteractor {
val appContext = context.applicationContext
return colorPickerInteractor
?: ColorPickerInteractor(
repository =
ColorPickerRepositoryImpl(
wallpaperColorsViewModel,
getColorCustomizationManager(appContext)
),
snapshotRestorer = {
getColorPickerSnapshotRestorer(appContext, wallpaperColorsViewModel)
}
)
.also { colorPickerInteractor = it }
}
override fun getColorPickerViewModelFactory(
context: Context,
wallpaperColorsViewModel: WallpaperColorsViewModel,
): ColorPickerViewModel.Factory {
return colorPickerViewModelFactory
?: ColorPickerViewModel.Factory(
context.applicationContext,
getColorPickerInteractor(context, wallpaperColorsViewModel),
)
.also { colorPickerViewModelFactory = it }
}
private fun getColorPickerSnapshotRestorer(
context: Context,
wallpaperColorsViewModel: WallpaperColorsViewModel,
): ColorPickerSnapshotRestorer {
return colorPickerSnapshotRestorer
?: ColorPickerSnapshotRestorer(
getColorPickerInteractor(context, wallpaperColorsViewModel)
)
.also { colorPickerSnapshotRestorer = it }
}
private fun getColorCustomizationManager(context: Context): ColorCustomizationManager {
return colorCustomizationManager
?: ColorCustomizationManager.getInstance(context, OverlayManagerCompat(context)).also {
colorCustomizationManager = it
}
}
fun getDarkModeSnapshotRestorer(
context: Context,
): DarkModeSnapshotRestorer {
val appContext = context.applicationContext
return darkModeSnapshotRestorer
?: DarkModeSnapshotRestorer(
context = appContext,
manager = appContext.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager,
backgroundDispatcher = Dispatchers.IO,
)
.also { darkModeSnapshotRestorer = it }
}
protected fun getThemedIconSnapshotRestorer(
context: Context,
): ThemedIconSnapshotRestorer {
val optionProvider = ThemedIconSwitchProvider.getInstance(context)
return themedIconSnapshotRestorer
?: ThemedIconSnapshotRestorer(
isActivated = { optionProvider.isThemedIconEnabled },
setActivated = { isActivated ->
optionProvider.isThemedIconEnabled = isActivated
},
interactor = getThemedIconInteractor(),
)
.also { themedIconSnapshotRestorer = it }
}
protected fun getThemedIconInteractor(): ThemedIconInteractor {
return themedIconInteractor
?: ThemedIconInteractor(
repository = ThemeIconRepository(),
)
.also { themedIconInteractor = it }
}
override fun getClockSettingsViewModelFactory(
context: Context,
wallpaperColorsViewModel: WallpaperColorsViewModel,
clockViewFactory: ClockViewFactory,
): ClockSettingsViewModel.Factory {
return clockSettingsViewModelFactory
?: ClockSettingsViewModel.Factory(
context.applicationContext,
getClockPickerInteractor(context),
getColorPickerInteractor(
context,
wallpaperColorsViewModel,
),
) { clockId ->
clockId?.let { clockViewFactory.getController(clockId).config.isReactiveToTone }
?: false
}
.also { clockSettingsViewModelFactory = it }
}
fun getGridScreenViewModelFactory(
context: Context,
): ViewModelProvider.Factory {
return gridScreenViewModelFactory
?: GridScreenViewModel.Factory(
context = context,
interactor = getGridInteractor(context),
)
.also { gridScreenViewModelFactory = it }
}
private fun getGridInteractor(
context: Context,
): GridInteractor {
val appContext = context.applicationContext
return gridInteractor
?: GridInteractor(
applicationScope = getApplicationCoroutineScope(),
repository =
GridRepositoryImpl(
applicationScope = getApplicationCoroutineScope(),
manager = GridOptionsManager.getInstance(context),
backgroundDispatcher = Dispatchers.IO,
),
snapshotRestorer = { getGridSnapshotRestorer(appContext) },
)
.also { gridInteractor = it }
}
private fun getGridSnapshotRestorer(
context: Context,
): GridSnapshotRestorer {
return gridSnapshotRestorer
?: GridSnapshotRestorer(
interactor = getGridInteractor(context),
)
.also { gridSnapshotRestorer = it }
}
companion object {
@JvmStatic
private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER =
WallpaperPicker2Injector.MIN_SNAPSHOT_RESTORER_KEY
@JvmStatic
private val KEY_WALLPAPER_SNAPSHOT_RESTORER = KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER + 1
@JvmStatic
private val KEY_NOTIFICATIONS_SNAPSHOT_RESTORER = KEY_WALLPAPER_SNAPSHOT_RESTORER + 1
@JvmStatic
private val KEY_DARK_MODE_SNAPSHOT_RESTORER = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1
@JvmStatic
private val KEY_THEMED_ICON_SNAPSHOT_RESTORER = KEY_DARK_MODE_SNAPSHOT_RESTORER + 1
@JvmStatic
private val KEY_APP_GRID_SNAPSHOT_RESTORER = KEY_THEMED_ICON_SNAPSHOT_RESTORER + 1
@JvmStatic
private val KEY_COLOR_PICKER_SNAPSHOT_RESTORER = KEY_APP_GRID_SNAPSHOT_RESTORER + 1
@JvmStatic private val KEY_CLOCKS_SNAPSHOT_RESTORER = KEY_COLOR_PICKER_SNAPSHOT_RESTORER + 1
/**
* When this injector is overridden, this is the minimal value that should be used by
* restorers returns in [getSnapshotRestorers].
*
* It should always be greater than the biggest restorer key.
*/
@JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_CLOCKS_SNAPSHOT_RESTORER + 1
}
}