From fc90fdb17d2e129602b267981a831f8d2e8e12eb Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Tue, 15 Feb 2022 19:29:12 +0100 Subject: [PATCH] Apply HTML to Wikipedia text --- ui/build.gradle.kts | 2 + .../search/wikipedia/WikipediaItem.kt | 3 +- .../ui/utils/htmlToAnnotatedString.kt | 105 ++++++++++++++++++ .../mm20/launcher2/wikipedia/WikipediaApi.kt | 2 +- 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/utils/htmlToAnnotatedString.kt diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 1ab52024..e672350f 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -66,6 +66,8 @@ dependencies { implementation(libs.composecolorpicker) + implementation(libs.jsoup) + // Legacy dependencies implementation(libs.androidx.transition) diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/WikipediaItem.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/WikipediaItem.kt index 12e6f3d9..a3ce0b1b 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/WikipediaItem.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/wikipedia/WikipediaItem.kt @@ -33,6 +33,7 @@ import de.mm20.launcher2.ui.component.ToolbarAction import de.mm20.launcher2.ui.ktx.toDp import de.mm20.launcher2.ui.launcher.search.website.WebsiteItem import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled +import de.mm20.launcher2.ui.utils.htmlToAnnotatedString @Composable fun WikipediaItem( @@ -72,7 +73,7 @@ fun WikipediaItem( ) Text( modifier = Modifier.padding(vertical = 8.dp), - text = wikipedia.text, + text = htmlToAnnotatedString(wikipedia.text), style = MaterialTheme.typography.bodySmall ) } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/utils/htmlToAnnotatedString.kt b/ui/src/main/java/de/mm20/launcher2/ui/utils/htmlToAnnotatedString.kt new file mode 100644 index 00000000..ceff4591 --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/utils/htmlToAnnotatedString.kt @@ -0,0 +1,105 @@ +package de.mm20.launcher2.ui.utils + +import androidx.compose.ui.text.* +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextIndent +import androidx.compose.ui.unit.em +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.TextNode + +fun htmlToAnnotatedString(html: String): AnnotatedString { + val document = Jsoup.parse(html.replace("\n", "")) + return buildAnnotatedString { + addNodes(this, document) + } +} + +private val blockElements = arrayOf("p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "pre") + +private val inlineElements = arrayOf("i", "b", "strong", "em", "sup", "sub", "u", "s", "code", "br") + +/** + * @param inParagraph: ParagraphStyles may not be nested, this parameter indicates if we are already inside a paragraph + */ +private fun addNodes(builder: AnnotatedString.Builder, element: Element, inParagraph: Boolean = false) { + val tagName = element.tag().normalName() + when { + blockElements.contains(tagName) -> { + val spanStyle = SpanStyle( + fontSize = when (tagName) { + "h1" -> 2.em + "h2" -> 1.75.em + "h3" -> 1.5.em + "h4" -> 1.25.em + "h5" -> 1.125.em + "h6" -> 1.em + else -> 1.em + }, + fontWeight = if (tagName.matches(Regex("h[1-6]"))) FontWeight.Bold else FontWeight.Normal, + fontFamily = if (tagName == "pre") FontFamily.Monospace else null + ) + + if (!inParagraph) { + builder.withStyle(ParagraphStyle( + textIndent = if (tagName == "li") TextIndent(0.em, 1.em) else TextIndent.None + )) { + withStyle( + spanStyle + ) { + if (tagName == "li") builder.append(" • ") + children(builder, element, true) + } + } + } else { + builder.append("\n") + builder.withStyle( + spanStyle + ) { + if (tagName == "li") builder.append(" • ") + children(builder, element, true) + } + } + } + inlineElements.contains(tagName) -> { + builder.withStyle( + SpanStyle( + fontStyle = if (tagName == "i" || tagName == "em") FontStyle.Italic else FontStyle.Normal, + fontWeight = if (tagName == "b" || tagName == "strong") FontWeight.Bold else FontWeight.Normal, + textDecoration = when (tagName) { + "u" -> TextDecoration.Underline + "s" -> TextDecoration.LineThrough + else -> TextDecoration.None + }, + baselineShift = when (tagName) { + "sup" -> BaselineShift.Superscript + "sub" -> BaselineShift.Subscript + else -> BaselineShift.None + }, + fontFamily = if (tagName == "code") FontFamily.Monospace else null + ) + ) { + if(tagName == "br") builder.append("\n") + children(builder, element, inParagraph) + } + } + else -> { + children(builder, element, inParagraph) + } + } +} + +private fun children(builder: AnnotatedString.Builder, element: Element, inParagraph: Boolean) { + for (node in element.childNodes()) { + if (node is TextNode) { + builder.append(node.text()) + } + if (node is Element) { + addNodes(builder, node, inParagraph) + } + } +} \ No newline at end of file diff --git a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt index 8da75883..646a655c 100644 --- a/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt +++ b/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt @@ -34,7 +34,7 @@ data class WikipediaGetPageImageResultQueryPageThumnail( ) interface WikipediaApi { - @GET("w/api.php?action=query&generator=search&redirects=true&gsrlimit=1&explaintext=true&exchars=500&prop=extracts&exintro=true&format=json") + @GET("w/api.php?action=query&generator=search&redirects=true&gsrlimit=1&exchars=500&prop=extracts&exintro=true&format=json") suspend fun search(@Query("gsrsearch") query: String): WikipediaSearchResult @GET("w/api.php?action=query&prop=pageimages&format=json")