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:
		| @ -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() | ||||
|         } | ||||
|     } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user