in ClassicsKotlin/app/src/main/java/com/android/tv/classics/fragments/NowPlayingFragment.kt [173:304]
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
backgroundType = PlaybackSupportFragment.BG_NONE
database = TvMediaDatabase.getInstance(requireContext())
val metadata = args.metadata
// Adds this program to the continue watching row, in case the user leaves before finishing
val programUri = TvLauncherUtils.upsertWatchNext(requireContext(), metadata)
if (programUri != null) lifecycleScope.launch(Dispatchers.IO) {
database.metadata().update(metadata.apply { watchNext = true })
}
// Initializes the video player
player = ExoPlayerFactory.newSimpleInstance(requireContext())
mediaSession = MediaSessionCompat(requireContext(), getString(R.string.app_name))
mediaSessionConnector = MediaSessionConnector(mediaSession)
// Listen to media session events. This is necessary for things like closed captions which
// can be triggered by things outside of our app, for example via Google Assistant
mediaSessionConnector.setCaptionCallback(object : MediaSessionConnector.CaptionCallback {
override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?): Boolean {
return false
}
override fun hasCaptions(player: Player): Boolean {
// TODO(owahltinez): handle captions
return true
}
override fun onSetCaptioningEnabled(player: Player, enabled: Boolean) {
// TODO(owahltinez): handle captions
Log.d(TAG, "onSetCaptioningEnabled() enabled=$enabled")
}
})
mediaSessionConnector.setRatingCallback(object : MediaSessionConnector.RatingCallback {
override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?): Boolean {
return false
}
override fun onSetRating(player: Player, rating: RatingCompat) {
// TODO(plammer): handle ratings
Log.d(TAG, "onSetRating() rating=$rating")
}
override fun onSetRating(player: Player, rating: RatingCompat, extras: Bundle) {
// TODO(plammer): handle ratings
Log.d(TAG, "onSetRating() rating=$rating")
}
})
// Links our video player with this Leanback video playback fragment
val playerAdapter = LeanbackPlayerAdapter(
requireContext(), player, PLAYER_UPDATE_INTERVAL_MILLIS)
// Enables pass-through of transport controls to our player instance
playerGlue = MediaPlayerGlue(requireContext(), playerAdapter).apply {
host = VideoSupportFragmentGlueHost(this@NowPlayingFragment)
// Adds playback state listeners
addPlayerCallback(object : PlaybackGlue.PlayerCallback() {
override fun onPreparedStateChanged(glue: PlaybackGlue?) {
super.onPreparedStateChanged(glue)
if (glue?.isPrepared == true) {
// When playback is ready, skip to last known position
val startingPosition = metadata.playbackPositionMillis ?: 0
Log.d(TAG, "Setting starting playback position to $startingPosition")
seekTo(startingPosition)
}
}
override fun onPlayCompleted(glue: PlaybackGlue?) {
super.onPlayCompleted(glue)
// Don't forget to remove irrelevant content from the continue watching row
TvLauncherUtils.removeFromWatchNext(requireContext(), args.metadata)
// When playback is finished, go back to the previous screen
val navController = Navigation.findNavController(
requireActivity(), R.id.fragment_container)
navController.currentDestination?.id?.let {
navController.popBackStack(it, true)
}
}
})
// Begins playback automatically
playWhenPrepared()
// Displays the current item's metadata
setMetadata(metadata)
}
// Setup the fragment adapter with our player glue presenter
adapter = ArrayObjectAdapter(playerGlue.playbackRowPresenter).apply {
add(playerGlue.controlsRow)
}
// Adds key listeners
playerGlue.host.setOnKeyInterceptListener { view, keyCode, event ->
// Early exit: if the controls overlay is visible, don't intercept any keys
if (playerGlue.host.isControlsOverlayVisible) return@setOnKeyInterceptListener false
// TODO(owahltinez): This workaround is necessary for navigation library to work with
// Leanback's [PlaybackSupportFragment]
if (!playerGlue.host.isControlsOverlayVisible &&
keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) {
Log.d(TAG, "Intercepting BACK key for fragment navigation")
val navController = Navigation.findNavController(
requireActivity(), R.id.fragment_container)
navController.currentDestination?.id?.let { navController.popBackStack(it, true) }
return@setOnKeyInterceptListener true
}
// Skips ahead when user presses DPAD_RIGHT
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && event.action == KeyEvent.ACTION_DOWN) {
playerGlue.skipForward()
preventControlsOverlay(playerGlue)
return@setOnKeyInterceptListener true
}
// Rewinds when user presses DPAD_LEFT
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && event.action == KeyEvent.ACTION_DOWN) {
playerGlue.skipBackward()
preventControlsOverlay(playerGlue)
return@setOnKeyInterceptListener true
}
false
}
}