feat: 贴内视频内容显示
This commit is contained in:
parent
f1b5c083ef
commit
5ebbb5ad02
|
|
@ -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
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue