// 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), ) } } }