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 + +