diff --git a/readmore-foundation/src/main/java/com/webtoonscorp/android/readmore/foundation/BasicReadMoreText.kt b/readmore-foundation/src/main/java/com/webtoonscorp/android/readmore/foundation/BasicReadMoreText.kt index e5c754d..6532412 100644 --- a/readmore-foundation/src/main/java/com/webtoonscorp/android/readmore/foundation/BasicReadMoreText.kt +++ b/readmore-foundation/src/main/java/com/webtoonscorp/android/readmore/foundation/BasicReadMoreText.kt @@ -33,6 +33,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.LinkInteractionListener import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextLayoutResult @@ -192,8 +193,9 @@ public fun BasicReadMoreText( // CoreReadMoreText // //////////////////////////////////// -private const val ReadMoreTag = "read_more" -private const val ReadLessTag = "read_less" +private const val ReadMoreTag = "readmore:read_more" +private const val ReadLessTag = "readmore:read_less" +private const val ContentsTag = "readmore:contents" @SuppressLint("UnusedBoxWithConstraintsScope") @Composable @@ -254,41 +256,28 @@ private fun CoreReadMoreText( val state = remember { ReadMoreState() } val currentText = buildAnnotatedString { - if (expanded) { - append(text) - if (state.isCollapsible && readLessTextWithStyle.isNotEmpty()) { - append(' ') - if (toggleArea == ToggleArea.More) { - withLink( - LinkAnnotation.Clickable(tag = ReadLessTag) { - onExpandedChange?.invoke(false) - }, - ) { - append(readLessTextWithStyle) - } - } else { - append(readLessTextWithStyle) - } - } - } else { - val collapsedText = state.collapsedText - if (collapsedText.isNotEmpty()) { - append(collapsedText) - append(overflowText) - - if (toggleArea == ToggleArea.More) { - withLink( - LinkAnnotation.Clickable(tag = ReadMoreTag) { - onExpandedChange?.invoke(true) - }, - ) { - append(readMoreTextWithStyle) - } - } else { - append(readMoreTextWithStyle) - } + withContentsLink( + hasLink = onExpandedChange != null && toggleArea == ToggleArea.All && + text.hasLinks() && state.isCollapsible, + linkInteractionListener = { onExpandedChange?.invoke(!expanded) }, + ) { + if (expanded) { + appendExpandedText( + text = text, + onExpandedChange = onExpandedChange, + readLessTextWithStyle = readLessTextWithStyle, + toggleArea = toggleArea, + isCollapsible = state.isCollapsible, + ) } else { - append(text) + appendCollapsedText( + text = text, + collapsedText = state.collapsedText, + overflowText = overflowText, + onExpandedChange = onExpandedChange, + readMoreTextWithStyle = readMoreTextWithStyle, + toggleArea = toggleArea, + ) } } } @@ -345,6 +334,78 @@ private fun CoreReadMoreText( } } +private fun AnnotatedString.Builder.appendCollapsedText( + text: AnnotatedString, + collapsedText: AnnotatedString, + overflowText: String, + onExpandedChange: ((Boolean) -> Unit)?, + readMoreTextWithStyle: AnnotatedString, + toggleArea: ToggleArea, +) { + if (collapsedText.isNotEmpty()) { + append(collapsedText) + append(overflowText) + + if (toggleArea == ToggleArea.More) { + withLink( + LinkAnnotation.Clickable(tag = ReadMoreTag) { + onExpandedChange?.invoke(true) + }, + ) { + append(readMoreTextWithStyle) + } + } else { + append(readMoreTextWithStyle) + } + } else { + append(text) + } +} + +private fun AnnotatedString.Builder.appendExpandedText( + text: AnnotatedString, + onExpandedChange: ((Boolean) -> Unit)?, + readLessTextWithStyle: AnnotatedString, + toggleArea: ToggleArea, + isCollapsible: Boolean, +) { + append(text) + if (isCollapsible && readLessTextWithStyle.isNotEmpty()) { + append(' ') + if (toggleArea == ToggleArea.More) { + withLink( + LinkAnnotation.Clickable(tag = ReadLessTag) { + onExpandedChange?.invoke(false) + }, + ) { + append(readLessTextWithStyle) + } + } else { + append(readLessTextWithStyle) + } + } +} + +private inline fun AnnotatedString.Builder.withContentsLink( + hasLink: Boolean, + linkInteractionListener: LinkInteractionListener?, + block: AnnotatedString.Builder.() -> R, +): R { + return if (hasLink) { + withLink( + LinkAnnotation.Clickable( + tag = ContentsTag, + linkInteractionListener = linkInteractionListener, + ), + block = block, + ) + } else { + block() + } +} + +private fun AnnotatedString.hasLinks() = hasLinkAnnotations(0, length) + // //////////////////////////////////// // ReadMoreState // //////////////////////////////////// diff --git a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/foundation/BasicReadMoreTextDemo.kt b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/foundation/BasicReadMoreTextDemo.kt index 07c6b7d..8ba4441 100644 --- a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/foundation/BasicReadMoreTextDemo.kt +++ b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/foundation/BasicReadMoreTextDemo.kt @@ -592,7 +592,9 @@ private fun Item_Hyperlink(showMessage: (String) -> Unit) { withLink( LinkAnnotation.Clickable( tag = "TAG$index", - styles = TextLinkStyles(style = SpanStyle(color = Color.Blue)), + styles = TextLinkStyles( + style = SpanStyle(color = Color.Blue), + ), ) { showMessage("#TAG$index Clicked!") }, diff --git a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material/ReadMoreTextDemo.kt b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material/ReadMoreTextDemo.kt index da947a7..92b278b 100644 --- a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material/ReadMoreTextDemo.kt +++ b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material/ReadMoreTextDemo.kt @@ -554,7 +554,9 @@ private fun Item_Hyperlink(showMessage: (String) -> Unit) { withLink( LinkAnnotation.Clickable( tag = "TAG$index", - styles = TextLinkStyles(style = SpanStyle(color = Color.Blue)), + styles = TextLinkStyles( + style = SpanStyle(color = Color.Blue), + ), ) { showMessage("#TAG$index Clicked!") }, diff --git a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material3/ReadMoreTextDemo.kt b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material3/ReadMoreTextDemo.kt index 2359289..3d4ff3c 100644 --- a/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material3/ReadMoreTextDemo.kt +++ b/sample/src/main/java/com/webtoonscorp/android/readmore/sample/compose/material3/ReadMoreTextDemo.kt @@ -573,7 +573,9 @@ private fun Item_Hyperlink(showMessage: (String) -> Unit) { withLink( LinkAnnotation.Clickable( tag = "TAG$index", - styles = TextLinkStyles(style = SpanStyle(color = Color.Blue)), + styles = TextLinkStyles( + style = SpanStyle(color = Color.Blue), + ), ) { showMessage("#TAG$index Clicked!") }, diff --git a/sample/src/main/res/layout/item_hyperlink.xml b/sample/src/main/res/layout/item_hyperlink.xml index b1b7b8c..2b98fdc 100644 --- a/sample/src/main/res/layout/item_hyperlink.xml +++ b/sample/src/main/res/layout/item_hyperlink.xml @@ -26,7 +26,6 @@ android:layout_marginTop="5dp" android:layout_marginEnd="18dp" android:layout_marginBottom="18dp" - android:background="?selectableItemBackground" android:lineSpacingExtra="4sp" android:textColor="?colorOnSurface" android:textSize="15sp"