From 5ebbb5ad0222ff112f96269b5e20e7f26e04c741 Mon Sep 17 00:00:00 2001 From: HuanCheng65 <22636177+HuanCheng65@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:35:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B4=B4=E5=86=85=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tieba/post/ui/common/PbContentRender.kt | 185 +++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt index 8ccece5a..8be5226a 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt @@ -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.appendText( + text: String + ) { + val lastRender = lastOrNull() + if (lastRender is TextContentRender) { + removeLast() + add(lastRender + text) + } else + add(TextContentRender(text)) + } + + fun MutableList.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 = emptyMap(), + onTextLayout: (TextLayoutResult) -> Unit = {}, + style: TextStyle = LocalTextStyle.current +) { + val context = LocalContext.current + + val layoutResult = remember { mutableStateOf(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 + ) } \ No newline at end of file