1 /* <lambda>null2 * Copyright (C) 2024 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.app.WallpaperColors 20 import android.app.WallpaperManager 21 import android.os.Handler 22 import android.os.Looper 23 import android.util.Log 24 import com.android.customization.model.CustomizationManager 25 import com.android.customization.model.color.ColorCustomizationManager 26 import com.android.customization.model.color.ColorOption 27 import com.android.customization.model.color.ColorOptionImpl 28 import com.android.customization.picker.color.shared.model.ColorType 29 import com.android.wallpaper.model.Screen 30 import com.android.wallpaper.picker.customization.data.content.WallpaperClient 31 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher 32 import javax.inject.Inject 33 import javax.inject.Singleton 34 import kotlinx.coroutines.CoroutineScope 35 import kotlinx.coroutines.channels.awaitClose 36 import kotlinx.coroutines.flow.Flow 37 import kotlinx.coroutines.flow.SharingStarted 38 import kotlinx.coroutines.flow.callbackFlow 39 import kotlinx.coroutines.flow.combine 40 import kotlinx.coroutines.flow.filter 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.shareIn 43 import kotlinx.coroutines.suspendCancellableCoroutine 44 45 @Singleton 46 class ColorPickerRepositoryImpl2 47 @Inject 48 constructor( 49 @BackgroundDispatcher private val scope: CoroutineScope, 50 private val colorManager: ColorCustomizationManager, 51 client: WallpaperClient, 52 ) : ColorPickerRepository2 { 53 54 private val wallpaperColorsCallback: Flow<Pair<Screen, WallpaperColors?>> = 55 callbackFlow { 56 trySend(Pair(Screen.HOME_SCREEN, client.getWallpaperColors(Screen.HOME_SCREEN))) 57 trySend(Pair(Screen.LOCK_SCREEN, client.getWallpaperColors(Screen.LOCK_SCREEN))) 58 val listener = { colors: WallpaperColors?, which: Int -> 59 if (which and WallpaperManager.FLAG_SYSTEM != 0) { 60 trySend(Pair(Screen.HOME_SCREEN, colors)) 61 } 62 if (which and WallpaperManager.FLAG_LOCK != 0) { 63 trySend(Pair(Screen.LOCK_SCREEN, colors)) 64 } 65 } 66 client.addOnColorsChangedListener(listener, Handler(Looper.getMainLooper())) 67 awaitClose { client.removeOnColorsChangedListener(listener) } 68 } 69 // Make this a shared flow to make sure only one listener is added. 70 .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1) 71 private val homeWallpaperColors: Flow<WallpaperColors?> = 72 wallpaperColorsCallback 73 .filter { (screen, _) -> screen == Screen.HOME_SCREEN } 74 .map { (_, colors) -> colors } 75 private val lockWallpaperColors: Flow<WallpaperColors?> = 76 wallpaperColorsCallback 77 .filter { (screen, _) -> screen == Screen.LOCK_SCREEN } 78 .map { (_, colors) -> colors } 79 80 override val colorOptions: Flow<Map<ColorType, List<ColorOption>>> = 81 combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors -> 82 suspendCancellableCoroutine { continuation -> 83 colorManager.setWallpaperColors(homeColors, lockColors) 84 colorManager.fetchOptions( 85 object : CustomizationManager.OptionsFetchedListener<ColorOption> { 86 override fun onOptionsLoaded(options: MutableList<ColorOption>?) { 87 val wallpaperColorOptions: MutableList<ColorOption> = 88 mutableListOf() 89 val presetColorOptions: MutableList<ColorOption> = mutableListOf() 90 options?.forEach { option -> 91 when ((option as ColorOptionImpl).type) { 92 ColorType.WALLPAPER_COLOR -> 93 wallpaperColorOptions.add(option) 94 ColorType.PRESET_COLOR -> presetColorOptions.add(option) 95 } 96 } 97 continuation.resumeWith( 98 Result.success( 99 mapOf( 100 ColorType.WALLPAPER_COLOR to wallpaperColorOptions, 101 ColorType.PRESET_COLOR to presetColorOptions, 102 ) 103 ) 104 ) 105 } 106 107 override fun onError(throwable: Throwable?) { 108 Log.e(TAG, "Error loading theme bundles", throwable) 109 continuation.resumeWith( 110 Result.failure( 111 throwable ?: Throwable("Error loading theme bundles") 112 ) 113 ) 114 } 115 }, 116 /* reload= */ false, 117 ) 118 } 119 } 120 .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1) 121 122 private val settingsChanged = 123 callbackFlow { 124 trySend(Unit) 125 colorManager.setListener { trySend(Unit) } 126 awaitClose { colorManager.setListener(null) } 127 } 128 // Make this a shared flow to prevent colorManager.setListener from being called 129 // every time this flow is collected, since colorManager is a singleton. 130 .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1) 131 132 override val selectedColorOption = 133 combine(colorOptions, settingsChanged) { options, _ -> 134 options.forEach { (_, optionsByType) -> 135 optionsByType.forEach { 136 if (it.isActive(colorManager)) { 137 return@combine it 138 } 139 } 140 } 141 return@combine null 142 } 143 .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1) 144 145 override suspend fun select(colorOption: ColorOption) { 146 suspendCancellableCoroutine { continuation -> 147 colorManager.apply( 148 colorOption, 149 object : CustomizationManager.Callback { 150 override fun onSuccess() { 151 continuation.resumeWith(Result.success(Unit)) 152 } 153 154 override fun onError(throwable: Throwable?) { 155 Log.w(TAG, "Apply theme with error", throwable) 156 continuation.resumeWith( 157 Result.failure(throwable ?: Throwable("Error loading theme bundles")) 158 ) 159 } 160 }, 161 ) 162 } 163 } 164 165 companion object { 166 private const val TAG = "ColorPickerRepositoryImpl2" 167 } 168 } 169