• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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