From dfccf7627c797417588aae6a7af1941fb943c8da Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 21:55:11 +0100 Subject: [PATCH 1/6] feat: add ImageProperties data model for image component configuration --- .../data/model/image/ImageProperties.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/data/model/image/ImageProperties.kt diff --git a/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/data/model/image/ImageProperties.kt b/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/data/model/image/ImageProperties.kt new file mode 100644 index 0000000..3568ee5 --- /dev/null +++ b/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/data/model/image/ImageProperties.kt @@ -0,0 +1,21 @@ +package com.github.codandotv.craftd.androidcore.data.model.image + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import com.github.codandotv.craftd.androidcore.data.model.action.ActionProperties +import com.github.codandotv.craftd.androidcore.domain.CraftDAlign +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@Immutable +@Stable +data class ImageProperties( + @SerialName("url") val url: String? = null, + @SerialName("contentDescription") val contentDescription: String? = null, + @SerialName("align") val align: CraftDAlign? = null, + @SerialName("fillMaxSize") val fillMaxSize: Boolean? = false, + @SerialName("aspectRatio") val aspectRatio: Float? = null, + @SerialName("contentScale") val contentScale: String? = null, + @SerialName("actionProperties") var actionProperties: ActionProperties? = null, +) From ac550c1cae5fe6455e02ed4dc729ddb8ffd7696c Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 22:00:01 +0100 Subject: [PATCH 2/6] Add image component implementation for both XML and Compose UI frameworks. --- .../craftd/compose/ui/image/CraftDImage.kt | 60 ++++++++++++ .../compose/ui/image/CraftDImageBuilder.kt | 20 ++++ .../presentation/CraftDComponentKey.kt | 4 +- .../xml/ui/image/CraftDImageComponent.kt | 91 +++++++++++++++++++ .../ui/image/CraftDImageComponentRender.kt | 32 +++++++ .../craftd-xml/src/main/res/layout/image.xml | 16 ++++ 6 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt create mode 100644 android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt create mode 100644 android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt create mode 100644 android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponentRender.kt create mode 100644 android_kmp/craftd-xml/src/main/res/layout/image.xml diff --git a/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt new file mode 100644 index 0000000..e6e7d8d --- /dev/null +++ b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt @@ -0,0 +1,60 @@ +package com.github.codandotv.craftd.compose.ui.image + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import coil3.compose.AsyncImage +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.compose.extensions.toArrangementCompose + +@Composable +fun CraftDImage( + imageProperties: ImageProperties, + modifier: Modifier = Modifier, + clickable: (() -> Unit)? = null, +) { + val modifierCustom = clickable?.let { Modifier.clickable { clickable.invoke() } } ?: modifier + + Row( + horizontalArrangement = imageProperties.align.toArrangementCompose(), + modifier = Modifier.fillMaxWidth() + ) { + AsyncImage( + model = imageProperties.url, + contentDescription = imageProperties.contentDescription, + modifier = + modifierCustom + .then( + if (imageProperties.fillMaxSize == true) { + Modifier.fillMaxSize() + } else { + Modifier + } + ) + .then( + imageProperties.aspectRatio?.let { + Modifier.aspectRatio(it) + } + ?: Modifier + ), + contentScale = imageProperties.contentScale?.toContentScale() ?: ContentScale.Fit + ) + } +} + +private fun String.toContentScale(): ContentScale = + when (this.lowercase()) { + "crop" -> ContentScale.Crop + "fit" -> ContentScale.Fit + "fillbounds" -> ContentScale.FillBounds + "fillwidth" -> ContentScale.FillWidth + "fillheight" -> ContentScale.FillHeight + "inside" -> ContentScale.Inside + "none" -> ContentScale.None + else -> ContentScale.Fit + } diff --git a/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt new file mode 100644 index 0000000..4dcd382 --- /dev/null +++ b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt @@ -0,0 +1,20 @@ +package com.github.codandotv.craftd.compose.ui.image + +import androidx.compose.runtime.Composable +import com.github.codandotv.craftd.androidcore.data.convertToElement +import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.androidcore.presentation.CraftDComponentKey +import com.github.codandotv.craftd.androidcore.presentation.CraftDViewListener +import com.github.codandotv.craftd.compose.builder.CraftDBuilder + +class CraftDImageBuilder(override val key: String = CraftDComponentKey.IMAGE_COMPONENT.key) : + CraftDBuilder { + @Composable + override fun craft(model: SimpleProperties, listener: CraftDViewListener) { + val imageProperties = model.value.convertToElement() + imageProperties?.let { + CraftDImage(it) { imageProperties.actionProperties?.let { listener.invoke(it) } } + } + } +} diff --git a/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKey.kt b/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKey.kt index 92e7529..a6ffb19 100644 --- a/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKey.kt +++ b/android_kmp/craftd-core/src/commonMain/kotlin/com/github/codandotv/craftd/androidcore/presentation/CraftDComponentKey.kt @@ -1,10 +1,10 @@ package com.github.codandotv.craftd.androidcore.presentation - enum class CraftDComponentKey(val key: String) { TEXT_VIEW_COMPONENT("${CRAFT_D}TextView"), BUTTON_COMPONENT("${CRAFT_D}Button"), CHECK_BOX_COMPONENT("${CRAFT_D}CheckBox"), + IMAGE_COMPONENT("${CRAFT_D}Image"), } -internal const val CRAFT_D = "CraftD" \ No newline at end of file +internal const val CRAFT_D = "CraftD" diff --git a/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt new file mode 100644 index 0000000..be089f7 --- /dev/null +++ b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt @@ -0,0 +1,91 @@ +package com.github.codandotv.craftd.xml.ui.image + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.RelativeLayout +import coil3.load +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.androidcore.domain.CraftDAlign +import com.github.codandotv.craftd.xml.databinding.ImageBinding + +class CraftDImageComponent +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : RelativeLayout(context, attrs, defStyleAttr) { + private var binding: ImageBinding + + init { + binding = ImageBinding.inflate(LayoutInflater.from(context), this) + } + + fun setProperties(imageProperties: ImageProperties) { + imageProperties.url?.let { url -> binding.imageView.load(url) } + + imageProperties.contentDescription?.let { description -> + binding.imageView.contentDescription = description + } + + setupFillMaxSize(imageProperties) + + imageProperties.aspectRatio?.let { ratio -> + binding.imageView.adjustViewBounds = true + // AspectRatio can be handled via custom logic or ConstraintLayout + } + + imageProperties.contentScale?.let { scale -> + binding.imageView.scaleType = scale.toScaleType() + } + + imageProperties.align?.let { + binding.imageView.layoutParams = + LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + .apply { addRule(it.toRelativeLayoutParams()) } + } + } + + private fun setupFillMaxSize(imageProperties: ImageProperties) { + binding.imageView.layoutParams = + imageProperties.fillMaxSize?.let { isFillMaxSize -> + LayoutParams( + if (isFillMaxSize) { + ViewGroup.LayoutParams.MATCH_PARENT + } else { + ViewGroup.LayoutParams.WRAP_CONTENT + }, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + ?: LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + private fun CraftDAlign?.toRelativeLayoutParams(): Int = + when (this) { + CraftDAlign.CENTER -> CENTER_IN_PARENT + CraftDAlign.RIGHT -> ALIGN_PARENT_END + else -> ALIGN_PARENT_START + } + + private fun String.toScaleType(): ImageView.ScaleType = + when (this.lowercase()) { + "crop" -> ImageView.ScaleType.CENTER_CROP + "fit" -> ImageView.ScaleType.FIT_CENTER + "fillbounds" -> ImageView.ScaleType.FIT_XY + "fillwidth" -> ImageView.ScaleType.FIT_START + "fillheight" -> ImageView.ScaleType.FIT_END + "inside" -> ImageView.ScaleType.CENTER_INSIDE + "none" -> ImageView.ScaleType.CENTER + else -> ImageView.ScaleType.FIT_CENTER + } +} diff --git a/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponentRender.kt b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponentRender.kt new file mode 100644 index 0000000..7e4b9f7 --- /dev/null +++ b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponentRender.kt @@ -0,0 +1,32 @@ +package com.github.codandotv.craftd.xml.ui.image + +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.github.codandotv.craftd.androidcore.data.convertToElement +import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.androidcore.presentation.CraftDComponentKey +import com.github.codandotv.craftd.androidcore.presentation.CraftDViewListener +import com.github.codandotv.craftd.xml.ui.CraftDViewRenderer + +class ImageComponentRender(override var onClickListener: CraftDViewListener?) : + CraftDViewRenderer( + CraftDComponentKey.IMAGE_COMPONENT.key, + CraftDComponentKey.IMAGE_COMPONENT.ordinal + ) { + + inner class ImageHolder(val image: CraftDImageComponent) : RecyclerView.ViewHolder(image) + + override fun bindView(model: SimpleProperties, holder: ImageHolder, position: Int) { + val imageProperties = model.value.convertToElement() + + imageProperties?.let { holder.image.setProperties(it) } + imageProperties?.actionProperties?.let { actionProperties -> + holder.image.setOnClickListener { onClickListener?.invoke(actionProperties) } + } + } + + override fun createViewHolder(parent: ViewGroup): ImageHolder { + return ImageHolder(CraftDImageComponent(parent.context)) + } +} diff --git a/android_kmp/craftd-xml/src/main/res/layout/image.xml b/android_kmp/craftd-xml/src/main/res/layout/image.xml new file mode 100644 index 0000000..ab41f5e --- /dev/null +++ b/android_kmp/craftd-xml/src/main/res/layout/image.xml @@ -0,0 +1,16 @@ + + + + + + From d2dce0cfe369ea5bdfb909cb04001996e5373ff9 Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 22:11:14 +0100 Subject: [PATCH 3/6] Add Coil 3 dependencies and update image component documentation for both XML and Compose. --- android_kmp/craftd-compose/build.gradle.kts | 6 ++---- .../codandotv/craftd/compose/ui/image/CraftDImage.kt | 9 +++++++-- android_kmp/craftd-xml/build.gradle.kts | 8 ++------ .../craftd/xml/ui/image/CraftDImageComponent.kt | 6 ++++++ android_kmp/gradle/libs.versions.toml | 7 +++++++ 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/android_kmp/craftd-compose/build.gradle.kts b/android_kmp/craftd-compose/build.gradle.kts index c71f863..01f2e5d 100644 --- a/android_kmp/craftd-compose/build.gradle.kts +++ b/android_kmp/craftd-compose/build.gradle.kts @@ -6,9 +6,7 @@ plugins { } kotlin { - androidTarget { - publishLibraryVariants("release", "debug") - } + androidTarget { publishLibraryVariants("release", "debug") } sourceSets { androidMain.dependencies { implementation(libs.androidx.core) @@ -24,4 +22,4 @@ kotlin { implementation(libs.kotlinx.collections.immutable) } } -} \ No newline at end of file +} diff --git a/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt index e6e7d8d..a79142c 100644 --- a/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt +++ b/android_kmp/craftd-compose/src/commonMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt @@ -12,6 +12,11 @@ import coil3.compose.AsyncImage import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties import com.github.codandotv.craftd.compose.extensions.toArrangementCompose +/** + * CraftDImage composable for rendering images using Coil 3. + * + * Supports both local and network images via Coil's AsyncImage. + */ @Composable fun CraftDImage( imageProperties: ImageProperties, @@ -37,8 +42,8 @@ fun CraftDImage( } ) .then( - imageProperties.aspectRatio?.let { - Modifier.aspectRatio(it) + imageProperties.aspectRatio?.let { ratio -> + Modifier.aspectRatio(ratio) } ?: Modifier ), diff --git a/android_kmp/craftd-xml/build.gradle.kts b/android_kmp/craftd-xml/build.gradle.kts index 332991a..80e391f 100644 --- a/android_kmp/craftd-xml/build.gradle.kts +++ b/android_kmp/craftd-xml/build.gradle.kts @@ -1,8 +1,4 @@ -android{ - buildFeatures{ - viewBinding = true - } -} +android { buildFeatures { viewBinding = true } } plugins { id("com.codandotv.android-library") @@ -14,4 +10,4 @@ dependencies { implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.google.material) -} \ No newline at end of file +} diff --git a/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt index be089f7..e7d4a1e 100644 --- a/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt +++ b/android_kmp/craftd-xml/src/main/kotlin/com/github/codandotv/craftd/xml/ui/image/CraftDImageComponent.kt @@ -11,6 +11,11 @@ import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties import com.github.codandotv.craftd.androidcore.domain.CraftDAlign import com.github.codandotv.craftd.xml.databinding.ImageBinding +/** + * CraftDImageComponent for XML-based image rendering using Coil 3. + * + * Supports both local and network images via Coil's load extension. + */ class CraftDImageComponent @JvmOverloads constructor( @@ -25,6 +30,7 @@ constructor( } fun setProperties(imageProperties: ImageProperties) { + // Load image using Coil imageProperties.url?.let { url -> binding.imageView.load(url) } imageProperties.contentDescription?.let { description -> diff --git a/android_kmp/gradle/libs.versions.toml b/android_kmp/gradle/libs.versions.toml index 9207595..0f642d0 100644 --- a/android_kmp/gradle/libs.versions.toml +++ b/android_kmp/gradle/libs.versions.toml @@ -20,6 +20,9 @@ kotlinx-collections-immutable = "0.4.0" kotlinx-serialization = "1.9.0" kotlinx-coroutines = "1.10.2" +# Coil +coil = "3.0.3" + # Jackson jackson = "2.17.2" @@ -59,6 +62,10 @@ compose_lifecycle = { group = "androidx.lifecycle", name = "lifecycle-runtime-co # KotlinX kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable" } +# Coil +coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } +coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" } + # Jackson fasterxml_jackson = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson" } fasterxml_jackson_databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } From 5b4aed0054e9611c4efa11c52720927716c8d86d Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 22:13:02 +0100 Subject: [PATCH 4/6] update Coil dependency to version 3.3.0 --- android_kmp/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android_kmp/gradle/libs.versions.toml b/android_kmp/gradle/libs.versions.toml index 0f642d0..63e1826 100644 --- a/android_kmp/gradle/libs.versions.toml +++ b/android_kmp/gradle/libs.versions.toml @@ -21,7 +21,7 @@ kotlinx-serialization = "1.9.0" kotlinx-coroutines = "1.10.2" # Coil -coil = "3.0.3" +coil = "3.3.0" # Jackson jackson = "2.17.2" From fd5ca809ac5407140cfc25e0f48dbb32ac2d1f3b Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 22:28:10 +0100 Subject: [PATCH 5/6] feat: Add Coil image loading to craftd-xml module and switch Coil network backend to OkHttp. --- android_kmp/craftd-compose/build.gradle.kts | 1 + android_kmp/craftd-xml/build.gradle.kts | 2 ++ android_kmp/gradle/libs.versions.toml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/android_kmp/craftd-compose/build.gradle.kts b/android_kmp/craftd-compose/build.gradle.kts index 01f2e5d..12c1d8c 100644 --- a/android_kmp/craftd-compose/build.gradle.kts +++ b/android_kmp/craftd-compose/build.gradle.kts @@ -23,3 +23,4 @@ kotlin { } } } + diff --git a/android_kmp/craftd-xml/build.gradle.kts b/android_kmp/craftd-xml/build.gradle.kts index 80e391f..46dce79 100644 --- a/android_kmp/craftd-xml/build.gradle.kts +++ b/android_kmp/craftd-xml/build.gradle.kts @@ -10,4 +10,6 @@ dependencies { implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.google.material) + implementation(libs.coil.compose) + implementation(libs.coil.network.okhttp) } diff --git a/android_kmp/gradle/libs.versions.toml b/android_kmp/gradle/libs.versions.toml index 63e1826..e5f4dbb 100644 --- a/android_kmp/gradle/libs.versions.toml +++ b/android_kmp/gradle/libs.versions.toml @@ -64,7 +64,7 @@ kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collec # Coil coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } -coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" } +coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" } # Jackson fasterxml_jackson = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson" } From 6b3af85915933898a8e3376bc30f7b5634ec322f Mon Sep 17 00:00:00 2001 From: Jackson Mafra Date: Mon, 24 Nov 2025 22:40:27 +0100 Subject: [PATCH 6/6] feat: add CraftDImage composable and migrate Coil network dependency from OkHttp to Ktor 3. --- android_kmp/craftd-compose/build.gradle.kts | 1 + .../craftd/compose/ui/image/CraftDImage.kt | 65 +++++++++++++++++++ .../compose/ui/image/CraftDImageBuilder.kt | 20 ++++++ android_kmp/craftd-xml/build.gradle.kts | 2 - 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt create mode 100644 android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt diff --git a/android_kmp/craftd-compose/build.gradle.kts b/android_kmp/craftd-compose/build.gradle.kts index 12c1d8c..2f17f9e 100644 --- a/android_kmp/craftd-compose/build.gradle.kts +++ b/android_kmp/craftd-compose/build.gradle.kts @@ -24,3 +24,4 @@ kotlin { } } + diff --git a/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt b/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt new file mode 100644 index 0000000..a79142c --- /dev/null +++ b/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImage.kt @@ -0,0 +1,65 @@ +package com.github.codandotv.craftd.compose.ui.image + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import coil3.compose.AsyncImage +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.compose.extensions.toArrangementCompose + +/** + * CraftDImage composable for rendering images using Coil 3. + * + * Supports both local and network images via Coil's AsyncImage. + */ +@Composable +fun CraftDImage( + imageProperties: ImageProperties, + modifier: Modifier = Modifier, + clickable: (() -> Unit)? = null, +) { + val modifierCustom = clickable?.let { Modifier.clickable { clickable.invoke() } } ?: modifier + + Row( + horizontalArrangement = imageProperties.align.toArrangementCompose(), + modifier = Modifier.fillMaxWidth() + ) { + AsyncImage( + model = imageProperties.url, + contentDescription = imageProperties.contentDescription, + modifier = + modifierCustom + .then( + if (imageProperties.fillMaxSize == true) { + Modifier.fillMaxSize() + } else { + Modifier + } + ) + .then( + imageProperties.aspectRatio?.let { ratio -> + Modifier.aspectRatio(ratio) + } + ?: Modifier + ), + contentScale = imageProperties.contentScale?.toContentScale() ?: ContentScale.Fit + ) + } +} + +private fun String.toContentScale(): ContentScale = + when (this.lowercase()) { + "crop" -> ContentScale.Crop + "fit" -> ContentScale.Fit + "fillbounds" -> ContentScale.FillBounds + "fillwidth" -> ContentScale.FillWidth + "fillheight" -> ContentScale.FillHeight + "inside" -> ContentScale.Inside + "none" -> ContentScale.None + else -> ContentScale.Fit + } diff --git a/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt b/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt new file mode 100644 index 0000000..4dcd382 --- /dev/null +++ b/android_kmp/craftd-compose/src/androidMain/kotlin/com/github/codandotv/craftd/compose/ui/image/CraftDImageBuilder.kt @@ -0,0 +1,20 @@ +package com.github.codandotv.craftd.compose.ui.image + +import androidx.compose.runtime.Composable +import com.github.codandotv.craftd.androidcore.data.convertToElement +import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties +import com.github.codandotv.craftd.androidcore.data.model.image.ImageProperties +import com.github.codandotv.craftd.androidcore.presentation.CraftDComponentKey +import com.github.codandotv.craftd.androidcore.presentation.CraftDViewListener +import com.github.codandotv.craftd.compose.builder.CraftDBuilder + +class CraftDImageBuilder(override val key: String = CraftDComponentKey.IMAGE_COMPONENT.key) : + CraftDBuilder { + @Composable + override fun craft(model: SimpleProperties, listener: CraftDViewListener) { + val imageProperties = model.value.convertToElement() + imageProperties?.let { + CraftDImage(it) { imageProperties.actionProperties?.let { listener.invoke(it) } } + } + } +} diff --git a/android_kmp/craftd-xml/build.gradle.kts b/android_kmp/craftd-xml/build.gradle.kts index 46dce79..80e391f 100644 --- a/android_kmp/craftd-xml/build.gradle.kts +++ b/android_kmp/craftd-xml/build.gradle.kts @@ -10,6 +10,4 @@ dependencies { implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.google.material) - implementation(libs.coil.compose) - implementation(libs.coil.network.okhttp) }