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:
parent
a95813e91e
commit
f49b5a2730
|
@ -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.LibraryFragment
|
||||||
import org.mosad.teapod.ui.activity.main.fragments.SearchFragment
|
import org.mosad.teapod.ui.activity.main.fragments.SearchFragment
|
||||||
import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity
|
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.DataTypes
|
||||||
import org.mosad.teapod.util.metadb.MetaDBController
|
import org.mosad.teapod.util.metadb.MetaDBController
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -190,17 +189,6 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
|
||||||
finish()
|
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
|
* use custom restart instead of recreate(), since it has animations
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,6 +27,7 @@ import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
@ -40,13 +41,10 @@ import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.FragmentHomeBinding
|
import org.mosad.teapod.databinding.FragmentHomeBinding
|
||||||
import org.mosad.teapod.ui.activity.main.viewmodel.HomeViewModel
|
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.MediaEpisodeListAdapter
|
||||||
import org.mosad.teapod.util.adapter.MediaItemListAdapter
|
import org.mosad.teapod.util.adapter.MediaItemListAdapter
|
||||||
import org.mosad.teapod.util.decoration.MediaItemDecoration
|
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() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
|
@ -54,6 +52,10 @@ class HomeFragment : Fragment() {
|
||||||
private val model: HomeViewModel by viewModels()
|
private val model: HomeViewModel by viewModels()
|
||||||
private lateinit var binding: FragmentHomeBinding
|
private lateinit var binding: FragmentHomeBinding
|
||||||
|
|
||||||
|
private val playerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
model.updateUpNextItems()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
return binding.root
|
return binding.root
|
||||||
|
@ -70,7 +72,7 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
binding.recyclerUpNext.adapter = MediaEpisodeListAdapter(
|
binding.recyclerUpNext.adapter = MediaEpisodeListAdapter(
|
||||||
MediaEpisodeListAdapter.OnClickListener {
|
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 {
|
binding.buttonPlayHighlight.setOnClickListener {
|
||||||
val panel = uiState.highlightItemUpNext.panel
|
val panel = uiState.highlightItemUpNext.panel
|
||||||
activity?.startPlayer(panel.episodeMetadata.seasonId, panel.id)
|
playerResult.launch(playerIntent(panel.episodeMetadata.seasonId, panel.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable the shimmer effect
|
// disable the shimmer effect
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -20,8 +21,8 @@ import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.FragmentMediaBinding
|
import org.mosad.teapod.databinding.FragmentMediaBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.NoneUpNextSeriesItem
|
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.ui.activity.main.viewmodel.MediaFragmentViewModel
|
||||||
|
import org.mosad.teapod.util.playerIntent
|
||||||
import org.mosad.teapod.util.tmdb.TMDBApiController
|
import org.mosad.teapod.util.tmdb.TMDBApiController
|
||||||
import org.mosad.teapod.util.tmdb.TMDBMovie
|
import org.mosad.teapod.util.tmdb.TMDBMovie
|
||||||
import org.mosad.teapod.util.tmdb.TMDBTVShow
|
import org.mosad.teapod.util.tmdb.TMDBTVShow
|
||||||
|
@ -40,7 +41,10 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
|
||||||
|
|
||||||
private val fragments = arrayListOf<Fragment>()
|
private val fragments = arrayListOf<Fragment>()
|
||||||
private var watchlistJobRunning = false
|
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 {
|
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
|
* if tmdb data is present, use it, else use the aod data
|
||||||
*/
|
*/
|
||||||
|
@ -218,15 +195,25 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun playerFinishedCallback() = lifecycleScope.launch {
|
||||||
* play the current episode
|
model.updateOnResume()
|
||||||
* 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")
|
|
||||||
|
|
||||||
//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")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.mosad.teapod.ui.activity.main.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -13,7 +12,6 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.FragmentMediaEpisodesBinding
|
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.ui.activity.main.viewmodel.MediaFragmentViewModel
|
||||||
import org.mosad.teapod.util.adapter.EpisodeItemAdapter
|
import org.mosad.teapod.util.adapter.EpisodeItemAdapter
|
||||||
|
|
||||||
|
@ -37,7 +35,7 @@ class MediaFragmentEpisodes : Fragment() {
|
||||||
model.tmdbTVSeason.episodes,
|
model.tmdbTVSeason.episodes,
|
||||||
model.currentPlayheads,
|
model.currentPlayheads,
|
||||||
EpisodeItemAdapter.OnClickListener { episode ->
|
EpisodeItemAdapter.OnClickListener { episode ->
|
||||||
playEpisode(episode.seasonId, episode.id)
|
(requireParentFragment() as? MediaFragment)?.playEpisode(episode.seasonId, episode.id)
|
||||||
},
|
},
|
||||||
EpisodeItemAdapter.ViewType.MEDIA_FRAGMENT
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -33,6 +33,8 @@ import kotlin.random.Random
|
||||||
|
|
||||||
class HomeViewModel : ViewModel() {
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val WATCHLIST_LENGTH = 50
|
||||||
|
|
||||||
private val uiState = MutableStateFlow<UiState>(UiState.Loading)
|
private val uiState = MutableStateFlow<UiState>(UiState.Loading)
|
||||||
|
|
||||||
sealed class UiState {
|
sealed class UiState {
|
||||||
|
@ -64,7 +66,7 @@ class HomeViewModel : ViewModel() {
|
||||||
try {
|
try {
|
||||||
// run the loading in parallel to speed up the process
|
// run the loading in parallel to speed up the process
|
||||||
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount().items }
|
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 {
|
val recommendationsJob = viewModelScope.async {
|
||||||
Crunchyroll.recommendations(20).items
|
Crunchyroll.recommendations(20).items
|
||||||
}
|
}
|
||||||
|
@ -111,7 +113,7 @@ class HomeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the watchlist after a item has been added/removed
|
// 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(
|
currentUiState.copy(
|
||||||
watchlistItems = watchlistItems,
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -142,14 +142,6 @@ class PlayerActivity : AppCompatActivity() {
|
||||||
if (isInPiPMode()) { finishAndRemoveTask() }
|
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
|
* used, when the player is in pip and the user selects a new media
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
|
||||||
import kotlin.system.exitProcess
|
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
|
* hide the status and navigation bar
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,16 +1,31 @@
|
||||||
package org.mosad.teapod.util
|
package org.mosad.teapod.util
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
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.Collection
|
||||||
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
|
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
|
||||||
import org.mosad.teapod.parser.crunchyroll.Item
|
import org.mosad.teapod.parser.crunchyroll.Item
|
||||||
|
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
||||||
import java.util.*
|
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) {
|
fun TextView.setDrawableTop(drawable: Int) {
|
||||||
this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0)
|
this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue