From 1f383869e6adba0ff141e4d538021df256c6b408 Mon Sep 17 00:00:00 2001 From: VeldtJumper Date: Fri, 5 Jun 2026 12:15:00 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20comprehensive=20improvements=20?= =?UTF-8?q?=E2=80=94=20new=20resource=20types,=20API=20enhancements,=20cod?= =?UTF-8?q?e=20quality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New resource types: - fraction (addFraction) - int-quantity / int-quantity-array (addIntQuantity, addIntQuantityArray) - color-array (addColorArray) - bool-array (addBoolArray) - reference (addReference with Reference data class) API improvements: - addColor(name, Int) overload for integer color values - buildBytes() for ByteArray output - create(file, indent, elementType) for font-family files - translatable attribute on addStringArray and addTypedArray - format attribute on addStyle - FontFamilyRes data class with addFontFamilyRes method Code quality: - Replace manual format string concatenation with joinToString - Remove stale 'Does this mess up ordering?' comments - Expand add(type, name, value) to support FRACTION, COLOR_ARRAY, BOOL_ARRAY - Update add(type) doc to reflect expanded support Tests: 13 new test files, 73 total tests passing README: Updated with all new features and examples --- README.md | 84 ++++++- .../commit451/resourcespoet/FontFamilyRes.kt | 14 ++ .../com/commit451/resourcespoet/Quantity.kt | 12 + .../com/commit451/resourcespoet/Reference.kt | 17 ++ .../commit451/resourcespoet/ResourcesPoet.kt | 238 ++++++++++++++++-- .../com/commit451/resourcespoet/Type.kt | 8 +- .../resourcespoet/AddColorIntTest.kt | 35 +++ .../commit451/resourcespoet/BoolArrayTest.kt | 25 ++ .../resourcespoet/BuildByteArrayTest.kt | 34 +++ .../commit451/resourcespoet/ColorArrayTest.kt | 25 ++ .../CreateFileWithElementTypeTest.kt | 33 +++ .../resourcespoet/FontFamilyResTest.kt | 25 ++ .../commit451/resourcespoet/FractionTest.kt | 25 ++ .../resourcespoet/IntQuantityArrayTest.kt | 33 +++ .../resourcespoet/IntQuantityTest.kt | 49 ++++ .../commit451/resourcespoet/ReferenceTest.kt | 41 +++ .../StringArrayTranslatableTest.kt | 25 ++ .../resourcespoet/StyleFormatTest.kt | 39 +++ .../TypedArrayTranslatableTest.kt | 25 ++ src/test/resources/bool_array.xml | 7 + .../resources/bool_array_tools_ignore.xml | 7 + src/test/resources/color_array.xml | 7 + .../resources/color_array_tools_ignore.xml | 7 + src/test/resources/color_int.xml | 4 + src/test/resources/color_int_tools_ignore.xml | 4 + .../create_file_with_element_type.xml | 4 + src/test/resources/font_family_res.xml | 4 + .../font_family_res_tools_ignore.xml | 4 + src/test/resources/fraction.xml | 4 + src/test/resources/fraction_tools_ignore.xml | 4 + src/test/resources/int_quantity.xml | 7 + src/test/resources/int_quantity_all.xml | 11 + src/test/resources/int_quantity_array.xml | 7 + .../int_quantity_array_tools_ignore.xml | 7 + .../resources/int_quantity_tools_ignore.xml | 7 + src/test/resources/reference.xml | 4 + src/test/resources/reference_generic.xml | 4 + src/test/resources/reference_tools_ignore.xml | 4 + .../string_array_translatable_false.xml | 7 + src/test/resources/style_format.xml | 6 + .../resources/style_format_tools_ignore.xml | 6 + src/test/resources/style_without_format.xml | 6 + .../typed_array_translatable_false.xml | 7 + 43 files changed, 908 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/com/commit451/resourcespoet/FontFamilyRes.kt create mode 100644 src/main/kotlin/com/commit451/resourcespoet/Quantity.kt create mode 100644 src/main/kotlin/com/commit451/resourcespoet/Reference.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/AddColorIntTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/BoolArrayTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/BuildByteArrayTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/ColorArrayTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/CreateFileWithElementTypeTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/FontFamilyResTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/FractionTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/IntQuantityArrayTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/IntQuantityTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/ReferenceTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/StringArrayTranslatableTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/StyleFormatTest.kt create mode 100644 src/test/kotlin/com/commit451/resourcespoet/TypedArrayTranslatableTest.kt create mode 100644 src/test/resources/bool_array.xml create mode 100644 src/test/resources/bool_array_tools_ignore.xml create mode 100644 src/test/resources/color_array.xml create mode 100644 src/test/resources/color_array_tools_ignore.xml create mode 100644 src/test/resources/color_int.xml create mode 100644 src/test/resources/color_int_tools_ignore.xml create mode 100644 src/test/resources/create_file_with_element_type.xml create mode 100644 src/test/resources/font_family_res.xml create mode 100644 src/test/resources/font_family_res_tools_ignore.xml create mode 100644 src/test/resources/fraction.xml create mode 100644 src/test/resources/fraction_tools_ignore.xml create mode 100644 src/test/resources/int_quantity.xml create mode 100644 src/test/resources/int_quantity_all.xml create mode 100644 src/test/resources/int_quantity_array.xml create mode 100644 src/test/resources/int_quantity_array_tools_ignore.xml create mode 100644 src/test/resources/int_quantity_tools_ignore.xml create mode 100644 src/test/resources/reference.xml create mode 100644 src/test/resources/reference_generic.xml create mode 100644 src/test/resources/reference_tools_ignore.xml create mode 100644 src/test/resources/string_array_translatable_false.xml create mode 100644 src/test/resources/style_format.xml create mode 100644 src/test/resources/style_format_tools_ignore.xml create mode 100644 src/test/resources/style_without_format.xml create mode 100644 src/test/resources/typed_array_translatable_false.xml diff --git a/README.md b/README.md index bd4c6ca..394968b 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ look similar in usage: val poet = ResourcesPoet.create() .addBool("is_cool", true) .addColor("color_primary", "#FF0000") + .addColor("color_accent", 0xFF0000FF) // Int overload .addComment("This is a comment") .addDimension("margin", "2dp") .addDrawable("logo", "@drawable/logo") @@ -81,9 +82,16 @@ val poet = ResourcesPoet.create() .addIntegerArray("numbers", numbers) .addPlurals("songs", plurals) .addString("app_name", "Test") - .addStringArray("stuff", strings) + .addStringArray("stuff", strings, translatable = false) .addStyle("AppTheme.Dark", "Base.AppTheme.Dark") - .addTypedArray("some_typed_array", typedArray) + .addTypedArray("some_typed_array", typedArray, translatable = false) + .addFraction("width", "50%p") + .addIntQuantity("count", quantities) + .addIntQuantityArray("counts", quantities) + .addColorArray("colors", listOf("#FF0000", "#00FF00")) + .addBoolArray("flags", listOf(true, false)) + .addReference("my_ref", Reference("drawable", "logo")) + .addFontFamilyRes(FontFamilyRes("normal", "400", R.font.roboto)) ``` We do not allow configuration of more complicated resources like `drawable` and `anim` in the creation sense. @@ -124,6 +132,78 @@ val poet = ResourcesPoet.create() Top-level and per-element ignores can be combined — the top-level applies to all children, and per-element ignores override or add to it. +## Additional Features + +### Color with Int + +Add colors using an `Int` value (e.g., `R.color.primary`) instead of a hex string: + +```kotlin +val poet = ResourcesPoet.create() + .addColor("color_primary", 0xFF0000) // outputs #000000 + .addColor("color_accent", 0xFF0000FF) // outputs #0000FF (alpha stripped) +``` + +### Translatable Attribute on Arrays + +Mark string-arrays and typed-arrays as non-translatable: + +```kotlin +val poet = ResourcesPoet.create() + .addStringArray("countries", listOf("US", "UK"), translatable = false) + .addTypedArray("items", listOf("@string/a", "@string/b"), translatable = false) +``` + +### Style Format Attribute + +Add a `format` attribute to styles: + +```kotlin +val poet = ResourcesPoet.create() + .addStyle("MyStyle", parentRef = "Base.Style", + styleItems = listOf(StyleItem("android:background", "@color/white")), + format = "string|reference") +``` + +### Font Family with Resource ID + +Add font-family entries using an integer resource reference: + +```kotlin +val poet = ResourcesPoet.create() + .addFontFamilyRes(FontFamilyRes("normal", "400", R.font.roboto_regular)) +``` + +### Build to ByteArray + +Get the XML as a `ByteArray` for in-memory processing: + +```kotlin +val bytes: ByteArray = poet.buildBytes() +``` + +### Load Font-Family Files + +Use `create(file, indent, elementType)` to load existing font-family XML files: + +```kotlin +val file = File("res/font/my_fonts.xml") +val poet = ResourcesPoet.create(file, indent = true, elementType = ResourcesPoet.ELEMENT.FONT_FAMILIES) +``` + +## Adding Resource Comments + +You can add a `comment` attribute to `` elements. This comment is displayed in Android Studio's resource inspector, making it easier for translators and developers to understand the purpose of each string: + +```kotlin +val poet = ResourcesPoet.create() + .addString("dialog_close_button", "Close", comment = "Button to dismiss the dialog") +``` + +```xml +Close +``` + License -------- diff --git a/src/main/kotlin/com/commit451/resourcespoet/FontFamilyRes.kt b/src/main/kotlin/com/commit451/resourcespoet/FontFamilyRes.kt new file mode 100644 index 0000000..903e348 --- /dev/null +++ b/src/main/kotlin/com/commit451/resourcespoet/FontFamilyRes.kt @@ -0,0 +1,14 @@ +package com.commit451.resourcespoet + +/** + * Represents an Android font-family resource using an integer resource reference + * @param fontStyle the font style (e.g. "normal", "italic") + * @param fontWeight the font weight (e.g. "400", "700") + * @param fontRes the integer resource ID of the font + * See [the Android docs](https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml) + */ +data class FontFamilyRes( + val fontStyle: String, + val fontWeight: String, + val fontRes: Int +) diff --git a/src/main/kotlin/com/commit451/resourcespoet/Quantity.kt b/src/main/kotlin/com/commit451/resourcespoet/Quantity.kt new file mode 100644 index 0000000..32c1916 --- /dev/null +++ b/src/main/kotlin/com/commit451/resourcespoet/Quantity.kt @@ -0,0 +1,12 @@ +package com.commit451.resourcespoet + +/** + * Represents an Android int-quantity resource + * @param quantity the quantity (zero, one, two, few, many, other) + * @param value the integer value + * See [the Android docs](https://developer.android.com/guide/topics/resources/string-resource.html#Plurals) + */ +data class Quantity( + val quantity: Plural.Quantity, + val value: Int +) diff --git a/src/main/kotlin/com/commit451/resourcespoet/Reference.kt b/src/main/kotlin/com/commit451/resourcespoet/Reference.kt new file mode 100644 index 0000000..f3c72d1 --- /dev/null +++ b/src/main/kotlin/com/commit451/resourcespoet/Reference.kt @@ -0,0 +1,17 @@ +package com.commit451.resourcespoet + +/** + * Represents an Android @reference resource + * @param type the reference type (e.g. "drawable", "color"), or null for a generic reference + * @param name the resource name + */ +data class Reference( + val type: String? = null, + val name: String +) { + /** + * Format the reference as an Android resource reference string + * e.g. "@drawable/logo" or "@color/primary" or "@string/app_name" + */ + fun toValue(): String = "@${if (type != null) "$type/" else ""}$name" +} diff --git a/src/main/kotlin/com/commit451/resourcespoet/ResourcesPoet.kt b/src/main/kotlin/com/commit451/resourcespoet/ResourcesPoet.kt index 9370361..beaeb9e 100644 --- a/src/main/kotlin/com/commit451/resourcespoet/ResourcesPoet.kt +++ b/src/main/kotlin/com/commit451/resourcespoet/ResourcesPoet.kt @@ -64,11 +64,13 @@ class ResourcesPoet private constructor( * Creates a builder on top of the current resources XML file * * @param file the resources file you want to add to + * @param indent whether to use indentation + * @param elementType the type of resource element * @return poet */ - fun create(file: File, indent: Boolean = INDENT_DEFAULT): ResourcesPoet { + fun create(file: File, indent: Boolean = INDENT_DEFAULT, elementType: ELEMENT = ELEMENT.RESOURCES): ResourcesPoet { try { - return create(FileInputStream(file), indent) + return create(FileInputStream(file), indent, elementType) } catch (e: FileNotFoundException) { throw IllegalStateException( "Unable to parse the resource file you passed. Make sure it is properly formatted", @@ -158,13 +160,7 @@ class ResourcesPoet private constructor( val element = document.createElement(Type.ATTR.toString()) element.setAttribute("name", attr.name) if (!attr.formats.isEmpty()) { - var formatString = "" - for (format in attr.formats) { - formatString = formatString + format.toString() + "|" - } - //remove last | - formatString = formatString.substring(0, formatString.length - 1) - element.setAttribute("format", formatString) + element.setAttribute("format", attr.formats.joinToString("|") { it.toString() }) } setToolsIgnore(element, toolsIgnore) resourceElement.appendChild(element) @@ -220,6 +216,19 @@ class ResourcesPoet private constructor( return this } + /** + * Add a color to the XML file + * + * @param name the name + * @param value the color value as an Int (e.g. 0xFF0000 for #FF0000) + * @param toolsIgnore lint rule names to suppress (e.g., "UnusedResource") + * @return poet + */ + fun addColor(name: String, value: Int, toolsIgnore: String? = null): ResourcesPoet { + addColor(name, String.format("#%06X", 0xFFFFFF and value), toolsIgnore) + return this + } + /** * Add a comment to the XML file * @@ -352,7 +361,6 @@ class ResourcesPoet private constructor( element.setAttribute("name", name) setToolsIgnore(element, toolsIgnore) for (value in values) { - //Does this mess up the ordering? val valueElement = document.createElement("item") valueElement.appendChild(document.createTextNode(value)) element.appendChild(valueElement) @@ -379,7 +387,6 @@ class ResourcesPoet private constructor( element.setAttribute("name", name) setToolsIgnore(element, toolsIgnore) for (plural in plurals) { - //Does this mess up the ordering? val valueElement = document.createElement("item") valueElement.setAttribute("quantity", plural.quantity.toString()) valueElement.appendChild(document.createTextNode(plural.value)) @@ -417,19 +424,22 @@ class ResourcesPoet private constructor( * * @param name the name * @param values the value + * @param translatable whether this array should be translatable * @param toolsIgnore lint rule names to suppress (e.g., "UnusedResource") * @return poet */ - fun addStringArray(name: String, values: List, toolsIgnore: String? = null): ResourcesPoet { + fun addStringArray(name: String, values: List, translatable: Boolean = true, toolsIgnore: String? = null): ResourcesPoet { // // Country // United States // val element = document.createElement(Type.STRING_ARRAY.toString()) element.setAttribute("name", name) + if (!translatable) { + element.setAttribute("translatable", "false") + } setToolsIgnore(element, toolsIgnore) for (value in values) { - //Does this mess up the ordering? val valueElement = document.createElement("item") valueElement.appendChild(document.createTextNode(value)) element.appendChild(valueElement) @@ -444,16 +454,20 @@ class ResourcesPoet private constructor( * @param name the name * @param parentRef a ref to the style parent * @param styleItems list of style items + * @param format the format attribute for the style * @param toolsIgnore lint rule names to suppress (e.g., "UnusedResource") * @return poet */ - fun addStyle(name: String, parentRef: String? = null, styleItems: List? = null, toolsIgnore: String? = null): ResourcesPoet { + fun addStyle(name: String, parentRef: String? = null, styleItems: List? = null, format: String? = null, toolsIgnore: String? = null): ResourcesPoet { // + diff --git a/src/test/resources/style_format_tools_ignore.xml b/src/test/resources/style_format_tools_ignore.xml new file mode 100644 index 0000000..7051a9e --- /dev/null +++ b/src/test/resources/style_format_tools_ignore.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/test/resources/style_without_format.xml b/src/test/resources/style_without_format.xml new file mode 100644 index 0000000..b658b01 --- /dev/null +++ b/src/test/resources/style_without_format.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/test/resources/typed_array_translatable_false.xml b/src/test/resources/typed_array_translatable_false.xml new file mode 100644 index 0000000..bbc739b --- /dev/null +++ b/src/test/resources/typed_array_translatable_false.xml @@ -0,0 +1,7 @@ + + + + One + Two + +