From 0287b5e5836d953f3e7b24cf0cc3eae4cc9e6ff1 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:44:52 +0200 Subject: [PATCH] Fix markdown links CLose #427 --- .../ui/component/markdown/MarkdownText.kt | 43 +++++++++++++++++-- .../component/markdown/StringAnnotations.kt | 12 +++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/MarkdownText.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/MarkdownText.kt index 91b18825..c0c81e0a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/MarkdownText.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/MarkdownText.kt @@ -1,7 +1,13 @@ package de.mm20.launcher2.ui.component.markdown +import android.content.Intent +import android.net.Uri import android.util.Log import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.waitForUpOrCancellation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -21,10 +27,14 @@ import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextAlign @@ -134,11 +144,38 @@ fun ParagraphNode(node: ASTNode, text: String) { val substring = text.substring(start, end) val colorScheme = MaterialTheme.colorScheme val typography = MaterialTheme.typography + + val layoutResult = remember { mutableStateOf(null) } + + val text = buildAnnotatedString { + append(substring) + applyStyles(node, colorScheme, typography, SpanStyle(fontSize = 0.sp), text, node.startOffset) + } + + val context = LocalContext.current + Text( - text = buildAnnotatedString { - append(substring) - applyStyles(node, colorScheme, typography, SpanStyle(fontSize = 0.sp), node.startOffset) + text = text, + onTextLayout = { + layoutResult.value = it }, + modifier = Modifier.pointerInput(Unit) { + awaitEachGesture { + val down = awaitFirstDown(true) + val offset = down.position + val position = layoutResult.value?.getOffsetForPosition(offset) ?: return@awaitEachGesture + val downUrlAnnotation = text.getUrlAnnotations(position, position).firstOrNull() + val downUrl = downUrlAnnotation?.item?.url ?: return@awaitEachGesture + val up = waitForUpOrCancellation()?.takeIf { !it.isConsumed } ?: return@awaitEachGesture + val upPosition = layoutResult.value?.getOffsetForPosition(offset) ?: return@awaitEachGesture + val upAnnotation = text.getUrlAnnotations(upPosition, upPosition).firstOrNull() + val url = upAnnotation?.item?.url ?: return@awaitEachGesture + if (url == downUrl) { + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + up.consume() + } + } + } ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/StringAnnotations.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/StringAnnotations.kt index 8f36ac02..a5adc201 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/StringAnnotations.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/markdown/StringAnnotations.kt @@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Typography import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.UrlAnnotation import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight @@ -19,6 +20,7 @@ fun AnnotatedString.Builder.applyStyles( colorScheme: ColorScheme, typography: Typography, delimiterStyle: SpanStyle, + fullText: String? = null, rootOffset: Int = 0, ) { require(node.startOffset >= rootOffset) { @@ -143,11 +145,19 @@ fun AnnotatedString.Builder.applyStyles( destination.startOffset - rootOffset, destination.endOffset - rootOffset, ) + if (fullText != null) { + val url = fullText.substring(destination.startOffset, destination.endOffset) + addUrlAnnotation( + UrlAnnotation(url), + text.startOffset - rootOffset, + text.endOffset - rootOffset, + ) + } } } } for (child in node.children) { - applyStyles(child, colorScheme, typography, delimiterStyle, rootOffset) + applyStyles(child, colorScheme, typography, delimiterStyle, fullText, rootOffset) } if (node.children.isEmpty() &&