From e832488698c68ecbe8323d25c2051ab4722c0f93 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:22:15 +0200 Subject: [PATCH 01/19] Fix styledColor to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another resource, and it would crash if the color value was set inline. Now, these cases are properly handled and return the correct color. --- modules/resources/build.gradle.kts | 2 + .../splitties/resources/ColorResources.kt | 15 ++++++- .../splitties/resources/StyledAttributes.kt | 41 +++++++++++++++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/modules/resources/build.gradle.kts b/modules/resources/build.gradle.kts index a4e3aa7ad..74f7e6009 100644 --- a/modules/resources/build.gradle.kts +++ b/modules/resources/build.gradle.kts @@ -20,9 +20,11 @@ kotlin { sourceSets { androidMain.dependencies { api(AndroidX.annotation) + api(splitties("experimental")) compileOnly(AndroidX.fragment) implementation(splitties("appctx")) implementation(splitties("mainthread")) + implementation(splitties("exceptions")) } } } diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt index ec4753b55..fd9cf564b 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt @@ -3,6 +3,7 @@ */ @file:Suppress("NOTHING_TO_INLINE") +@file:OptIn(InternalSplittiesApi::class) package splitties.resources @@ -10,11 +11,14 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.os.Build.VERSION.SDK_INT +import android.util.TypedValue import android.view.View import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.fragment.app.Fragment +import splitties.exceptions.illegalArg +import splitties.experimental.InternalSplittiesApi import splitties.init.appCtx /** @@ -63,7 +67,16 @@ inline fun appColorSL(@ColorRes colorRes: Int) = appCtx.colorSL(colorRes) private inline val defaultColor get() = Color.RED @ColorInt -fun Context.styledColor(@AttrRes attr: Int): Int = color(resolveThemeAttribute(attr)) +fun Context.styledColor(@AttrRes attr: Int): Int = withResolvedThemeAttribute(attr) { + when (type) { + in TypedValue.TYPE_FIRST_COLOR_INT..TypedValue.TYPE_LAST_COLOR_INT -> data + TypedValue.TYPE_STRING -> { + if (string.startsWith("res/color/")) color(resourceId) + else illegalArg(unexpectedThemeAttributeTypeErrorMessage(expectedKind = "color")) + } + else -> illegalArg(unexpectedThemeAttributeTypeErrorMessage(expectedKind = "color")) + } +} inline fun Fragment.styledColor(@AttrRes attr: Int) = context!!.styledColor(attr) inline fun View.styledColor(@AttrRes attr: Int) = context.styledColor(attr) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/StyledAttributes.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/StyledAttributes.kt index 8cdec6ac0..0aaa2f42b 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/StyledAttributes.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/StyledAttributes.kt @@ -11,6 +11,7 @@ import android.content.res.Resources import android.util.TypedValue import androidx.annotation.AnyRes import androidx.annotation.AttrRes +import splitties.experimental.InternalSplittiesApi import splitties.mainthread.isMainThread @AnyRes @@ -18,13 +19,13 @@ fun Context.resolveThemeAttribute( @AttrRes attrRes: Int, resolveRefs: Boolean = true ): Int = if (isMainThread) { - if (theme.resolveAttribute(attrRes, uiThreadConfinedCachedTypeValue, resolveRefs).not()) { + if (theme.resolveAttribute(attrRes, uiThreadConfinedCachedTypedValue, resolveRefs).not()) { throw Resources.NotFoundException( "Couldn't resolve attribute resource #0x" + Integer.toHexString(attrRes) + " from the theme of this Context." ) } - uiThreadConfinedCachedTypeValue.resourceId + uiThreadConfinedCachedTypedValue.resourceId } else synchronized(cachedTypeValue) { if (theme.resolveAttribute(attrRes, cachedTypeValue, resolveRefs).not()) { throw Resources.NotFoundException( @@ -35,5 +36,37 @@ fun Context.resolveThemeAttribute( cachedTypeValue.resourceId } -private val uiThreadConfinedCachedTypeValue = TypedValue() -private val cachedTypeValue = TypedValue() +@InternalSplittiesApi +inline fun Context.withResolvedThemeAttribute( + @AttrRes attrRes: Int, + resolveRefs: Boolean = true, + crossinline block: TypedValue.() -> R +): R = if (isMainThread) { + if (theme.resolveAttribute(attrRes, uiThreadConfinedCachedTypedValue, resolveRefs).not()) { + throw Resources.NotFoundException( + "Couldn't resolve attribute resource #0x" + Integer.toHexString(attrRes) + + " from the theme of this Context." + ) + } + block(uiThreadConfinedCachedTypedValue) +} else synchronized(cachedTypeValue) { + if (theme.resolveAttribute(attrRes, cachedTypeValue, resolveRefs).not()) { + throw Resources.NotFoundException( + "Couldn't resolve attribute resource #0x" + Integer.toHexString(attrRes) + + " from the theme of this Context." + ) + } + block(cachedTypeValue) +} + +@PublishedApi @JvmField internal val uiThreadConfinedCachedTypedValue = TypedValue() +@PublishedApi @JvmField internal val cachedTypeValue = TypedValue() + +internal fun TypedValue.unexpectedThemeAttributeTypeErrorMessage(expectedKind: String): String { + val article = when (expectedKind.firstOrNull() ?: ' ') { + in "aeio" -> "an" + else -> "a" + } + return "Expected $article $expectedKind theme attribute but got type 0x${type.toString(16)} " + + "(see what it corresponds to in android.util.TypedValue constants)" +} From 4b5d5daea4e89487097b09791d09b3291e62f874 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:23:20 +0200 Subject: [PATCH 02/19] Fix styledColorSL to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another color resource, and it would crash if the color value was set inline. Now, these cases are properly handled and return the correct ColorStateList. --- .../splitties/resources/ColorResources.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt index fd9cf564b..174b8cdd0 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt @@ -89,7 +89,22 @@ inline fun View.styledColor(@AttrRes attr: Int) = context.styledColor(attr) */ inline fun appStyledColor(@AttrRes attr: Int) = appCtx.styledColor(attr) -fun Context.styledColorSL(@AttrRes attr: Int): ColorStateList = colorSL(resolveThemeAttribute(attr)) +fun Context.styledColorSL(@AttrRes attr: Int): ColorStateList = withResolvedThemeAttribute(attr) { + when (resourceId) { + 0 -> { + require(type in TypedValue.TYPE_FIRST_COLOR_INT..TypedValue.TYPE_LAST_COLOR_INT) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "color") + } + ColorStateList.valueOf(data) + } + else -> { + require(type == TypedValue.TYPE_STRING && string.startsWith("res/color/")) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "color") + } + colorSL(resourceId) + } + } +} inline fun Fragment.styledColorSL(@AttrRes attr: Int) = context!!.styledColorSL(attr) inline fun View.styledColorSL(@AttrRes attr: Int) = context.styledColorSL(attr) From 4c057b786b3e0928eccb9384127e33b2d84bb5eb Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:24:58 +0200 Subject: [PATCH 03/19] Remove no longer used private property --- .../androidMain/kotlin/splitties/resources/ColorResources.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt index 174b8cdd0..bad9867d5 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt @@ -9,7 +9,6 @@ package splitties.resources import android.content.Context import android.content.res.ColorStateList -import android.graphics.Color import android.os.Build.VERSION.SDK_INT import android.util.TypedValue import android.view.View @@ -64,8 +63,6 @@ inline fun appColorSL(@ColorRes colorRes: Int) = appCtx.colorSL(colorRes) // Styled resources below -private inline val defaultColor get() = Color.RED - @ColorInt fun Context.styledColor(@AttrRes attr: Int): Int = withResolvedThemeAttribute(attr) { when (type) { From 4522022703b35a68fee2863515dba8a47d9d6e31 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:25:19 +0200 Subject: [PATCH 04/19] Add line breaks for better clarity --- .../kotlin/splitties/resources/ColorResources.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt index bad9867d5..d43aa67e2 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/ColorResources.kt @@ -31,6 +31,7 @@ fun Context.color(@ColorRes colorRes: Int): Int = if (SDK_INT >= 23) getColor(co inline fun Fragment.color(@ColorRes colorRes: Int) = context!!.color(colorRes) inline fun View.color(@ColorRes colorRes: Int) = context.color(colorRes) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -40,6 +41,7 @@ inline fun View.color(@ColorRes colorRes: Int) = context.color(colorRes) */ inline fun appColor(@ColorRes colorRes: Int) = appCtx.color(colorRes) + /** * @see [androidx.core.content.ContextCompat.getColorStateList] */ @@ -52,6 +54,7 @@ fun Context.colorSL(@ColorRes colorRes: Int): ColorStateList { inline fun Fragment.colorSL(@ColorRes colorRes: Int) = context!!.colorSL(colorRes) inline fun View.colorSL(@ColorRes colorRes: Int) = context.colorSL(colorRes) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -61,6 +64,7 @@ inline fun View.colorSL(@ColorRes colorRes: Int) = context.colorSL(colorRes) */ inline fun appColorSL(@ColorRes colorRes: Int) = appCtx.colorSL(colorRes) + // Styled resources below @ColorInt @@ -77,6 +81,8 @@ fun Context.styledColor(@AttrRes attr: Int): Int = withResolvedThemeAttribute(at inline fun Fragment.styledColor(@AttrRes attr: Int) = context!!.styledColor(attr) inline fun View.styledColor(@AttrRes attr: Int) = context.styledColor(attr) + + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -105,6 +111,7 @@ fun Context.styledColorSL(@AttrRes attr: Int): ColorStateList = withResolvedThem inline fun Fragment.styledColorSL(@AttrRes attr: Int) = context!!.styledColorSL(attr) inline fun View.styledColorSL(@AttrRes attr: Int) = context.styledColorSL(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not From 2f88f5f727dbe1de0647a98e74a0892aced1800b Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:34:01 +0200 Subject: [PATCH 05/19] Fix styledDimen to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another dimension resource, and it would crash if the dimension value was set inline. Now, these cases are properly handled and return the correct dimension. --- .../splitties/resources/DimenResources.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt index 412f61eaf..7b0bb3835 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt @@ -3,14 +3,17 @@ */ @file:Suppress("NOTHING_TO_INLINE") +@file:OptIn(InternalSplittiesApi::class) package splitties.resources import android.content.Context +import android.util.TypedValue import android.view.View import androidx.annotation.AttrRes import androidx.annotation.DimenRes import androidx.fragment.app.Fragment +import splitties.experimental.InternalSplittiesApi import splitties.init.appCtx inline fun Context.dimen(@DimenRes dimenResId: Int): Float = resources.getDimension(dimenResId) @@ -55,9 +58,21 @@ inline fun View.dimenPxOffset(@DimenRes dimenResId: Int) = context.dimenPxOffset */ inline fun appDimenPxOffset(@DimenRes dimenResId: Int) = appCtx.dimenPxOffset(dimenResId) + +private fun TypedValue.checkOfDimensionType() { + require(type == TypedValue.TYPE_DIMENSION) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "dimension") + } +} + + // Styled resources below -fun Context.styledDimen(@AttrRes attr: Int): Float = dimen(resolveThemeAttribute(attr)) +fun Context.styledDimen(@AttrRes attr: Int): Float = withResolvedThemeAttribute(attr) { + checkOfDimensionType() + TypedValue.complexToDimension(data, resources.displayMetrics) +} + inline fun Fragment.styledDimen(@AttrRes attr: Int) = context!!.styledDimen(attr) inline fun View.styledDimen(@AttrRes attr: Int) = context.styledDimen(attr) /** From ff47a827d7a301da74d1882ebb6adea4a0a89377 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:34:17 +0200 Subject: [PATCH 06/19] Fix styledDimenPxSize to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another dimension resource, and it would crash if the dimension value was set inline. Now, these cases are properly handled and return the correct dimension. --- .../kotlin/splitties/resources/DimenResources.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt index 7b0bb3835..18d3c636e 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt @@ -84,7 +84,12 @@ inline fun View.styledDimen(@AttrRes attr: Int) = context.styledDimen(attr) */ inline fun appStyledDimen(@AttrRes attr: Int) = appCtx.styledDimen(attr) -fun Context.styledDimenPxSize(@AttrRes attr: Int): Int = dimenPxSize(resolveThemeAttribute(attr)) + +fun Context.styledDimenPxSize(@AttrRes attr: Int): Int = withResolvedThemeAttribute(attr) { + checkOfDimensionType() + TypedValue.complexToDimensionPixelSize(data, resources.displayMetrics) +} + inline fun Fragment.styledDimenPxSize(@AttrRes attr: Int) = context!!.styledDimenPxSize(attr) inline fun View.styledDimenPxSize(@AttrRes attr: Int) = context.styledDimenPxSize(attr) /** From 644839deaedd30ca41c658a0dda16c246e38d038 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:34:26 +0200 Subject: [PATCH 07/19] Fix styledDimenPxOffset to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another dimension resource, and it would crash if the dimension value was set inline. Now, these cases are properly handled and return the correct dimension. --- .../androidMain/kotlin/splitties/resources/DimenResources.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt index 18d3c636e..9c298f71a 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt @@ -103,7 +103,10 @@ inline fun appStyledDimenPxSize(@AttrRes attr: Int) = appCtx.styledDimenPxSize(a fun Context.styledDimenPxOffset( @AttrRes attr: Int -): Int = dimenPxOffset(resolveThemeAttribute(attr)) +): Int = withResolvedThemeAttribute(attr) { + checkOfDimensionType() + TypedValue.complexToDimensionPixelOffset(data, resources.displayMetrics) +} inline fun Fragment.styledDimenPxOffset(@AttrRes attr: Int) = context!!.styledDimenPxOffset(attr) inline fun View.styledDimenPxOffset(@AttrRes attr: Int) = context.styledDimenPxOffset(attr) From 976f52507b31fc21188f3c53ba4e53fea9314c68 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:35:10 +0200 Subject: [PATCH 08/19] Add line breaks for better clarity --- .../kotlin/splitties/resources/DimenResources.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt index 9c298f71a..6bd0ce83d 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/DimenResources.kt @@ -19,6 +19,7 @@ import splitties.init.appCtx inline fun Context.dimen(@DimenRes dimenResId: Int): Float = resources.getDimension(dimenResId) inline fun Fragment.dimen(@DimenRes dimenResId: Int) = context!!.dimen(dimenResId) inline fun View.dimen(@DimenRes dimenResId: Int) = context.dimen(dimenResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -28,12 +29,14 @@ inline fun View.dimen(@DimenRes dimenResId: Int) = context.dimen(dimenResId) */ inline fun appDimen(@DimenRes dimenResId: Int) = appCtx.dimen(dimenResId) + inline fun Context.dimenPxSize( @DimenRes dimenResId: Int ): Int = resources.getDimensionPixelSize(dimenResId) inline fun Fragment.dimenPxSize(@DimenRes dimenResId: Int) = context!!.dimenPxSize(dimenResId) inline fun View.dimenPxSize(@DimenRes dimenResId: Int) = context.dimenPxSize(dimenResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -43,12 +46,14 @@ inline fun View.dimenPxSize(@DimenRes dimenResId: Int) = context.dimenPxSize(dim */ inline fun appDimenPxSize(@DimenRes dimenResId: Int) = appCtx.dimenPxSize(dimenResId) + inline fun Context.dimenPxOffset( @DimenRes dimenResId: Int ): Int = resources.getDimensionPixelOffset(dimenResId) inline fun Fragment.dimenPxOffset(@DimenRes dimenResId: Int) = context!!.dimenPxOffset(dimenResId) inline fun View.dimenPxOffset(@DimenRes dimenResId: Int) = context.dimenPxOffset(dimenResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -75,6 +80,7 @@ fun Context.styledDimen(@AttrRes attr: Int): Float = withResolvedThemeAttribute( inline fun Fragment.styledDimen(@AttrRes attr: Int) = context!!.styledDimen(attr) inline fun View.styledDimen(@AttrRes attr: Int) = context.styledDimen(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -92,6 +98,7 @@ fun Context.styledDimenPxSize(@AttrRes attr: Int): Int = withResolvedThemeAttrib inline fun Fragment.styledDimenPxSize(@AttrRes attr: Int) = context!!.styledDimenPxSize(attr) inline fun View.styledDimenPxSize(@AttrRes attr: Int) = context.styledDimenPxSize(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -101,6 +108,7 @@ inline fun View.styledDimenPxSize(@AttrRes attr: Int) = context.styledDimenPxSiz */ inline fun appStyledDimenPxSize(@AttrRes attr: Int) = appCtx.styledDimenPxSize(attr) + fun Context.styledDimenPxOffset( @AttrRes attr: Int ): Int = withResolvedThemeAttribute(attr) { @@ -110,6 +118,7 @@ fun Context.styledDimenPxOffset( inline fun Fragment.styledDimenPxOffset(@AttrRes attr: Int) = context!!.styledDimenPxOffset(attr) inline fun View.styledDimenPxOffset(@AttrRes attr: Int) = context.styledDimenPxOffset(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not From 970ab76e8fa422496298b655c80585a1d3cb62c1 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:38:14 +0200 Subject: [PATCH 09/19] Add line break for better clarity --- .../androidMain/kotlin/splitties/resources/DrawableResources.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/DrawableResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/DrawableResources.kt index a737c0848..ac6474d6c 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/DrawableResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/DrawableResources.kt @@ -52,6 +52,7 @@ inline fun View.drawable(@DrawableRes drawableResId: Int) = context.drawable(dra */ inline fun appDrawable(@DrawableRes drawableResId: Int) = appCtx.drawable(drawableResId) + // Styled resources below fun Context.styledDrawable(@AttrRes attr: Int): Drawable? = drawable(resolveThemeAttribute(attr)) From 5b95c4bc72158c285e459a5a6a112d1f2ed08a46 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Mon, 2 Aug 2021 21:39:13 +0200 Subject: [PATCH 10/19] Add line breaks for better clarity --- .../kotlin/splitties/resources/PrimitiveResources.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt index b1833ad9b..b31d93a5b 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt @@ -27,6 +27,7 @@ inline fun View.bool(@BoolRes boolResId: Int) = context.bool(boolResId) */ inline fun appBool(@BoolRes boolResId: Int) = appCtx.bool(boolResId) + inline fun Context.int(@IntegerRes intResId: Int): Int = resources.getInteger(intResId) inline fun Fragment.int(@IntegerRes intResId: Int) = context!!.int(intResId) inline fun View.int(@IntegerRes intResId: Int) = context.int(intResId) @@ -39,6 +40,7 @@ inline fun View.int(@IntegerRes intResId: Int) = context.int(intResId) */ inline fun appInt(@IntegerRes intResId: Int) = appCtx.int(intResId) + inline fun Context.intArray( @ArrayRes intArrayResId: Int ): IntArray = resources.getIntArray(intArrayResId) @@ -54,6 +56,7 @@ inline fun View.intArray(@ArrayRes intArrayResId: Int) = context.intArray(intArr */ inline fun appIntArray(@ArrayRes intArrayResId: Int) = appCtx.intArray(intArrayResId) + // Styled resources below fun Context.styledBool(@AttrRes attr: Int): Boolean = bool(resolveThemeAttribute(attr)) @@ -69,6 +72,7 @@ inline fun View.styledBool(@AttrRes attr: Int) = context.styledBool(attr) */ inline fun appStyledBool(@AttrRes attr: Int) = appCtx.styledBool(attr) + fun Context.styledInt(@AttrRes attr: Int): Int = int(resolveThemeAttribute(attr)) inline fun Fragment.styledInt(@AttrRes attr: Int) = context!!.styledInt(attr) inline fun View.styledInt(@AttrRes attr: Int) = context.styledInt(attr) From 55120b07136eccc34a7d5c155bcaab0261b86f47 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 09:34:48 +0200 Subject: [PATCH 11/19] Fix styledBool to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another bool resource, and it would crash if the bool value was set inline. Now, these cases are properly handled and return the correct value. --- .../splitties/resources/PrimitiveResources.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt index b31d93a5b..2d9eb03f2 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt @@ -3,16 +3,19 @@ */ @file:Suppress("NOTHING_TO_INLINE") +@file:OptIn(InternalSplittiesApi::class) package splitties.resources import android.content.Context +import android.util.TypedValue import android.view.View import androidx.annotation.ArrayRes import androidx.annotation.AttrRes import androidx.annotation.BoolRes import androidx.annotation.IntegerRes import androidx.fragment.app.Fragment +import splitties.experimental.InternalSplittiesApi import splitties.init.appCtx inline fun Context.bool(@BoolRes boolResId: Int): Boolean = resources.getBoolean(boolResId) @@ -59,7 +62,16 @@ inline fun appIntArray(@ArrayRes intArrayResId: Int) = appCtx.intArray(intArrayR // Styled resources below -fun Context.styledBool(@AttrRes attr: Int): Boolean = bool(resolveThemeAttribute(attr)) +fun Context.styledBool(@AttrRes attr: Int): Boolean = withResolvedThemeAttribute(attr) { + require(type == TypedValue.TYPE_INT_BOOLEAN) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "bool") + } + when (val value = data) { + 0 -> false + 1 -> true + else -> error("Expected 0 or 1 but got $value") + } +} inline fun Fragment.styledBool(@AttrRes attr: Int) = context!!.styledBool(attr) inline fun View.styledBool(@AttrRes attr: Int) = context.styledBool(attr) From 1819506e2c02386a64d65bf0d04c54353c914461 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 09:35:13 +0200 Subject: [PATCH 12/19] Fix styledInt to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another int resource, and it would crash if the integer value was set inline. Now, these cases are properly handled and return the correct value. --- .../kotlin/splitties/resources/PrimitiveResources.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt index 2d9eb03f2..c7f98557e 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/PrimitiveResources.kt @@ -85,7 +85,13 @@ inline fun View.styledBool(@AttrRes attr: Int) = context.styledBool(attr) inline fun appStyledBool(@AttrRes attr: Int) = appCtx.styledBool(attr) -fun Context.styledInt(@AttrRes attr: Int): Int = int(resolveThemeAttribute(attr)) +fun Context.styledInt(@AttrRes attr: Int): Int = withResolvedThemeAttribute(attr) { + require(type == TypedValue.TYPE_INT_DEC || type == TypedValue.TYPE_INT_HEX) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "int") + } + data +} + inline fun Fragment.styledInt(@AttrRes attr: Int) = context!!.styledInt(attr) inline fun View.styledInt(@AttrRes attr: Int) = context.styledInt(attr) /** From 82ec9e0906cfa3a0d89f2064c8b8bcf7889fbe89 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 10:30:13 +0200 Subject: [PATCH 13/19] Fix nullability of several functions The following functions never returned null, so their signature has been updated to reflect that fact. - styledTxt - styledStr - styledTxtArray --- .../kotlin/splitties/resources/TextResources.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt index 8ec1646f1..0606bf067 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt @@ -180,7 +180,7 @@ inline fun appStrArray(@ArrayRes stringResId: Int) = appCtx.strArray(stringResId // Styled resources below -fun Context.styledTxt(@AttrRes attr: Int): CharSequence? = txt(resolveThemeAttribute(attr)) +fun Context.styledTxt(@AttrRes attr: Int): CharSequence = txt(resolveThemeAttribute(attr)) inline fun Fragment.styledTxt(@AttrRes attr: Int) = context!!.styledTxt(attr) inline fun View.styledTxt(@AttrRes attr: Int) = context.styledTxt(attr) /** @@ -192,7 +192,7 @@ inline fun View.styledTxt(@AttrRes attr: Int) = context.styledTxt(attr) */ inline fun appStyledTxt(@AttrRes attr: Int) = appCtx.styledTxt(attr) -fun Context.styledStr(@AttrRes attr: Int): String? = str(resolveThemeAttribute(attr)) +fun Context.styledStr(@AttrRes attr: Int): String = str(resolveThemeAttribute(attr)) inline fun Fragment.styledStr(@AttrRes attr: Int) = context!!.styledStr(attr) inline fun View.styledStr(@AttrRes attr: Int) = context.styledStr(attr) /** @@ -207,7 +207,7 @@ inline fun appStyledStr(@AttrRes attr: Int) = appCtx.styledStr(attr) fun Context.styledStr( @AttrRes attr: Int, vararg formatArgs: Any? -): String? = str(resolveThemeAttribute(attr), *formatArgs) +): String = str(resolveThemeAttribute(attr), *formatArgs) inline fun Fragment.styledStr( @AttrRes attr: Int, @@ -233,7 +233,7 @@ inline fun appStyledStr( fun Context.styledTxtArray( @AttrRes attr: Int -): Array? = txtArray(resolveThemeAttribute(attr)) +): Array = txtArray(resolveThemeAttribute(attr)) inline fun Fragment.styledTxtArray(@AttrRes attr: Int) = context!!.styledTxtArray(attr) inline fun View.styledTxtArray(@AttrRes attr: Int) = context.styledTxtArray(attr) From 3986f2bc71a0d6ec57346cec4000d26fb465aa15 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 16:32:41 +0200 Subject: [PATCH 14/19] Fix styledTxt to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another string resource, and it would crash if the text was set inline. Now, these cases are properly handled and return the correct text. --- .../kotlin/splitties/resources/TextResources.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt index 0606bf067..f48b0e629 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt @@ -3,16 +3,20 @@ */ @file:Suppress("NOTHING_TO_INLINE") +@file:OptIn(InternalSplittiesApi::class) package splitties.resources import android.content.Context +import android.os.Build.VERSION.SDK_INT +import android.util.TypedValue import android.view.View import androidx.annotation.ArrayRes import androidx.annotation.AttrRes import androidx.annotation.PluralsRes import androidx.annotation.StringRes import androidx.fragment.app.Fragment +import splitties.experimental.InternalSplittiesApi import splitties.init.appCtx inline fun Context.txt(@StringRes stringResId: Int): CharSequence = resources.getText(stringResId) @@ -178,9 +182,19 @@ inline fun View.strArray(@ArrayRes stringResId: Int) = context.strArray(stringRe */ inline fun appStrArray(@ArrayRes stringResId: Int) = appCtx.strArray(stringResId) +private fun TypedValue.checkOfStringType() { + require(type == TypedValue.TYPE_STRING) { + unexpectedThemeAttributeTypeErrorMessage(expectedKind = "string") + } +} + // Styled resources below -fun Context.styledTxt(@AttrRes attr: Int): CharSequence = txt(resolveThemeAttribute(attr)) +fun Context.styledTxt(@AttrRes attr: Int): CharSequence = withResolvedThemeAttribute(attr) { + checkOfStringType() + string +} + inline fun Fragment.styledTxt(@AttrRes attr: Int) = context!!.styledTxt(attr) inline fun View.styledTxt(@AttrRes attr: Int) = context.styledTxt(attr) /** From 3254a27668e2c59f5e94ea16e6dbf914885d8cca Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 16:33:19 +0200 Subject: [PATCH 15/19] Fix styledStr to work with all possible cases Before, it'd only work when the theme attribute was set to a reference to another string resource, and it would crash if the text was set inline. Now, these cases are properly handled and return the correct text. --- .../kotlin/splitties/resources/TextResources.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt index f48b0e629..8d9e4b5df 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt @@ -206,7 +206,12 @@ inline fun View.styledTxt(@AttrRes attr: Int) = context.styledTxt(attr) */ inline fun appStyledTxt(@AttrRes attr: Int) = appCtx.styledTxt(attr) -fun Context.styledStr(@AttrRes attr: Int): String = str(resolveThemeAttribute(attr)) + +fun Context.styledStr(@AttrRes attr: Int): String = withResolvedThemeAttribute(attr) { + checkOfStringType() + string.toString() +} + inline fun Fragment.styledStr(@AttrRes attr: Int) = context!!.styledStr(attr) inline fun View.styledStr(@AttrRes attr: Int) = context.styledStr(attr) /** @@ -221,7 +226,13 @@ inline fun appStyledStr(@AttrRes attr: Int) = appCtx.styledStr(attr) fun Context.styledStr( @AttrRes attr: Int, vararg formatArgs: Any? -): String = str(resolveThemeAttribute(attr), *formatArgs) +): String = withResolvedThemeAttribute(attr) { + checkOfStringType() + val locale = resources.configuration.let { + if (SDK_INT >= 24) it.locales[0] else @Suppress("deprecation") it.locale + } + String.format(locale, string.toString(), *formatArgs) +} inline fun Fragment.styledStr( @AttrRes attr: Int, From d2acc1524bf2527b3934ca7efa1255b594c9790b Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 16:33:31 +0200 Subject: [PATCH 16/19] Add line breaks for better clarity --- .../kotlin/splitties/resources/TextResources.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt index 8d9e4b5df..f7679fdaf 100644 --- a/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt +++ b/modules/resources/src/androidMain/kotlin/splitties/resources/TextResources.kt @@ -22,6 +22,7 @@ import splitties.init.appCtx inline fun Context.txt(@StringRes stringResId: Int): CharSequence = resources.getText(stringResId) inline fun Fragment.txt(@StringRes stringResId: Int) = context!!.txt(stringResId) inline fun View.txt(@StringRes stringResId: Int) = context.txt(stringResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -61,6 +62,7 @@ inline fun appStr( inline fun Context.str(@StringRes stringResId: Int): String = resources.getString(stringResId) inline fun Fragment.str(@StringRes stringResId: Int) = context!!.str(stringResId) inline fun View.str(@StringRes stringResId: Int) = context.str(stringResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -158,6 +160,7 @@ inline fun Context.txtArray( inline fun Fragment.txtArray(@ArrayRes stringResId: Int) = context!!.txtArray(stringResId) inline fun View.txtArray(@ArrayRes stringResId: Int) = context.txtArray(stringResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -173,6 +176,7 @@ inline fun Context.strArray( inline fun Fragment.strArray(@ArrayRes stringResId: Int) = context!!.strArray(stringResId) inline fun View.strArray(@ArrayRes stringResId: Int) = context.strArray(stringResId) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -197,6 +201,7 @@ fun Context.styledTxt(@AttrRes attr: Int): CharSequence = withResolvedThemeAttri inline fun Fragment.styledTxt(@AttrRes attr: Int) = context!!.styledTxt(attr) inline fun View.styledTxt(@AttrRes attr: Int) = context.styledTxt(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -214,6 +219,7 @@ fun Context.styledStr(@AttrRes attr: Int): String = withResolvedThemeAttribute(a inline fun Fragment.styledStr(@AttrRes attr: Int) = context!!.styledStr(attr) inline fun View.styledStr(@AttrRes attr: Int) = context.styledStr(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not @@ -223,6 +229,7 @@ inline fun View.styledStr(@AttrRes attr: Int) = context.styledStr(attr) */ inline fun appStyledStr(@AttrRes attr: Int) = appCtx.styledStr(attr) + fun Context.styledStr( @AttrRes attr: Int, vararg formatArgs: Any? @@ -256,12 +263,14 @@ inline fun appStyledStr( vararg formatArgs: Any? ) = appCtx.styledStr(attr, *formatArgs) + fun Context.styledTxtArray( @AttrRes attr: Int ): Array = txtArray(resolveThemeAttribute(attr)) inline fun Fragment.styledTxtArray(@AttrRes attr: Int) = context!!.styledTxtArray(attr) inline fun View.styledTxtArray(@AttrRes attr: Int) = context.styledTxtArray(attr) + /** * Use this method for non configuration dependent resources when you don't have a [Context] * or when you're calling it from an Activity or a Fragment member (as the Context is not From cf79b577172fe74dee4030ece9505f71baf5ac73 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 16:34:37 +0200 Subject: [PATCH 17/19] Update CHANGELOG --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e570cca4..07a2bfca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Change log for Splitties +## [Unreleased] Version 3.0.0-rc02 (2021-08-03) + +Compiled with Kotlin 1.5.21 and kotlinx.coroutines 1.5.1-native-mt. + +### Resources + +#### Fixes + +By resolving a subtle issue that could break IDE preview, the version 3.0.0-alpha07 of Splitties also broke the `styledColor` function and some other in come cases. If you had a color theme attribute and had a theme that was setting its value, pointing to another color resource, you'd be in luck. However, if the color value was set inline, right into the theme, it'd crash ([as you can see in this issue](https://github.com/LouisCAD/Splitties/issues/258)). This release fixes this kind of problem for all the +affected functions: +- `styledColor` +- `styledColorSL` +- `styledDimen` +- `styledDimenPxSize` +- `styledDimenPxOffset` +- `styledBool` +- `styledInt` +- `styledTxt` +- `styledStr` + ## Version 3.0.0-rc01 (2021-08-01) Compiled with Kotlin 1.5.21 and kotlinx.coroutines 1.5.1-native-mt. From 6b4867901fccbd30138dc2354013c3436e59575d Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 19:23:56 +0200 Subject: [PATCH 18/19] Update the local release branch after release PR merge --- Releasing.main.kts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Releasing.main.kts b/Releasing.main.kts index 38e138089..5948e485f 100755 --- a/Releasing.main.kts +++ b/Releasing.main.kts @@ -7,7 +7,7 @@ @file:Repository("https://repo.maven.apache.org/maven2/") //@file:Repository("https://oss.sonatype.org/content/repositories/snapshots") //@file:Repository("file:///Users/louiscad/.m2/repository") -@file:DependsOn("com.louiscad.incubator:lib-publishing-helpers:0.2.2") +@file:DependsOn("com.louiscad.incubator:lib-publishing-helpers:0.2.3") import Releasing_main.CiReleaseFailureCause.* import java.io.File @@ -288,6 +288,10 @@ fun CliUi.runReleaseStep(step: ReleaseStep): Unit = when (step) { } `Request PR merge` -> { requestManualAction("Merge the pull request for the new version on GitHub.") + printInfo("Now that the pull request has been merged into the release branch on GitHub,") + printInfo("we are going to update our local release branch") + requestUserConfirmation("Ready?") + git.updateBranchFromOrigin(targetBranch = "release") } `Request GitHub release publication` -> { requestManualAction("Publish release on GitHub with the changelog.") From 8028431a95f8e7e6ae15d1d69f70d4cf55737e1f Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Tue, 3 Aug 2021 19:24:25 +0200 Subject: [PATCH 19/19] Link the GitHub releases page in Releasing.main.kts --- Releasing.main.kts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Releasing.main.kts b/Releasing.main.kts index 5948e485f..c8e6324c8 100755 --- a/Releasing.main.kts +++ b/Releasing.main.kts @@ -294,7 +294,10 @@ fun CliUi.runReleaseStep(step: ReleaseStep): Unit = when (step) { git.updateBranchFromOrigin(targetBranch = "release") } `Request GitHub release publication` -> { - requestManualAction("Publish release on GitHub with the changelog.") + printInfo("It's now time to publish the release on GitHub, so people get notified.") + printInfo("Copy the section of this release from the CHANGELOG file, and head over to the following url to proceed:") + printInfo("https://github.com/LouisCAD/Splitties/releases/new") + requestManualAction("Publish the release ${OngoingRelease.newVersion} on GitHub with the changelog.") } `Change this library version back to a SNAPSHOT` -> { val newVersion = Version(OngoingRelease.newVersion)