Skip to content

Commit

Permalink
Use AnnotatedString to handle HTML parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Isira-Seneviratne committed Jun 19, 2024
1 parent 713a9df commit 29a63c1
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 48 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ dependencies {
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString

// Paging
implementation 'androidx.paging:paging-rxjava3:3.3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
Expand All @@ -38,6 +44,18 @@ import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy

@Composable
fun rememberParsedText(commentText: Description): AnnotatedString {
// TODO: Handle links and hashtags, Markdown.
return remember(commentText) {
if (commentText.type == Description.HTML) {
AnnotatedString.fromHtml(commentText.content)
} else {
AnnotatedString(commentText.content, ParagraphStyle())
}
}
}

@Composable
fun Comment(comment: CommentsInfoItem) {
val context = LocalContext.current
Expand Down Expand Up @@ -79,23 +97,22 @@ fun Comment(comment: CommentsInfoItem) {
)
}

val date = Localization.relativeTimeOrTextual(
context, comment.uploadDate, comment.textualUploadDate
)
Text(
text = Localization.concatenateStrings(comment.uploaderName, date),
color = MaterialTheme.colorScheme.secondary
)
val nameAndDate = remember(comment) {
val date = Localization.relativeTimeOrTextual(
context, comment.uploadDate, comment.textualUploadDate
)
Localization.concatenateStrings(comment.uploaderName, date)
}
Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
}

// TODO: Handle HTML and Markdown formats.
Text(
text = comment.commentText.content,
text = rememberParsedText(comment.commentText),
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodyMedium,
)

Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Expand Down Expand Up @@ -138,12 +155,21 @@ fun CommentsInfoItem(
this.isPinned = isPinned
}

class DescriptionPreviewProvider : PreviewParameterProvider<Description> {
override val values = sequenceOf(
Description("Hello world!<br><br>This line should be hidden by default.", Description.HTML),
Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
)
}

@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CommentPreview() {
private fun CommentPreview(
@PreviewParameter(DescriptionPreviewProvider::class) description: Description
) {
val comment = CommentsInfoItem(
commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
commentText = description,
uploaderName = "Test",
likeCount = 100,
isPinned = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
Expand All @@ -18,14 +17,13 @@ import org.schabi.newpipe.ui.theme.AppTheme
@Composable
fun CommentReplies(
comment: CommentsInfoItem,
flow: Flow<PagingData<CommentsInfoItem>>,
disposables: CompositeDisposable
flow: Flow<PagingData<CommentsInfoItem>>
) {
val replies = flow.collectAsLazyPagingItems()

LazyColumn {
item {
CommentRepliesHeader(comment = comment, disposables = disposables)
CommentRepliesHeader(comment = comment)
HorizontalDivider(thickness = 1.dp)
}

Expand Down Expand Up @@ -58,6 +56,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(listOf(reply1, reply2)))

AppTheme {
CommentReplies(comment = comment, flow = flow, disposables = CompositeDisposable())
CommentReplies(comment = comment, flow = flow)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.paging.Pager
import androidx.paging.PagingConfig
import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.ktx.serializable
import org.schabi.newpipe.ui.theme.AppTheme

class CommentRepliesFragment : Fragment() {
private val disposables = CompositeDisposable()
lateinit var comment: CommentsInfoItem

override fun onCreateView(
Expand All @@ -33,17 +31,12 @@ class CommentRepliesFragment : Fragment() {
}

AppTheme {
CommentReplies(comment = comment, flow = flow, disposables = disposables)
CommentReplies(comment = comment, flow = flow)
}
}
}
}

override fun onDestroyView() {
super.onDestroyView()
disposables.clear()
}

companion object {
@JvmField
val TAG = CommentRepliesFragment::class.simpleName!!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.list.comments

import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -25,24 +24,18 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.text.HtmlCompat
import androidx.core.text.method.LinkMovementMethodCompat
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.image.ImageStrategy
import org.schabi.newpipe.util.text.TextLinkifier

@Composable
fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) {
fun CommentRepliesHeader(comment: CommentsInfoItem) {
val context = LocalContext.current

Surface(color = MaterialTheme.colorScheme.background) {
Expand Down Expand Up @@ -117,20 +110,9 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
}
}

AndroidView(
factory = { context ->
TextView(context).apply {
movementMethod = LinkMovementMethodCompat.getInstance()
}
},
update = { view ->
// setup comment content
TextLinkifier.fromDescription(
view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
null
)
}
Text(
text = rememberParsedText(comment.commentText),
style = MaterialTheme.typography.bodyMedium
)
}
}
Expand All @@ -149,6 +131,6 @@ fun CommentRepliesHeaderPreview() {
)

AppTheme {
CommentRepliesHeader(comment, CompositeDisposable())
CommentRepliesHeader(comment)
}
}

0 comments on commit 29a63c1

Please sign in to comment.