1 /*
<lambda>null2 * Copyright (C) 2021 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.egg.widget
18
19 import android.app.PendingIntent
20 import android.appwidget.AppWidgetManager
21 import android.appwidget.AppWidgetProvider
22 import android.content.ComponentName
23 import android.content.Context
24 import android.content.Intent
25 import android.os.Bundle
26 import android.util.Log
27 import android.util.SizeF
28 import android.widget.RemoteViews
29
30 import com.android.egg.R
31
32 /**
33 * A homescreen widget to explore the current dynamic system theme.
34 */
35 class PaintChipsWidget : AppWidgetProvider() {
36 override fun onUpdate(
37 context: Context,
38 appWidgetManager: AppWidgetManager,
39 appWidgetIds: IntArray
40 ) {
41 for (appWidgetId in appWidgetIds) {
42 updateAppWidget(context, appWidgetManager, appWidgetId)
43 }
44 }
45
46 override fun onAppWidgetOptionsChanged(
47 context: Context,
48 appWidgetManager: AppWidgetManager,
49 appWidgetId: Int,
50 newOptions: Bundle?
51 ) {
52 // Log.v(TAG, "onAppWidgetOptionsChanged: id=${appWidgetId}")
53 updateAppWidget(context, appWidgetManager, appWidgetId)
54 super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
55 }
56 }
57
58 const val TAG = "PaintChips"
59
60 val SHADE_NUMBERS = intArrayOf(0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
61
62 val COLORS_NEUTRAL1 = intArrayOf(
63 android.R.color.system_neutral1_0,
64 android.R.color.system_neutral1_10,
65 android.R.color.system_neutral1_50,
66 android.R.color.system_neutral1_100,
67 android.R.color.system_neutral1_200,
68 android.R.color.system_neutral1_300,
69 android.R.color.system_neutral1_400,
70 android.R.color.system_neutral1_500,
71 android.R.color.system_neutral1_600,
72 android.R.color.system_neutral1_700,
73 android.R.color.system_neutral1_800,
74 android.R.color.system_neutral1_900,
75 android.R.color.system_neutral1_1000
76 )
77
78 val COLORS_NEUTRAL2 = intArrayOf(
79 android.R.color.system_neutral2_0,
80 android.R.color.system_neutral2_10,
81 android.R.color.system_neutral2_50,
82 android.R.color.system_neutral2_100,
83 android.R.color.system_neutral2_200,
84 android.R.color.system_neutral2_300,
85 android.R.color.system_neutral2_400,
86 android.R.color.system_neutral2_500,
87 android.R.color.system_neutral2_600,
88 android.R.color.system_neutral2_700,
89 android.R.color.system_neutral2_800,
90 android.R.color.system_neutral2_900,
91 android.R.color.system_neutral2_1000
92 )
93
94 var COLORS_ACCENT1 = intArrayOf(
95 android.R.color.system_accent1_0,
96 android.R.color.system_accent1_10,
97 android.R.color.system_accent1_50,
98 android.R.color.system_accent1_100,
99 android.R.color.system_accent1_200,
100 android.R.color.system_accent1_300,
101 android.R.color.system_accent1_400,
102 android.R.color.system_accent1_500,
103 android.R.color.system_accent1_600,
104 android.R.color.system_accent1_700,
105 android.R.color.system_accent1_800,
106 android.R.color.system_accent1_900,
107 android.R.color.system_accent1_1000
108 )
109
110 var COLORS_ACCENT2 = intArrayOf(
111 android.R.color.system_accent2_0,
112 android.R.color.system_accent2_10,
113 android.R.color.system_accent2_50,
114 android.R.color.system_accent2_100,
115 android.R.color.system_accent2_200,
116 android.R.color.system_accent2_300,
117 android.R.color.system_accent2_400,
118 android.R.color.system_accent2_500,
119 android.R.color.system_accent2_600,
120 android.R.color.system_accent2_700,
121 android.R.color.system_accent2_800,
122 android.R.color.system_accent2_900,
123 android.R.color.system_accent2_1000
124 )
125
126 var COLORS_ACCENT3 = intArrayOf(
127 android.R.color.system_accent3_0,
128 android.R.color.system_accent3_10,
129 android.R.color.system_accent3_50,
130 android.R.color.system_accent3_100,
131 android.R.color.system_accent3_200,
132 android.R.color.system_accent3_300,
133 android.R.color.system_accent3_400,
134 android.R.color.system_accent3_500,
135 android.R.color.system_accent3_600,
136 android.R.color.system_accent3_700,
137 android.R.color.system_accent3_800,
138 android.R.color.system_accent3_900,
139 android.R.color.system_accent3_1000
140 )
141
142 var COLOR_NAMES = arrayOf(
143 "N1", "N2", "A1", "A2", "A3"
144 )
145
146 var COLORS = arrayOf(
147 COLORS_NEUTRAL1,
148 COLORS_NEUTRAL2,
149 COLORS_ACCENT1,
150 COLORS_ACCENT2,
151 COLORS_ACCENT3
152 )
153
updateAppWidgetnull154 internal fun updateAppWidget(
155 context: Context,
156 appWidgetManager: AppWidgetManager,
157 appWidgetId: Int
158 ) {
159 // val opts = appWidgetManager.getAppWidgetOptions(appWidgetId)
160 // Log.v(TAG, "requested sizes=${opts[OPTION_APPWIDGET_SIZES]}")
161
162 val allSizes = mapOf(
163 SizeF(50f, 50f)
164 to buildWidget(context, 1, 1, ClickBehavior.LAUNCH),
165 SizeF(100f, 50f)
166 to buildWidget(context, 1, 2, ClickBehavior.LAUNCH),
167 SizeF(150f, 50f)
168 to buildWidget(context, 1, 3, ClickBehavior.LAUNCH),
169 SizeF(200f, 50f)
170 to buildWidget(context, 1, 4, ClickBehavior.LAUNCH),
171 SizeF(250f, 50f)
172 to buildWidget(context, 1, 5, ClickBehavior.LAUNCH),
173
174 SizeF(50f, 120f)
175 to buildWidget(context, 3, 1, ClickBehavior.LAUNCH),
176 SizeF(100f, 120f)
177 to buildWidget(context, 3, 2, ClickBehavior.LAUNCH),
178 SizeF(150f, 120f)
179 to buildWidget(context, 3, 3, ClickBehavior.LAUNCH),
180 SizeF(200f, 120f)
181 to buildWidget(context, 3, 4, ClickBehavior.LAUNCH),
182 SizeF(250f, 120f)
183 to buildWidget(context, 3, 5, ClickBehavior.LAUNCH),
184
185 SizeF(50f, 250f)
186 to buildWidget(context, 5, 1, ClickBehavior.LAUNCH),
187 SizeF(100f, 250f)
188 to buildWidget(context, 5, 2, ClickBehavior.LAUNCH),
189 SizeF(150f, 250f)
190 to buildWidget(context, 5, 3, ClickBehavior.LAUNCH),
191 SizeF(200f, 250f)
192 to buildWidget(context, 5, 4, ClickBehavior.LAUNCH),
193 SizeF(250f, 250f)
194 to buildWidget(context, 5, 5, ClickBehavior.LAUNCH),
195
196 SizeF(300f, 300f)
197 to buildWidget(context, SHADE_NUMBERS.size, COLORS.size, ClickBehavior.LAUNCH)
198 )
199
200 // Instruct the widget manager to update the widget
201 appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(allSizes))
202 }
203
buildFullWidgetnull204 fun buildFullWidget(context: Context, clickable: ClickBehavior): RemoteViews {
205 return buildWidget(context, SHADE_NUMBERS.size, COLORS.size, clickable)
206 }
207
buildWidgetnull208 fun buildWidget(context: Context, numShades: Int, numColors: Int, clickable: ClickBehavior):
209 RemoteViews {
210 val grid = RemoteViews(context.packageName, R.layout.paint_chips_grid)
211
212 // shouldn't be necessary but sometimes the RV instructions get played twice in launcher.
213 grid.removeAllViews(R.id.paint_grid)
214
215 grid.setInt(R.id.paint_grid, "setRowCount", numShades)
216 grid.setInt(R.id.paint_grid, "setColumnCount", numColors)
217
218 Log.v(TAG, "building widget: shade rows=$numShades, color columns=$numColors")
219
220 COLORS.forEachIndexed colorLoop@{ i, colorlist ->
221 when (colorlist) {
222 COLORS_NEUTRAL1 -> if (numColors < 2) return@colorLoop
223 COLORS_NEUTRAL2 -> if (numColors < 4) return@colorLoop
224 COLORS_ACCENT2 -> if (numColors < 3) return@colorLoop
225 COLORS_ACCENT3 -> if (numColors < 5) return@colorLoop
226 else -> {} // always do ACCENT1
227 }
228 colorlist.forEachIndexed shadeLoop@{ j, resId ->
229 when (SHADE_NUMBERS[j]) {
230 500 -> {}
231 300, 700 -> if (numShades < 3) return@shadeLoop
232 100, 900 -> if (numShades < 5) return@shadeLoop
233 else -> if (numShades < SHADE_NUMBERS.size) return@shadeLoop
234 }
235 val cell = RemoteViews(context.packageName, R.layout.paint_chip)
236 cell.setTextViewText(R.id.chip, "${COLOR_NAMES[i]}-${SHADE_NUMBERS[j]}")
237 val textColor = if (SHADE_NUMBERS[j] > 500)
238 colorlist[0]
239 else colorlist[colorlist.size - 1]
240 cell.setTextColor(R.id.chip, context.getColor(textColor))
241 cell.setColorStateList(R.id.chip, "setBackgroundTintList", resId)
242 val text = """
243 ${COLOR_NAMES[i]}-${SHADE_NUMBERS[j]} (@${
244 context.resources.getResourceName(resId) })
245 currently: #${ String.format("%06x", context.getColor(resId) and 0xFFFFFF) }
246 """.trimIndent()
247 when (clickable) {
248 ClickBehavior.SHARE -> cell.setOnClickPendingIntent(
249 R.id.chip,
250 makeTextSharePendingIntent(context, text)
251 )
252 ClickBehavior.LAUNCH -> cell.setOnClickPendingIntent(
253 R.id.chip,
254 makeActivityLaunchPendingIntent(context)
255 )
256 ClickBehavior.NONE -> { }
257 }
258 grid.addView(R.id.paint_grid, cell)
259 }
260 }
261
262 return grid
263 }
264
265 enum class ClickBehavior {
266 NONE,
267 SHARE,
268 LAUNCH
269 }
270
makeTextSharePendingIntentnull271 fun makeTextSharePendingIntent(context: Context, text: String): PendingIntent {
272 val shareIntent: Intent = Intent().apply {
273 action = Intent.ACTION_SEND
274 putExtra(Intent.EXTRA_TEXT, text)
275 type = "text/plain"
276 }
277
278 val chooserIntent = Intent.createChooser(shareIntent, null).apply {
279 identifier = text // incredible quality-of-life improvement, thanks framework team
280 }
281
282 return PendingIntent.getActivity(context, 0, chooserIntent,
283 PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
284 }
285
makeActivityLaunchPendingIntentnull286 fun makeActivityLaunchPendingIntent(context: Context): PendingIntent {
287 return PendingIntent.getActivity(context, 0,
288 Intent().apply {
289 component = ComponentName(context, PaintChipsActivity::class.java)
290 action = Intent.ACTION_MAIN
291 },
292 PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
293 }
294