From 2cb446209750e59a738183364b9c7165737675e7 Mon Sep 17 00:00:00 2001 From: Fernando Garcia Date: Thu, 19 Dec 2024 13:12:26 +0100 Subject: [PATCH] ANDROID-14792 Support Checkbox with Links for Compose implementation --- .../mistica/compose/input/CheckBoxInput.kt | 10 +- .../mistica/compose/input/TextWithLinks.kt | 109 ++++++------------ 2 files changed, 42 insertions(+), 77 deletions(-) diff --git a/library/src/main/java/com/telefonica/mistica/compose/input/CheckBoxInput.kt b/library/src/main/java/com/telefonica/mistica/compose/input/CheckBoxInput.kt index b06b4fd51..fd36cfff3 100644 --- a/library/src/main/java/com/telefonica/mistica/compose/input/CheckBoxInput.kt +++ b/library/src/main/java/com/telefonica/mistica/compose/input/CheckBoxInput.kt @@ -4,10 +4,12 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.toggleable import androidx.compose.material.Checkbox import androidx.compose.material.CheckboxDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.telefonica.mistica.compose.theme.MisticaTheme @@ -27,10 +29,14 @@ fun CheckBoxInput( Column( modifier = modifier.alpha(enabled) ) { - Row { + Row(modifier = Modifier.toggleable( + value = checked, + onValueChange = onCheckedChange, + role = Role.Checkbox + )) { Checkbox( checked = checked, - onCheckedChange = onCheckedChange, + onCheckedChange = null, enabled = enabled, colors = CheckboxDefaults.colors( checkedColor = MisticaTheme.colors.controlActivated, diff --git a/library/src/main/java/com/telefonica/mistica/compose/input/TextWithLinks.kt b/library/src/main/java/com/telefonica/mistica/compose/input/TextWithLinks.kt index 3137aaccd..7e86dd8ab 100644 --- a/library/src/main/java/com/telefonica/mistica/compose/input/TextWithLinks.kt +++ b/library/src/main/java/com/telefonica/mistica/compose/input/TextWithLinks.kt @@ -1,16 +1,16 @@ package com.telefonica.mistica.compose.input import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.ClickableText +import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.withStyle +import androidx.compose.ui.text.withLink import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.telefonica.mistica.compose.theme.MisticaTheme @@ -22,92 +22,51 @@ internal fun TextWithLinks( modifier: Modifier = Modifier, ) { val textLinkColour = MisticaTheme.colors.textLink - val linksText = remember { getAnnotatedLinksString(text = text, links = links, highlightColor = textLinkColour) } - ClickableText( + val linksText = remember { getAnnotatedLinksString(originalText = text, links = links, highlightColor = textLinkColour) } + + BasicText( text = linksText, style = MisticaTheme.typography.preset3, modifier = modifier, - onClick = { offset -> - links.forEach { textLink -> - linksText.getStringAnnotations( - tag = textLink.link, - start = offset, - end = offset, - ).firstOrNull()?.let { - textLink.onLinkTapped() - } - } - }, ) } -internal fun getAnnotatedLinksString( - text: String, +fun getAnnotatedLinksString( + originalText: String, links: List, highlightColor: Color, -): AnnotatedString = buildAnnotatedString { - val toHighlight: List = links.map { it.link } - val toHighlightRegex = - toHighlight.joinToString(separator = "|").toRegex() - val notHighlights = getNotHighlightedElements(text, toHighlightRegex) - if (toHighlight.isNotEmpty()) { - notHighlights.zip(toHighlight + "") { message, highlight -> - append(message) - addHighlightedChunk(highlight, highlightColor) - } - } else { - append(text) - } -} - -private fun getNotHighlightedElements( - substring: String, - toHighlightRegex: Regex, -) = substring.split(toHighlightRegex) - -private fun AnnotatedString.Builder.addHighlightedChunk( - highlight: String, - highlightColor: Color, -) { - if (highlight.isNotEmpty()) { - highlight( - text = highlight, - tag = highlight, - highlightColor = highlightColor, - ) - } -} +) = buildAnnotatedString { + val linkMap = links.associateBy { it.link } + var currentIndex = 0 -private fun AnnotatedString.Builder.highlight( - text: String, - tag: String, - textTransformation: (String) -> String = { it }, - highlightColor: Color, -) { - pushStringAnnotation( - tag = tag, - annotation = text, - ) - highlightText(textTransformation(text), highlightColor) - pop() -} + while (currentIndex < originalText.length) { + val linkEntry = linkMap.entries.find { (linkText, _) -> + originalText.startsWith(linkText, currentIndex) + } -private fun AnnotatedString.Builder.highlightText( - highlight: String, - highlightColor: Color, -) { - withStyle( - style = SpanStyle( - color = highlightColor, - textDecoration = TextDecoration.None, - ), - ) { - append(highlight) + if (linkEntry != null) { + val (linkText, link) = linkEntry + withLink( + link = LinkAnnotation.Clickable( + tag = TAG, + styles = TextLinkStyles(style = SpanStyle(color = highlightColor)), + linkInteractionListener = { link.onLinkTapped.invoke() }, + ), + ) { + append(linkText) + } + currentIndex += linkText.length + } else { + append(originalText[currentIndex].toString()) + currentIndex++ + } } } data class TextLink(val link: String, val onLinkTapped: () -> Unit) +private const val TAG = "TextWithLinks" + @Preview(showBackground = true) @Composable fun PreviewTextWithLinks() {