Skip to content
This repository was archived by the owner on Aug 7, 2024. It is now read-only.

Commit eb00530

Browse files
author
Nick Rout
committed
Clean up core ResourceUtils
1 parent 5edc31d commit eb00530

File tree

4 files changed

+256
-230
lines changed
  • core/src/main/java/com/google/android/material/composethemeadapter/core
  • material3Lib/src/main/java/com/google/android/material/composethemeadapter3
  • materialLib/src/main/java/com/google/android/material/composethemeadapter
  • sample

4 files changed

+256
-230
lines changed

core/src/main/java/com/google/android/material/composethemeadapter/core/ResourceUtils.kt

Lines changed: 134 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,32 @@ import androidx.core.content.res.getColorOrThrow
4949
import androidx.core.content.res.use
5050
import kotlin.concurrent.getOrSet
5151

52-
fun textStyleFromTextAppearance(
52+
/**
53+
* Returns the given index as a [Color], or [fallbackColor] if the value can't be coerced to a
54+
* [Color].
55+
*
56+
* @param index Index of attribute to retrieve.
57+
* @param fallbackColor Value to return if the attribute is not defined or can't be coerced to a
58+
* [Color].
59+
*/
60+
fun TypedArray.parseColor(
61+
index: Int,
62+
fallbackColor: Color = Color.Unspecified
63+
): Color = if (hasValue(index)) Color(getColorOrThrow(index)) else fallbackColor
64+
65+
/**
66+
* Returns the given style resource ID as a [TextStyle].
67+
*
68+
* @param context The current context.
69+
* @param id ID of style resource to retrieve.
70+
* @param density The current display density.
71+
* @param setTextColors Whether to read and set text colors from the style. Defaults to `false`.
72+
* @param defaultFontFamily Optional default font family to use in [TextStyle]s.
73+
*/
74+
fun parseTextAppearance(
5375
context: Context,
54-
density: Density,
5576
@StyleRes id: Int,
77+
density: Density,
5678
setTextColors: Boolean,
5779
defaultFontFamily: FontFamily?
5880
): TextStyle {
@@ -65,22 +87,24 @@ fun textStyleFromTextAppearance(
6587
// Variable fonts are not supported in Compose yet
6688

6789
// FYI, this only works with static font files in assets
68-
val fontFamily: FontFamilyWithWeight? = a.getFontFamilyOrNull(
90+
val fontFamily: FontFamilyWithWeight? = a.parseFontFamily(
6991
R.styleable.ComposeThemeAdapterTextAppearance_fontFamily
70-
) ?: a.getFontFamilyOrNull(R.styleable.ComposeThemeAdapterTextAppearance_android_fontFamily)
92+
) ?: a.parseFontFamily(R.styleable.ComposeThemeAdapterTextAppearance_android_fontFamily)
7193

7294
TextStyle(
7395
color = when {
7496
setTextColors -> {
75-
a.getComposeColor(R.styleable.ComposeThemeAdapterTextAppearance_android_textColor)
97+
a.parseColor(R.styleable.ComposeThemeAdapterTextAppearance_android_textColor)
7698
}
7799
else -> Color.Unspecified
78100
},
79-
fontSize = a.getTextUnit(R.styleable.ComposeThemeAdapterTextAppearance_android_textSize, density),
101+
fontSize = a.parseTextUnit(R.styleable.ComposeThemeAdapterTextAppearance_android_textSize, density),
80102
lineHeight = run {
81-
a.getTextUnitOrNull(R.styleable.ComposeThemeAdapterTextAppearance_lineHeight, density)
82-
?: a.getTextUnitOrNull(R.styleable.ComposeThemeAdapterTextAppearance_android_lineHeight, density)
83-
?: TextUnit.Unspecified
103+
a.parseTextUnit(R.styleable.ComposeThemeAdapterTextAppearance_lineHeight, density,
104+
fallbackTextUnit = a.parseTextUnit(
105+
R.styleable.ComposeThemeAdapterTextAppearance_android_lineHeight, density
106+
)
107+
)
84108
},
85109
fontFamily = when {
86110
defaultFontFamily != null -> defaultFontFamily
@@ -113,7 +137,7 @@ fun textStyleFromTextAppearance(
113137
},
114138
fontFeatureSettings = a.getString(R.styleable.ComposeThemeAdapterTextAppearance_android_fontFeatureSettings),
115139
shadow = run {
116-
val shadowColor = a.getComposeColor(R.styleable.ComposeThemeAdapterTextAppearance_android_shadowColor)
140+
val shadowColor = a.parseColor(R.styleable.ComposeThemeAdapterTextAppearance_android_shadowColor)
117141
if (shadowColor != Color.Unspecified) {
118142
val dx = a.getFloat(R.styleable.ComposeThemeAdapterTextAppearance_android_shadowDx, 0f)
119143
val dy = a.getFloat(R.styleable.ComposeThemeAdapterTextAppearance_android_shadowDy, 0f)
@@ -134,74 +158,13 @@ fun textStyleFromTextAppearance(
134158
}
135159
}
136160

137-
fun parseShapeAppearance(
138-
context: Context,
139-
@StyleRes id: Int,
140-
fallbackShape: CornerBasedShape,
141-
layoutDirection: LayoutDirection
142-
): CornerBasedShape {
143-
return context.obtainStyledAttributes(id, R.styleable.ComposeThemeAdapterShapeAppearance).use { a ->
144-
val defaultCornerSize = a.getCornerSizeOrNull(
145-
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSize
146-
)
147-
val cornerSizeTL = a.getCornerSizeOrNull(
148-
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeTopLeft
149-
)
150-
val cornerSizeTR = a.getCornerSizeOrNull(
151-
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeTopRight
152-
)
153-
val cornerSizeBL = a.getCornerSizeOrNull(
154-
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeBottomLeft
155-
)
156-
val cornerSizeBR = a.getCornerSizeOrNull(
157-
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeBottomRight
158-
)
159-
val isRtl = layoutDirection == LayoutDirection.Rtl
160-
val cornerSizeTS = if (isRtl) cornerSizeTR else cornerSizeTL
161-
val cornerSizeTE = if (isRtl) cornerSizeTL else cornerSizeTR
162-
val cornerSizeBS = if (isRtl) cornerSizeBR else cornerSizeBL
163-
val cornerSizeBE = if (isRtl) cornerSizeBL else cornerSizeBR
164-
165-
/**
166-
* We do not support the individual `cornerFamilyTopLeft`, etc, since Compose only supports
167-
* one corner type per shape. Therefore we only read the `cornerFamily` attribute.
168-
*/
169-
when (a.getInt(R.styleable.ComposeThemeAdapterShapeAppearance_cornerFamily, 0)) {
170-
0 -> {
171-
RoundedCornerShape(
172-
topStart = cornerSizeTS ?: defaultCornerSize ?: fallbackShape.topStart,
173-
topEnd = cornerSizeTE ?: defaultCornerSize ?: fallbackShape.topEnd,
174-
bottomEnd = cornerSizeBE ?: defaultCornerSize ?: fallbackShape.bottomEnd,
175-
bottomStart = cornerSizeBS ?: defaultCornerSize ?: fallbackShape.bottomStart
176-
)
177-
}
178-
1 -> {
179-
CutCornerShape(
180-
topStart = cornerSizeTS ?: defaultCornerSize ?: fallbackShape.topStart,
181-
topEnd = cornerSizeTE ?: defaultCornerSize ?: fallbackShape.topEnd,
182-
bottomEnd = cornerSizeBE ?: defaultCornerSize ?: fallbackShape.bottomEnd,
183-
bottomStart = cornerSizeBS ?: defaultCornerSize ?: fallbackShape.bottomStart
184-
)
185-
}
186-
else -> throw IllegalArgumentException("Unknown cornerFamily set in ShapeAppearance")
187-
}
188-
}
189-
}
190-
191-
private val tempTypedValue = ThreadLocal<TypedValue>()
192-
193-
fun TypedArray.getComposeColor(
194-
index: Int,
195-
fallbackColor: Color = Color.Unspecified
196-
): Color = if (hasValue(index)) Color(getColorOrThrow(index)) else fallbackColor
197-
198161
/**
199-
* Returns the given index as a [FontFamily] and [FontWeight],
200-
* or `null` if the value can not be coerced to a [FontFamily].
162+
* Returns the given index as a [FontFamilyWithWeight], or `null` if the value can't be coerced to
163+
* a [FontFamilyWithWeight].
201164
*
202-
* @param index index of attribute to retrieve.
165+
* @param index Index of attribute to retrieve.
203166
*/
204-
fun TypedArray.getFontFamilyOrNull(index: Int): FontFamilyWithWeight? {
167+
fun TypedArray.parseFontFamily(index: Int): FontFamilyWithWeight? {
205168
val tv = tempTypedValue.getOrSet(::TypedValue)
206169
if (getValue(index, tv) && tv.type == TypedValue.TYPE_STRING) {
207170
return when (tv.string) {
@@ -233,10 +196,24 @@ fun TypedArray.getFontFamilyOrNull(index: Int): FontFamilyWithWeight? {
233196
return null
234197
}
235198

199+
/**
200+
* A lightweight class for storing a [FontFamily] and [FontWeight].
201+
*/
202+
data class FontFamilyWithWeight(
203+
val fontFamily: FontFamily,
204+
val weight: FontWeight = FontWeight.Normal
205+
)
206+
207+
/**
208+
* Returns the given XML resource ID as a [FontFamily], or `null` if the value can't be coerced to
209+
* a [FontFamily].
210+
*
211+
* @param id ID of XML resource to retrieve.
212+
*/
236213
@SuppressLint("RestrictedApi") // FontResourcesParserCompat.*
237-
@RequiresApi(23) // XML font families with >1 fonts are only supported on API 23+
238-
private fun Resources.parseXmlFontFamily(resourceId: Int): FontFamily? {
239-
val parser = getXml(resourceId)
214+
@RequiresApi(23) // XML font families with > 1 fonts are only supported on API 23+
215+
fun Resources.parseXmlFontFamily(id: Int): FontFamily? {
216+
val parser = getXml(id)
240217

241218
// Can't use {} since XmlResourceParser is AutoCloseable, not Closeable
242219
@Suppress("ConvertTryFinallyToUseCall")
@@ -272,37 +249,20 @@ private fun fontWeightOf(weight: Int): FontWeight = when (weight) {
272249
else -> FontWeight.W400
273250
}
274251

275-
data class FontFamilyWithWeight(
276-
val fontFamily: FontFamily,
277-
val weight: FontWeight = FontWeight.Normal
278-
)
279-
280252
/**
281-
* Returns the given index as a [TextUnit], or [fallback] if the value can not be coerced to
253+
* Returns the given index as a [TextUnit], or [fallbackTextUnit] if the value can't be coerced to
282254
* a [TextUnit].
283255
*
284-
* @param index index of attribute to retrieve.
285-
* @param density the current display density.
286-
* @param fallback Value to return if the attribute is not defined or cannot be coerced to
287-
* a [TextUnit].
256+
* @param index Index of attribute to retrieve.
257+
* @param density The current display density.
258+
* @param fallbackTextUnit Value to return if the attribute is not defined or can't be coerced to a
259+
* [TextUnit].
288260
*/
289-
internal fun TypedArray.getTextUnit(
261+
fun TypedArray.parseTextUnit(
290262
index: Int,
291263
density: Density,
292-
fallback: TextUnit = TextUnit.Unspecified
293-
): TextUnit = getTextUnitOrNull(index, density) ?: fallback
294-
295-
/**
296-
* Returns the given index as a [TextUnit], or `null` if the value can not be coerced to
297-
* a [TextUnit].
298-
*
299-
* @param index index of attribute to retrieve.
300-
* @param density the current display density.
301-
*/
302-
internal fun TypedArray.getTextUnitOrNull(
303-
index: Int,
304-
density: Density
305-
): TextUnit? {
264+
fallbackTextUnit: TextUnit = TextUnit.Unspecified
265+
): TextUnit {
306266
val tv = tempTypedValue.getOrSet { TypedValue() }
307267
if (getValue(index, tv) && tv.type == TypedValue.TYPE_DIMENSION) {
308268
return when (tv.complexUnitCompat) {
@@ -315,16 +275,80 @@ internal fun TypedArray.getTextUnitOrNull(
315275
else -> with(density) { getDimension(index, 0f).toSp() }
316276
}
317277
}
318-
return null
278+
return fallbackTextUnit
279+
}
280+
281+
/**
282+
* Returns the given style resource ID as a [CornerBasedShape], or [fallbackShape] if the value
283+
* can't be coerced to a [CornerBasedShape].
284+
*
285+
* @param context The current context.
286+
* @param id ID of style resource to retrieve.
287+
* @param layoutDirection The current display layout direction.
288+
* @param fallbackShape Value to return if the style resource ID is not defined or can't be coerced
289+
* to a [CornerBasedShape].
290+
*/
291+
fun parseShapeAppearance(
292+
context: Context,
293+
@StyleRes id: Int,
294+
layoutDirection: LayoutDirection,
295+
fallbackShape: CornerBasedShape
296+
): CornerBasedShape {
297+
return context.obtainStyledAttributes(id, R.styleable.ComposeThemeAdapterShapeAppearance).use { a ->
298+
val defaultCornerSize = a.parseCornerSize(
299+
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSize
300+
)
301+
val cornerSizeTL = a.parseCornerSize(
302+
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeTopLeft
303+
)
304+
val cornerSizeTR = a.parseCornerSize(
305+
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeTopRight
306+
)
307+
val cornerSizeBL = a.parseCornerSize(
308+
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeBottomLeft
309+
)
310+
val cornerSizeBR = a.parseCornerSize(
311+
R.styleable.ComposeThemeAdapterShapeAppearance_cornerSizeBottomRight
312+
)
313+
val isRtl = layoutDirection == LayoutDirection.Rtl
314+
val cornerSizeTS = if (isRtl) cornerSizeTR else cornerSizeTL
315+
val cornerSizeTE = if (isRtl) cornerSizeTL else cornerSizeTR
316+
val cornerSizeBS = if (isRtl) cornerSizeBR else cornerSizeBL
317+
val cornerSizeBE = if (isRtl) cornerSizeBL else cornerSizeBR
318+
319+
/**
320+
* We do not support the individual `cornerFamilyTopLeft`, etc, since Compose only supports
321+
* one corner type per shape. Therefore we only read the `cornerFamily` attribute.
322+
*/
323+
when (a.getInt(R.styleable.ComposeThemeAdapterShapeAppearance_cornerFamily, 0)) {
324+
0 -> {
325+
RoundedCornerShape(
326+
topStart = cornerSizeTS ?: defaultCornerSize ?: fallbackShape.topStart,
327+
topEnd = cornerSizeTE ?: defaultCornerSize ?: fallbackShape.topEnd,
328+
bottomEnd = cornerSizeBE ?: defaultCornerSize ?: fallbackShape.bottomEnd,
329+
bottomStart = cornerSizeBS ?: defaultCornerSize ?: fallbackShape.bottomStart
330+
)
331+
}
332+
1 -> {
333+
CutCornerShape(
334+
topStart = cornerSizeTS ?: defaultCornerSize ?: fallbackShape.topStart,
335+
topEnd = cornerSizeTE ?: defaultCornerSize ?: fallbackShape.topEnd,
336+
bottomEnd = cornerSizeBE ?: defaultCornerSize ?: fallbackShape.bottomEnd,
337+
bottomStart = cornerSizeBS ?: defaultCornerSize ?: fallbackShape.bottomStart
338+
)
339+
}
340+
else -> throw IllegalArgumentException("Unknown cornerFamily set in ShapeAppearance")
341+
}
342+
}
319343
}
320344

321345
/**
322-
* Returns the given index as a [CornerSize], or `null` if the value can not be coerced
323-
* to a [CornerSize].
346+
* Returns the given index as a [CornerSize], or `null` if the value can't be coerced to a
347+
* [CornerSize].
324348
*
325-
* @param index index of attribute to retrieve.
349+
* @param index Index of attribute to retrieve.
326350
*/
327-
internal fun TypedArray.getCornerSizeOrNull(index: Int): CornerSize? {
351+
fun TypedArray.parseCornerSize(index: Int): CornerSize? {
328352
val tv = tempTypedValue.getOrSet { TypedValue() }
329353
if (getValue(index, tv)) {
330354
return when (tv.type) {
@@ -352,3 +376,5 @@ private inline val TypedValue.complexUnitCompat
352376
Build.VERSION.SDK_INT > 22 -> complexUnit
353377
else -> TypedValue.COMPLEX_UNIT_MASK and (data shr TypedValue.COMPLEX_UNIT_SHIFT)
354378
}
379+
380+
private val tempTypedValue = ThreadLocal<TypedValue>()

0 commit comments

Comments
 (0)