- // Embedded Twitter/X video player
- @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
- @Composable
- private fun TwitterVideoPlayer(videoUrl: String, onClose: () -> Unit) {
- val context = LocalContext.current
- val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
- // One player instance per URL. fxtwitter hands back either a progressive MP4 or an HLS
- // (.m3u8) URL; the default media-source factory picks the right one from the URL because
- // the HLS module is on the classpath, so no explicit MediaSource wiring is needed.
- val exoPlayer = remember(videoUrl) {
- androidx.media3.exoplayer.ExoPlayer.Builder(context).build().apply {
- setMediaItem(androidx.media3.common.MediaItem.fromUri(videoUrl))
- playWhenReady = true
- prepare()
- }
- }
- // Pause when the app goes to the background so audio doesn't keep playing, and always
- // release the player when this preview leaves composition (scrolled away / dismissed).
- androidx.compose.runtime.DisposableEffect(lifecycleOwner, videoUrl) {
- val observer = androidx.lifecycle.LifecycleEventObserver { _, event ->
- if (event == androidx.lifecycle.Lifecycle.Event.ON_PAUSE) exoPlayer.pause()
- }
- lifecycleOwner.lifecycle.addObserver(observer)
- onDispose {
- lifecycleOwner.lifecycle.removeObserver(observer)
- exoPlayer.release()
- }
- }
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .heightIn(max = 240.dp)
- .clip(RoundedCornerShape(8.dp))
- .background(Color.Black),
- contentAlignment = Alignment.Center,
- ) {
- AndroidView(
- factory = { ctx ->
- androidx.media3.ui.PlayerView(ctx).apply {
- player = exoPlayer
- useController = true
- setShowNextButton(false)
- setShowPreviousButton(false)
- layoutParams = android.view.ViewGroup.LayoutParams(
- android.view.ViewGroup.LayoutParams.MATCH_PARENT,
- android.view.ViewGroup.LayoutParams.MATCH_PARENT,
- )
- }
- },
- modifier = Modifier.fillMaxSize(),
- onRelease = { it.player = null },
- )
- IconButton(
- onClick = onClose,
- modifier = Modifier
- .align(Alignment.TopEnd)
- .padding(4.dp)
- .size(36.dp)
- .background(Color.Black.copy(alpha = 0.55f), RoundedCornerShape(18.dp)),
- ) {
- Icon(
- imageVector = Icons.Default.Close,
- contentDescription = "Close player",
- tint = Color.White,
- modifier = Modifier.size(18.dp),
- )
- }
- }
- }