mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-08-06 22:14:25 +00:00
Address some review comments
This commit is contained in:
parent
31d164d116
commit
8f9faf3e53
@ -298,6 +298,9 @@ dependencies {
|
|||||||
// Coroutines interop
|
// Coroutines interop
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.8.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.8.1'
|
||||||
|
|
||||||
|
// Custom browser tab
|
||||||
|
implementation 'androidx.browser:browser:1.8.0'
|
||||||
|
|
||||||
/** Debugging **/
|
/** Debugging **/
|
||||||
// Memory leak detection
|
// Memory leak detection
|
||||||
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
||||||
|
@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.fragment.compose.content
|
import androidx.fragment.compose.content
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo
|
import org.schabi.newpipe.extractor.stream.StreamInfo
|
||||||
import org.schabi.newpipe.ktx.serializable
|
import org.schabi.newpipe.ktx.serializable
|
||||||
import org.schabi.newpipe.ui.components.video.VideoDescriptionSection
|
import org.schabi.newpipe.ui.components.video.StreamDescriptionSection
|
||||||
import org.schabi.newpipe.ui.theme.AppTheme
|
import org.schabi.newpipe.ui.theme.AppTheme
|
||||||
import org.schabi.newpipe.util.KEY_INFO
|
import org.schabi.newpipe.util.KEY_INFO
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ class DescriptionFragment : Fragment() {
|
|||||||
) = content {
|
) = content {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
Surface(color = MaterialTheme.colorScheme.background) {
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
VideoDescriptionSection(requireArguments().serializable(KEY_INFO)!!)
|
StreamDescriptionSection(requireArguments().serializable(KEY_INFO)!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
package org.schabi.newpipe.ui.components.common
|
|
||||||
|
|
||||||
import androidx.compose.material3.LocalTextStyle
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.TextLayoutResult
|
|
||||||
import androidx.compose.ui.text.TextLinkStyles
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.fromHtml
|
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import org.schabi.newpipe.extractor.stream.Description
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DescriptionText(
|
|
||||||
description: Description,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
overflow: TextOverflow = TextOverflow.Clip,
|
|
||||||
maxLines: Int = Int.MAX_VALUE,
|
|
||||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
|
||||||
style: TextStyle = LocalTextStyle.current
|
|
||||||
) {
|
|
||||||
// TODO: Handle links and hashtags, Markdown.
|
|
||||||
val parsedDescription = remember(description) {
|
|
||||||
if (description.type == Description.HTML) {
|
|
||||||
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
|
|
||||||
AnnotatedString.fromHtml(description.content, styles)
|
|
||||||
} else {
|
|
||||||
AnnotatedString(description.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
modifier = modifier,
|
|
||||||
text = parsedDescription,
|
|
||||||
maxLines = maxLines,
|
|
||||||
style = style,
|
|
||||||
overflow = overflow,
|
|
||||||
onTextLayout = onTextLayout
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,165 @@
|
|||||||
|
package org.schabi.newpipe.ui.components.common
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.text.Layout
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.text.style.AbsoluteSizeSpan
|
||||||
|
import android.text.style.AlignmentSpan
|
||||||
|
import android.text.style.BackgroundColorSpan
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.RelativeSizeSpan
|
||||||
|
import android.text.style.StrikethroughSpan
|
||||||
|
import android.text.style.StyleSpan
|
||||||
|
import android.text.style.SubscriptSpan
|
||||||
|
import android.text.style.SuperscriptSpan
|
||||||
|
import android.text.style.TypefaceSpan
|
||||||
|
import android.text.style.URLSpan
|
||||||
|
import android.text.style.UnderlineSpan
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.LinkAnnotation
|
||||||
|
import androidx.compose.ui.text.LinkInteractionListener
|
||||||
|
import androidx.compose.ui.text.ParagraphStyle
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextLinkStyles
|
||||||
|
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.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.unit.em
|
||||||
|
import androidx.core.text.getSpans
|
||||||
|
|
||||||
|
// The code below is copied from Html.android.kt in the Compose Text library, with some minor
|
||||||
|
// changes.
|
||||||
|
|
||||||
|
internal fun Spanned.toAnnotatedString(
|
||||||
|
linkStyles: TextLinkStyles? = null,
|
||||||
|
linkInteractionListener: LinkInteractionListener? = null
|
||||||
|
): AnnotatedString {
|
||||||
|
return AnnotatedString.Builder(capacity = length)
|
||||||
|
.append(this)
|
||||||
|
.also {
|
||||||
|
it.addSpans(this, linkStyles, linkInteractionListener)
|
||||||
|
}
|
||||||
|
.toAnnotatedString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AnnotatedString.Builder.addSpans(
|
||||||
|
spanned: Spanned,
|
||||||
|
linkStyles: TextLinkStyles?,
|
||||||
|
linkInteractionListener: LinkInteractionListener?
|
||||||
|
) {
|
||||||
|
spanned.getSpans<Any>().forEach { span ->
|
||||||
|
addSpan(
|
||||||
|
span,
|
||||||
|
spanned.getSpanStart(span),
|
||||||
|
spanned.getSpanEnd(span),
|
||||||
|
linkStyles,
|
||||||
|
linkInteractionListener
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AnnotatedString.Builder.addSpan(
|
||||||
|
span: Any,
|
||||||
|
start: Int,
|
||||||
|
end: Int,
|
||||||
|
linkStyles: TextLinkStyles?,
|
||||||
|
linkInteractionListener: LinkInteractionListener?
|
||||||
|
) {
|
||||||
|
when (span) {
|
||||||
|
is AbsoluteSizeSpan -> {
|
||||||
|
// TODO: Add Compose's implementation when it is available.
|
||||||
|
}
|
||||||
|
|
||||||
|
is AlignmentSpan -> {
|
||||||
|
addStyle(span.toParagraphStyle(), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is BackgroundColorSpan -> {
|
||||||
|
addStyle(SpanStyle(background = Color(span.backgroundColor)), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ForegroundColorSpan -> {
|
||||||
|
addStyle(SpanStyle(color = Color(span.foregroundColor)), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is RelativeSizeSpan -> {
|
||||||
|
addStyle(SpanStyle(fontSize = span.sizeChange.em), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is StrikethroughSpan -> {
|
||||||
|
addStyle(SpanStyle(textDecoration = TextDecoration.LineThrough), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is StyleSpan -> {
|
||||||
|
span.toSpanStyle()?.let { addStyle(it, start, end) }
|
||||||
|
}
|
||||||
|
|
||||||
|
is SubscriptSpan -> {
|
||||||
|
addStyle(SpanStyle(baselineShift = BaselineShift.Subscript), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SuperscriptSpan -> {
|
||||||
|
addStyle(SpanStyle(baselineShift = BaselineShift.Superscript), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is TypefaceSpan -> {
|
||||||
|
addStyle(span.toSpanStyle(), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnderlineSpan -> {
|
||||||
|
addStyle(SpanStyle(textDecoration = TextDecoration.Underline), start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
is URLSpan -> {
|
||||||
|
span.url?.let { url ->
|
||||||
|
val link = LinkAnnotation.Url(url, linkStyles, linkInteractionListener)
|
||||||
|
addLink(link, start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AlignmentSpan.toParagraphStyle(): ParagraphStyle {
|
||||||
|
val alignment = when (this.alignment) {
|
||||||
|
Layout.Alignment.ALIGN_NORMAL -> TextAlign.Start
|
||||||
|
Layout.Alignment.ALIGN_CENTER -> TextAlign.Center
|
||||||
|
Layout.Alignment.ALIGN_OPPOSITE -> TextAlign.End
|
||||||
|
else -> TextAlign.Unspecified
|
||||||
|
}
|
||||||
|
return ParagraphStyle(textAlign = alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StyleSpan.toSpanStyle(): SpanStyle? {
|
||||||
|
return when (style) {
|
||||||
|
Typeface.BOLD -> SpanStyle(fontWeight = FontWeight.Bold)
|
||||||
|
Typeface.ITALIC -> SpanStyle(fontStyle = FontStyle.Italic)
|
||||||
|
Typeface.BOLD_ITALIC -> SpanStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TypefaceSpan.toSpanStyle(): SpanStyle {
|
||||||
|
val fontFamily = when (family) {
|
||||||
|
FontFamily.Cursive.name -> FontFamily.Cursive
|
||||||
|
FontFamily.Monospace.name -> FontFamily.Monospace
|
||||||
|
FontFamily.SansSerif.name -> FontFamily.SansSerif
|
||||||
|
FontFamily.Serif.name -> FontFamily.Serif
|
||||||
|
else -> {
|
||||||
|
optionalFontFamilyFromName(family)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SpanStyle(fontFamily = fontFamily)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optionalFontFamilyFromName(familyName: String?): FontFamily? {
|
||||||
|
if (familyName.isNullOrEmpty()) return null
|
||||||
|
val typeface = Typeface.create(familyName, Typeface.NORMAL)
|
||||||
|
return typeface.takeIf {
|
||||||
|
typeface != Typeface.DEFAULT &&
|
||||||
|
typeface != Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
|
||||||
|
}?.let { FontFamily(it) }
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package org.schabi.newpipe.ui.components.common
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextLinkStyles
|
||||||
|
import androidx.compose.ui.text.fromHtml
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList
|
||||||
|
import org.schabi.newpipe.extractor.stream.Description
|
||||||
|
import org.schabi.newpipe.ui.components.common.link.YouTubeLinkHandler
|
||||||
|
import org.schabi.newpipe.ui.theme.AppTheme
|
||||||
|
import org.schabi.newpipe.util.NO_SERVICE_ID
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun parseDescription(description: Description, serviceId: Int): AnnotatedString {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val linkHandler = remember(serviceId) {
|
||||||
|
if (serviceId == ServiceList.YouTube.serviceId) {
|
||||||
|
YouTubeLinkHandler(context)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
|
||||||
|
|
||||||
|
return remember(description) {
|
||||||
|
when (description.type) {
|
||||||
|
Description.HTML -> AnnotatedString.fromHtml(description.content, styles, linkHandler)
|
||||||
|
Description.MARKDOWN -> {
|
||||||
|
Markwon.builder(context)
|
||||||
|
.usePlugin(LinkifyPlugin.create())
|
||||||
|
.build()
|
||||||
|
.toMarkdown(description.content)
|
||||||
|
.toAnnotatedString(styles, linkHandler)
|
||||||
|
}
|
||||||
|
else -> AnnotatedString(description.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DescriptionPreviewProvider : PreviewParameterProvider<Description> {
|
||||||
|
override val values = sequenceOf(
|
||||||
|
Description("This is a description.", Description.PLAIN_TEXT),
|
||||||
|
Description("This is a <b>bold description</b>.", Description.HTML),
|
||||||
|
Description("This is a [link](https://example.com).", Description.MARKDOWN),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun ParseDescriptionPreview(
|
||||||
|
@PreviewParameter(DescriptionPreviewProvider::class) description: Description
|
||||||
|
) {
|
||||||
|
AppTheme {
|
||||||
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
|
Text(text = parseDescription(description, NO_SERVICE_ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.schabi.newpipe.ui.components.common.link
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
|
import androidx.compose.ui.text.LinkAnnotation
|
||||||
|
import androidx.compose.ui.text.LinkInteractionListener
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper
|
||||||
|
|
||||||
|
class YouTubeLinkHandler(private val context: Context) : LinkInteractionListener {
|
||||||
|
override fun onClick(link: LinkAnnotation) {
|
||||||
|
val uri = (link as LinkAnnotation.Url).url.toUri()
|
||||||
|
|
||||||
|
// TODO: Handle other links in NewPipe as well.
|
||||||
|
if ("hashtag" in uri.pathSegments) {
|
||||||
|
NavigationHelper.openSearch(
|
||||||
|
context, ServiceList.YouTube.serviceId, "#${uri.lastPathSegment}"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Open link in custom browser tab.
|
||||||
|
CustomTabsIntent.Builder().build().launchUrl(context, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ fun MetadataItem(@StringRes title: Int, value: AnnotatedString) {
|
|||||||
Text(
|
Text(
|
||||||
modifier = Modifier.width(96.dp),
|
modifier = Modifier.width(96.dp),
|
||||||
textAlign = TextAlign.End,
|
textAlign = TextAlign.End,
|
||||||
text = stringResource(title),
|
text = stringResource(title).uppercase(),
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.ElevatedSuggestionChip
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SuggestionChip
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -34,14 +34,14 @@ fun TagsSection(serviceId: Int, tags: List<String>) {
|
|||||||
Column(modifier = Modifier.padding(4.dp)) {
|
Column(modifier = Modifier.padding(4.dp)) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
text = stringResource(R.string.metadata_tags),
|
text = stringResource(R.string.metadata_tags).uppercase(),
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|
||||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
for (tag in sortedTags) {
|
for (tag in sortedTags) {
|
||||||
SuggestionChip(
|
ElevatedSuggestionChip(
|
||||||
onClick = {
|
onClick = {
|
||||||
NavigationHelper.openSearchFragment(
|
NavigationHelper.openSearchFragment(
|
||||||
(context as FragmentActivity).supportFragmentManager, serviceId, tag
|
(context as FragmentActivity).supportFragmentManager, serviceId, tag
|
||||||
|
@ -50,7 +50,7 @@ import org.schabi.newpipe.extractor.stream.Description
|
|||||||
import org.schabi.newpipe.extractor.stream.StreamExtractor
|
import org.schabi.newpipe.extractor.stream.StreamExtractor
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo
|
import org.schabi.newpipe.extractor.stream.StreamInfo
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType
|
import org.schabi.newpipe.extractor.stream.StreamType
|
||||||
import org.schabi.newpipe.ui.components.common.DescriptionText
|
import org.schabi.newpipe.ui.components.common.parseDescription
|
||||||
import org.schabi.newpipe.ui.components.metadata.MetadataItem
|
import org.schabi.newpipe.ui.components.metadata.MetadataItem
|
||||||
import org.schabi.newpipe.ui.components.metadata.TagsSection
|
import org.schabi.newpipe.ui.components.metadata.TagsSection
|
||||||
import org.schabi.newpipe.ui.components.metadata.imageMetadataItem
|
import org.schabi.newpipe.ui.components.metadata.imageMetadataItem
|
||||||
@ -61,7 +61,7 @@ import java.time.OffsetDateTime
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun VideoDescriptionSection(streamInfo: StreamInfo) {
|
fun StreamDescriptionSection(streamInfo: StreamInfo) {
|
||||||
var isSelectable by rememberSaveable { mutableStateOf(false) }
|
var isSelectable by rememberSaveable { mutableStateOf(false) }
|
||||||
val hasDescription = streamInfo.description != Description.EMPTY_DESCRIPTION
|
val hasDescription = streamInfo.description != Description.EMPTY_DESCRIPTION
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
@ -131,12 +131,14 @@ fun VideoDescriptionSection(streamInfo: StreamInfo) {
|
|||||||
|
|
||||||
if (hasDescription) {
|
if (hasDescription) {
|
||||||
item {
|
item {
|
||||||
|
val description = parseDescription(streamInfo.description, streamInfo.serviceId)
|
||||||
|
|
||||||
if (isSelectable) {
|
if (isSelectable) {
|
||||||
SelectionContainer {
|
SelectionContainer {
|
||||||
DescriptionText(description = streamInfo.description)
|
Text(text = description)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DescriptionText(description = streamInfo.description)
|
Text(text = description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,14 +214,14 @@ private fun LazyListScope.metadataItem(@StringRes title: Int, value: String) {
|
|||||||
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
@Composable
|
@Composable
|
||||||
private fun VideoDescriptionSectionPreview() {
|
private fun StreamDescriptionSectionPreview() {
|
||||||
val info = StreamInfo(NO_SERVICE_ID, "", "", StreamType.VIDEO_STREAM, "", "", 0)
|
val info = StreamInfo(NO_SERVICE_ID, "", "", StreamType.VIDEO_STREAM, "", "", 0)
|
||||||
info.uploadDate = DateWrapper(OffsetDateTime.now())
|
info.uploadDate = DateWrapper(OffsetDateTime.now())
|
||||||
info.description = Description("This is an <b>example</b> description", Description.HTML)
|
info.description = Description("This is an <b>example</b> description", Description.HTML)
|
||||||
|
|
||||||
AppTheme {
|
AppTheme {
|
||||||
Surface(color = MaterialTheme.colorScheme.background) {
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
VideoDescriptionSection(info)
|
StreamDescriptionSection(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user