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