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.wallpaper.picker.preview.data.repository
18 
19 import android.content.ContentValues
20 import android.content.Context
21 import android.net.Uri
22 import android.util.Log
23 import com.android.wallpaper.model.WallpaperAction
24 import com.android.wallpaper.model.WallpaperInfoContract
25 import com.android.wallpaper.picker.data.CreativeWallpaperEffectsData
26 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
27 import com.android.wallpaper.picker.preview.shared.model.CreativeEffectsModel
28 import dagger.hilt.android.qualifiers.ApplicationContext
29 import dagger.hilt.android.scopes.ActivityRetainedScoped
30 import javax.inject.Inject
31 import kotlinx.coroutines.CoroutineDispatcher
32 import kotlinx.coroutines.flow.MutableStateFlow
33 import kotlinx.coroutines.flow.asStateFlow
34 import kotlinx.coroutines.withContext
35 
36 @ActivityRetainedScoped
37 class CreativeEffectsRepository
38 @Inject
39 constructor(
40     @ApplicationContext private val context: Context,
41     @BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher,
42 ) {
43 
44     private val _creativeEffectsModel = MutableStateFlow<CreativeEffectsModel?>(null)
45     val creativeEffectsModel = _creativeEffectsModel.asStateFlow()
46 
47     private var clearActionUri: Uri? = null
48 
49     suspend fun initializeEffect(data: CreativeWallpaperEffectsData) {
50         withContext(bgDispatcher) {
51             clearActionUri = data.clearActionUri
52             try {
53                 data.effectsUri.authority
54                     ?.let { context.contentResolver.acquireContentProviderClient(it) }
55                     ?.use { it.query(data.effectsUri, null, null, null, null) }
56                     ?.use { effectsCursor ->
57                         while (effectsCursor.moveToNext()) {
58                             val effectsToggleUri =
59                                 Uri.parse(
60                                     effectsCursor.getString(
61                                         effectsCursor.getColumnIndex(
62                                             WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_URI
63                                         )
64                                     )
65                                 )
66                             val effectsButtonLabel: String =
67                                 effectsCursor.getString(
68                                     effectsCursor.getColumnIndex(
69                                         WallpaperInfoContract.WALLPAPER_EFFECTS_BUTTON_LABEL
70                                     )
71                                 )
72                             val effectsId: String =
73                                 effectsCursor.getString(
74                                     effectsCursor.getColumnIndex(
75                                         WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_ID
76                                     )
77                                 )
78                             _creativeEffectsModel.value =
79                                 CreativeEffectsModel(
80                                     title = data.effectsBottomSheetTitle,
81                                     subtitle = data.effectsBottomSheetSubtitle,
82                                     actions =
83                                         listOf(
84                                             WallpaperAction(
85                                                 label = effectsButtonLabel,
86                                                 applyActionUri = effectsToggleUri,
87                                                 effectId = effectsId,
88                                                 toggled = effectsId == data.currentEffectId,
89                                             )
90                                         ),
91                                 )
92                         }
93                     }
94             } catch (e: Exception) {
95                 Log.e(TAG, "Read wallpaper effects with exception.", e)
96             }
97         }
98     }
99 
100     suspend fun turnOnCreativeEffect(actionPosition: Int) {
101         withContext(bgDispatcher) {
102             val clearActionUri =
103                 clearActionUri
104                     ?: throw NullPointerException(
105                         "clearActionUri should be initialized already if creative wallpaper" +
106                             " effects are available."
107                     )
108             val model = _creativeEffectsModel.value ?: return@withContext
109             val updatedActions =
110                 model.actions.mapIndexed { index, action ->
111                     val applyActionUri = action.applyActionUri
112                     if (actionPosition == index && applyActionUri != null) {
113                         context.contentResolver.update(applyActionUri, ContentValues(), null)
114                     }
115                     action.copy(toggled = actionPosition == index && applyActionUri != null)
116                 }
117             if (actionPosition < 0) {
118                 context.contentResolver.update(clearActionUri, ContentValues(), null)
119             }
120             _creativeEffectsModel.value = model.copy(actions = updatedActions)
121         }
122     }
123 
124     fun destroy() {
125         _creativeEffectsModel.value = null
126         clearActionUri = null
127     }
128 
129     companion object {
130         private const val TAG = "CreativeEffectsRepository"
131     }
132 }
133