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