1 /*
<lambda>null2 * Copyright (C) 2023 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.systemui.util
18
19 import android.content.res.ColorStateList
20 import android.graphics.BlendMode
21 import android.graphics.BlendModeColorFilter
22 import android.graphics.ColorFilter
23 import android.graphics.LightingColorFilter
24 import android.graphics.PorterDuffColorFilter
25 import android.graphics.drawable.Drawable
26 import android.graphics.drawable.DrawableWrapper
27 import android.graphics.drawable.GradientDrawable
28 import android.graphics.drawable.LayerDrawable
29 import android.graphics.drawable.RippleDrawable
30 import android.util.Log
31
32 fun dumpToString(drawable: Drawable?): String =
33 if (Compile.IS_DEBUG) StringBuilder().appendDrawable(drawable).toString()
34 else drawable.toString()
35
36 fun getSolidColor(drawable: Drawable?): String =
37 if (Compile.IS_DEBUG) hexColorString(getSolidColors(drawable)?.defaultColor)
38 else if (drawable == null) "null" else "?"
39
40 private fun getSolidColors(drawable: Drawable?): ColorStateList? {
41 return when (drawable) {
42 is GradientDrawable -> {
43 return drawable.getStateField<ColorStateList>("mSolidColors")
44 }
45 is LayerDrawable -> {
46 for (iLayer in 0 until drawable.numberOfLayers) {
47 getSolidColors(drawable.getDrawable(iLayer))?.let {
48 return it
49 }
50 }
51 null
52 }
53 is DrawableWrapper -> {
54 return getSolidColors(drawable.drawable)
55 }
56 else -> null
57 }
58 }
59
StringBuildernull60 private fun StringBuilder.appendDrawable(drawable: Drawable?): StringBuilder {
61 if (drawable == null) {
62 append("null")
63 return this
64 }
65 append("<")
66 append(drawable.javaClass.simpleName)
67
68 drawable.getStateField<ColorStateList>("mTint", fieldRequired = false)?.let {
69 append(" tint=")
70 appendColors(it)
71 append(" blendMode=")
72 append(drawable.getStateField<BlendMode>("mBlendMode"))
73 }
74 drawable.colorFilter
75 ?.takeUnless { drawable is DrawableWrapper }
76 ?.let {
77 append(" colorFilter=")
78 appendColorFilter(it)
79 }
80 when (drawable) {
81 is DrawableWrapper -> {
82 append(" wrapped=")
83 appendDrawable(drawable.drawable)
84 }
85 is LayerDrawable -> {
86 if (drawable is RippleDrawable) {
87 drawable.getStateField<ColorStateList>("mColor")?.let {
88 append(" color=")
89 appendColors(it)
90 }
91 drawable.effectColor?.let {
92 append(" effectColor=")
93 appendColors(it)
94 }
95 }
96 append(" layers=[")
97 for (iLayer in 0 until drawable.numberOfLayers) {
98 if (iLayer != 0) append(", ")
99 appendDrawable(drawable.getDrawable(iLayer))
100 }
101 append("]")
102 }
103 is GradientDrawable -> {
104 drawable
105 .getStateField<Int>("mShape")
106 ?.takeIf { it != 0 }
107 ?.let {
108 append(" shape=")
109 append(it)
110 }
111 drawable.getStateField<ColorStateList>("mSolidColors")?.let {
112 append(" solidColors=")
113 appendColors(it)
114 }
115 drawable.getStateField<ColorStateList>("mStrokeColors")?.let {
116 append(" strokeColors=")
117 appendColors(it)
118 }
119 drawable.colors?.let {
120 append(" gradientColors=[")
121 it.forEachIndexed { iColor, color ->
122 if (iColor != 0) append(", ")
123 append(hexColorString(color))
124 }
125 append("]")
126 }
127 }
128 }
129 append(">")
130 return this
131 }
132
getStateFieldnull133 private inline fun <reified T> Drawable.getStateField(
134 name: String,
135 fieldRequired: Boolean = true
136 ): T? {
137 val state = this.constantState ?: return null
138 val clazz = state.javaClass
139 return try {
140 val field = clazz.getDeclaredField(name)
141 field.isAccessible = true
142 field.get(state) as T?
143 } catch (ex: Exception) {
144 if (fieldRequired) {
145 Log.w(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName}", ex)
146 } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
147 Log.v(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName} ($ex)")
148 }
149 null
150 }
151 }
152
Appendablenull153 private fun Appendable.appendColors(colorStateList: ColorStateList?) {
154 if (colorStateList == null) {
155 append("null")
156 return
157 }
158 val colors = colorStateList.colors
159 if (colors.size == 1) {
160 append(hexColorString(colors[0]))
161 return
162 }
163 append("<ColorStateList size=")
164 append(colors.size.toString())
165 append(" default=")
166 append(hexColorString(colorStateList.defaultColor))
167 append(">")
168 }
169
Appendablenull170 private fun Appendable.appendColorFilter(colorFilter: ColorFilter?) {
171 if (colorFilter == null) {
172 append("null")
173 return
174 }
175 append("<")
176 append(colorFilter.javaClass.simpleName)
177 when (colorFilter) {
178 is PorterDuffColorFilter -> {
179 append(" color=")
180 append(hexColorString(colorFilter.color))
181 append(" mode=")
182 append(colorFilter.mode.toString())
183 }
184 is BlendModeColorFilter -> {
185 append(" color=")
186 append(hexColorString(colorFilter.color))
187 append(" mode=")
188 append(colorFilter.mode.toString())
189 }
190 is LightingColorFilter -> {
191 append(" multiply=")
192 append(hexColorString(colorFilter.colorMultiply))
193 append(" add=")
194 append(hexColorString(colorFilter.colorAdd))
195 }
196 else -> append(" unhandled")
197 }
198 append(">")
199 }
200
201 private const val TAG = "DrawableDump"
202