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:
parent
94da8c6cee
commit
7845770067
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue