• 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.model.grid
18 
19 import android.content.ContentValues
20 import android.content.Context
21 import android.content.res.Resources
22 import android.graphics.drawable.Drawable
23 import android.util.Log
24 import androidx.core.content.res.ResourcesCompat
25 import com.android.wallpaper.R
26 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
27 import com.android.wallpaper.util.PreviewUtils
28 import dagger.hilt.android.qualifiers.ApplicationContext
29 import javax.inject.Inject
30 import javax.inject.Singleton
31 import kotlinx.coroutines.CoroutineDispatcher
32 import kotlinx.coroutines.withContext
33 
34 @Singleton
35 class DefaultShapeGridManager
36 @Inject
37 constructor(
38     @ApplicationContext private val context: Context,
39     @BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher,
40 ) : ShapeGridManager {
41 
42     private val authorityMetadataKey: String =
43         context.getString(R.string.grid_control_metadata_name)
44     private val previewUtils: PreviewUtils = PreviewUtils(context, authorityMetadataKey)
45 
46     override suspend fun getGridOptions(): List<GridOptionModel>? =
47         withContext(bgDispatcher) {
48             if (previewUtils.supportsPreview()) {
49                 context.contentResolver
50                     .query(previewUtils.getUri(GRID_OPTIONS), null, null, null, null)
51                     ?.use { cursor ->
52                         buildList {
53                                 while (cursor.moveToNext()) {
54                                     val rows = cursor.getInt(cursor.getColumnIndex(COL_ROWS))
55                                     val cols = cursor.getInt(cursor.getColumnIndex(COL_COLS))
56                                     val title =
57                                         cursor.getString(cursor.getColumnIndex(COL_GRID_TITLE))
58                                             ?: context.getString(
59                                                 com.android.themepicker.R.string.grid_title_pattern,
60                                                 cols,
61                                                 rows,
62                                             )
63                                     add(
64                                         GridOptionModel(
65                                             key =
66                                                 cursor.getString(
67                                                     cursor.getColumnIndex(COL_GRID_KEY)
68                                                 ),
69                                             title = title,
70                                             isCurrent =
71                                                 cursor
72                                                     .getString(
73                                                         cursor.getColumnIndex(COL_IS_DEFAULT)
74                                                     )
75                                                     .toBoolean(),
76                                             rows = rows,
77                                             cols = cols,
78                                             iconId =
79                                                 cursor.getInt(
80                                                     cursor.getColumnIndex(KEY_GRID_ICON_ID)
81                                                 ),
82                                         )
83                                     )
84                                 }
85                             }
86                             .let { list ->
87                                 // In this list, exactly one item should have isCurrent true.
88                                 val isCurrentCount = list.count { it.isCurrent }
89                                 if (isCurrentCount != 1) {
90                                     throw IllegalStateException(
91                                         "Exactly one grid option should have isCurrent = true. Found $isCurrentCount."
92                                     )
93                                 }
94                                 list
95                             }
96                             .sortedByDescending { it.rows * it.cols }
97                     }
98             } else {
99                 null
100             }
101         }
102 
103     override suspend fun getShapeOptions(): List<ShapeOptionModel>? =
104         withContext(bgDispatcher) {
105             if (previewUtils.supportsPreview()) {
106                 context.contentResolver
107                     .query(previewUtils.getUri(SHAPE_OPTIONS), null, null, null, null)
108                     ?.use { cursor ->
109                         buildList {
110                                 while (cursor.moveToNext()) {
111                                     add(
112                                         ShapeOptionModel(
113                                             key =
114                                                 cursor.getString(
115                                                     cursor.getColumnIndex(COL_SHAPE_KEY)
116                                                 ),
117                                             title =
118                                                 cursor.getString(
119                                                     cursor.getColumnIndex(COL_SHAPE_TITLE)
120                                                 ),
121                                             path =
122                                                 cursor.getString(cursor.getColumnIndex(COL_PATH)),
123                                             isCurrent =
124                                                 cursor
125                                                     .getString(
126                                                         cursor.getColumnIndex(COL_IS_DEFAULT)
127                                                     )
128                                                     .toBoolean(),
129                                         )
130                                     )
131                                 }
132                             }
133                             .let { list ->
134                                 // In this list, exactly one item should have isCurrent true.
135                                 val isCurrentCount = list.count { it.isCurrent }
136                                 if (isCurrentCount != 1) {
137                                     throw IllegalStateException(
138                                         "Exactly one shape option should have isCurrent = true. Found $isCurrentCount."
139                                     )
140                                 }
141                                 list
142                             }
143                     }
144             } else {
145                 null
146             }
147         }
148 
149     override fun applyShapeGridOption(shapeKey: String, gridKey: String): Int {
150         return context.contentResolver.update(
151             previewUtils.getUri(SHAPE_GRID),
152             ContentValues().apply {
153                 put(COL_SHAPE_KEY, shapeKey)
154                 put(COL_GRID_KEY, gridKey)
155             },
156             null,
157             null,
158         )
159     }
160 
161     override fun getGridOptionDrawable(iconId: Int): Drawable? {
162         try {
163             val drawable =
164                 ResourcesCompat.getDrawable(
165                     context.packageManager.getResourcesForApplication(APP_RESOURCES_PACKAGE_NAME),
166                     iconId,
167                     /* theme = */ null,
168                 )
169             return drawable
170         } catch (exception: Resources.NotFoundException) {
171             Log.w(
172                 TAG,
173                 "Unable to find drawable resource from package $APP_RESOURCES_PACKAGE_NAME with resource ID $iconId",
174             )
175             return null
176         }
177     }
178 
179     companion object {
180         const val TAG = "DefaultShapeGridManager"
181         const val SHAPE_OPTIONS: String = "shape_options"
182         const val GRID_OPTIONS: String = "list_options"
183         const val SHAPE_GRID: String = "default_grid"
184         const val SET_SHAPE: String = "set_shape"
185         const val COL_SHAPE_KEY: String = "shape_key"
186         const val COL_GRID_KEY: String = "name"
187         const val COL_GRID_NAME: String = "grid_name"
188         const val COL_GRID_TITLE: String = "grid_title"
189         const val COL_SHAPE_TITLE: String = "shape_title"
190         const val COL_ROWS: String = "rows"
191         const val COL_COLS: String = "cols"
192         const val COL_IS_DEFAULT: String = "is_default"
193         const val COL_PATH: String = "path"
194         const val KEY_GRID_ICON_ID: String = "grid_icon_id"
195         private const val APP_RESOURCES_PACKAGE_NAME: String =
196             "com.google.android.apps.nexuslauncher"
197     }
198 }
199