1 /*
2 * Copyright 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 androidx.glance.appwidget.unit
18
19 import android.content.Context
20 import android.content.res.Configuration
21 import android.content.res.Resources
22 import android.util.Log
23 import androidx.annotation.ColorRes
24 import androidx.compose.ui.graphics.Color
25 import androidx.core.content.ContextCompat
26 import androidx.glance.appwidget.GlanceAppWidgetTag
27 import androidx.glance.appwidget.R
28 import androidx.glance.color.DayNightColorProvider
29 import androidx.glance.unit.ColorProvider
30 import androidx.glance.unit.FixedColorProvider
31 import androidx.glance.unit.ResourceColorProvider
32
33 /** Provider of different colors depending on a checked state. */
34 internal sealed interface CheckableColorProvider
35
36 internal data class ResourceCheckableColorProvider(
37 @ColorRes val resId: Int,
38 ) : CheckableColorProvider
39
40 /**
41 * Combination of two different [ColorProvider]s representing checked and unchecked states. These
42 * must be [FixedColorProvider]s or [DayNightColorProvider]s.
43 */
44 @Suppress("DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING")
45 internal data class CheckedUncheckedColorProvider
46 private constructor(
47 private val source: String,
48 private val checked: ColorProvider,
49 private val unchecked: ColorProvider,
50 ) : CheckableColorProvider {
51
52 init {
<lambda>null53 require(checked !is ResourceColorProvider && unchecked !is ResourceColorProvider) {
54 "Cannot provide resource-backed ColorProviders to $source"
55 }
56 }
57
58 /**
59 * Resolves the [CheckedUncheckedColorProvider] to a single [Color] given the night mode and
60 * checked states.
61 */
getColornull62 fun getColor(context: Context, isNightMode: Boolean, isChecked: Boolean) =
63 when {
64 isChecked ->
65 getColor(colorProvider = checked, isNightMode = isNightMode, context = context)
66 else ->
67 getColor(colorProvider = unchecked, isNightMode = isNightMode, context = context)
68 }
69
getColornull70 private fun getColor(colorProvider: ColorProvider, isNightMode: Boolean, context: Context) =
71 if (colorProvider is DayNightColorProvider) {
72 colorProvider.getColor(isNightMode)
73 } else {
74 colorProvider.getColor(context)
75 }
76
77 companion object {
createCheckableColorProvidernull78 fun createCheckableColorProvider(
79 source: String,
80 checked: ColorProvider,
81 unchecked: ColorProvider,
82 ): CheckableColorProvider {
83 return CheckedUncheckedColorProvider(source, checked, unchecked)
84 }
85 }
86 }
87
88 /** Resolves a color resource to a single color for the given checked state. */
resolveCheckedColornull89 internal fun resolveCheckedColor(
90 context: Context,
91 @ColorRes resId: Int,
92 isChecked: Boolean,
93 isNightMode: Boolean? = null
94 ): Color? {
95 if (resId == 0) return null
96
97 val resolveContext =
98 if (isNightMode == null) {
99 context
100 } else {
101 val configuration = Configuration()
102 configuration.uiMode =
103 if (isNightMode) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO
104 context.createConfigurationContext(configuration)
105 }
106 val colorStateList =
107 try {
108 ContextCompat.getColorStateList(resolveContext, resId) ?: return null
109 } catch (e: Resources.NotFoundException) {
110 Log.w(GlanceAppWidgetTag, "Could not resolve the checked color", e)
111 return null
112 }
113 return Color(
114 colorStateList.getColorForState(
115 if (isChecked) CheckedStateSet else UncheckedStateSet,
116 colorStateList.defaultColor
117 )
118 )
119 }
120
121 internal val CheckedStateSet = intArrayOf(android.R.attr.state_checked)
122 internal val UncheckedStateSet = intArrayOf(-android.R.attr.state_checked)
123