Clean up PlayerActivity and PlayerViewModel

* use Local instead of streamURL to save selected language, this allows nextEp/ selected Eps to use previously selected language
* hide episodes button, if media is a movie
This commit is contained in:
Jannik 2020-12-27 20:11:01 +01:00
parent 94da8c6cee
commit 7845770067
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
5 changed files with 75 additions and 91 deletions

View File

@ -11,7 +11,7 @@ android {
minSdkVersion 23
targetSdkVersion 30
versionCode 2100 //00.02.100
versionName "0.2.91"
versionName "0.2.92"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime()

View File

@ -38,7 +38,6 @@ class PlayerActivity : AppCompatActivity() {
private lateinit var gestureDetector: GestureDetectorCompat
private lateinit var timerUpdates: TimerTask
private var nextEpManually = false
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition: Long = 0
@ -62,13 +61,13 @@ class PlayerActivity : AppCompatActivity() {
intent.getIntExtra(getString(R.string.intent_media_id), 0),
intent.getIntExtra(getString(R.string.intent_episode_id), 0)
)
model.currentEpisodeChangedListener.add { onMediaChanged() }
gestureDetector = GestureDetectorCompat(this, PlayerGestureListener())
initGUI()
initActions()
}
override fun onStart() {
super.onStart()
if (Util.SDK_INT > 23) {
@ -117,17 +116,10 @@ class PlayerActivity : AppCompatActivity() {
initExoPlayer()
initVideoView()
initTimeUpdates()
// add listener after initial media is started
model.currentEpisodeChangedListener.add {
nextEpManually = true // make sure on STATE_ENDED doesn't skip another episode
playCurrentMedia(false)
}
}
private fun initExoPlayer() {
controller = video_view.findViewById(R.id.exo_controller)
controller.isAnimationEnabled = false // disable controls (time-bar) animation
model.player.playWhenReady = playWhenReady
@ -148,16 +140,13 @@ class PlayerActivity : AppCompatActivity() {
}
if (state == ExoPlayer.STATE_ENDED && model.nextEpisode != null && Preferences.autoplay) {
// if next episode btn was clicked, skipp playNextEpisode() on STATE_ENDED
if (!nextEpManually) {
playNextEpisode()
}
nextEpManually = false
playNextEpisode()
}
}
})
playCurrentMedia(true) // start initial media
// start playing the current episode, after all needed player components have been initialized
model.playEpisode(model.currentEpisode, true, playbackPosition)
}
@SuppressLint("ClickableViewAccessibility")
@ -188,6 +177,12 @@ class PlayerActivity : AppCompatActivity() {
button_next_ep_c.setOnClickListener { playNextEpisode() }
}
private fun initGUI() {
if (model.media.type == DataTypes.MediaType.MOVIE) {
button_episodes.visibility = View.GONE
}
}
private fun initTimeUpdates() {
if (this::timerUpdates.isInitialized) {
timerUpdates.cancel()
@ -250,6 +245,17 @@ class PlayerActivity : AppCompatActivity() {
}
}
/**
* update title text and next ep button visibility, set ignoreNextStateEnded
*/
private fun onMediaChanged() {
exo_text_title.text = model.getMediaTitle()
if (model.nextEpisode == null) {
button_next_ep_c.visibility = View.GONE
}
}
/**
* TODO set position of rewind/fast forward indicators programmatically
*/
@ -290,36 +296,11 @@ class PlayerActivity : AppCompatActivity() {
ffwd_10_indicator.runOnClickAnimation()
}
private fun playNextEpisode() = model.nextEpisode?.let {
model.nextEpisode() // current = next, next = new or null
private fun playNextEpisode() {
model.playNextEpisode()
hideButtonNextEp()
}
/**
* start playing a episode
* Note: movies are episodes too!
*/
private fun playCurrentMedia(seekToPosition: Boolean) {
// update the gui
exo_text_title.text = if (model.media.type == DataTypes.MediaType.TVSHOW) {
getString(
R.string.component_episode_title,
model.currentEpisode.number,
model.currentEpisode.description
)
} else {
model.currentEpisode.title
}
if (model.nextEpisode == null) {
button_next_ep_c.visibility = View.GONE
}
// update player/media item
val seekPosition = if (seekToPosition) playbackPosition else 0
model.playMedia(model.currentEpisode, true, seekPosition)
}
/**
* hide the status and navigation bar
*/

View File

@ -11,6 +11,7 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import kotlinx.coroutines.runBlocking
import org.mosad.teapod.R
import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.fragments.MediaFragment
@ -19,7 +20,6 @@ import org.mosad.teapod.util.Episode
import org.mosad.teapod.util.Media
import java.util.*
import kotlin.collections.ArrayList
import kotlin.properties.Delegates
/**
* PlayerViewModel handles all stuff related to media/episodes.
@ -35,18 +35,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
var media: Media = Media(-1, "", DataTypes.MediaType.OTHER)
internal set
// TODO rework
var currentEpisode: Episode by Delegates.observable(Episode()) { _, _, _ ->
currentEpisodeChangedListener.forEach { it() }
MediaFragment.instance.updateWatchedState(currentEpisode) // watchedCallback for the new episode
currentStreamUrl = autoSelectStream(currentEpisode)
nextEpisode = selectNextEpisode() // update next ep
}
var currentStreamUrl = "" // TODO don't save selected stream for language, instead save selected language
var currentEpisode = Episode()
internal set
var nextEpisode: Episode? = null
internal set
var currentLanguage: Locale = Locale.ROOT
internal set
fun loadMedia(mediaId: Int, episodeId: Int) {
runBlocking {
@ -54,31 +48,23 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
}
currentEpisode = media.getEpisodeById(episodeId)
currentStreamUrl = autoSelectStream(currentEpisode)
currentLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN
nextEpisode = selectNextEpisode()
}
fun changeLanguage(url: String) {
println("new stream is: $url")
fun setLanguage(language: Locale) {
println("new language is: $language")
currentLanguage = language
val seekTime = player.currentPosition
val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.fromUri(Uri.parse(url))
MediaItem.fromUri(Uri.parse(currentEpisode.getPreferredStream(language).url))
)
currentStreamUrl = url
playMedia(mediaSource, true, seekTime)
}
/**
* update currentEpisode
* updateWatchedState for the next (now current) episode
*/
fun nextEpisode() = nextEpisode?.let { nextEp ->
currentEpisode = nextEp // set current ep to next ep
}
// player actions
fun seekToOffset(offset: Long) {
player.seekTo(player.currentPosition + offset)
}
@ -87,12 +73,30 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
if (player.isPlaying) player.pause() else player.play()
}
fun playMedia(episode: Episode, replace: Boolean = false, seekPosition: Long = 0) {
val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.fromUri(Uri.parse(autoSelectStream(episode)))
)
/**
* play the next episode, if nextEpisode is not null
*/
fun playNextEpisode() = nextEpisode?.let { it ->
playEpisode(it, replace = true)
}
/**
* set currentEpisode to the param episode and start playing it
* update nextEpisode to reflect the change
*
* updateWatchedState for the next (now current) episode
*/
fun playEpisode(episode: Episode, replace: Boolean = false, seekPosition: Long = 0) {
currentEpisode = episode
nextEpisode = selectNextEpisode()
currentEpisodeChangedListener.forEach { it() } // update player gui (title)
val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.fromUri(Uri.parse( episode.getPreferredStream(currentLanguage).url))
)
playMedia(mediaSource, replace, seekPosition)
MediaFragment.instance.updateWatchedState(currentEpisode) // watchedCallback for the new episode
}
fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) {
@ -104,6 +108,18 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
}
}
fun getMediaTitle(): String {
return if (media.type == DataTypes.MediaType.TVSHOW) {
getApplication<Application>().getString(
R.string.component_episode_title,
currentEpisode.number,
currentEpisode.description
)
} else {
currentEpisode.title
}
}
/**
* Based on the current episodeId, get the next episode. If there is no next
* episode, return null
@ -117,18 +133,4 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
}
}
/**
* If preferSecondary use the japanese stream, if present.
* If the preferred stream is not present the default (first)
* stream will be used
*/
private fun autoSelectStream(episode: Episode): String {
return if (Preferences.preferSecondary) {
episode.getPreferredStream(Locale.JAPANESE).url
} else {
episode.getPreferredStream(Locale.GERMAN).url
}
}
}

View File

@ -32,7 +32,7 @@ class EpisodesListPlayer @JvmOverloads constructor(
adapterRecEpisodes.onImageClick = { _, position ->
(this.parent as ViewGroup).removeView(this)
model.currentEpisode = model.media.episodes[position]
model.playEpisode(model.media.episodes[position], replace = true)
}
binding.recyclerEpisodesPlayer.adapter = adapterRecEpisodes

View File

@ -14,6 +14,7 @@ import androidx.core.view.children
import org.mosad.teapod.R
import org.mosad.teapod.databinding.PlayerLanguageSettingsBinding
import org.mosad.teapod.player.PlayerViewModel
import java.util.*
class LanguageSettingsPlayer @JvmOverloads constructor(
context: Context,
@ -25,13 +26,13 @@ class LanguageSettingsPlayer @JvmOverloads constructor(
private val binding = PlayerLanguageSettingsBinding.inflate(LayoutInflater.from(context), this, true)
var onViewRemovedAction: (() -> Unit)? = null // TODO find a better solution for this
private var currentStreamUrl = model?.currentStreamUrl ?: ""
private var currentLanguage = model?.currentLanguage ?: Locale.ROOT
init {
model?.let {
model.currentEpisode.streams.forEach { stream ->
addLanguage(stream.language.displayName, stream.url == currentStreamUrl) {
currentStreamUrl = stream.url
addLanguage(stream.language.displayName, stream.language == currentLanguage) {
currentLanguage = stream.language
updateSelectedLanguage(it as TextView)
}
}
@ -40,7 +41,7 @@ class LanguageSettingsPlayer @JvmOverloads constructor(
binding.buttonCloseLanguageSettings.setOnClickListener { close() }
binding.buttonCancel.setOnClickListener { close() }
binding.buttonSelect.setOnClickListener {
model?.changeLanguage(currentStreamUrl)
model?.setLanguage(currentLanguage)
close()
}
}