diff --git a/app/src/main/res/raw/mock.json b/app/src/main/res/raw/mock.json index 4bff9c6..8fb4398 100644 --- a/app/src/main/res/raw/mock.json +++ b/app/src/main/res/raw/mock.json @@ -19678,5 +19678,148 @@ "centerScrollIndex": 5 } } + }, + "spannableTextWithImage": { + "view": [ + { + "property": { + "width": "MATCH_PARENT", + "height": "WRAP_CONTENT", + "layoutId": "footer_container_oxCyD", + "viewType": "Column", + "arrangementData": { + "arrangementType": "Center" + }, + "backgroundColor": "#F5F5F5", + "horizontalAlignment": "CenterHorizontally" + }, + "childrenViews": [ + { + "property": { + "width": "MATCH_PARENT", + "height": "WRAP_CONTENT", + "padding": { + "end": 16, + "top": 16, + "start": 16 + }, + "layoutId": "privacy_terms_consent_container_oxCyD", + "viewType": "ConstraintLayout", + "isVisible": "true" + }, + "childrenViews": [ + { + "property": { + "width": "FILL_TO_CONSTRAINTS", + "height": "WRAP_CONTENT", + "padding": { + "top": 2, + "start": 12 + }, + "layoutId": "privacy_terms_text_oxCyD", + "viewType": "SpannableText", + "spanProperty": [ + { + "property": { + "fontSize": 12, + "isVisible": "true", + "textColor": "#6B6B6B", + "fontFamily": "ttComposeFontFamily", + "fontWeight": "TT_REGULAR" + }, + "stringId": "agreement_text" + }, + { + "property": { + "fontSize": 12, + "isVisible": "true", + "textColor": "#6B6B6B", + "fontFamily": "ttComposeFontFamily", + "fontWeight": "TT_MEDIUM", + "textDecoration": "Underline" + }, + "stringId": "privacy_policy_text" + }, + { + "property": { + "fontSize": 20, + "isVisible": "true", + "textColor": "#6B6B6B", + "fontFamily": "ttComposeFontFamily", + "fontWeight": "TT_SEMI_BOLD" + }, + "stringId": "and_symbol_text" + }, + { + "imageProperty": { + "width": "14", + "height": "14", + "layoutId": "coin_icon", + "contentScale": "FillBounds" + }, + "imageId": "coin_icon" + }, + { + "property": { + "fontSize": 50, + "isVisible": "true", + "textColor": "#6B6B6B", + "fontFamily": "ttComposeFontFamily", + "fontWeight": "TT_SEMI_BOLD" + }, + "stringId": "and_symbol_text" + } + ], + "constraintLinks": { + "end": { + "layoutId": "parent", + "constraint": "END" + }, + "top": { + "layoutId": "parent", + "constraint": "TOP" + }, + "start": { + "layoutId": "parent", + "constraint": "START" + } + } + } + } + ] + }, + { + "property": { + "layoutId": "output_text", + "viewType": "Text", + "width": "MATCH_PARENT", + "height": "WRAP_CONTENT", + "textColor": "#000000", + "margin": { + "start": 24, + "top": 16 + }, + "isStateFul": "true" + } + } + ] + } + ], + "data": { + "privacy_terms_text_oxCyD": { + "inlineContentMap": { + "coin_icon": { + "viewType": "Image", + "iconUrl": "https://public-assets.prod.navi-sa.in/navi-coin/png/minified_images/AP_Coins/Navi+coin_32px.png" + } + }, + "textMap": { + "agreement_text": "This is a new sample", + "privacy_policy_text": "Spannable text.", + "and_symbol_text": "Please enjoy while it renders" + }, + "viewType": "SpannableText" + } + } } } \ No newline at end of file diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/data/SpannableTextData.kt b/navi-uitron/src/main/java/com/navi/uitron/model/data/SpannableTextData.kt index 1c2fc51..134a22f 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/model/data/SpannableTextData.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/model/data/SpannableTextData.kt @@ -17,12 +17,14 @@ package com.navi.uitron.model.data */ data class SpannableTextData( val textMap: HashMap? = null, - val spanData: List? = null + val spanData: List? = null, + val inlineContentMap: HashMap? = null ) : UiTronData() data class SpanTextData( val stringId: String? = null, val textData: TextData? = null, val tag: String? = null, - val annotation: String? = null + val annotation: String? = null, + val imageId: String? = null ) diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/ui/SpannableProperty.kt b/navi-uitron/src/main/java/com/navi/uitron/model/ui/SpannableProperty.kt index 63fb11c..77e869d 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/model/ui/SpannableProperty.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/model/ui/SpannableProperty.kt @@ -29,4 +29,9 @@ data class SpannableProperty( } } -data class SpanProperty(val stringId: String? = null, val property: TextProperty) +data class SpanProperty( + val stringId: String? = null, + val property: TextProperty? = null, + val imageId: String? = null, + val imageProperty: ImageProperty? = null, +) diff --git a/navi-uitron/src/main/java/com/navi/uitron/render/SpannableTextRenderer.kt b/navi-uitron/src/main/java/com/navi/uitron/render/SpannableTextRenderer.kt index 91497bf..8d2a813 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/render/SpannableTextRenderer.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/render/SpannableTextRenderer.kt @@ -9,13 +9,18 @@ package com.navi.uitron.render import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.ClickableText +import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.foundation.text.appendInlineContent import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.text.Placeholder +import androidx.compose.ui.text.PlaceholderVerticalAlign import androidx.compose.ui.text.PlatformTextStyle import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle @@ -30,6 +35,7 @@ import com.navi.uitron.model.animations.AnimatedProperties import com.navi.uitron.model.data.SpannableTextData import com.navi.uitron.model.data.UiTronData import com.navi.uitron.model.ui.BrushData +import com.navi.uitron.model.ui.ImageProperty import com.navi.uitron.model.ui.SpanProperty import com.navi.uitron.model.ui.SpannableProperty import com.navi.uitron.model.ui.TextProperty @@ -85,7 +91,6 @@ class SpannableTextRenderer : Renderer { // ifVisible if (property.visible.orTrue()) { - val text = java.lang.StringBuilder() val textStyle = TextStyle( textAlign = getTextAlignment(property.textAlign), @@ -134,7 +139,35 @@ class SpannableTextRenderer : Renderer { // Build Spannable val annotatedString = - buildSpannable(animatedProperties, brushData, spannableData, spanProperties, text) + buildSpannable(animatedProperties, brushData, spannableData, spanProperties) + val inlineTextContent = + remember(spannableData?.inlineContentMap) { + spannableData + ?.inlineContentMap + ?.map { (key, value) -> + val imageProperty = + spanProperties.firstOrNull { it.imageId == key }?.imageProperty + key to + InlineTextContent( + Placeholder( + width = imageProperty?.width?.toFloat()?.sp ?: 0.sp, + height = imageProperty?.height?.toFloat()?.sp ?: 0.sp, + placeholderVerticalAlign = + PlaceholderVerticalAlign.TextCenter + ) + ) { + ImageRenderer() + .Render( + property = imageProperty ?: ImageProperty(), + uiTronData = value, + uiTronViewModel = uiTronViewModel, + modifier = Modifier + ) + } + } + ?.toMap() + ?.toMutableMap() ?: mutableMapOf() + } // Render if (spannableData?.textMap.isNotNull()) { @@ -143,7 +176,8 @@ class SpannableTextRenderer : Renderer { modifier = textModifier, style = textStyle, maxLines = property.maxLines ?: Int.MAX_VALUE, - overflow = getTextOverflow(property.overflow) + overflow = getTextOverflow(property.overflow), + inlineContent = inlineTextContent ) } else { ClickableText( @@ -178,32 +212,38 @@ class SpannableTextRenderer : Renderer { animatedProperties: AnimatedProperties?, brushData: BrushData?, spannableData: SpannableTextData?, - spanProperties: List, - text: StringBuilder + spanProperties: List ) = buildAnnotatedString { spannableData?.let { data -> data.textMap?.let { textMap -> spanProperties.forEach { spanProperty -> - textMap[spanProperty.stringId]?.let { textData -> - val transformedText = - getTransformedText(spanProperty.property.valueTransformation, textData) - ?: textData - val start = text.length - val end = start + transformedText.length - addStyle( - style = - getSpanTextStyle( - brushData, - spanProperty.property, - animatedProperties - ), - start = start, - end = end - ) - text.append("$transformedText ") + if (spanProperty.imageId != null) { + appendInlineContent(spanProperty.imageId) + append(" ") + } else { + textMap[spanProperty.stringId]?.let { textData -> + val transformedText = + getTransformedText( + spanProperty.property?.valueTransformation, + textData + ) ?: textData + val start = this.length + val end = start + transformedText.length + + addStyle( + style = + getSpanTextStyle( + brushData, + spanProperty.property ?: TextProperty(), + animatedProperties + ), + start = start, + end = end + ) + append("$transformedText ") + } } } - append(text.toString()) } ?: run { spanProperties.forEach { spanProperty -> @@ -213,7 +253,7 @@ class SpannableTextRenderer : Renderer { spanData.textData?.text?.let { text -> val transformedText = getTransformedText( - spanProperty.property.valueTransformation, + spanProperty.property?.valueTransformation, text ) ?: text if (spanData.tag != null) { @@ -225,7 +265,7 @@ class SpannableTextRenderer : Renderer { style = getSpanTextStyle( brushData, - spanProperty.property, + spanProperty.property ?: TextProperty(), animatedProperties ) ) { @@ -241,7 +281,7 @@ class SpannableTextRenderer : Renderer { style = getSpanTextStyle( brushData, - spanProperty.property, + spanProperty.property ?: TextProperty(), animatedProperties ) ) {