2022-01-02 17:59:23 +01:00
/ * *
* Teapod
*
* Copyright 2020 - 2022 < seil0 @mosad . xyz >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*
* /
2021-02-06 19:02:12 +01:00
package org.mosad.teapod.ui.activity.player
2020-10-11 13:18:20 +02:00
2020-11-15 13:39:33 +01:00
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
2020-11-08 18:05:46 +01:00
import android.annotation.SuppressLint
2021-01-06 15:07:31 +01:00
import android.app.PictureInPictureParams
2021-01-08 09:31:50 +01:00
import android.content.Intent
2021-01-06 15:07:31 +01:00
import android.content.pm.PackageManager
import android.content.res.Configuration
2021-06-17 19:36:13 +02:00
import android.graphics.Rect
2020-10-11 14:14:38 +02:00
import android.os.Build
2020-10-11 13:18:20 +02:00
import android.os.Bundle
2020-10-11 14:14:38 +02:00
import android.util.Log
2021-01-06 15:07:31 +01:00
import android.util.Rational
2021-01-08 10:58:24 +01:00
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
2020-11-25 16:04:04 +01:00
import androidx.activity.viewModels
2021-01-06 15:07:31 +01:00
import androidx.annotation.RequiresApi
2020-10-11 13:18:20 +02:00
import androidx.appcompat.app.AppCompatActivity
2020-11-08 18:05:46 +01:00
import androidx.core.view.GestureDetectorCompat
2020-11-13 15:36:12 +01:00
import androidx.core.view.isVisible
2021-06-06 17:54:19 +02:00
import androidx.lifecycle.lifecycleScope
2020-11-21 19:40:55 +01:00
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.Player
2020-11-06 10:21:57 +01:00
import com.google.android.exoplayer2.ui.StyledPlayerControlView
2020-10-11 13:18:20 +02:00
import com.google.android.exoplayer2.util.Util
import kotlinx.android.synthetic.main.activity_player.*
2020-11-05 20:07:41 +01:00
import kotlinx.android.synthetic.main.player_controls.*
2020-11-20 11:20:11 +01:00
import kotlinx.coroutines.launch
2020-11-25 22:35:55 +01:00
import org.mosad.teapod.R
2021-12-26 20:22:00 +01:00
import org.mosad.teapod.parser.crunchyroll.NoneEpisode
2020-11-13 11:23:09 +01:00
import org.mosad.teapod.preferences.Preferences
2020-12-15 23:15:14 +01:00
import org.mosad.teapod.ui.components.EpisodesListPlayer
2020-12-20 20:21:27 +01:00
import org.mosad.teapod.ui.components.LanguageSettingsPlayer
2021-07-17 19:40:16 +02:00
import org.mosad.teapod.util.*
2020-11-15 17:17:56 +01:00
import java.util.*
2020-11-07 13:33:59 +01:00
import java.util.concurrent.TimeUnit
2020-11-15 17:17:56 +01:00
import kotlin.concurrent.scheduleAtFixedRate
2020-10-11 13:18:20 +02:00
class PlayerActivity : AppCompatActivity ( ) {
2020-11-25 22:35:55 +01:00
private val model : PlayerViewModel by viewModels ( )
2020-11-06 10:21:57 +01:00
private lateinit var controller : StyledPlayerControlView
2020-11-08 18:05:46 +01:00
private lateinit var gestureDetector : GestureDetectorCompat
2020-11-15 17:17:56 +01:00
private lateinit var timerUpdates : TimerTask
2020-10-11 13:18:20 +02:00
2021-01-08 10:58:24 +01:00
private var wasInPiP = false
2020-11-07 13:33:59 +01:00
private var remainingTime : Long = 0
2020-10-11 13:18:20 +02:00
2020-12-26 20:09:35 +01:00
private val rwdTime : Long = 10000.u naryMinus ( )
private val fwdTime : Long = 10000
2020-12-30 20:08:19 +01:00
private val defaultShowTimeoutMs = 5000
2020-10-11 14:14:38 +02:00
2020-10-11 13:18:20 +02:00
override fun onCreate ( savedInstanceState : Bundle ? ) {
super . onCreate ( savedInstanceState )
setContentView ( R . layout . activity _player )
2020-10-11 14:14:38 +02:00
hideBars ( ) // Initial hide the bars
2020-10-11 13:18:20 +02:00
2022-01-29 19:56:39 +01:00
model . loadMediaAsync (
2021-12-20 22:14:58 +01:00
intent . getStringExtra ( getString ( R . string . intent _season _id ) ) ?: " " ,
intent . getStringExtra ( getString ( R . string . intent _episode _id ) ) ?: " "
2020-11-25 16:04:04 +01:00
)
2020-12-27 20:11:01 +01:00
model . currentEpisodeChangedListener . add { onMediaChanged ( ) }
2020-11-08 18:05:46 +01:00
gestureDetector = GestureDetectorCompat ( this , PlayerGestureListener ( ) )
2020-12-28 20:45:52 +01:00
controller = video _view . findViewById ( R . id . exo _controller )
controller . isAnimationEnabled = false // disable controls (time-bar) animation
2020-12-30 20:08:19 +01:00
initExoPlayer ( ) // call in onCreate, exoplayer lives in view model
2020-12-27 20:11:01 +01:00
initGUI ( )
2020-11-05 20:07:41 +01:00
initActions ( )
2020-10-11 13:18:20 +02:00
}
2021-01-06 15:07:31 +01:00
/ * *
* once minimum is android 7.0 this can be simplified
* only onStart and onStop should be needed then
* see : https : //developer.android.com/guide/topics/ui/picture-in-picture#continuing_playback
* /
2020-10-11 13:18:20 +02:00
override fun onStart ( ) {
super . onStart ( )
if ( Util . SDK _INT > 23 ) {
initPlayer ( )
2020-11-25 22:35:55 +01:00
video _view ?. onResume ( )
2020-10-11 13:18:20 +02:00
}
}
override fun onResume ( ) {
super . onResume ( )
2021-01-06 15:07:31 +01:00
if ( isInPiPMode ( ) ) { return }
2020-10-11 13:18:20 +02:00
if ( Util . SDK _INT <= 23 ) {
initPlayer ( )
2020-11-25 22:35:55 +01:00
video _view ?. onResume ( )
2020-10-11 13:18:20 +02:00
}
}
override fun onPause ( ) {
super . onPause ( )
2021-01-06 15:07:31 +01:00
2021-03-11 19:11:12 +01:00
if ( isInPiPMode ( ) ) { return }
if ( Util . SDK _INT <= 23 ) { onPauseOnStop ( ) }
2020-10-11 13:18:20 +02:00
}
override fun onStop ( ) {
super . onStop ( )
2021-01-08 09:31:50 +01:00
2021-03-11 19:11:12 +01:00
if ( Util . SDK _INT > 23 ) { onPauseOnStop ( ) }
2021-01-08 10:58:24 +01:00
// if the player was in pip, it's on a different task
if ( wasInPiP ) { navToLauncherTask ( ) }
2021-03-04 20:19:54 +01:00
// if the player is in pip, remove the task, else we'll get a zombie
if ( isInPiPMode ( ) ) { finishAndRemoveTask ( ) }
2020-10-11 13:18:20 +02:00
}
2021-01-08 10:58:24 +01:00
/ * *
* used , when the player is in pip and the user selects a new media
* /
override fun onNewIntent ( intent : Intent ? ) {
super . onNewIntent ( intent )
2021-09-05 11:54:55 +02:00
// when the intent changed, load the new media and play it
2021-01-08 10:58:24 +01:00
intent ?. let {
2022-01-29 19:56:39 +01:00
model . loadMediaAsync (
2021-12-20 22:14:58 +01:00
it . getStringExtra ( getString ( R . string . intent _season _id ) ) ?: " " ,
it . getStringExtra ( getString ( R . string . intent _episode _id ) ) ?: " "
2021-01-08 10:58:24 +01:00
)
2021-12-26 20:22:00 +01:00
model . playCurrentMedia ( )
2021-01-08 10:58:24 +01:00
}
}
2021-01-06 15:07:31 +01:00
/ * *
* previous to android n , don ' t override
* /
@RequiresApi ( Build . VERSION_CODES . N )
override fun onUserLeaveHint ( ) {
super . onUserLeaveHint ( )
// start pip mode, if supported
if ( packageManager . hasSystemFeature ( PackageManager . FEATURE _PICTURE _IN _PICTURE ) ) {
if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . O ) {
@Suppress ( " deprecation " )
enterPictureInPictureMode ( )
} else {
val width = model . player . videoFormat ?. width ?: 0
val height = model . player . videoFormat ?. height ?: 0
2021-06-17 19:36:13 +02:00
val contentFrame : View = video _view . 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 )
}
2021-01-06 15:07:31 +01:00
val params = PictureInPictureParams . Builder ( )
. setAspectRatio ( Rational ( width , height ) )
2021-06-17 19:36:13 +02:00
. setSourceRectHint ( contentRect )
2021-01-06 15:07:31 +01:00
. build ( )
enterPictureInPictureMode ( params )
}
2021-01-08 09:31:50 +01:00
2021-01-08 10:58:24 +01:00
wasInPiP = isInPiPMode ( )
2021-01-06 15:07:31 +01:00
}
}
2021-01-08 09:31:50 +01:00
override fun onPictureInPictureModeChanged (
isInPictureInPictureMode : Boolean ,
newConfig : Configuration ?
) {
2021-01-06 15:07:31 +01:00
super . onPictureInPictureModeChanged ( isInPictureInPictureMode , newConfig )
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
2021-03-04 20:19:54 +01:00
video _view . useController = !is InPictureInPictureMode
2021-01-06 15:07:31 +01:00
}
2020-10-11 13:18:20 +02:00
private fun initPlayer ( ) {
2020-11-08 18:05:46 +01:00
initVideoView ( )
2020-11-13 15:36:12 +01:00
initTimeUpdates ( )
2020-12-28 20:45:52 +01:00
// if the player is ready or buffering we can simply play the file again, else do nothing
2021-03-04 20:19:54 +01:00
val playbackState = model . player . playbackState
if ( ( playbackState == ExoPlayer . STATE _READY || playbackState == ExoPlayer . STATE _BUFFERING ) ) {
2020-12-28 20:45:52 +01:00
model . player . play ( )
}
2020-11-08 18:05:46 +01:00
}
2020-12-28 20:45:52 +01:00
/ * *
* set play when ready and listeners
* /
2020-11-08 18:05:46 +01:00
private fun initExoPlayer ( ) {
2021-06-12 20:57:12 +02:00
model . player . addListener ( object : Player . Listener {
2020-10-11 13:18:20 +02:00
override fun onPlaybackStateChanged ( state : Int ) {
super . onPlaybackStateChanged ( state )
loading . visibility = when ( state ) {
2020-10-11 14:14:38 +02:00
ExoPlayer . STATE _READY -> View . GONE
2020-10-11 13:18:20 +02:00
ExoPlayer . STATE _BUFFERING -> View . VISIBLE
else -> View . GONE
}
2020-11-05 20:07:41 +01:00
exo _play _pause . visibility = when ( loading . visibility ) {
View . GONE -> View . VISIBLE
View . VISIBLE -> View . INVISIBLE
else -> View . VISIBLE
}
2020-11-13 15:36:12 +01:00
2022-01-18 18:03:56 +01:00
if ( state == ExoPlayer . STATE _ENDED && hasNextEpisode ( ) && Preferences . autoplay ) {
2020-12-27 20:11:01 +01:00
playNextEpisode ( )
2020-11-13 15:36:12 +01:00
}
2020-10-11 13:18:20 +02:00
}
} )
2022-01-29 19:56:39 +01:00
// revert back to the old behaviour (blocking init) in case there are any issues with async init
2020-12-27 20:11:01 +01:00
// start playing the current episode, after all needed player components have been initialized
2022-01-29 19:56:39 +01:00
//model.playCurrentMedia(model.currentPlayhead)
2020-11-08 18:05:46 +01:00
}
@SuppressLint ( " ClickableViewAccessibility " )
private fun initVideoView ( ) {
2020-12-26 20:09:35 +01:00
video _view . player = model . player
2020-10-11 13:18:20 +02:00
2020-10-11 14:14:38 +02:00
// when the player controls get hidden, hide the bars too
video _view . setControllerVisibilityListener {
2020-11-21 18:05:34 +01:00
when ( it ) {
2021-07-17 19:40:16 +02:00
View . GONE -> {
hideBars ( )
// TODO also hide the skip op button
}
2020-11-21 18:05:34 +01:00
View . VISIBLE -> updateControls ( )
2020-11-07 13:33:59 +01:00
}
}
2020-11-08 18:05:46 +01:00
video _view . setOnTouchListener { _ , event ->
gestureDetector . onTouchEvent ( event )
true
}
}
2020-11-05 20:07:41 +01:00
private fun initActions ( ) {
2021-01-06 15:07:31 +01:00
exo _close _player . setOnClickListener {
this . finish ( )
}
2020-11-16 19:23:06 +01:00
rwd _10 . setOnButtonClickListener { rewind ( ) }
2020-11-25 23:33:06 +01:00
ffwd _10 . setOnButtonClickListener { fastForward ( ) }
2020-11-13 15:36:12 +01:00
button _next _ep . setOnClickListener { playNextEpisode ( ) }
2021-07-17 19:40:16 +02:00
button _skip _op . setOnClickListener { skipOpening ( ) }
2020-12-20 20:21:27 +01:00
button _language . setOnClickListener { showLanguageSettings ( ) }
2020-12-14 23:46:55 +01:00
button _episodes . setOnClickListener { showEpisodesList ( ) }
2020-12-20 20:21:27 +01:00
button _next _ep _c . setOnClickListener { playNextEpisode ( ) }
2020-11-13 15:36:12 +01:00
}
2020-12-27 20:11:01 +01:00
private fun initGUI ( ) {
2021-12-26 20:22:00 +01:00
// TODO reimplement for cr
// if (model.media.type == DataTypes.MediaType.MOVIE) {
// button_episodes.visibility = View.GONE
// }
2020-12-27 20:11:01 +01:00
}
2020-11-15 17:17:56 +01:00
private fun initTimeUpdates ( ) {
if ( this :: timerUpdates . isInitialized ) {
timerUpdates . cancel ( )
}
2020-11-21 18:05:34 +01:00
timerUpdates = Timer ( ) . scheduleAtFixedRate ( 0 , 500 ) {
2021-06-06 17:54:19 +02:00
lifecycleScope . launch {
2021-07-17 19:40:16 +02:00
val currentPosition = model . player . currentPosition
2021-06-12 20:57:12 +02:00
val btnNextEpIsVisible = button _next _ep . isVisible
val controlsVisible = controller . isVisible
2020-11-13 15:36:12 +01:00
2021-07-17 19:40:16 +02:00
// make sure remaining time is > 0
2021-06-12 20:57:12 +02:00
if ( model . player . duration > 0 ) {
2021-07-17 19:40:16 +02:00
remainingTime = model . player . duration - currentPosition
2021-06-12 20:57:12 +02:00
remainingTime = if ( remainingTime < 0 ) 0 else remainingTime
2020-11-21 18:05:34 +01:00
}
2021-07-17 19:40:16 +02:00
// TODO add metaDB ending_start support
// if remaining time < 20 sec, a next ep is set, autoplay is enabled and not in pip:
// show next ep button
2020-11-22 14:20:17 +01:00
if ( remainingTime in 1. . 20000 ) {
2022-01-18 18:03:56 +01:00
if ( ! btnNextEpIsVisible && hasNextEpisode ( ) && Preferences . autoplay && !is InPiPMode ( ) ) {
2021-06-12 20:57:12 +02:00
showButtonNextEp ( )
2020-11-22 14:20:17 +01:00
}
2020-11-21 18:05:34 +01:00
} else if ( btnNextEpIsVisible ) {
2021-06-12 20:57:12 +02:00
hideButtonNextEp ( )
2020-11-15 13:39:33 +01:00
}
2020-11-15 17:17:56 +01:00
2021-07-17 19:40:16 +02:00
// if meta data is present and opening_start & opening_duration are valid, show skip opening
model . currentEpisodeMeta ?. let {
if ( it . openingDuration > 0 &&
currentPosition in it . openingStart .. ( it . openingStart + 10000 ) &&
! button _skip _op . isVisible
) {
showButtonSkipOp ( )
} else if ( button _skip _op . isVisible && currentPosition !in it . openingStart .. ( it . openingStart + 10000 ) ) {
// the button should only be visible, if currentEpisodeMeta != null
hideButtonSkipOp ( )
}
}
2020-11-21 18:05:34 +01:00
// if controls are visible, update them
if ( controlsVisible ) {
2021-06-12 20:57:12 +02:00
updateControls ( )
2020-11-13 15:36:12 +01:00
}
}
}
2020-11-05 20:07:41 +01:00
}
2021-03-11 19:11:12 +01:00
private fun onPauseOnStop ( ) {
video _view ?. onPause ( )
2020-12-27 20:31:18 +01:00
model . player . pause ( )
2020-11-15 17:17:56 +01:00
timerUpdates . cancel ( )
2020-10-11 13:18:20 +02:00
}
2020-11-21 18:05:34 +01:00
/ * *
* update the custom controls
* /
private fun updateControls ( ) {
// update remaining time label
val hours = TimeUnit . MILLISECONDS . toHours ( remainingTime ) % 24
val minutes = TimeUnit . MILLISECONDS . toMinutes ( remainingTime ) % 60
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 ) {
getString ( R . string . time _min _sec , minutes , seconds )
} else {
getString ( R . string . time _hour _min _sec , hours , minutes , seconds )
}
}
2020-12-27 20:11:01 +01:00
/ * *
2022-01-29 19:56:39 +01:00
* This methode is called , if the current episode has changed .
* Update title text and next ep button visibility .
* If the currentEpisode changed to NoneEpisode , exit the activity .
2020-12-27 20:11:01 +01:00
* /
private fun onMediaChanged ( ) {
2022-01-29 19:56:39 +01:00
if ( model . currentEpisode == NoneEpisode ) {
Log . e ( javaClass . name , " No media was set. " )
this . finish ( )
}
2020-12-27 20:11:01 +01:00
exo _text _title . text = model . getMediaTitle ( )
2022-01-18 18:03:56 +01:00
// hide the next episode button, if there is none
button _next _ep _c . visibility = if ( hasNextEpisode ( ) ) View . VISIBLE else View . GONE
}
/ * *
* Check if the current episode has a next episode .
*
* @return Boolean : true if there is a next episode , else false .
* /
private fun hasNextEpisode ( ) : Boolean {
return ( model . currentEpisode . nextEpisodeId != null && ! model . currentEpisodeIsLastEpisode ( ) )
2020-12-27 20:11:01 +01:00
}
2020-11-20 11:20:11 +01:00
/ * *
* TODO set position of rewind / fast forward indicators programmatically
* /
2020-11-08 18:05:46 +01:00
private fun rewind ( ) {
2020-12-26 20:09:35 +01:00
model . seekToOffset ( rwdTime )
2020-11-20 11:20:11 +01:00
// hide/show needed components
exo _double _tap _indicator . visibility = View . VISIBLE
ffwd _10 _indicator . visibility = View . INVISIBLE
2020-11-21 18:05:34 +01:00
rwd _10 . visibility = View . INVISIBLE
2020-11-20 11:20:11 +01:00
rwd _10 _indicator . onAnimationEndCallback = {
exo _double _tap _indicator . visibility = View . GONE
ffwd _10 _indicator . visibility = View . VISIBLE
2020-11-21 18:05:34 +01:00
rwd _10 . visibility = View . VISIBLE
2020-11-20 11:20:11 +01:00
}
// run animation
rwd _10 _indicator . runOnClickAnimation ( )
2020-11-08 18:05:46 +01:00
}
2020-11-25 23:33:06 +01:00
private fun fastForward ( ) {
2020-12-26 20:09:35 +01:00
model . seekToOffset ( fwdTime )
2020-11-20 11:20:11 +01:00
// hide/show needed components
exo _double _tap _indicator . visibility = View . VISIBLE
rwd _10 _indicator . visibility = View . INVISIBLE
ffwd _10 . 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
}
// run animation
ffwd _10 _indicator . runOnClickAnimation ( )
}
2020-12-27 20:11:01 +01:00
private fun playNextEpisode ( ) {
model . playNextEpisode ( )
2020-11-21 18:05:34 +01:00
hideButtonNextEp ( )
2020-12-02 11:14:09 +01:00
}
2021-07-17 19:40:16 +02:00
private fun skipOpening ( ) {
// calculate the seek time
model . currentEpisodeMeta ?. let {
val seekTime = ( it . openingStart + it . openingDuration ) - model . player . currentPosition
model . seekToOffset ( seekTime )
}
}
2020-11-15 13:39:33 +01:00
/ * *
* show the next episode button
* TODO improve the show animation
* /
private fun showButtonNextEp ( ) {
2021-07-17 19:40:16 +02:00
button _next _ep . isVisible = true
2020-11-15 13:39:33 +01:00
button _next _ep . alpha = 0.0f
button _next _ep . animate ( )
. alpha ( 1.0f )
. setListener ( null )
}
/ * *
* hide the next episode button
* TODO improve the hide animation
* /
private fun hideButtonNextEp ( ) {
button _next _ep . animate ( )
. alpha ( 0.0f )
. setListener ( object : AnimatorListenerAdapter ( ) {
2020-11-20 11:20:11 +01:00
override fun onAnimationEnd ( animation : Animator ? ) {
super . onAnimationEnd ( animation )
2021-07-17 19:40:16 +02:00
button _next _ep . isVisible = false
}
} )
}
private fun showButtonSkipOp ( ) {
button _skip _op . isVisible = true
button _skip _op . alpha = 0.0f
button _skip _op . animate ( )
. alpha ( 1.0f )
. setListener ( null )
}
private fun hideButtonSkipOp ( ) {
button _skip _op . animate ( )
. alpha ( 0.0f )
. setListener ( object : AnimatorListenerAdapter ( ) {
override fun onAnimationEnd ( animation : Animator ? ) {
super . onAnimationEnd ( animation )
button _skip _op . isVisible = false
2020-11-20 11:20:11 +01:00
}
} )
2020-11-15 13:39:33 +01:00
}
2020-12-14 23:46:55 +01:00
private fun showEpisodesList ( ) {
2020-12-15 23:15:14 +01:00
val episodesList = EpisodesListPlayer ( this , model = model ) . apply {
2020-12-26 20:09:35 +01:00
onViewRemovedAction = { model . player . play ( ) }
2020-12-15 23:15:14 +01:00
}
player _layout . addView ( episodesList )
2020-12-30 20:08:19 +01:00
pauseAndHideControls ( )
2020-12-20 20:21:27 +01:00
}
private fun showLanguageSettings ( ) {
val languageSettings = LanguageSettingsPlayer ( this , model = model ) . apply {
2020-12-26 20:09:35 +01:00
onViewRemovedAction = { model . player . play ( ) }
2020-12-20 20:21:27 +01:00
}
player _layout . addView ( languageSettings )
2020-12-30 20:08:19 +01:00
pauseAndHideControls ( )
}
2020-12-20 20:21:27 +01:00
2020-12-30 20:08:19 +01:00
/ * *
* pause playback and hide controls
* /
private fun pauseAndHideControls ( ) {
model . player . pause ( ) // showTimeoutMs is set to 0 when calling pause, but why
controller . showTimeoutMs = defaultShowTimeoutMs // fix showTimeoutMs set to 0
controller . hide ( )
2020-12-14 23:46:55 +01:00
}
2020-11-08 18:05:46 +01:00
inner class PlayerGestureListener : GestureDetector . SimpleOnGestureListener ( ) {
/ * *
* on single tap hide or show the controls
* /
override fun onSingleTapConfirmed ( e : MotionEvent ? ) : Boolean {
2021-01-06 15:07:31 +01:00
if ( !is InPiPMode ( ) ) {
2021-07-17 19:40:16 +02:00
if ( controller . isVisible ) controller . hide ( ) else controller . show ( )
2021-01-06 15:07:31 +01:00
}
2020-11-08 18:05:46 +01:00
return true
}
/ * *
* on double tap rewind or forward
* /
override fun onDoubleTap ( e : MotionEvent ? ) : Boolean {
2020-11-15 17:17:56 +01:00
val eventPosX = e ?. x ?. toInt ( ) ?: 0
val viewCenterX = video _view . measuredWidth / 2
2020-11-08 18:05:46 +01:00
// if the event position is on the left side rewind, if it's on the right forward
2020-12-15 23:15:14 +01:00
if ( eventPosX < viewCenterX ) rewind ( ) else fastForward ( )
2020-11-08 18:05:46 +01:00
return true
}
/ * *
* not used
* /
override fun onDoubleTapEvent ( e : MotionEvent ? ) : Boolean {
return true
}
2020-11-21 18:05:34 +01:00
/ * *
* on long press toggle pause / play
* /
2020-11-20 11:20:11 +01:00
override fun onLongPress ( e : MotionEvent ? ) {
2020-12-26 20:09:35 +01:00
model . togglePausePlay ( )
2020-11-20 11:20:11 +01:00
}
2020-11-08 18:05:46 +01:00
}
2020-10-11 13:18:20 +02:00
}