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 kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.MutableStateFlow
31 import kotlinx.coroutines.flow.StateFlow
32 import kotlinx.coroutines.flow.asStateFlow
33 import kotlinx.coroutines.flow.combine
34 import kotlinx.coroutines.flow.map
35 import kotlinx.coroutines.suspendCancellableCoroutine
36 
37 // TODO (b/262924623): refactor to remove dependency on ColorCustomizationManager & ColorOption
38 // TODO (b/268203200): Create test for ColorPickerRepositoryImpl
39 class ColorPickerRepositoryImpl(
40     wallpaperColorsRepository: WallpaperColorsRepository,
41     private val colorManager: ColorCustomizationManager,
42 ) : ColorPickerRepository {
43 
44     private val homeWallpaperColors: StateFlow<WallpaperColorsModel?> =
45         wallpaperColorsRepository.homeWallpaperColors
46     private val lockWallpaperColors: StateFlow<WallpaperColorsModel?> =
47         wallpaperColorsRepository.lockWallpaperColors
48     private var selectedColorOption: MutableStateFlow<ColorOptionModel> =
49         MutableStateFlow(getCurrentColorOption())
50 
51     private val _isApplyingSystemColor = MutableStateFlow(false)
52     override val isApplyingSystemColor = _isApplyingSystemColor.asStateFlow()
53 
54     // TODO (b/299510645): update color options on selected option change after restart is disabled
55     override val colorOptions: Flow<Map<ColorType, List<ColorOptionModel>>> =
56         combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
57                 homeColors to lockColors
58             }
59             .map { (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(
78                         homeColorsLoaded.colors,
79                         lockColorsLoaded.colors
80                     )
81                     colorManager.fetchOptions(
82                         object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
83                             override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
84                                 val wallpaperColorOptions: MutableList<ColorOptionModel> =
85                                     mutableListOf()
86                                 val presetColorOptions: MutableList<ColorOptionModel> =
87                                     mutableListOf()
88                                 options?.forEach { option ->
89                                     when ((option as ColorOptionImpl).type) {
90                                         ColorType.WALLPAPER_COLOR ->
91                                             wallpaperColorOptions.add(option.toModel())
92                                         ColorType.PRESET_COLOR ->
93                                             presetColorOptions.add(option.toModel())
94                                     }
95                                 }
96                                 continuation.resumeWith(
97                                     Result.success(
98                                         mapOf(
99                                             ColorType.WALLPAPER_COLOR to wallpaperColorOptions,
100                                             ColorType.PRESET_COLOR to presetColorOptions
101                                         )
102                                     )
103                                 )
104                             }
105 
106                             override fun onError(throwable: Throwable?) {
107                                 Log.e(TAG, "Error loading theme bundles", throwable)
108                                 continuation.resumeWith(
109                                     Result.failure(
110                                         throwable ?: Throwable("Error loading theme bundles")
111                                     )
112                                 )
113                             }
114                         },
115                         /* reload= */ false
116                     )
117                 }
118             }
119 
120     override suspend fun select(colorOptionModel: ColorOptionModel) {
121         _isApplyingSystemColor.value = true
122         suspendCancellableCoroutine { continuation ->
123             colorManager.apply(
124                 colorOptionModel.colorOption,
125                 object : CustomizationManager.Callback {
126                     override fun onSuccess() {
127                         _isApplyingSystemColor.value = false
128                         selectedColorOption.value = colorOptionModel
129                         continuation.resumeWith(Result.success(Unit))
130                     }
131 
132                     override fun onError(throwable: Throwable?) {
133                         Log.w(TAG, "Apply theme with error", throwable)
134                         _isApplyingSystemColor.value = false
135                         continuation.resumeWith(
136                             Result.failure(throwable ?: Throwable("Error loading theme bundles"))
137                         )
138                     }
139                 }
140             )
141         }
142     }
143 
144     override fun getCurrentColorOption(): ColorOptionModel {
145         val overlays = colorManager.currentOverlays
146         val styleOrNull = colorManager.currentStyle
147         val style = styleOrNull?.let { Style.valueOf(it) } ?: Style.TONAL_SPOT
148         val source = colorManager.currentColorSource
149         val colorOptionBuilder = ColorOptionImpl.Builder()
150         colorOptionBuilder.source = source
151         colorOptionBuilder.style = style
152         for (overlay in overlays) {
153             colorOptionBuilder.addOverlayPackage(overlay.key, overlay.value)
154         }
155         val colorOption = colorOptionBuilder.build()
156         return ColorOptionModel(
157             key = "",
158             colorOption = colorOption,
159             isSelected = false,
160         )
161     }
162 
163     override fun getCurrentColorSource(): String? {
164         return colorManager.currentColorSource
165     }
166 
167     private fun ColorOptionImpl.toModel(): ColorOptionModel {
168         return ColorOptionModel(
169             key = "${this.type}::${this.style}::${this.serializedPackages}",
170             colorOption = this,
171             isSelected = isActive(colorManager),
172         )
173     }
174 
175     companion object {
176         private const val TAG = "ColorPickerRepositoryImpl"
177     }
178 }
179