• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.customization.picker.color.data.repository
18 
19 import android.util.Log
20 import com.android.customization.model.CustomizationManager
21 import com.android.customization.model.color.ColorCustomizationManager
22 import com.android.customization.model.color.ColorOption
23 import com.android.customization.model.color.ColorOptionImpl
24 import com.android.customization.picker.color.shared.model.ColorOptionModel
25 import com.android.customization.picker.color.shared.model.ColorType
26 import com.android.systemui.monet.Style
27 import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
28 import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel
29 import javax.inject.Inject
30 import javax.inject.Singleton
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.MutableStateFlow
33 import kotlinx.coroutines.flow.StateFlow
34 import kotlinx.coroutines.flow.asStateFlow
35 import kotlinx.coroutines.flow.combine
36 import kotlinx.coroutines.suspendCancellableCoroutine
37 
38 // TODO (b/262924623): refactor to remove dependency on ColorCustomizationManager & ColorOption
39 // TODO (b/268203200): Create test for ColorPickerRepositoryImpl
40 @Singleton
41 class ColorPickerRepositoryImpl
42 @Inject
43 constructor(
44     wallpaperColorsRepository: WallpaperColorsRepository,
45     private val colorManager: ColorCustomizationManager,
46 ) : ColorPickerRepository {
47 
48     private val homeWallpaperColors: StateFlow<WallpaperColorsModel?> =
49         wallpaperColorsRepository.homeWallpaperColors
50     private val lockWallpaperColors: StateFlow<WallpaperColorsModel?> =
51         wallpaperColorsRepository.lockWallpaperColors
52     private var selectedColorOption: MutableStateFlow<ColorOptionModel> =
53         MutableStateFlow(getCurrentColorOption())
54 
55     private val _isApplyingSystemColor = MutableStateFlow(false)
56     override val isApplyingSystemColor = _isApplyingSystemColor.asStateFlow()
57 
58     override val colorOptions: Flow<Map<ColorType, List<ColorOptionModel>>> =
59         combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
60             suspendCancellableCoroutine { continuation ->
61                 if (
62                     homeColors is WallpaperColorsModel.Loading ||
63                         lockColors is WallpaperColorsModel.Loading
64                 ) {
65                     continuation.resumeWith(
66                         Result.success(
67                             mapOf(
68                                 ColorType.WALLPAPER_COLOR to listOf(),
69                                 ColorType.PRESET_COLOR to listOf(),
70                             )
71                         )
72                     )
73                     return@suspendCancellableCoroutine
74                 }
75                 val homeColorsLoaded = homeColors as WallpaperColorsModel.Loaded
76                 val lockColorsLoaded = lockColors as WallpaperColorsModel.Loaded
77                 colorManager.setWallpaperColors(homeColorsLoaded.colors, lockColorsLoaded.colors)
78                 colorManager.fetchOptions(
79                     object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
80                         override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
81                             val wallpaperColorOptions: MutableList<ColorOptionModel> =
82                                 mutableListOf()
83                             val presetColorOptions: MutableList<ColorOptionModel> = mutableListOf()
84                             options?.forEach { option ->
85                                 when ((option as ColorOptionImpl).type) {
86                                     ColorType.WALLPAPER_COLOR ->
87                                         wallpaperColorOptions.add(option.toModel())
88                                     ColorType.PRESET_COLOR ->
89                                         presetColorOptions.add(option.toModel())
90                                 }
91                             }
92                             continuation.resumeWith(
93                                 Result.success(
94                                     mapOf(
95                                         ColorType.WALLPAPER_COLOR to wallpaperColorOptions,
96                                         ColorType.PRESET_COLOR to presetColorOptions,
97                                     )
98                                 )
99                             )
100                         }
101 
102                         override fun onError(throwable: Throwable?) {
103                             Log.e(TAG, "Error loading theme bundles", throwable)
104                             continuation.resumeWith(
105                                 Result.failure(
106                                     throwable ?: Throwable("Error loading theme bundles")
107                                 )
108                             )
109                         }
110                     },
111                     /* reload= */ false,
112                 )
113             }
114         }
115 
116     override suspend fun select(colorOptionModel: ColorOptionModel) {
117         _isApplyingSystemColor.value = true
118         suspendCancellableCoroutine { continuation ->
119             colorManager.apply(
120                 colorOptionModel.colorOption,
121                 object : CustomizationManager.Callback {
122                     override fun onSuccess() {
123                         _isApplyingSystemColor.value = false
124                         selectedColorOption.value = colorOptionModel
125                         continuation.resumeWith(Result.success(Unit))
126                     }
127 
128                     override fun onError(throwable: Throwable?) {
129                         Log.w(TAG, "Apply theme with error", throwable)
130                         _isApplyingSystemColor.value = false
131                         continuation.resumeWith(
132                             Result.failure(throwable ?: Throwable("Error loading theme bundles"))
133                         )
134                     }
135                 },
136             )
137         }
138     }
139 
140     override fun getCurrentColorOption(): ColorOptionModel {
141         val overlays = colorManager.currentOverlays
142         val styleOrNull = colorManager.currentStyle
143         val style = styleOrNull?.let { Style.valueOf(it) } ?: Style.TONAL_SPOT
144         val source = colorManager.currentColorSource
145         val colorOptionBuilder = ColorOptionImpl.Builder()
146         colorOptionBuilder.source = source
147         colorOptionBuilder.style = style
148         for (overlay in overlays) {
149             colorOptionBuilder.addOverlayPackage(overlay.key, overlay.value)
150         }
151         val colorOption = colorOptionBuilder.build()
152         return ColorOptionModel(key = "", colorOption = colorOption, isSelected = false)
153     }
154 
155     override fun getCurrentColorSource(): String? {
156         return colorManager.currentColorSource
157     }
158 
159     private fun ColorOptionImpl.toModel(): ColorOptionModel {
160         return ColorOptionModel(
161             key = "${this.type}::${this.style}::${this.serializedPackages}",
162             colorOption = this,
163             // Instead of using the selectedColorOption flow to determine isSelected, we check the
164             // source of truth, which is the settings, using ColorOption::isActive
165             isSelected = isActive(colorManager),
166         )
167     }
168 
169     companion object {
170         private const val TAG = "ColorPickerRepositoryImpl"
171     }
172 }
173