up next rework
* start playback, when up next episode is clicked * add playhead progress indicator to up next episodes
This commit is contained in:
@ -171,7 +171,7 @@ data class ContinueWatchingItem(
|
||||
@SerialName("fully_watched") val fullyWatched: Boolean = false,
|
||||
)
|
||||
|
||||
// EpisodePanel is used in ContinueWatchingItem
|
||||
// EpisodePanel is used in ContinueWatchingItem and UpNextSeriesItem
|
||||
@Serializable
|
||||
data class EpisodePanel(
|
||||
@SerialName("id") val id: String,
|
||||
@ -187,13 +187,16 @@ data class EpisodePanel(
|
||||
@Serializable
|
||||
data class EpisodeMetadata(
|
||||
@SerialName("duration_ms") val durationMs: Int,
|
||||
@SerialName("episode_number") val episodeNumber: Int? = null, // default/nullable value since optional
|
||||
@SerialName("season_id") val seasonId: String,
|
||||
@SerialName("season_number") val seasonNumber: Int,
|
||||
@SerialName("season_title") val seasonTitle: String,
|
||||
@SerialName("series_id") val seriesId: String,
|
||||
@SerialName("series_title") val seriesTitle: String,
|
||||
)
|
||||
|
||||
val NoneItem = Item("", "", "", "", "", Images(emptyList(), emptyList()))
|
||||
val NoneEpisodeMetadata = EpisodeMetadata(0, "", "", "")
|
||||
val NoneEpisodeMetadata = EpisodeMetadata(0, 0, "", 0, "", "", "")
|
||||
val NoneEpisodePanel = EpisodePanel("", "", "", "", "", NoneEpisodeMetadata, Thumbnail(listOf()), "")
|
||||
|
||||
val NoneCollection = Collection<Item>(0, emptyList())
|
||||
|
@ -36,7 +36,9 @@ import com.bumptech.glide.Glide
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mosad.teapod.R
|
||||
import org.mosad.teapod.databinding.FragmentHomeBinding
|
||||
import org.mosad.teapod.ui.activity.main.MainActivity
|
||||
import org.mosad.teapod.ui.activity.main.viewmodel.HomeViewModel
|
||||
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
|
||||
@ -57,14 +59,17 @@ class HomeFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9))
|
||||
binding.recyclerUpNext.addItemDecoration(MediaItemDecoration(9))
|
||||
binding.recyclerWatchlist.addItemDecoration(MediaItemDecoration(9))
|
||||
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
|
||||
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
|
||||
|
||||
binding.recyclerNewEpisodes.adapter = MediaItemListAdapter(
|
||||
MediaItemListAdapter.OnClickListener {
|
||||
activity?.showFragment(MediaFragment(it.id))
|
||||
binding.recyclerUpNext.adapter = MediaEpisodeListAdapter(
|
||||
MediaEpisodeListAdapter.OnClickListener {
|
||||
val activity = activity
|
||||
if (activity is MainActivity) {
|
||||
activity.startPlayer(it.panel.episodeMetadata.seasonId, it.panel.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ -118,8 +123,8 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun bindUiStateNormal(uiState: HomeViewModel.UiState.Normal) {
|
||||
val adapterUpNext = binding.recyclerNewEpisodes.adapter as MediaItemListAdapter
|
||||
adapterUpNext.submitList(uiState.upNextItems.filter { !it.fullyWatched }.toItemMediaList())
|
||||
val adapterUpNext = binding.recyclerUpNext.adapter as MediaEpisodeListAdapter
|
||||
adapterUpNext.submitList(uiState.upNextItems.filter { !it.fullyWatched })
|
||||
|
||||
val adapterWatchlist = binding.recyclerWatchlist.adapter as MediaItemListAdapter
|
||||
adapterWatchlist.submitList(uiState.watchlistItems.toItemMediaList())
|
||||
|
@ -0,0 +1,70 @@
|
||||
package org.mosad.teapod.util.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import org.mosad.teapod.R
|
||||
import org.mosad.teapod.databinding.ItemMediaBinding
|
||||
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
|
||||
|
||||
class MediaEpisodeListAdapter(private val onClickListener: OnClickListener) : ListAdapter<ContinueWatchingItem, MediaEpisodeListAdapter.MediaViewHolder>(DiffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaViewHolder {
|
||||
return MediaViewHolder(
|
||||
ItemMediaBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.root.setOnClickListener {
|
||||
onClickListener.onClick(item)
|
||||
}
|
||||
holder.bind(item)
|
||||
}
|
||||
|
||||
inner class MediaViewHolder(val binding: ItemMediaBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: ContinueWatchingItem) {
|
||||
val metadata = item.panel.episodeMetadata
|
||||
|
||||
binding.textTitle.text = binding.root.context.getString(R.string.season_episode_title,
|
||||
metadata.seasonNumber, metadata.episodeNumber, metadata.seriesTitle
|
||||
)
|
||||
|
||||
Glide.with(binding.imagePoster)
|
||||
.load(item.panel.images.thumbnail[0][0].source)
|
||||
.into(binding.imagePoster)
|
||||
|
||||
// add watched progress
|
||||
val playheadProgress = ((item.playhead.toFloat() / (metadata.durationMs / 1000)) * 100)
|
||||
.toInt()
|
||||
binding.progressPlayhead.setProgressCompat(playheadProgress, false)
|
||||
binding.progressPlayhead.visibility = if (playheadProgress <= 0)
|
||||
View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
companion object DiffCallback : DiffUtil.ItemCallback<ContinueWatchingItem>() {
|
||||
override fun areItemsTheSame(oldItem: ContinueWatchingItem, newItem: ContinueWatchingItem): Boolean {
|
||||
return oldItem.panel.id == newItem.panel.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ContinueWatchingItem, newItem: ContinueWatchingItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
class OnClickListener(val clickListener: (item: ContinueWatchingItem) -> Unit) {
|
||||
fun onClick(item: ContinueWatchingItem) = clickListener(item)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package org.mosad.teapod.util.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -34,8 +35,13 @@ class MediaItemListAdapter(private val onClickListener: OnClickListener) : ListA
|
||||
|
||||
fun bind(item: ItemMedia) {
|
||||
binding.textTitle.text = item.title
|
||||
// can we use the view instead of context here?
|
||||
Glide.with(binding.root.context).load(item.posterUrl).into(binding.imagePoster)
|
||||
|
||||
Glide.with(binding.imagePoster)
|
||||
.load(item.posterUrl)
|
||||
.into(binding.imagePoster)
|
||||
|
||||
binding.imageEpisodePlay.isVisible = false
|
||||
binding.progressPlayhead.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import jp.wasabeef.glide.transformations.RoundedCornersTransformation
|
||||
import org.mosad.teapod.R
|
||||
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
|
||||
import org.mosad.teapod.parser.crunchyroll.Episodes
|
||||
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
||||
|
||||
class PlayerEpisodeItemAdapter(private val episodes: Episodes) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() {
|
||||
|
||||
@ -37,11 +36,7 @@ class PlayerEpisodeItemAdapter(private val episodes: Episodes) : RecyclerView.Ad
|
||||
}
|
||||
|
||||
holder.binding.textEpisodeTitle2.text = titleText
|
||||
holder.binding.textEpisodeDesc2.text = if (ep.description.isNotEmpty()) {
|
||||
ep.description
|
||||
} else {
|
||||
""
|
||||
}
|
||||
holder.binding.textEpisodeDesc2.text = ep.description.ifEmpty { "" }
|
||||
|
||||
if (ep.images.thumbnail[0][0].source.isNotEmpty()) {
|
||||
Glide.with(context).load(ep.images.thumbnail[0][0].source)
|
||||
|
Reference in New Issue
Block a user