rework the player activity starting behaviour

* add callbacks on player finish to update episode watch head progress in gui
* directly start the player from the fragment and not from MainActivity
This commit is contained in:
Jannik 2022-11-26 17:46:25 +01:00
parent a95813e91e
commit f49b5a2730
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
8 changed files with 69 additions and 91 deletions

View File

@ -43,7 +43,6 @@ import org.mosad.teapod.ui.activity.main.fragments.HomeFragment
import org.mosad.teapod.ui.activity.main.fragments.LibraryFragment
import org.mosad.teapod.ui.activity.main.fragments.SearchFragment
import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity
import org.mosad.teapod.ui.activity.player.PlayerActivity
import org.mosad.teapod.util.DataTypes
import org.mosad.teapod.util.metadb.MetaDBController
import java.util.*
@ -190,17 +189,6 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
finish()
}
/**
* start the player as new activity
*/
fun startPlayer(seasonId: String, episodeId: String) {
val intent = Intent(this, PlayerActivity::class.java).apply {
putExtra(getString(R.string.intent_season_id), seasonId)
putExtra(getString(R.string.intent_episode_id), episodeId)
}
startActivity(intent)
}
/**
* use custom restart instead of recreate(), since it has animations
*/

View File

@ -27,6 +27,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
@ -40,13 +41,10 @@ import kotlinx.coroutines.launch
import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentHomeBinding
import org.mosad.teapod.ui.activity.main.viewmodel.HomeViewModel
import org.mosad.teapod.util.*
import org.mosad.teapod.util.adapter.MediaEpisodeListAdapter
import org.mosad.teapod.util.adapter.MediaItemListAdapter
import org.mosad.teapod.util.decoration.MediaItemDecoration
import org.mosad.teapod.util.setDrawableTop
import org.mosad.teapod.util.showFragment
import org.mosad.teapod.util.startPlayer
import org.mosad.teapod.util.toItemMediaList
class HomeFragment : Fragment() {
@ -54,6 +52,10 @@ class HomeFragment : Fragment() {
private val model: HomeViewModel by viewModels()
private lateinit var binding: FragmentHomeBinding
private val playerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
model.updateUpNextItems()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
@ -70,7 +72,7 @@ class HomeFragment : Fragment() {
binding.recyclerUpNext.adapter = MediaEpisodeListAdapter(
MediaEpisodeListAdapter.OnClickListener {
activity?.startPlayer(it.panel.episodeMetadata.seasonId, it.panel.id)
playerResult.launch(playerIntent(it.panel.episodeMetadata.seasonId, it.panel.id))
}
)
@ -154,7 +156,7 @@ class HomeFragment : Fragment() {
binding.buttonPlayHighlight.setOnClickListener {
val panel = uiState.highlightItemUpNext.panel
activity?.startPlayer(panel.episodeMetadata.seasonId, panel.id)
playerResult.launch(playerIntent(panel.episodeMetadata.seasonId, panel.id))
}
// disable the shimmer effect

View File

@ -7,6 +7,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
@ -20,8 +21,8 @@ import kotlinx.coroutines.launch
import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentMediaBinding
import org.mosad.teapod.parser.crunchyroll.NoneUpNextSeriesItem
import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
import org.mosad.teapod.util.playerIntent
import org.mosad.teapod.util.tmdb.TMDBApiController
import org.mosad.teapod.util.tmdb.TMDBMovie
import org.mosad.teapod.util.tmdb.TMDBTVShow
@ -40,7 +41,10 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
private val fragments = arrayListOf<Fragment>()
private var watchlistJobRunning = false
private var runOnResume = false
private val playerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
playerFinishedCallback()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -74,33 +78,6 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
}
}
override fun onResume() {
super.onResume()
if (runOnResume) {
/**
* FIXME
* this is currently also run on back press when multiple MediaFragments have
* been open and closed via similar tab
*/
lifecycleScope.launch {
model.updateOnResume()
if (model.upNextSeries != NoneUpNextSeriesItem) {
binding.textTitle.text = model.upNextSeries.panel.title
}
// needs to be called after model.updateOnResume()
if (fragments.elementAtOrNull(0) is MediaFragmentEpisodes) {
(fragments[0] as MediaFragmentEpisodes).updateWatchedState()
}
}
} else {
runOnResume = true
}
}
/**
* if tmdb data is present, use it, else use the aod data
*/
@ -218,15 +195,25 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
}
}
/**
* play the current episode
* TODO this is also used in MediaFragmentEpisode, we should only have on implementation
*/
private fun playEpisode(seasonId: String, episodeId: String) {
(activity as MainActivity).startPlayer(seasonId, episodeId)
Log.d(javaClass.name, "Started Player with episodeId: $episodeId")
private fun playerFinishedCallback() = lifecycleScope.launch {
model.updateOnResume()
//model.updateNextEpisode(episodeId) // set the correct next episode
if (model.upNextSeries != NoneUpNextSeriesItem) {
binding.textTitle.text = model.upNextSeries.panel.title
}
// needs to be called after model.updateOnResume()
(fragments.elementAtOrNull(0) as? MediaFragmentEpisodes)?.updateWatchedState()
Log.d(javaClass.name, "Updated model and gui after player closed")
}
/**
* play a episode, also runs callback on player result return
*/
fun playEpisode(seasonId: String, episodeId: String) {
playerResult.launch(playerIntent(seasonId, episodeId))
Log.d(javaClass.name, "Started Player with episodeId: $episodeId")
}
/**

View File

@ -2,7 +2,6 @@ package org.mosad.teapod.ui.activity.main.fragments
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -13,7 +12,6 @@ import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentMediaEpisodesBinding
import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
import org.mosad.teapod.util.adapter.EpisodeItemAdapter
@ -37,7 +35,7 @@ class MediaFragmentEpisodes : Fragment() {
model.tmdbTVSeason.episodes,
model.currentPlayheads,
EpisodeItemAdapter.OnClickListener { episode ->
playEpisode(episode.seasonId, episode.id)
(requireParentFragment() as? MediaFragment)?.playEpisode(episode.seasonId, episode.id)
},
EpisodeItemAdapter.ViewType.MEDIA_FRAGMENT
)
@ -106,11 +104,4 @@ class MediaFragmentEpisodes : Fragment() {
}
}
private fun playEpisode(seasonId: String, episodeId: String) {
(activity as MainActivity).startPlayer(seasonId, episodeId)
Log.d(javaClass.name, "Started Player with episodeId: $episodeId")
//model.updateNextEpisode(episodeId) // set the correct next episode
}
}

View File

@ -33,6 +33,8 @@ import kotlin.random.Random
class HomeViewModel : ViewModel() {
private val WATCHLIST_LENGTH = 50
private val uiState = MutableStateFlow<UiState>(UiState.Loading)
sealed class UiState {
@ -64,7 +66,7 @@ class HomeViewModel : ViewModel() {
try {
// run the loading in parallel to speed up the process
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount().items }
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(50).items }
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).items }
val recommendationsJob = viewModelScope.async {
Crunchyroll.recommendations(20).items
}
@ -111,7 +113,7 @@ class HomeViewModel : ViewModel() {
}
// update the watchlist after a item has been added/removed
val watchlistItems = Crunchyroll.watchlist(50).items
val watchlistItems = Crunchyroll.watchlist(WATCHLIST_LENGTH).items
currentUiState.copy(
watchlistItems = watchlistItems,
@ -123,4 +125,20 @@ class HomeViewModel : ViewModel() {
}
}
}
/**
* Update the up next list. To be used on player result callbacks.
*/
fun updateUpNextItems() {
viewModelScope.launch {
uiState.update { currentUiState ->
if (currentUiState is UiState.Normal) {
val upNextItems = Crunchyroll.upNextAccount().items
currentUiState.copy(upNextItems = upNextItems)
} else {
currentUiState
}
}
}
}
}

View File

@ -142,14 +142,6 @@ class PlayerActivity : AppCompatActivity() {
if (isInPiPMode()) { finishAndRemoveTask() }
}
// override fun onDestroy() {
// super.onDestroy()
//
// if (isFinishing) {
// // TODO notify about player finishing
// }
// }
/**
* used, when the player is in pip and the user selects a new media
*/

View File

@ -9,7 +9,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.commit
import org.mosad.teapod.R
import org.mosad.teapod.ui.activity.player.PlayerActivity
import kotlin.system.exitProcess
/**
@ -25,20 +24,6 @@ fun FragmentActivity.showFragment(fragment: Fragment) {
}
}
/**
* Start the player as new activity.
*
* @param seasonId The ID of the season the episode to be played is in
* @param episodeId The ID of the episode to play
*/
fun Activity.startPlayer(seasonId: String, episodeId: String) {
val intent = Intent(this, PlayerActivity::class.java).apply {
putExtra(getString(R.string.intent_season_id), seasonId)
putExtra(getString(R.string.intent_episode_id), episodeId)
}
startActivity(intent)
}
/**
* hide the status and navigation bar
*/

View File

@ -1,16 +1,31 @@
package org.mosad.teapod.util
import android.content.Intent
import android.view.View
import android.view.Window
import android.widget.TextView
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import org.mosad.teapod.R
import org.mosad.teapod.parser.crunchyroll.Collection
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
import org.mosad.teapod.parser.crunchyroll.Item
import org.mosad.teapod.ui.activity.player.PlayerActivity
import java.util.*
/**
* Create a Intent for PlayerActivity with season and episode id.
*
* @param seasonId The ID of the season the episode to be played is in
* @param episodeId The ID of the episode to play
*/
fun Fragment.playerIntent(seasonId: String, episodeId: String) = Intent(context, PlayerActivity::class.java).apply {
putExtra(getString(R.string.intent_season_id), seasonId)
putExtra(getString(R.string.intent_episode_id), episodeId)
}
fun TextView.setDrawableTop(drawable: Int) {
this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0)
}