From 7845770067c5720893206c2765faa6584a889b86 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 27 Dec 2020 20:11:01 +0100 Subject: [PATCH] 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 --- app/build.gradle | 2 +- .../org/mosad/teapod/player/PlayerActivity.kt | 67 ++++++--------- .../mosad/teapod/player/PlayerViewModel.kt | 86 ++++++++++--------- .../ui/components/EpisodesListPlayer.kt | 2 +- .../ui/components/LanguageSettingsPlayer.kt | 9 +- 5 files changed, 75 insertions(+), 91 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 26df716..1c44f02 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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() diff --git a/app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt b/app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt index 9c13c45..45f6fc9 100644 --- a/app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt +++ b/app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt @@ -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 */ diff --git a/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt index e87b378..2f46238 100644 --- a/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt @@ -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().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 - } - } - - } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt index 5799a53..7b85c5e 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt @@ -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 diff --git a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt index 9329cc6..ff4510a 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt @@ -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() } }