Chat video messages: add thumbnail preview card with play overlay
Some checks failed
Android CI / android (push) Has been cancelled
Android Release / release (push) Has been cancelled
CI / test (push) Has been cancelled

This commit is contained in:
2026-03-11 06:12:36 +03:00
parent e591a3fa8d
commit 2ffc4cce09
2 changed files with 44 additions and 5 deletions

View File

@@ -102,6 +102,7 @@ dependencies {
implementation("io.coil-kt:coil:2.7.0") implementation("io.coil-kt:coil:2.7.0")
implementation("io.coil-kt:coil-compose:2.7.0") implementation("io.coil-kt:coil-compose:2.7.0")
implementation("io.coil-kt:coil-gif:2.7.0") implementation("io.coil-kt:coil-gif:2.7.0")
implementation("io.coil-kt:coil-video:2.7.0")
implementation("androidx.media3:media3-exoplayer:1.4.1") implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-datasource:1.4.1") implementation("androidx.media3:media3-datasource:1.4.1")
implementation("androidx.media3:media3-datasource-okhttp:1.4.1") implementation("androidx.media3:media3-datasource-okhttp:1.4.1")

View File

@@ -152,6 +152,8 @@ import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material.icons.automirrored.filled.Reply import androidx.compose.material.icons.automirrored.filled.Reply
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest
import coil.request.videoFrameMillis
import ru.daemonlord.messenger.R import ru.daemonlord.messenger.R
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@@ -2590,6 +2592,14 @@ private fun VideoAttachmentCard(
fileType: String, fileType: String,
onOpenViewer: () -> Unit, onOpenViewer: () -> Unit,
) { ) {
val context = LocalContext.current
val previewRequest = remember(url) {
ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.videoFrameMillis(1000)
.build()
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -2597,13 +2607,41 @@ private fun VideoAttachmentCard(
.clickable(onClick = onOpenViewer) .clickable(onClick = onOpenViewer)
.padding(8.dp), .padding(8.dp),
) { ) {
Row( Box(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
horizontalArrangement = Arrangement.spacedBy(6.dp), .fillMaxWidth()
.height(186.dp)
.clip(RoundedCornerShape(10.dp)),
) { ) {
Icon(imageVector = Icons.Filled.Movie, contentDescription = "Video") AsyncImage(
Text(text = "Video", style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.SemiBold) model = previewRequest,
contentDescription = stringResource(id = R.string.chat_media_badge_video),
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
)
Box(
modifier = Modifier
.align(Alignment.Center)
.size(42.dp)
.clip(CircleShape)
.background(Color.Black.copy(alpha = 0.45f)),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Filled.PlayArrow,
contentDescription = stringResource(id = R.string.chat_media_badge_video),
tint = Color.White,
modifier = Modifier.size(28.dp),
)
}
MediaTypeBadge(
label = stringResource(id = R.string.chat_media_badge_video),
modifier = Modifier
.align(Alignment.BottomStart)
.padding(8.dp),
)
} }
Spacer(modifier = Modifier.height(6.dp))
Text(text = extractFileName(url), style = MaterialTheme.typography.bodySmall, maxLines = 1) Text(text = extractFileName(url), style = MaterialTheme.typography.bodySmall, maxLines = 1)
Text(text = fileType, style = MaterialTheme.typography.labelSmall) Text(text = fileType, style = MaterialTheme.typography.labelSmall)
} }