Skip to content

Instantly share code, notes, and snippets.

@jamshidi2718
Created July 2, 2024 08:42
Show Gist options
  • Save jamshidi2718/d99cc0c9971e7c97736d727c07b76d6b to your computer and use it in GitHub Desktop.
Save jamshidi2718/d99cc0c9971e7c97736d727c07b76d6b to your computer and use it in GitHub Desktop.

ExoPlayer in Jetpack compose

gradle dependencies

   implementation 'androidx.media3:media3-exoplayer:1.2.0'
   implementation "androidx.media3:media3-ui:1.2.0"
   implementation "androidx.compose.runtime:runtime-livedata:1.5.4"

Variables

var fullScreenDialog: Dialog? = null

fun Context.findActivity(): Activity? = when (this) {
    is Activity -> this
    is ContextWrapper -> baseContext.findActivity()
    else -> null
}

ExoPlayer composable

@Composable
fun ExoPlayer(
    viewModel: LearnViewModel,
    context: Context,
    onFinish: () -> Unit,
) {
  
  val playerView = remember { mutableStateOf(PlayerView(context)) }
  val lifecycleOwner = LocalLifecycleOwner.current
  var lifecycle by remember { mutableStateOf(Lifecycle.Event.ON_CREATE) }

  DisposableEffect(lifecycleOwner) {
      val observer = LifecycleEventObserver { _, event ->
          lifecycle = event
      }
      lifecycleOwner.lifecycle.addObserver(observer)
      onDispose {
          lifecycleOwner.lifecycle.removeObserver(observer = observer)
      }
  }
    
  LaunchedEffect(Unit) {
      while (true) {
          delay(500)
          if (viewModel.player.duration != -1L) {
              if (viewModel.player.duration / 100 == viewModel.player.currentPosition / 100) {
                  exitFullScreen(
                      playerView = playerView.value,
                      context = context,
                      player = viewModel.player
                  )
                  if (fullScreenDialog != null)
                      fullScreenDialog?.dismiss()
                  onFinish()
                  playerView.value.player?.pause()
              }
          }
      }
  }
  
  Box {
      AndroidView(
          factory = { context ->
              PlayerView(context).also {
                  it.player = viewModel.player

                  it.setShowNextButton(false)
                  it.setShowPreviousButton(false)
                  it.setShowFastForwardButton(false)
                  it.setShowRewindButton(false)

                  playerView.value = it

              }
          },
          update = {

              when (lifecycle) {
                  Lifecycle.Event.ON_PAUSE -> {
                      it.onPause()
                      it.player?.pause()
                  }

                  Lifecycle.Event.ON_RESUME -> {
                      it.onResume()
                  }

                  else -> Unit
              }
          },
          modifier = Modifier
              .fillMaxWidth()
              .aspectRatio(16 / 9f, matchHeightConstraintsFirst = true)
      )

      Image(
          painterResource(R.drawable.maximize),
          contentDescription = "",
          modifier = Modifier
              .height(24.dp)
              .width(24.dp)
              .clickable(
                  enabled = true,
                  onClick = {
                      enterFullScreen(
                          playerView = playerView.value,
                          context = context,
                          player = viewModel.player,
                      ) {
                          exitFullScreen(
                              playerView = playerView.value,
                              context = context,
                              player = viewModel.player
                          )
                      }
                  }
              ),
          )
      }
}

full screen function

private fun enterFullScreen(
    playerView: PlayerView,
    context: Context,
    player: Player,
    backPress: () -> Unit

) {

    context.findActivity()?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

    val fullScreenPlayerView = FullScreenPlayerView(context)

    fullScreenDialog =
        object : Dialog(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen) {
            @Deprecated("Deprecated in Java")
            @androidx.annotation.OptIn(UnstableApi::class)
            override fun onBackPressed() {
                backPress()
                super.onBackPressed()
            }
        }


    fullScreenDialog?.addContentView(
        fullScreenPlayerView,
        ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
    )

    fullScreenDialog?.show()


    fullScreenPlayerView.setShowNextButton(false)
    fullScreenPlayerView.setShowPreviousButton(false)
    fullScreenPlayerView.setShowFastForwardButton(false)
    fullScreenPlayerView.setShowRewindButton(false)

    val playbackParameters = PlaybackParameters(1f)
    player.playbackParameters = playbackParameters

    fullScreenPlayerView.setFullscreenButtonClickListener { full ->
        if (full)
            exitFullScreen(
                playerView = playerView,
                context = context,
                player = player
            )
        fullScreenDialog?.dismiss()

    }

    PlayerView.switchTargetView(player, playerView, fullScreenPlayerView)
}

minimize screen function

private fun exitFullScreen(player: Player, playerView: PlayerView, context: Context) {
    context.findActivity()?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    val fullScreenPlayerView = FullScreenPlayerView(context)
    PlayerView.switchTargetView(player, fullScreenPlayerView, playerView)
}
@Composable
fun LearnScreen(
navController: NavController,
viewModel: ExoPlayerViewModel = hiltViewModel(),
) {
ExoPlayer(
viewModel,
context,
) {
// end of video functions
}
}
@HiltViewModel
class LearnViewModel @Inject constructor(
val player: Player,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
private fun playVideo() {
savedStateHandle["videoUri"] = "YOUR URL"
player.setMediaItem(MediaItem.fromUri(savedStateHandle.get<String>("videoUri")!!))
player.prepare()
}
override fun onCleared() {
super.onCleared()
player.release()
}
init {
playVideo()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment