From 5ab7f6bcd6477e9fd9967d575daabb12654cc0af Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 16:49:22 +0900 Subject: [PATCH 1/6] [android] - refactor custome modifier - support background img for flex --- .../nativebrik/sdk/component/renderer/flex.kt | 117 ++++++++++++------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt index 24b7823..4976283 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -20,7 +21,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import coil.compose.AsyncImage +import coil.request.ImageRequest import com.nativebrik.sdk.component.provider.event.eventDispatcher import com.nativebrik.sdk.schema.AlignItems import com.nativebrik.sdk.schema.FlexDirection @@ -57,29 +63,37 @@ private fun childFrameWeight(block: UIBlock, direction: FlexDirection): Float? { } } -internal fun framedModifier(modifier: Modifier, frame: FrameData?): Modifier { - var mod: Modifier = modifier +@Composable +internal fun Modifier.styleByFrame(frame: FrameData?): Modifier { + return this + .frameSize(frame) + .framePadding(frame) +} + +@Composable +internal fun Modifier.frameSize(frame: FrameData?): Modifier { + var mod = this // size should be set most lastly to make padding insets. // width should be content fit by default if (frame?.width != null) { - if (frame.width == 0) { + mod = if (frame.width == 0) { // parent fit - mod = mod.fillMaxWidth() + mod.fillMaxWidth() } else { // fixed size - mod = mod.width(frame.width.dp) + mod.width(frame.width.dp) } } // height should be content fit by default if (frame?.height != null) { - if (frame.height == 0) { + mod = if (frame.height == 0) { // parent fit - mod = mod.fillMaxHeight() + mod.fillMaxHeight() } else { // fixed size - mod = mod.height(frame.height.dp) + mod.height(frame.height.dp) } } @@ -94,16 +108,20 @@ internal fun framedModifier(modifier: Modifier, frame: FrameData?): Modifier { shape = roundedShape, ) - mod = mod.padding( + return mod +} + +@Composable +internal fun Modifier.framePadding(frame: FrameData?): Modifier { + return this.padding( start = frame?.paddingLeft?.dp ?: 0.dp, top = frame?.paddingTop?.dp ?: 0.dp, end = frame?.paddingRight?.dp ?: 0.dp, bottom = frame?.paddingBottom?.dp ?: 0.dp, ) - - return mod } + internal fun parseFramePadding(frame: FrameData?): PaddingValues { return PaddingValues( start = frame?.paddingLeft?.dp ?: 0.dp, @@ -112,16 +130,15 @@ internal fun parseFramePadding(frame: FrameData?): PaddingValues { bottom = frame?.paddingBottom?.dp ?: 0.dp, ) } - @Composable -internal fun overflowModifier(modifier: Modifier, direction: FlexDirection, overflow: Overflow?): Modifier { - val overflow = overflow ?: return modifier - if (overflow != Overflow.SCROLL) return modifier - if (direction == FlexDirection.ROW) { - return modifier +internal fun Modifier.flexOverflow(direction: FlexDirection, overflow: Overflow?): Modifier { + val overflow = overflow ?: return this + if (overflow != Overflow.SCROLL) return this + return if (direction == FlexDirection.ROW) { + this .horizontalScroll(rememberScrollState()) } else { - return modifier + this .verticalScroll(rememberScrollState()) } } @@ -186,34 +203,54 @@ internal fun Flex( modifier: Modifier = Modifier, ) { val direction: FlexDirection = block.data?.direction ?: FlexDirection.ROW - var modifier = framedModifier(modifier, block.data?.frame) - modifier = overflowModifier(modifier, direction, block.data?.overflow) - modifier = modifier.eventDispatcher(block.data?.onClick) + val modifier = modifier.frameSize(block.data?.frame) + val flexModifier = modifier + .framePadding(block.data?.frame) + .flexOverflow(direction, block.data?.overflow) + .eventDispatcher(block.data?.onClick) + val gap = block.data?.gap val justifyContent = block.data?.justifyContent val alignItems = block.data?.alignItems - if (direction == FlexDirection.ROW) { - Row( - modifier = modifier, - horizontalArrangement = parseHorizontalJustifyContent(gap, justifyContent), - verticalAlignment = parseVerticalAlignItems(alignItems), - ) { - block.data?.children?.map { - val weight = childFrameWeight(it, direction) - Block(block = it, if (weight != null) Modifier.weight(weight) else Modifier) - } + Box(modifier = modifier) { + if (block.data?.frame?.backgroundSrc != null) { + AsyncImage( + modifier = Modifier + .zIndex(0f) + .matchParentSize(), + model = ImageRequest.Builder(LocalContext.current) + .data(block.data.frame.backgroundSrc) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + ) } - } else { - Column( - modifier = modifier, - horizontalAlignment = parseHorizontalAlignItems(alignItems), - verticalArrangement = parseVerticalJustifyContent(gap, justifyContent) - ) { - block.data?.children?.map { - val weight = childFrameWeight(it, direction) - Block(block = it, if (weight != null) Modifier.weight(weight) else Modifier) + if (direction == FlexDirection.ROW) { + Row( + modifier = flexModifier.zIndex(1f), + horizontalArrangement = parseHorizontalJustifyContent(gap, justifyContent), + verticalAlignment = parseVerticalAlignItems(alignItems), + ) { + block.data?.children?.map { + val weight = childFrameWeight(it, direction) + Block(block = it, if (weight != null) Modifier.weight(weight) else Modifier) + } + } + } else { + Column( + modifier = flexModifier.zIndex(1f), + horizontalAlignment = parseHorizontalAlignItems(alignItems), + verticalArrangement = parseVerticalJustifyContent(gap, justifyContent) + ) { + block.data?.children?.map { + val weight = childFrameWeight(it, direction) + Block(block = it, if (weight != null) Modifier.weight(weight) else Modifier) + } } } } + + } From d4d268c918e1274f70d5c23a87e68fa8622353cf Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 16:50:05 +0900 Subject: [PATCH 2/6] [android] use refactored custome modifeiers --- .../java/com/nativebrik/sdk/component/renderer/image.kt | 6 ++++-- .../java/com/nativebrik/sdk/component/renderer/select.kt | 4 ++-- .../java/com/nativebrik/sdk/component/renderer/textinput.kt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/image.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/image.kt index 6c5fa53..bb92fe6 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/image.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/image.kt @@ -55,8 +55,10 @@ internal fun Image(block: UIImageBlock, modifier: Modifier = Modifier) { src = if (loading) block.data?.src ?: "" else compile(block.data?.src ?: "", data.data) } - var modifier = framedModifier(modifier, block.data?.frame) - modifier = modifier.eventDispatcher(block.data?.onClick).skeleton(skeleton) + val modifier = modifier + .styleByFrame(block.data?.frame) + .eventDispatcher(block.data?.onClick) + .skeleton(skeleton) val fallback = parseImageFallbackToBlurhash(src) val decoded = BlurHashDecoder.decode( diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/select.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/select.kt index 8cd95fe..ae9588b 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/select.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/select.kt @@ -65,7 +65,7 @@ internal fun Select(block: UISelectInputBlock, modifier: Modifier = Modifier) { (it.width.toFloat() / this.density).dp } } - val selectModifier = framedModifier(Modifier, block.data?.frame) + val selectModifier = modifier.styleByFrame(block.data?.frame) val fontStyle = parseFontStyle( size = block.data?.size, color = block.data?.color, @@ -172,7 +172,7 @@ internal fun MultiSelect(block: UIMultiSelectInputBlock, modifier: Modifier = Mo (it.width.toFloat() / this.density).dp } } - val selectModifier = framedModifier(Modifier, block.data?.frame) + val selectModifier = Modifier.styleByFrame(block.data?.frame) .fillMaxWidth() val fontStyle = parseFontStyle( size = block.data?.size, diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/textinput.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/textinput.kt index cc13034..41c9585 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/textinput.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/textinput.kt @@ -38,7 +38,7 @@ internal fun TextInput(block: UITextInputBlock, modifier: Modifier = Modifier) { fontDesign = block.data?.design, alignment = block.data?.textAlign, ) - val modifier = framedModifier(modifier, block.data?.frame).fillMaxWidth() + val modifier = modifier.styleByFrame(block.data?.frame).fillMaxWidth() BasicTextField( value = value, From ca9aafe7ad7b7bbaa50964d15fffa0bd66cbe375 Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 16:50:57 +0900 Subject: [PATCH 3/6] [android] - support maxlines for text - support background img for text --- .../nativebrik/sdk/component/renderer/text.kt | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt index 9cae7de..ecace74 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt @@ -1,10 +1,16 @@ package com.nativebrik.sdk.component.renderer +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex +import coil.compose.AsyncImage +import coil.request.ImageRequest import com.nativebrik.sdk.component.provider.data.DataContext import com.nativebrik.sdk.component.provider.event.eventDispatcher import com.nativebrik.sdk.component.provider.event.skeleton @@ -75,9 +81,11 @@ internal fun Text(block: UITextBlock, modifier: Modifier = Modifier) { skeleton = loading value = if (loading) block.data?.value ?: "" else compile(block.data?.value ?: "", data.data) } + var modifier = modifier + .styleByFrame(block.data?.frame) + .skeleton(skeleton) + .eventDispatcher(block.data?.onClick) - var modifier = framedModifier(modifier, block.data?.frame) - modifier = modifier.skeleton(skeleton).eventDispatcher(block.data?.onClick) val fontStyle = parseFontStyle( size = block.data?.size, color = block.data?.color, @@ -86,10 +94,30 @@ internal fun Text(block: UITextBlock, modifier: Modifier = Modifier) { alignment = null, transparent = skeleton, ) + var maxLines = block.data?.maxLines ?: Int.MAX_VALUE + if (maxLines <= 0) { + maxLines = Int.MAX_VALUE + } - BasicText( - text = value, - modifier = modifier, - style = fontStyle, - ) + Box(modifier = modifier) { + if (block.data?.frame?.backgroundSrc != null) { + AsyncImage( + modifier = Modifier + .zIndex(0f) + .matchParentSize(), + model = ImageRequest.Builder(LocalContext.current) + .data(block.data.frame.backgroundSrc) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + ) + } + BasicText( + text = value, + modifier = Modifier.zIndex(1f), + style = fontStyle, + maxLines = maxLines, + ) + } } \ No newline at end of file From cc74d63f9178d642f9f733b05bd1fdcc61120ca9 Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 16:58:36 +0900 Subject: [PATCH 4/6] [android] async --- .../com/nativebrik/sdk/schema/generated.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/schema/generated.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/schema/generated.kt index 8dce1b5..61bbe1b 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/schema/generated.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/schema/generated.kt @@ -3,15 +3,16 @@ */ package com.nativebrik.sdk.schema -import kotlinx.serialization.json.JsonArray +import android.os.Build +import java.time.ZonedDateTime +import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive -import java.time.ZonedDateTime +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonArray internal typealias ID = String @@ -229,8 +230,8 @@ internal class ApiHttpRequest ( url = StringDecoder.decode(element.jsonObject["url"]), method = ApiHttpRequestMethod.decode(element.jsonObject["method"]), headers = ListDecoder.decode(element.jsonObject["headers"]) { element: JsonElement? -> - ApiHttpHeader.decode(element) - }, + ApiHttpHeader.decode(element) + }, body = StringDecoder.decode(element.jsonObject["body"]), ) } @@ -290,8 +291,8 @@ internal class ApiHttpResponseAssertion ( return ApiHttpResponseAssertion( statusCodes = ListDecoder.decode(element.jsonObject["statusCodes"]) { element: JsonElement? -> - IntDecoder.decode(element) - }, + IntDecoder.decode(element) + }, ) } } @@ -1837,6 +1838,7 @@ internal class UITextBlockData ( val color: Color? = null, val design: FontDesign? = null, val weight: FontWeight? = null, + val maxLines: Int? = null, val frame: FrameData? = null, val onClick: UIBlockEventDispatcher? = null, ) { @@ -1855,6 +1857,7 @@ internal class UITextBlockData ( color = Color.decode(element.jsonObject["color"]), design = FontDesign.decode(element.jsonObject["design"]), weight = FontWeight.decode(element.jsonObject["weight"]), + maxLines = IntDecoder.decode(element.jsonObject["maxLines"]), frame = FrameData.decode(element.jsonObject["frame"]), onClick = UIBlockEventDispatcher.decode(element.jsonObject["onClick"]), ) From 9378d8f2a6711f29cb5e68203d76e527f27adff6 Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 16:58:58 +0900 Subject: [PATCH 5/6] [android] Update version to 0.1.1 --- android/nativebrik/build.gradle.kts | 2 +- android/nativebrik/src/main/java/com/nativebrik/sdk/sdk.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/nativebrik/build.gradle.kts b/android/nativebrik/build.gradle.kts index 675eac3..58317e6 100644 --- a/android/nativebrik/build.gradle.kts +++ b/android/nativebrik/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "com.nativebrik" -version = "0.1.0" +version = "0.1.1" android { namespace = "com.nativebrik.sdk" diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/sdk.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/sdk.kt index fee0d90..4c4313f 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/sdk.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/sdk.kt @@ -24,7 +24,7 @@ import com.nativebrik.sdk.data.user.NativebrikUser import com.nativebrik.sdk.remoteconfig.RemoteConfigLoadingState import com.nativebrik.sdk.schema.UIBlock -const val VERSION = "0.1.0" +const val VERSION = "0.1.1" data class Endpoint( val cdn: String = "https://cdn.nativebrik.com", From 9e14256306d72601a9b77f14e274d2cd613316e0 Mon Sep 17 00:00:00 2001 From: RyosukeCla Date: Wed, 27 Mar 2024 17:12:05 +0900 Subject: [PATCH 6/6] [android] support compiling backgroundSrc and support blurhash for it --- .../com/nativebrik/sdk/component/renderer/flex.kt | 15 ++++++++++++++- .../com/nativebrik/sdk/component/renderer/text.kt | 12 +++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt index 4976283..4c4f4b1 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/flex.kt @@ -26,7 +26,9 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import coil.compose.AsyncImage +import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest +import com.nativebrik.sdk.component.provider.data.DataContext import com.nativebrik.sdk.component.provider.event.eventDispatcher import com.nativebrik.sdk.schema.AlignItems import com.nativebrik.sdk.schema.FlexDirection @@ -35,6 +37,8 @@ import com.nativebrik.sdk.schema.JustifyContent import com.nativebrik.sdk.schema.Overflow import com.nativebrik.sdk.schema.UIBlock import com.nativebrik.sdk.schema.UIFlexContainerBlock +import com.nativebrik.sdk.template.compile +import com.nativebrik.sdk.vendor.blurhash.BlurHashDecoder import com.nativebrik.sdk.schema.Color as SchemaColor private fun calcWeight(frameData: FrameData?, flexDirection: FlexDirection): Float? { @@ -202,6 +206,7 @@ internal fun Flex( block: UIFlexContainerBlock, modifier: Modifier = Modifier, ) { + val data = DataContext.state val direction: FlexDirection = block.data?.direction ?: FlexDirection.ROW val modifier = modifier.frameSize(block.data?.frame) val flexModifier = modifier @@ -215,16 +220,24 @@ internal fun Flex( Box(modifier = modifier) { if (block.data?.frame?.backgroundSrc != null) { + val src = compile(block.data.frame.backgroundSrc, data.data) + val fallback = parseImageFallbackToBlurhash(src) + val decoded = BlurHashDecoder.decode( + blurHash = fallback.blurhash, + height = fallback.height, + width = fallback.width + ) AsyncImage( modifier = Modifier .zIndex(0f) .matchParentSize(), model = ImageRequest.Builder(LocalContext.current) - .data(block.data.frame.backgroundSrc) + .data(src) .crossfade(true) .build(), contentDescription = null, contentScale = ContentScale.Crop, + placeholder = rememberAsyncImagePainter(decoded), ) } if (direction == FlexDirection.ROW) { diff --git a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt index ecace74..8c79099 100644 --- a/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt +++ b/android/nativebrik/src/main/java/com/nativebrik/sdk/component/renderer/text.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import coil.compose.AsyncImage +import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest import com.nativebrik.sdk.component.provider.data.DataContext import com.nativebrik.sdk.component.provider.event.eventDispatcher @@ -21,6 +22,7 @@ import com.nativebrik.sdk.schema.TextAlign import com.nativebrik.sdk.schema.UITextBlock import com.nativebrik.sdk.template.compile import com.nativebrik.sdk.template.hasPlaceholder +import com.nativebrik.sdk.vendor.blurhash.BlurHashDecoder import androidx.compose.ui.graphics.Color as PrimitiveColor import androidx.compose.ui.text.font.FontFamily as PrimitiveFontFamily import androidx.compose.ui.text.font.FontWeight as PrimitiveFontWeight @@ -101,16 +103,24 @@ internal fun Text(block: UITextBlock, modifier: Modifier = Modifier) { Box(modifier = modifier) { if (block.data?.frame?.backgroundSrc != null) { + val src = compile(block.data.frame.backgroundSrc, data.data) + val fallback = parseImageFallbackToBlurhash(src) + val decoded = BlurHashDecoder.decode( + blurHash = fallback.blurhash, + height = fallback.height, + width = fallback.width + ) AsyncImage( modifier = Modifier .zIndex(0f) .matchParentSize(), model = ImageRequest.Builder(LocalContext.current) - .data(block.data.frame.backgroundSrc) + .data(src) .crossfade(true) .build(), contentDescription = null, contentScale = ContentScale.Crop, + placeholder = rememberAsyncImagePainter(decoded), ) } BasicText(