feat: 贴内视频内容显示

This commit is contained in:
HuanCheng65 2023-07-13 18:35:06 +08:00
parent f1b5c083ef
commit 5ebbb5ad02
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
1 changed files with 184 additions and 1 deletions

View File

@ -1,30 +1,64 @@
package com.huanchengfly.tieba.post.ui.common
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
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.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
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.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.github.panpf.sketch.compose.AsyncImage
import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.activities.UserActivity
import com.huanchengfly.tieba.post.activities.WebViewActivity
import com.huanchengfly.tieba.post.arch.BaseComposeActivity.Companion.LocalWindowSizeClass
import com.huanchengfly.tieba.post.arch.ImmutableHolder
import com.huanchengfly.tieba.post.models.protos.PhotoViewData
import com.huanchengfly.tieba.post.ui.common.windowsizeclass.WindowWidthSizeClass
import com.huanchengfly.tieba.post.ui.widgets.compose.EmoticonText
import com.huanchengfly.tieba.post.ui.widgets.compose.NetworkImage
import com.huanchengfly.tieba.post.ui.widgets.compose.VideoPlayer
import com.huanchengfly.tieba.post.utils.appPreferences
import com.huanchengfly.tieba.post.utils.launchUrl
@Stable
interface PbContentRender {
@Composable
fun Render()
fun toAnnotationString(): AnnotatedString {
return buildAnnotatedString {}
}
}
@Stable
data class TextContentRender(
val text: AnnotatedString
) : PbContentRender {
@ -32,7 +66,11 @@ data class TextContentRender(
@Composable
override fun Render() {
EmoticonText(text = text, fontSize = 15.sp, style = MaterialTheme.typography.body1)
PbContentText(text = text, fontSize = 15.sp, style = MaterialTheme.typography.body1)
}
override fun toAnnotationString(): AnnotatedString {
return text
}
operator fun plus(text: String): TextContentRender {
@ -42,8 +80,33 @@ data class TextContentRender(
operator fun plus(text: AnnotatedString): TextContentRender {
return TextContentRender(this.text + text)
}
companion object {
fun MutableList<PbContentRender>.appendText(
text: String
) {
val lastRender = lastOrNull()
if (lastRender is TextContentRender) {
removeLast()
add(lastRender + text)
} else
add(TextContentRender(text))
}
fun MutableList<PbContentRender>.appendText(
text: AnnotatedString
) {
val lastRender = lastOrNull()
if (lastRender is TextContentRender) {
removeLast()
add(lastRender + text)
} else
add(TextContentRender(text))
}
}
}
@Stable
data class PicContentRender(
val picUrl: String,
val originUrl: String,
@ -71,4 +134,124 @@ data class PicContentRender(
contentScale = ContentScale.Crop
)
}
}
@Stable
data class VideoContentRender(
val videoUrl: String,
val picUrl: String,
val webUrl: String,
val width: Int,
val height: Int
) : PbContentRender {
@Composable
override fun Render() {
val widthFraction =
if (LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact) 1f else 0.5f
val context = LocalContext.current
if (picUrl.isNotBlank()) {
if (videoUrl.isNotBlank()) {
VideoPlayer(
videoUrl = videoUrl,
thumbnailUrl = picUrl,
modifier = Modifier
.clip(RoundedCornerShape(context.appPreferences.radius.dp))
.fillMaxWidth(widthFraction)
.aspectRatio(width * 1f / height)
)
} else {
AsyncImage(
imageUri = picUrl,
contentDescription = stringResource(id = R.string.desc_video),
modifier = Modifier
.clip(RoundedCornerShape(context.appPreferences.radius.dp))
.fillMaxWidth(widthFraction)
.aspectRatio(width * 1f / height)
.clickable {
WebViewActivity.launch(context, webUrl)
},
contentScale = ContentScale.Crop
)
}
}
}
}
@Composable
fun PbContentText(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
emoticonSize: Float = 0.9f,
inlineContent: Map<String, InlineTextContent> = emptyMap(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
val context = LocalContext.current
val layoutResult = remember { mutableStateOf<TextLayoutResult?>(null) }
EmoticonText(
text = text,
modifier = modifier.pointerInput(Unit) {
awaitEachGesture {
val change = awaitFirstDown()
val annotation =
layoutResult.value?.getOffsetForPosition(change.position)?.let {
text.getStringAnnotations(start = it, end = it)
.firstOrNull()
}
if (annotation != null) {
if (change.pressed != change.previousPressed) change.consume()
val up =
waitForUpOrCancellation()?.also { if (it.pressed != it.previousPressed) it.consume() }
if (up != null) {
when (annotation.tag) {
"url" -> {
val url = annotation.item
launchUrl(context, url)
}
"user" -> {
val uid = annotation.item
UserActivity.launch(context, uid)
}
}
}
}
}
},
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
minLines = minLines,
emoticonSize = emoticonSize,
inlineContent = inlineContent,
onTextLayout = {
layoutResult.value = it
onTextLayout(it)
},
style = style
)
}