diff --git a/README.md b/README.md index 80e0436..f86aa75 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ [![CI](https://github.com/ioki-mobility/TextRef/actions/workflows/test-lib.yml/badge.svg)](https://github.com/ioki-mobility/TextRef/actions/workflows/test-lib.yml) [![Maven Central](https://img.shields.io/maven-central/v/com.ioki.textref/textref?labelColor=%2324292E&color=%233246c8)](https://central.sonatype.com/namespace/com.ioki.textref) -[![javadoc](https://javadoc.io/badge2/com.ioki.textref/textref/javadoc.svg?labelColor=%2324292E&color=%236eaaff)](https://javadoc.io/doc/com.ioki.textref) +[![javadoc](https://javadoc.io/badge2/com.ioki.textref/textref/javadoc.svg?labelColor=%2324292E&color=%236eaaff)](https://javadoc.io/doc/com.ioki.textref) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg?labelColor=%2324292E&color=%23d11064)](https://github.com/ioki-mobility/TextRef/blob/master/LICENSE.md) ## What? TextRef is an abstraction over Android text. It wraps a `String` or a string/plurals resource ID. -With the help of an Android `Context` a final `String` can be resolved. Format args are supported too! +With the help of Android `Context` or `Resources` a final `String` can be resolved. Format args are supported too! ## How? @@ -24,6 +24,7 @@ TextRef.string("The arguments are %d and %s", 5, "foo") **Resolve** ```kotlin val text: String = textRef.resolve(context) +val textFromResources: String = textRef.resolve(resources) ``` ## Why? diff --git a/compose/src/main/kotlin/com/ioki/textref/compose/TextRef.kt b/compose/src/main/kotlin/com/ioki/textref/compose/TextRef.kt index 8f17ea5..f54fd31 100644 --- a/compose/src/main/kotlin/com/ioki/textref/compose/TextRef.kt +++ b/compose/src/main/kotlin/com/ioki/textref/compose/TextRef.kt @@ -1,11 +1,11 @@ package com.ioki.textref.compose import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import com.ioki.textref.TextRef @Composable -fun textRef(textRef: TextRef): String = textRef.resolve(LocalContext.current) +fun textRef(textRef: TextRef): String = textRef.resolve(LocalResources.current) @Composable -fun textRefOrNull(textRef: TextRef?): String? = textRef?.resolve(LocalContext.current) +fun textRefOrNull(textRef: TextRef?): String? = textRef?.resolve(LocalResources.current) diff --git a/core/src/main/java/com/ioki/textref/TextRef.kt b/core/src/main/java/com/ioki/textref/TextRef.kt index 0d9d985..84bf829 100644 --- a/core/src/main/java/com/ioki/textref/TextRef.kt +++ b/core/src/main/java/com/ioki/textref/TextRef.kt @@ -1,6 +1,7 @@ package com.ioki.textref import android.content.Context +import android.content.res.Resources import android.os.Parcel import android.os.ParcelFormatException import android.os.Parcelable @@ -26,30 +27,40 @@ class TextRef private constructor( * @param context The context used to resolve the string if created from a [StringRes] ID * @return A String, formatted with any args passed on creation */ - fun resolve(context: Context): String { - val args = processArgs(context) + fun resolve(context: Context): String = resolve(context.resources) + + /** + * Resolves the contents of the TextRef to a [String]. + * + * Any format args passed on creation will be used to format the string. + * + * @param resources The resources used to resolve string and plurals resource IDs + * @return A String, formatted with any args passed on creation + */ + fun resolve(resources: Resources): String { + val args = processArgs(resources) return when { value is String && args.isEmpty() -> value value is String -> value.format(*args) - value is Int && args.isEmpty() -> context.getString(value) - value is Int -> context.getString(value, *args) + value is Int && args.isEmpty() -> resources.getString(value) + value is Int -> resources.getString(value, *args) value is Pair<*, *> && args.isEmpty() -> { val id = value.first as Int val quantity = value.second as Int - context.resources.getQuantityString(id, quantity) + resources.getQuantityString(id, quantity) } value is Pair<*, *> -> { val id = value.first as Int val quantity = value.second as Int - context.resources.getQuantityString(id, quantity, *args) + resources.getQuantityString(id, quantity, *args) } else -> error("Unsupported type: ${value.javaClass.name}") } } - private fun processArgs(context: Context): Array = + private fun processArgs(resources: Resources): Array = args.map { - if (it is TextRef) it.resolve(context) + if (it is TextRef) it.resolve(resources) else it }.toTypedArray() diff --git a/core/src/test/java/com/ioki/textref/TextRefTest.kt b/core/src/test/java/com/ioki/textref/TextRefTest.kt index 03570be..d8d12eb 100644 --- a/core/src/test/java/com/ioki/textref/TextRefTest.kt +++ b/core/src/test/java/com/ioki/textref/TextRefTest.kt @@ -43,6 +43,7 @@ class TextRefTest { private val formattedIdWithIntArg = "biz $intArg" private val formattedIdWithStringArg = "biz bar" private val formattedIdWithTwoArgs = "biz $intArg bar" + private val formattedIdWithTextRefArg = "biz $formattedIdWithoutArg" private val formattedPluralWithoutArg = "boz" private val formattedPluralWithIntArg = "boz $intArg" @@ -52,10 +53,11 @@ class TextRefTest { @Before fun setUp() { `when`(mockContext.resources).thenReturn(mockResources) - `when`(mockContext.getString(idWithoutArg)).thenReturn(formattedIdWithoutArg) - `when`(mockContext.getString(idWithIntArg, intArg)).thenReturn(formattedIdWithIntArg) - `when`(mockContext.getString(idWithStringArg, stringArg)).thenReturn(formattedIdWithStringArg) - `when`(mockContext.getString(idWithTwoArgs, intArg, stringArg)).thenReturn(formattedIdWithTwoArgs) + `when`(mockResources.getString(idWithoutArg)).thenReturn(formattedIdWithoutArg) + `when`(mockResources.getString(idWithIntArg, intArg)).thenReturn(formattedIdWithIntArg) + `when`(mockResources.getString(idWithStringArg, stringArg)).thenReturn(formattedIdWithStringArg) + `when`(mockResources.getString(idWithStringArg, formattedIdWithoutArg)).thenReturn(formattedIdWithTextRefArg) + `when`(mockResources.getString(idWithTwoArgs, intArg, stringArg)).thenReturn(formattedIdWithTwoArgs) `when`(mockResources.getQuantityString(idWithoutArg, quantity)).thenReturn(formattedPluralWithoutArg) `when`(mockResources.getQuantityString(idWithIntArg, quantity, intArg)).thenReturn(formattedPluralWithIntArg) `when`(mockResources.getQuantityString(idWithStringArg, quantity, stringArg)).thenReturn( @@ -87,6 +89,13 @@ class TextRefTest { assertThat(result).isEqualTo(formattedIdWithoutArg) } + @Test + fun resolve_createdWithStringResAndResources_resultIsCorrect() { + val result = TextRef.stringRes(idWithoutArg).resolve(mockResources) + + assertThat(result).isEqualTo(formattedIdWithoutArg) + } + @Test fun createdWithNullStringRes_resultIsEmptyTextRef() { val result = TextRef.stringRes(null) @@ -101,6 +110,13 @@ class TextRefTest { assertThat(result).isEqualTo(formattedPluralWithoutArg) } + @Test + fun resolve_createdWithPluralsResAndResources_resultIsCorrect() { + val result = TextRef.pluralsRes(idWithoutArg, quantity).resolve(mockResources) + + assertThat(result).isEqualTo(formattedPluralWithoutArg) + } + @Test fun createdWithNullPluralsRes_resultIsEmptyTextRef() { val result = TextRef.pluralsRes(null, quantity) @@ -157,6 +173,13 @@ class TextRefTest { assertThat(result).isEqualTo(formattedIdWithStringArg) } + @Test + fun resolve_createdWithStringResAndTextRefArgAndResources_resultIsCorrect() { + val result = TextRef.stringRes(idWithStringArg, TextRef.stringRes(idWithoutArg)).resolve(mockResources) + + assertThat(result).isEqualTo(formattedIdWithTextRefArg) + } + @Test fun resolve_createdWithStringResAndTwoArgs_resultIsCorrect() { val result = TextRef.stringRes(idWithTwoArgs, intArg, stringArg).resolve(mockContext)