|
|
|
@ -47,10 +47,10 @@ import com.google.android.exoplayer2.ExoPlayer
|
|
|
|
|
import com.google.android.exoplayer2.Player
|
|
|
|
|
import com.google.android.exoplayer2.ui.StyledPlayerControlView
|
|
|
|
|
import com.google.android.exoplayer2.util.Util
|
|
|
|
|
import kotlinx.android.synthetic.main.activity_player.*
|
|
|
|
|
import kotlinx.android.synthetic.main.player_controls.*
|
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
import org.mosad.teapod.R
|
|
|
|
|
import org.mosad.teapod.databinding.ActivityPlayerBinding
|
|
|
|
|
import org.mosad.teapod.databinding.PlayerControlsBinding
|
|
|
|
|
import org.mosad.teapod.parser.crunchyroll.NoneEpisode
|
|
|
|
|
import org.mosad.teapod.preferences.Preferences
|
|
|
|
|
import org.mosad.teapod.ui.activity.player.fragment.EpisodeListDialogFragment
|
|
|
|
@ -65,6 +65,8 @@ import kotlin.concurrent.scheduleAtFixedRate
|
|
|
|
|
class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
|
|
|
|
|
private val model: PlayerViewModel by viewModels()
|
|
|
|
|
private lateinit var playerBinding: ActivityPlayerBinding
|
|
|
|
|
private lateinit var controlsBinding: PlayerControlsBinding
|
|
|
|
|
|
|
|
|
|
private lateinit var controller: StyledPlayerControlView
|
|
|
|
|
private lateinit var gestureDetector: GestureDetectorCompat
|
|
|
|
@ -82,6 +84,11 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
setContentView(R.layout.activity_player)
|
|
|
|
|
hideBars() // Initial hide the bars
|
|
|
|
|
|
|
|
|
|
playerBinding = ActivityPlayerBinding.bind(findViewById(R.id.player_root))
|
|
|
|
|
|
|
|
|
|
println(findViewById(R.id.player_controls_root))
|
|
|
|
|
controlsBinding = PlayerControlsBinding.bind(findViewById(R.id.player_controls_root))
|
|
|
|
|
|
|
|
|
|
model.loadMediaAsync(
|
|
|
|
|
intent.getStringExtra(getString(R.string.intent_season_id)) ?: "",
|
|
|
|
|
intent.getStringExtra(getString(R.string.intent_episode_id)) ?: ""
|
|
|
|
@ -89,7 +96,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
model.currentEpisodeChangedListener.add { onMediaChanged() }
|
|
|
|
|
gestureDetector = GestureDetectorCompat(this, PlayerGestureListener())
|
|
|
|
|
|
|
|
|
|
controller = video_view.findViewById(R.id.exo_controller)
|
|
|
|
|
controller = playerBinding.videoView.findViewById(R.id.exo_controller)
|
|
|
|
|
controller.isAnimationEnabled = false // disable controls (time-bar) animation
|
|
|
|
|
|
|
|
|
|
initExoPlayer() // call in onCreate, exoplayer lives in view model
|
|
|
|
@ -106,7 +113,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
super.onStart()
|
|
|
|
|
if (Util.SDK_INT > 23) {
|
|
|
|
|
initPlayer()
|
|
|
|
|
video_view?.onResume()
|
|
|
|
|
playerBinding.videoView.onResume()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -116,7 +123,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
|
|
|
|
|
if (Util.SDK_INT <= 23) {
|
|
|
|
|
initPlayer()
|
|
|
|
|
video_view?.onResume()
|
|
|
|
|
playerBinding.videoView.onResume()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -168,7 +175,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
} else {
|
|
|
|
|
val width = model.player.videoFormat?.width ?: 0
|
|
|
|
|
val height = model.player.videoFormat?.height ?: 0
|
|
|
|
|
val contentFrame: View = video_view.findViewById(R.id.exo_content_frame)
|
|
|
|
|
val contentFrame: View = playerBinding.videoView.findViewById(R.id.exo_content_frame)
|
|
|
|
|
val contentRect = with(contentFrame) {
|
|
|
|
|
val (x, y) = intArrayOf(0, 0).also(::getLocationInWindow)
|
|
|
|
|
Rect(x, y, x + width, y + height)
|
|
|
|
@ -192,7 +199,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
|
|
|
|
|
|
|
|
|
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
|
|
|
|
|
video_view.useController = !isInPictureInPictureMode
|
|
|
|
|
playerBinding.videoView.useController = !isInPictureInPictureMode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun initPlayer() {
|
|
|
|
@ -214,17 +221,13 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
override fun onPlaybackStateChanged(state: Int) {
|
|
|
|
|
super.onPlaybackStateChanged(state)
|
|
|
|
|
|
|
|
|
|
loading.visibility = when (state) {
|
|
|
|
|
playerBinding.loading.visibility = when (state) {
|
|
|
|
|
ExoPlayer.STATE_READY -> View.GONE
|
|
|
|
|
ExoPlayer.STATE_BUFFERING -> View.VISIBLE
|
|
|
|
|
else -> View.GONE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exo_play_pause.visibility = when (loading.visibility) {
|
|
|
|
|
View.GONE -> View.VISIBLE
|
|
|
|
|
View.VISIBLE -> View.INVISIBLE
|
|
|
|
|
else -> View.VISIBLE
|
|
|
|
|
}
|
|
|
|
|
controlsBinding.exoPlayPause.isVisible = !playerBinding.loading.isVisible
|
|
|
|
|
|
|
|
|
|
if (state == ExoPlayer.STATE_ENDED && hasNextEpisode() && Preferences.autoplay) {
|
|
|
|
|
playNextEpisode()
|
|
|
|
@ -239,10 +242,10 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
|
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
private fun initVideoView() {
|
|
|
|
|
video_view.player = model.player
|
|
|
|
|
playerBinding.videoView.player = model.player
|
|
|
|
|
|
|
|
|
|
// when the player controls get hidden, hide the bars too
|
|
|
|
|
video_view.setControllerVisibilityListener {
|
|
|
|
|
playerBinding.videoView.setControllerVisibilityListener {
|
|
|
|
|
when (it) {
|
|
|
|
|
View.GONE -> {
|
|
|
|
|
hideBars()
|
|
|
|
@ -252,23 +255,23 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video_view.setOnTouchListener { _, event ->
|
|
|
|
|
playerBinding.videoView.setOnTouchListener { _, event ->
|
|
|
|
|
gestureDetector.onTouchEvent(event)
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun initActions() {
|
|
|
|
|
exo_close_player.setOnClickListener {
|
|
|
|
|
controlsBinding.exoClosePlayer.setOnClickListener {
|
|
|
|
|
this.finish()
|
|
|
|
|
}
|
|
|
|
|
rwd_10.setOnButtonClickListener { rewind() }
|
|
|
|
|
ffwd_10.setOnButtonClickListener { fastForward() }
|
|
|
|
|
button_next_ep.setOnClickListener { playNextEpisode() }
|
|
|
|
|
button_skip_op.setOnClickListener { skipOpening() }
|
|
|
|
|
button_language.setOnClickListener { showLanguageSettings() }
|
|
|
|
|
button_episodes.setOnClickListener { showEpisodesList() }
|
|
|
|
|
button_next_ep_c.setOnClickListener { playNextEpisode() }
|
|
|
|
|
controlsBinding.rwd10.setOnButtonClickListener { rewind() }
|
|
|
|
|
controlsBinding.ffwd10.setOnButtonClickListener { fastForward() }
|
|
|
|
|
playerBinding.buttonNextEp.setOnClickListener { playNextEpisode() }
|
|
|
|
|
playerBinding.buttonSkipOp.setOnClickListener { skipOpening() }
|
|
|
|
|
controlsBinding.buttonLanguage.setOnClickListener { showLanguageSettings() }
|
|
|
|
|
controlsBinding.buttonEpisodes.setOnClickListener { showEpisodesList() }
|
|
|
|
|
controlsBinding.buttonNextEpC.setOnClickListener { playNextEpisode() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun initGUI() {
|
|
|
|
@ -286,7 +289,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
timerUpdates = Timer().scheduleAtFixedRate(0, 500) {
|
|
|
|
|
lifecycleScope.launch {
|
|
|
|
|
val currentPosition = model.player.currentPosition
|
|
|
|
|
val btnNextEpIsVisible = button_next_ep.isVisible
|
|
|
|
|
val btnNextEpIsVisible = playerBinding.buttonNextEp.isVisible
|
|
|
|
|
val controlsVisible = controller.isVisible
|
|
|
|
|
|
|
|
|
|
// make sure remaining time is > 0
|
|
|
|
@ -310,10 +313,12 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
model.currentEpisodeMeta?.let {
|
|
|
|
|
if (it.openingDuration > 0 &&
|
|
|
|
|
currentPosition in it.openingStart..(it.openingStart + 10000) &&
|
|
|
|
|
!button_skip_op.isVisible
|
|
|
|
|
!playerBinding.buttonSkipOp.isVisible
|
|
|
|
|
) {
|
|
|
|
|
showButtonSkipOp()
|
|
|
|
|
} else if (button_skip_op.isVisible && currentPosition !in it.openingStart..(it.openingStart + 10000)) {
|
|
|
|
|
} else if (playerBinding.buttonSkipOp.isVisible &&
|
|
|
|
|
currentPosition !in it.openingStart..(it.openingStart + 10000)
|
|
|
|
|
) {
|
|
|
|
|
// the button should only be visible, if currentEpisodeMeta != null
|
|
|
|
|
hideButtonSkipOp()
|
|
|
|
|
}
|
|
|
|
@ -328,7 +333,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun onPauseOnStop() {
|
|
|
|
|
video_view?.onPause()
|
|
|
|
|
playerBinding.videoView.onPause()
|
|
|
|
|
model.player.pause()
|
|
|
|
|
timerUpdates.cancel()
|
|
|
|
|
}
|
|
|
|
@ -343,7 +348,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
val seconds = TimeUnit.MILLISECONDS.toSeconds(remainingTime) % 60
|
|
|
|
|
|
|
|
|
|
// if remaining time is below 60 minutes, don't show hours
|
|
|
|
|
exo_remaining.text = if (TimeUnit.MILLISECONDS.toMinutes(remainingTime) < 60) {
|
|
|
|
|
controlsBinding.exoRemaining.text = if (TimeUnit.MILLISECONDS.toMinutes(remainingTime) < 60) {
|
|
|
|
|
getString(R.string.time_min_sec, minutes, seconds)
|
|
|
|
|
} else {
|
|
|
|
|
getString(R.string.time_hour_min_sec, hours, minutes, seconds)
|
|
|
|
@ -361,10 +366,10 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
this.finish()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exo_text_title.text = model.getMediaTitle()
|
|
|
|
|
controlsBinding.exoTextTitle.text = model.getMediaTitle()
|
|
|
|
|
|
|
|
|
|
// hide the next episode button, if there is none
|
|
|
|
|
button_next_ep_c.visibility = if (hasNextEpisode()) View.VISIBLE else View.GONE
|
|
|
|
|
controlsBinding.buttonNextEpC.isVisible = hasNextEpisode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -384,36 +389,36 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
model.seekToOffset(rwdTime)
|
|
|
|
|
|
|
|
|
|
// hide/show needed components
|
|
|
|
|
exo_double_tap_indicator.visibility = View.VISIBLE
|
|
|
|
|
ffwd_10_indicator.visibility = View.INVISIBLE
|
|
|
|
|
rwd_10.visibility = View.INVISIBLE
|
|
|
|
|
playerBinding.exoDoubleTapIndicator.visibility = View.VISIBLE
|
|
|
|
|
playerBinding.ffwd10Indicator.visibility = View.INVISIBLE
|
|
|
|
|
controlsBinding.rwd10.visibility = View.INVISIBLE
|
|
|
|
|
|
|
|
|
|
rwd_10_indicator.onAnimationEndCallback = {
|
|
|
|
|
exo_double_tap_indicator.visibility = View.GONE
|
|
|
|
|
ffwd_10_indicator.visibility = View.VISIBLE
|
|
|
|
|
rwd_10.visibility = View.VISIBLE
|
|
|
|
|
playerBinding.rwd10Indicator.onAnimationEndCallback = {
|
|
|
|
|
playerBinding.exoDoubleTapIndicator.visibility = View.GONE
|
|
|
|
|
playerBinding.ffwd10Indicator.visibility = View.VISIBLE
|
|
|
|
|
controlsBinding.rwd10.visibility = View.VISIBLE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// run animation
|
|
|
|
|
rwd_10_indicator.runOnClickAnimation()
|
|
|
|
|
playerBinding.rwd10Indicator.runOnClickAnimation()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun fastForward() {
|
|
|
|
|
model.seekToOffset(fwdTime)
|
|
|
|
|
|
|
|
|
|
// hide/show needed components
|
|
|
|
|
exo_double_tap_indicator.visibility = View.VISIBLE
|
|
|
|
|
rwd_10_indicator.visibility = View.INVISIBLE
|
|
|
|
|
ffwd_10.visibility = View.INVISIBLE
|
|
|
|
|
playerBinding.exoDoubleTapIndicator.visibility = View.VISIBLE
|
|
|
|
|
playerBinding.rwd10Indicator.visibility = View.INVISIBLE
|
|
|
|
|
controlsBinding.ffwd10.visibility = View.INVISIBLE
|
|
|
|
|
|
|
|
|
|
ffwd_10_indicator.onAnimationEndCallback = {
|
|
|
|
|
exo_double_tap_indicator.visibility = View.GONE
|
|
|
|
|
rwd_10_indicator.visibility = View.VISIBLE
|
|
|
|
|
ffwd_10.visibility = View.VISIBLE
|
|
|
|
|
playerBinding.ffwd10Indicator.onAnimationEndCallback = {
|
|
|
|
|
playerBinding.exoDoubleTapIndicator.visibility = View.GONE
|
|
|
|
|
playerBinding.rwd10Indicator.visibility = View.VISIBLE
|
|
|
|
|
controlsBinding.ffwd10.visibility = View.VISIBLE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// run animation
|
|
|
|
|
ffwd_10_indicator.runOnClickAnimation()
|
|
|
|
|
playerBinding.ffwd10Indicator.runOnClickAnimation()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun playNextEpisode() {
|
|
|
|
@ -434,10 +439,10 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
* TODO improve the show animation
|
|
|
|
|
*/
|
|
|
|
|
private fun showButtonNextEp() {
|
|
|
|
|
button_next_ep.isVisible = true
|
|
|
|
|
button_next_ep.alpha = 0.0f
|
|
|
|
|
playerBinding.buttonNextEp.isVisible = true
|
|
|
|
|
playerBinding.buttonNextEp.alpha = 0.0f
|
|
|
|
|
|
|
|
|
|
button_next_ep.animate()
|
|
|
|
|
playerBinding.buttonNextEp.animate()
|
|
|
|
|
.alpha(1.0f)
|
|
|
|
|
.setListener(null)
|
|
|
|
|
}
|
|
|
|
@ -447,33 +452,33 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
* TODO improve the hide animation
|
|
|
|
|
*/
|
|
|
|
|
private fun hideButtonNextEp() {
|
|
|
|
|
button_next_ep.animate()
|
|
|
|
|
playerBinding.buttonNextEp.animate()
|
|
|
|
|
.alpha(0.0f)
|
|
|
|
|
.setListener(object : AnimatorListenerAdapter() {
|
|
|
|
|
override fun onAnimationEnd(animation: Animator?) {
|
|
|
|
|
super.onAnimationEnd(animation)
|
|
|
|
|
button_next_ep.isVisible = false
|
|
|
|
|
playerBinding.buttonNextEp.isVisible = false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showButtonSkipOp() {
|
|
|
|
|
button_skip_op.isVisible = true
|
|
|
|
|
button_skip_op.alpha = 0.0f
|
|
|
|
|
playerBinding.buttonSkipOp.isVisible = true
|
|
|
|
|
playerBinding.buttonSkipOp.alpha = 0.0f
|
|
|
|
|
|
|
|
|
|
button_skip_op.animate()
|
|
|
|
|
playerBinding.buttonSkipOp.animate()
|
|
|
|
|
.alpha(1.0f)
|
|
|
|
|
.setListener(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun hideButtonSkipOp() {
|
|
|
|
|
button_skip_op.animate()
|
|
|
|
|
playerBinding.buttonSkipOp.animate()
|
|
|
|
|
.alpha(0.0f)
|
|
|
|
|
.setListener(object : AnimatorListenerAdapter() {
|
|
|
|
|
override fun onAnimationEnd(animation: Animator?) {
|
|
|
|
|
super.onAnimationEnd(animation)
|
|
|
|
|
button_skip_op.isVisible = false
|
|
|
|
|
playerBinding.buttonSkipOp.isVisible = false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -516,7 +521,7 @@ class PlayerActivity : AppCompatActivity() {
|
|
|
|
|
*/
|
|
|
|
|
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
|
|
|
|
val eventPosX = e?.x?.toInt() ?: 0
|
|
|
|
|
val viewCenterX = video_view.measuredWidth / 2
|
|
|
|
|
val viewCenterX = playerBinding.videoView.measuredWidth / 2
|
|
|
|
|
|
|
|
|
|
// if the event position is on the left side rewind, if it's on the right forward
|
|
|
|
|
if (eventPosX < viewCenterX) rewind() else fastForward()
|
|
|
|
|