Skip to content

Commit 57663ab

Browse files
authored
Use AppCompatResources to resolve colors and drawables (#38)
* Resolve colors with AppCompatResources to ensure attribute-backed selectors resolve correctly on API < 23 * Resolve drawables with AppCompatResources * Add AppCompat as a production dependency of Deferred Resources
1 parent e566b5b commit 57663ab

6 files changed

Lines changed: 16 additions & 29 deletions

File tree

deferred-resources/build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,9 @@ android {
4242
dependencies {
4343
implementation deps.androidAnnotations
4444

45+
implementation deps.appCompat
4546
implementation deps.coreKtx
4647

47-
// TODO WORKAROUND: Instrumented tests won't run without this
48-
debugImplementation deps.appCompat
49-
5048
androidTestImplementation deps.junitAndroidX
5149
androidTestImplementation deps.androidTestRules
5250
androidTestImplementation deps.androidTestRunner

deferred-resources/src/androidTest/java/com/backbase/deferredresources/DeferredColorTest.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.backbase.deferredresources
22

33
import android.graphics.Color
4-
import androidx.test.filters.SdkSuppress
54
import com.backbase.deferredresources.test.R
65
import com.google.common.truth.Truth.assertThat
76
import org.junit.Test
@@ -45,7 +44,6 @@ internal class DeferredColorTest {
4544
assertThat(deferred.resolve(AppCompatContext())).isEqualTo(Color.parseColor("#aaaaaa"))
4645
}
4746

48-
@SdkSuppress(minSdkVersion = 23)
4947
@Test fun resourceResolve_withSelectorColorWithAttribute_resolvesDefaultColor() {
5048
val deferred = DeferredColor.Resource(R.color.stateful_color_with_attr)
5149
assertThat(deferred.resolve(AppCompatContext())).isEqualTo(Color.parseColor("#987654"))
@@ -71,7 +69,6 @@ internal class DeferredColorTest {
7169
assertThat(resolved.defaultColor).isEqualTo(Color.parseColor("#aaaaaa"))
7270
}
7371

74-
@SdkSuppress(minSdkVersion = 23)
7572
@Test fun resourceResolveToStateList_withSelectorColorWithAttribute_resolvesExpectedStateList() {
7673
val deferred = DeferredColor.Resource(R.color.stateful_color_with_attr)
7774

@@ -95,7 +92,6 @@ internal class DeferredColorTest {
9592
assertThat(deferred.resolve(AppCompatContext())).isEqualTo(Color.parseColor("#aaaaaa"))
9693
}
9794

98-
@SdkSuppress(minSdkVersion = 23)
9995
@Test fun attributeResolve_withSelectorColorWithAttributeDefault_resolvesDefaultColor() {
10096
val deferred = DeferredColor.Attribute(R.attr.titleTextColor)
10197
assertThat(deferred.resolve(AppCompatContext())).isEqualTo(Color.parseColor("#987654"))
@@ -127,7 +123,6 @@ internal class DeferredColorTest {
127123
assertThat(resolved.defaultColor).isEqualTo(Color.parseColor("#aaaaaa"))
128124
}
129125

130-
@SdkSuppress(minSdkVersion = 23)
131126
@Test fun attributeResolveToStateList_withSelectorColorWithAttribute_resolvesExpectedStateList() {
132127
val deferred = DeferredColor.Attribute(R.attr.titleTextColor)
133128

deferred-resources/src/androidTest/java/com/backbase/deferredresources/DeferredDrawableTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.backbase.deferredresources
33
import android.graphics.Color
44
import android.graphics.drawable.GradientDrawable
55
import android.os.Build
6-
import androidx.core.content.ContextCompat
6+
import androidx.appcompat.content.res.AppCompatResources
77
import com.backbase.deferredresources.test.R
88
import com.google.common.truth.Truth.assertThat
99
import org.junit.After
@@ -22,7 +22,7 @@ internal class DeferredDrawableTest {
2222
// Radius XML is not honored on API 21, so mutate=false tests are skipped
2323
if (Build.VERSION.SDK_INT == 21) return
2424

25-
val loadedAgain = ContextCompat.getDrawable(context, R.drawable.oval) as GradientDrawable
25+
val loadedAgain = AppCompatResources.getDrawable(context, R.drawable.oval) as GradientDrawable
2626
loadedAgain.gradientRadius = defaultOvalGradientFraction
2727
}
2828

@@ -45,7 +45,7 @@ internal class DeferredDrawableTest {
4545
resolved.gradientRadius = 0.5f
4646

4747
// Since it's not mutated, transformations SHOULD apply to re-loaded instances:
48-
val loadedAgain = ContextCompat.getDrawable(context, R.drawable.oval) as GradientDrawable
48+
val loadedAgain = AppCompatResources.getDrawable(context, R.drawable.oval) as GradientDrawable
4949
assertThat(loadedAgain.gradientRadiusCompat).isEqualTo(0.5f)
5050
}
5151

@@ -62,7 +62,7 @@ internal class DeferredDrawableTest {
6262
resolved.gradientRadius = 0.4f
6363

6464
// Since it's mutated, transformations SHOULD NOT apply to re-loaded instances:
65-
val loadedAgain = ContextCompat.getDrawable(context, R.drawable.oval) as GradientDrawable
65+
val loadedAgain = AppCompatResources.getDrawable(context, R.drawable.oval) as GradientDrawable
6666
assertThat(loadedAgain.gradientRadiusCompat).isEqualTo(defaultOvalGradientFraction)
6767
}
6868

deferred-resources/src/main/java/com/backbase/deferredresources/DeferredColor.kt

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import android.util.TypedValue
77
import androidx.annotation.AttrRes
88
import androidx.annotation.ColorInt
99
import androidx.annotation.ColorRes
10-
import androidx.core.content.ContextCompat
10+
import androidx.appcompat.content.res.AppCompatResources
1111
import com.backbase.deferredresources.internal.resolveAttribute
1212
import dev.drewhamilton.extracare.DataApi
1313

@@ -46,7 +46,7 @@ public interface DeferredColor {
4646
/**
4747
* Always resolves to [value] wrapped in a new [ColorStateList].
4848
*/
49-
override fun resolveToStateList(context: Context): ColorStateList = ColorStateList.valueOf(value)
49+
override fun resolveToStateList(context: Context): ColorStateList = ColorStateList.valueOf(value)
5050
}
5151

5252
/**
@@ -58,18 +58,16 @@ public interface DeferredColor {
5858
/**
5959
* Resolve [resId] to a [ColorInt] with the given [context]. If [resId] resolves to a color selector resource,
6060
* resolves the default color of that selector.
61-
*
62-
* Warning: On API < 23, resolving a color selector with [context]'s theme is unsupported. Thus, a color
63-
* selector with an attribute reference as its default color will not resolve to the correct color on API 22 and
64-
* below. A color selector with a resource reference as its default color will resolve correctly.
6561
*/
66-
@ColorInt override fun resolve(context: Context): Int = ContextCompat.getColor(context, resId)
62+
@ColorInt override fun resolve(context: Context): Int =
63+
// Forward to `resolveToStateList` to ensure attribute-backed selectors resolve correctly:
64+
resolveToStateList(context).defaultColor
6765

6866
/**
6967
* Resolve [resId] to a [ColorStateList] with the given [context].
7068
*/
7169
override fun resolveToStateList(context: Context): ColorStateList =
72-
requireNotNull(ContextCompat.getColorStateList(context, resId)) {
70+
requireNotNull(AppCompatResources.getColorStateList(context, resId)) {
7371
"Could not resolve ${context.resources.getResourceName(resId)} to a ColorStateList with $context."
7472
}
7573
}
@@ -88,10 +86,6 @@ public interface DeferredColor {
8886
* Resolve [resId] to a [ColorInt] with the given [context]'s theme. If [resId] would resolve a color selector,
8987
* resolves to the default color of that selector.
9088
*
91-
* Warning: On API < 23, resolving a color selector with [context]'s theme is unsupported. Thus, a color
92-
* selector with an attribute reference as its default color will not resolve to the correct color on API 22 and
93-
* below. A color selector with a resource reference as its default color will resolve correctly.
94-
*
9589
* @throws IllegalArgumentException if [resId] cannot be resolved to a color.
9690
*/
9791
@ColorInt override fun resolve(context: Context): Int = context.resolveColorAttribute {
@@ -124,7 +118,7 @@ public interface DeferredColor {
124118
resolveRefs = false
125119
) {
126120
val colorSelectorResId = data
127-
ContextCompat.getColorStateList(this@resolveColorStateList, colorSelectorResId)!!
121+
AppCompatResources.getColorStateList(this@resolveColorStateList, colorSelectorResId)
128122
}
129123
}
130124
}

deferred-resources/src/main/java/com/backbase/deferredresources/DeferredDrawable.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.backbase.deferredresources
33
import android.content.Context
44
import android.graphics.drawable.Drawable
55
import androidx.annotation.DrawableRes
6-
import androidx.core.content.ContextCompat
6+
import androidx.appcompat.content.res.AppCompatResources
77
import dev.drewhamilton.extracare.DataApi
88

99
/**
@@ -53,7 +53,7 @@ public interface DeferredDrawable {
5353
* [Drawable.mutate] instead of the original Drawable. Applies [transformations] before returning.
5454
*/
5555
override fun resolve(context: Context): Drawable? {
56-
val original = ContextCompat.getDrawable(context, resId)
56+
val original = AppCompatResources.getDrawable(context, resId)
5757
val drawable = if (mutate) original?.mutate() else original
5858
return drawable?.apply { transformations(context) }
5959
}

deferred-resources/src/main/java/com/backbase/deferredresources/color/SdkIntDeferredColor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import dev.drewhamilton.extracare.DataApi
1010
/**
1111
* A [DeferredColor] with a different source depending on the runtime Android SDK version.
1212
*
13-
* This is useful because different SDK levels treat colors differently in some contexts. For example, SDK 23+ can
14-
* resolve color selectors backed by color attributes, but lower SDKs cannot.
13+
* This is useful because different SDK levels treat colors differently in some contexts. For example, SDK 27+ can
14+
* support light system navigation bar colors, but lower SDKs cannot.
1515
*/
1616
@DataApi public class SdkIntDeferredColor private constructor(
1717
private val source: DeferredColor

0 commit comments

Comments
 (0)