1 /*
2  * 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 package com.android.customization.picker.color.data.util
17 
18 import android.app.WallpaperColors
19 import android.content.Context
20 import android.content.res.Configuration
21 import android.provider.Settings
22 import android.util.Log
23 import android.util.SparseIntArray
24 import com.android.customization.model.ResourceConstants
25 import com.android.systemui.monet.ColorScheme
26 import com.android.systemui.monet.Style
27 import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
28 import dagger.hilt.android.qualifiers.ApplicationContext
29 import javax.inject.Inject
30 import javax.inject.Singleton
31 import org.json.JSONException
32 import org.json.JSONObject
33 
34 /**
35  * Extract material next colors from wallpaper colors. Based on Nexus Launcher's
36  * MaterialColorsGenerator, nexuslauncher/widget/MaterialColorsGenerator.java
37  */
38 @Singleton
39 class MaterialColorsGenerator
40 @Inject
41 constructor(
42     @ApplicationContext private val applicationContext: Context,
43     private val secureSettingsRepository: SecureSettingsRepository,
44 ) {
addShadesnull45     private fun addShades(shades: List<Int>, resources: IntArray, output: SparseIntArray) {
46         if (shades.size != resources.size) {
47             Log.e(TAG, "The number of shades computed doesn't match the number of resources.")
48             return
49         }
50         for (i in resources.indices) {
51             output.put(resources[i], 0xff000000.toInt() or shades[i])
52         }
53     }
54 
55     /**
56      * Generates the mapping from system color resources to values from wallpaper colors.
57      *
58      * @return a list of color resource IDs and a corresponding list of their color values
59      */
generatenull60     suspend fun generate(colors: WallpaperColors): Pair<IntArray, IntArray> {
61         val isDarkMode =
62             (applicationContext.resources.configuration.uiMode and
63                 Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
64         val colorScheme = ColorScheme(colors, isDarkMode, fetchThemeStyleFromSetting())
65         return generate(colorScheme)
66     }
67 
68     /**
69      * Generates the mapping from system color resources to values from color seed and style.
70      *
71      * @return a list of color resource IDs and a corresponding list of their color values
72      */
generatenull73     fun generate(colorSeed: Int, @Style.Type style: Int): Pair<IntArray, IntArray> {
74         val isDarkMode =
75             (applicationContext.resources.configuration.uiMode and
76                 Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
77         val colorScheme = ColorScheme(colorSeed, isDarkMode, style)
78         return generate(colorScheme)
79     }
80 
generatenull81     private fun generate(colorScheme: ColorScheme): Pair<IntArray, IntArray> {
82         val allNeutralColors: MutableList<Int> = ArrayList()
83         allNeutralColors.addAll(colorScheme.neutral1.allShades)
84         allNeutralColors.addAll(colorScheme.neutral2.allShades)
85 
86         val allAccentColors: MutableList<Int> = ArrayList()
87         allAccentColors.addAll(colorScheme.accent1.allShades)
88         allAccentColors.addAll(colorScheme.accent2.allShades)
89         allAccentColors.addAll(colorScheme.accent3.allShades)
90 
91         return Pair(
92             NEUTRAL_RESOURCES + ACCENT_RESOURCES,
93             (allNeutralColors + allAccentColors).toIntArray(),
94         )
95     }
96 
97     @Style.Type
fetchThemeStyleFromSettingnull98     private suspend fun fetchThemeStyleFromSetting(): Int {
99         val overlayPackageJson =
100             secureSettingsRepository.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)
101         return if (!overlayPackageJson.isNullOrEmpty()) {
102             try {
103                 val jsonObject = JSONObject(overlayPackageJson)
104                 Style.valueOf(jsonObject.getString(ResourceConstants.OVERLAY_CATEGORY_THEME_STYLE))
105             } catch (e: (JSONException)) {
106                 Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
107                 Style.TONAL_SPOT
108             } catch (e: IllegalArgumentException) {
109                 Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
110                 Style.TONAL_SPOT
111             }
112         } else {
113             Style.TONAL_SPOT
114         }
115     }
116 
117     companion object {
118         private const val TAG = "MaterialColorsGenerator"
119 
120         private val ACCENT_RESOURCES =
121             intArrayOf(
122                 android.R.color.system_accent1_0,
123                 android.R.color.system_accent1_10,
124                 android.R.color.system_accent1_50,
125                 android.R.color.system_accent1_100,
126                 android.R.color.system_accent1_200,
127                 android.R.color.system_accent1_300,
128                 android.R.color.system_accent1_400,
129                 android.R.color.system_accent1_500,
130                 android.R.color.system_accent1_600,
131                 android.R.color.system_accent1_700,
132                 android.R.color.system_accent1_800,
133                 android.R.color.system_accent1_900,
134                 android.R.color.system_accent1_1000,
135                 android.R.color.system_accent2_0,
136                 android.R.color.system_accent2_10,
137                 android.R.color.system_accent2_50,
138                 android.R.color.system_accent2_100,
139                 android.R.color.system_accent2_200,
140                 android.R.color.system_accent2_300,
141                 android.R.color.system_accent2_400,
142                 android.R.color.system_accent2_500,
143                 android.R.color.system_accent2_600,
144                 android.R.color.system_accent2_700,
145                 android.R.color.system_accent2_800,
146                 android.R.color.system_accent2_900,
147                 android.R.color.system_accent2_1000,
148                 android.R.color.system_accent3_0,
149                 android.R.color.system_accent3_10,
150                 android.R.color.system_accent3_50,
151                 android.R.color.system_accent3_100,
152                 android.R.color.system_accent3_200,
153                 android.R.color.system_accent3_300,
154                 android.R.color.system_accent3_400,
155                 android.R.color.system_accent3_500,
156                 android.R.color.system_accent3_600,
157                 android.R.color.system_accent3_700,
158                 android.R.color.system_accent3_800,
159                 android.R.color.system_accent3_900,
160                 android.R.color.system_accent3_1000,
161             )
162         private val NEUTRAL_RESOURCES =
163             intArrayOf(
164                 android.R.color.system_neutral1_0,
165                 android.R.color.system_neutral1_10,
166                 android.R.color.system_neutral1_50,
167                 android.R.color.system_neutral1_100,
168                 android.R.color.system_neutral1_200,
169                 android.R.color.system_neutral1_300,
170                 android.R.color.system_neutral1_400,
171                 android.R.color.system_neutral1_500,
172                 android.R.color.system_neutral1_600,
173                 android.R.color.system_neutral1_700,
174                 android.R.color.system_neutral1_800,
175                 android.R.color.system_neutral1_900,
176                 android.R.color.system_neutral1_1000,
177                 android.R.color.system_neutral2_0,
178                 android.R.color.system_neutral2_10,
179                 android.R.color.system_neutral2_50,
180                 android.R.color.system_neutral2_100,
181                 android.R.color.system_neutral2_200,
182                 android.R.color.system_neutral2_300,
183                 android.R.color.system_neutral2_400,
184                 android.R.color.system_neutral2_500,
185                 android.R.color.system_neutral2_600,
186                 android.R.color.system_neutral2_700,
187                 android.R.color.system_neutral2_800,
188                 android.R.color.system_neutral2_900,
189                 android.R.color.system_neutral2_1000,
190             )
191     }
192 }
193