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     fun isEffectInitialized() = _creativeEffectsModel.value != null
50 
51     // TODO (b/372890403): After either isNewPickerUi or isWallpaperCategoryRefactoringEnabled is
52     //  turned on and PersistentWallpaperModelRepository is used, we should inject
53     //  PersistentWallpaperModelRepository and listen to the view model data flow, instead of have
54     //  the WallpaperPreviewActivity calling initializeEffect when onCreate().
55     suspend fun initializeEffect(data: CreativeWallpaperEffectsData) {
56         withContext(bgDispatcher) {
57             clearActionUri = data.clearActionUri
58             try {
59                 data.effectsUri.authority
60                     ?.let { context.contentResolver.acquireContentProviderClient(it) }
61                     ?.use { it.query(data.effectsUri, null, null, null, null) }
62                     ?.use { effectsCursor ->
63                         while (effectsCursor.moveToNext()) {
64                             val effectsToggleUri =
65                                 Uri.parse(
66                                     effectsCursor.getString(
67                                         effectsCursor.getColumnIndex(
68                                             WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_URI
69                                         )
70                                     )
71                                 )
72                             val effectsButtonLabel: String =
73                                 effectsCursor.getString(
74                                     effectsCursor.getColumnIndex(
75                                         WallpaperInfoContract.WALLPAPER_EFFECTS_BUTTON_LABEL
76                                     )
77                                 )
78                             val effectsId: String =
79                                 effectsCursor.getString(
80                                     effectsCursor.getColumnIndex(
81                                         WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_ID
82                                     )
83                                 )
84                             _creativeEffectsModel.value =
85                                 CreativeEffectsModel(
86                                     title = data.effectsBottomSheetTitle,
87                                     subtitle = data.effectsBottomSheetSubtitle,
88                                     actions =
89                                         listOf(
90                                             WallpaperAction(
91                                                 label = effectsButtonLabel,
92                                                 applyActionUri = effectsToggleUri,
93                                                 effectId = effectsId,
94                                                 toggled = effectsId == data.currentEffectId,
95                                             )
96                                         ),
97                                 )
98                         }
99                     }
100             } catch (e: Exception) {
101                 Log.e(TAG, "Read wallpaper effects with exception.", e)
102             }
103         }
104     }
105 
106     suspend fun turnOnCreativeEffect(actionPosition: Int) {
107         withContext(bgDispatcher) {
108             val clearActionUri =
109                 clearActionUri
110                     ?: throw NullPointerException(
111                         "clearActionUri should be initialized already if creative wallpaper" +
112                             " effects are available."
113                     )
114             val model = _creativeEffectsModel.value ?: return@withContext
115             val updatedActions =
116                 model.actions.mapIndexed { index, action ->
117                     val applyActionUri = action.applyActionUri
118                     if (actionPosition == index && applyActionUri != null) {
119                         context.contentResolver.update(applyActionUri, ContentValues(), null)
120                     }
121                     action.copy(toggled = actionPosition == index && applyActionUri != null)
122                 }
123             if (actionPosition < 0) {
124                 context.contentResolver.update(clearActionUri, ContentValues(), null)
125             }
126             _creativeEffectsModel.value = model.copy(actions = updatedActions)
127         }
128     }
129 
130     fun destroy() {
131         _creativeEffectsModel.value = null
132         clearActionUri = null
133     }
134 
135     companion object {
136         private const val TAG = "CreativeEffectsRepository"
137     }
138 }
139