• 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 
18 package com.android.wallpaper.picker.customization.data.repository
19 
20 import android.app.WallpaperColors
21 import android.graphics.Bitmap
22 import android.graphics.Point
23 import android.graphics.Rect
24 import android.util.LruCache
25 import com.android.wallpaper.asset.Asset
26 import com.android.wallpaper.module.WallpaperPreferences
27 import com.android.wallpaper.module.logging.UserEventLogger.SetWallpaperEntryPoint
28 import com.android.wallpaper.picker.customization.data.content.WallpaperClient
29 import com.android.wallpaper.picker.customization.shared.model.WallpaperDestination
30 import com.android.wallpaper.picker.customization.shared.model.WallpaperModel
31 import com.android.wallpaper.picker.data.WallpaperModel.LiveWallpaperModel
32 import com.android.wallpaper.picker.data.WallpaperModel.StaticWallpaperModel
33 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
34 import com.android.wallpaper.picker.preview.shared.model.FullPreviewCropModel
35 import javax.inject.Inject
36 import javax.inject.Singleton
37 import kotlinx.coroutines.CoroutineDispatcher
38 import kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.flow.Flow
40 import kotlinx.coroutines.flow.MutableStateFlow
41 import kotlinx.coroutines.flow.SharingStarted
42 import kotlinx.coroutines.flow.StateFlow
43 import kotlinx.coroutines.flow.asStateFlow
44 import kotlinx.coroutines.flow.flowOn
45 import kotlinx.coroutines.flow.map
46 import kotlinx.coroutines.flow.stateIn
47 import kotlinx.coroutines.withContext
48 
49 /** Encapsulates access to wallpaper-related data. */
50 @Singleton
51 class WallpaperRepository
52 @Inject
53 constructor(
54     @BackgroundDispatcher private val scope: CoroutineScope,
55     private val client: WallpaperClient,
56     private val wallpaperPreferences: WallpaperPreferences,
57     @BackgroundDispatcher private val backgroundDispatcher: CoroutineDispatcher,
58 ) {
59     val maxOptions = MAX_OPTIONS
60 
61     private val thumbnailCache = LruCache<String, Bitmap>(maxOptions)
62 
63     /** The ID of the currently-selected wallpaper. */
64     fun selectedWallpaperId(destination: WallpaperDestination): StateFlow<String> {
65         return client
66             .recentWallpapers(destination = destination, limit = 1)
67             .map { previews -> currentWallpaperKey(destination, previews) }
68             .flowOn(backgroundDispatcher)
69             .stateIn(
70                 scope = scope,
71                 started = SharingStarted.WhileSubscribed(),
72                 initialValue = currentWallpaperKey(destination, null),
73             )
74     }
75 
76     private fun currentWallpaperKey(
77         destination: WallpaperDestination,
78         previews: List<WallpaperModel>?,
79     ): String {
80         val key =
81             when (destination) {
82                 WallpaperDestination.HOME -> wallpaperPreferences.getHomeWallpaperRecentsKey()
83                 WallpaperDestination.LOCK -> wallpaperPreferences.getLockWallpaperRecentsKey()
84                 else -> error("Unsupported destination")
85             }
86         return key ?: previews?.firstOrNull()?.wallpaperId ?: DEFAULT_KEY
87     }
88 
89     val areRecentsAvailable: Boolean by lazy { client.areRecentsAvailable() }
90 
91     private val _selectingWallpaperId =
92         MutableStateFlow<Map<WallpaperDestination, String?>>(emptyMap())
93     /**
94      * The ID of the wallpaper that is in the process of becoming the selected wallpaper or `null`
95      * if no such transaction is currently taking place.
96      */
97     val selectingWallpaperId: StateFlow<Map<WallpaperDestination, String?>> =
98         _selectingWallpaperId.asStateFlow()
99 
100     /** Lists the most recent wallpapers. The first one is the most recent (current) wallpaper. */
101     fun recentWallpapers(
102         destination: WallpaperDestination,
103         limit: Int,
104     ): Flow<List<WallpaperModel>> {
105         return client
106             .recentWallpapers(destination = destination, limit = limit)
107             .flowOn(backgroundDispatcher)
108     }
109 
110     /** Returns a thumbnail for the wallpaper with the given ID and destination. */
111     suspend fun loadThumbnail(
112         wallpaperId: String,
113         lastUpdatedTimestamp: Long,
114         destination: WallpaperDestination,
115     ): Bitmap? {
116         val cacheKey = "$wallpaperId-$lastUpdatedTimestamp"
117         return thumbnailCache[cacheKey]
118             ?: withContext(backgroundDispatcher) {
119                 val thumbnail = client.loadThumbnail(wallpaperId, destination)
120                 if (thumbnail != null) {
121                     thumbnailCache.put(cacheKey, thumbnail)
122                 }
123                 thumbnail
124             }
125     }
126 
127     suspend fun setStaticWallpaper(
128         @SetWallpaperEntryPoint setWallpaperEntryPoint: Int,
129         destination: WallpaperDestination,
130         wallpaperModel: StaticWallpaperModel,
131         bitmap: Bitmap,
132         wallpaperSize: Point,
133         asset: Asset,
134         fullPreviewCropModels: Map<Point, FullPreviewCropModel>? = null,
135     ) {
136         // TODO(b/303317694): provide set wallpaper status as flow
137         withContext(backgroundDispatcher) {
138             client.setStaticWallpaper(
139                 setWallpaperEntryPoint,
140                 destination,
141                 wallpaperModel,
142                 bitmap,
143                 wallpaperSize,
144                 asset,
145                 fullPreviewCropModels,
146             )
147         }
148     }
149 
150     suspend fun setLiveWallpaper(
151         @SetWallpaperEntryPoint setWallpaperEntryPoint: Int,
152         destination: WallpaperDestination,
153         wallpaperModel: LiveWallpaperModel,
154     ) {
155         withContext(backgroundDispatcher) {
156             client.setLiveWallpaper(setWallpaperEntryPoint, destination, wallpaperModel)
157         }
158     }
159 
160     /** Sets the wallpaper to the one with the given ID. */
161     suspend fun setRecentWallpaper(
162         @SetWallpaperEntryPoint setWallpaperEntryPoint: Int,
163         destination: WallpaperDestination,
164         wallpaperId: String,
165     ) {
166         _selectingWallpaperId.value =
167             _selectingWallpaperId.value.toMutableMap().apply { this[destination] = wallpaperId }
168         withContext(backgroundDispatcher) {
169             client.setRecentWallpaper(
170                 setWallpaperEntryPoint = setWallpaperEntryPoint,
171                 destination = destination,
172                 wallpaperId = wallpaperId,
173             ) {
174                 _selectingWallpaperId.value =
175                     _selectingWallpaperId.value.toMutableMap().apply { this[destination] = null }
176             }
177         }
178     }
179 
180     suspend fun getWallpaperColors(bitmap: Bitmap, cropHints: Map<Point, Rect>?): WallpaperColors? =
181         withContext(backgroundDispatcher) { client.getWallpaperColors(bitmap, cropHints) }
182 
183     companion object {
184         const val DEFAULT_KEY = "default_missing_key"
185         /** The maximum number of options to show, including the currently-selected one. */
186         private const val MAX_OPTIONS = 5
187     }
188 }
189