merge PlayerEpisodeItemAdapter into EpisodeItemAdapter
This commit is contained in:
parent
dd6ca8b90e
commit
80a7fc4398
|
@ -35,15 +35,14 @@ class MediaFragmentEpisodes : Fragment() {
|
||||||
adapterRecEpisodes = EpisodeItemAdapter(
|
adapterRecEpisodes = EpisodeItemAdapter(
|
||||||
model.currentEpisodesCrunchy,
|
model.currentEpisodesCrunchy,
|
||||||
model.tmdbTVSeason.episodes,
|
model.tmdbTVSeason.episodes,
|
||||||
model.currentPlayheads
|
model.currentPlayheads,
|
||||||
|
EpisodeItemAdapter.OnClickListener { episode ->
|
||||||
|
playEpisode(episode.seasonId, episode.id)
|
||||||
|
},
|
||||||
|
EpisodeItemAdapter.ViewType.MEDIA_FRAGMENT
|
||||||
)
|
)
|
||||||
binding.recyclerEpisodes.adapter = adapterRecEpisodes
|
binding.recyclerEpisodes.adapter = adapterRecEpisodes
|
||||||
|
|
||||||
// set onItemClick, adapter is initialized
|
|
||||||
adapterRecEpisodes.onImageClick = { seasonId, episodeId ->
|
|
||||||
playEpisode(seasonId, episodeId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't show season selection if only one season is present
|
// don't show season selection if only one season is present
|
||||||
if (model.seasonsCrunchy.total < 2) {
|
if (model.seasonsCrunchy.total < 2) {
|
||||||
binding.buttonSeasonSelection.visibility = View.GONE
|
binding.buttonSeasonSelection.visibility = View.GONE
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import org.mosad.teapod.databinding.PlayerEpisodesListBinding
|
import org.mosad.teapod.databinding.PlayerEpisodesListBinding
|
||||||
import org.mosad.teapod.ui.activity.player.PlayerViewModel
|
import org.mosad.teapod.ui.activity.player.PlayerViewModel
|
||||||
import org.mosad.teapod.util.adapter.PlayerEpisodeItemAdapter
|
import org.mosad.teapod.util.adapter.EpisodeItemAdapter
|
||||||
|
|
||||||
class EpisodesListPlayer @JvmOverloads constructor(
|
class EpisodesListPlayer @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -17,8 +17,6 @@ class EpisodesListPlayer @JvmOverloads constructor(
|
||||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
private val binding = PlayerEpisodesListBinding.inflate(LayoutInflater.from(context), this, true)
|
private val binding = PlayerEpisodesListBinding.inflate(LayoutInflater.from(context), this, true)
|
||||||
private lateinit var adapterRecEpisodes: PlayerEpisodeItemAdapter
|
|
||||||
|
|
||||||
var onViewRemovedAction: (() -> Unit)? = null // TODO find a better solution for this
|
var onViewRemovedAction: (() -> Unit)? = null // TODO find a better solution for this
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -28,11 +26,17 @@ class EpisodesListPlayer @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
model?.let {
|
model?.let {
|
||||||
adapterRecEpisodes = PlayerEpisodeItemAdapter(model.episodes)
|
val adapterRecEpisodes = EpisodeItemAdapter(
|
||||||
adapterRecEpisodes.onImageClick = {_, episodeId ->
|
model.episodes.items,
|
||||||
(this.parent as ViewGroup).removeView(this)
|
null,
|
||||||
model.setCurrentEpisode(episodeId, startPlayback = true)
|
mapOf(),
|
||||||
}
|
EpisodeItemAdapter.OnClickListener { episode ->
|
||||||
|
(this.parent as ViewGroup).removeView(this)
|
||||||
|
model.setCurrentEpisode(episode.id, startPlayback = true)
|
||||||
|
},
|
||||||
|
EpisodeItemAdapter.ViewType.PLAYER
|
||||||
|
)
|
||||||
|
|
||||||
// episodeNumber starts at 1, we need the episode index -> - 1
|
// episodeNumber starts at 1, we need the episode index -> - 1
|
||||||
adapterRecEpisodes.currentSelected = model.currentEpisode.episodeNumber?.minus(1) ?: 0
|
adapterRecEpisodes.currentSelected = model.currentEpisode.episodeNumber?.minus(1) ?: 0
|
||||||
|
|
||||||
|
|
|
@ -56,13 +56,13 @@ class LanguageSettingsPlayer @JvmOverloads constructor(
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setTextColor(context.resources.getColor(R.color.exo_white, context.theme))
|
setTextColor(context.resources.getColor(R.color.textPrimaryDark, context.theme))
|
||||||
setTypeface(null, Typeface.BOLD)
|
setTypeface(null, Typeface.BOLD)
|
||||||
setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_baseline_check_24, 0, 0, 0)
|
setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_baseline_check_24, 0, 0, 0)
|
||||||
compoundDrawablesRelative.getOrNull(0)?.setTint(Color.WHITE)
|
compoundDrawablesRelative.getOrNull(0)?.setTint(Color.WHITE)
|
||||||
compoundDrawablePadding = 12
|
compoundDrawablePadding = 12
|
||||||
} else {
|
} else {
|
||||||
setTextColor(context.resources.getColor(R.color.textPrimaryDark, context.theme))
|
setTextColor(context.resources.getColor(R.color.textSecondaryDark, context.theme))
|
||||||
setPadding(75, 0, 0, 0)
|
setPadding(75, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,69 +13,54 @@ import com.bumptech.glide.request.RequestOptions
|
||||||
import jp.wasabeef.glide.transformations.RoundedCornersTransformation
|
import jp.wasabeef.glide.transformations.RoundedCornersTransformation
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.ItemEpisodeBinding
|
import org.mosad.teapod.databinding.ItemEpisodeBinding
|
||||||
|
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.Episode
|
import org.mosad.teapod.parser.crunchyroll.Episode
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.PlayheadObject
|
||||||
import org.mosad.teapod.parser.crunchyroll.PlayheadsMap
|
import org.mosad.teapod.parser.crunchyroll.PlayheadsMap
|
||||||
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
||||||
|
|
||||||
class EpisodeItemAdapter(
|
class EpisodeItemAdapter(
|
||||||
private val episodes: List<Episode>,
|
private val episodes: List<Episode>,
|
||||||
private val tmdbEpisodes: List<TMDBTVEpisode>?,
|
private val tmdbEpisodes: List<TMDBTVEpisode>?,
|
||||||
private val playheads: PlayheadsMap
|
private val playheads: PlayheadsMap,
|
||||||
) : RecyclerView.Adapter<EpisodeItemAdapter.EpisodeViewHolder>() {
|
private val onClickListener: OnClickListener,
|
||||||
|
private val viewType: ViewType
|
||||||
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
var onImageClick: ((seasonId: String, episodeId: String) -> Unit)? = null
|
var currentSelected: Int = -1 // -1, since position should never be < 0
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return EpisodeViewHolder(ItemEpisodeBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
return when (viewType) {
|
||||||
|
ViewType.PLAYER.ordinal -> {
|
||||||
|
PlayerEpisodeViewHolder((ItemEpisodePlayerBinding.inflate(LayoutInflater.from(parent.context), parent, false)))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// media fragment episode list is default
|
||||||
|
EpisodeViewHolder(ItemEpisodeBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: EpisodeViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val context = holder.binding.root.context
|
val episode = episodes[position]
|
||||||
val ep = episodes[position]
|
val playhead = playheads[episode.id]
|
||||||
|
val tmdbEpisode = tmdbEpisodes?.getOrNull(position)
|
||||||
|
|
||||||
val titleText = if (ep.episodeNumber != null) {
|
when (holder.itemViewType) {
|
||||||
// for tv shows add ep prefix and episode number
|
ViewType.MEDIA_FRAGMENT.ordinal -> {
|
||||||
if (ep.isDubbed) {
|
(holder as EpisodeViewHolder).bind(episode, playhead, tmdbEpisode)
|
||||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
}
|
||||||
} else {
|
ViewType.PLAYER.ordinal -> {
|
||||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
(holder as PlayerEpisodeViewHolder).bind(episode, currentSelected)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ep.title
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
holder.binding.textEpisodeTitle.text = titleText
|
override fun getItemViewType(position: Int): Int {
|
||||||
holder.binding.textEpisodeDesc.text = if (ep.description.isNotEmpty()) {
|
return when (viewType) {
|
||||||
ep.description
|
ViewType.MEDIA_FRAGMENT -> ViewType.MEDIA_FRAGMENT.ordinal
|
||||||
} else if (tmdbEpisodes != null && position < tmdbEpisodes.size){
|
ViewType.PLAYER -> ViewType.PLAYER.ordinal
|
||||||
tmdbEpisodes[position].overview
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO is isNotEmpty() needed? also in PlayerEpisodeItemAdapter
|
|
||||||
if (ep.images.thumbnail[0][0].source.isNotEmpty()) {
|
|
||||||
Glide.with(context).load(ep.images.thumbnail[0][0].source)
|
|
||||||
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))
|
|
||||||
.apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(10, 0)))
|
|
||||||
.into(holder.binding.imageEpisode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add watched progress
|
|
||||||
val playheadProgress = playheads[ep.id]?.let { playhead ->
|
|
||||||
((playhead.playhead.toFloat() / (ep.durationMs / 1000)) * 100).toInt()
|
|
||||||
} ?: 0
|
|
||||||
holder.binding.progressPlayhead.setProgressCompat(playheadProgress, false)
|
|
||||||
holder.binding.progressPlayhead.visibility = if (playheadProgress <= 0)
|
|
||||||
View.GONE else View.VISIBLE
|
|
||||||
|
|
||||||
// add watched icon to episode, if the episode id is present in playheads and fullyWatched
|
|
||||||
val watchedImage: Drawable? = if (playheads[ep.id]?.fullyWatched == true) {
|
|
||||||
ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
holder.binding.imageWatched.setImageDrawable(watchedImage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
@ -84,14 +69,103 @@ class EpisodeItemAdapter(
|
||||||
|
|
||||||
inner class EpisodeViewHolder(val binding: ItemEpisodeBinding) :
|
inner class EpisodeViewHolder(val binding: ItemEpisodeBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
init {
|
|
||||||
// on image click return the episode id and index (within the adapter)
|
fun bind(episode: Episode, playhead: PlayheadObject?, tmdbEpisode: TMDBTVEpisode?) {
|
||||||
|
val context = binding.root.context
|
||||||
|
|
||||||
|
val titleText = if (episode.episodeNumber != null) {
|
||||||
|
// for tv shows add ep prefix and episode number
|
||||||
|
if (episode.isDubbed) {
|
||||||
|
context.getString(R.string.component_episode_title, episode.episode, episode.title)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.component_episode_title_sub, episode.episode, episode.title)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
episode.title
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.textEpisodeTitle.text = titleText
|
||||||
|
binding.textEpisodeDesc.text = episode.description.ifEmpty {
|
||||||
|
tmdbEpisode?.overview ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (episode.images.thumbnail[0][0].source.isNotEmpty()) {
|
||||||
|
Glide.with(context).load(episode.images.thumbnail[0][0].source)
|
||||||
|
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))
|
||||||
|
.apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(10, 0)))
|
||||||
|
.into(binding.imageEpisode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add watched progress
|
||||||
|
val playheadProgress = playhead?.playhead?.let {
|
||||||
|
((it.toFloat() / (episode.durationMs / 1000)) * 100).toInt()
|
||||||
|
} ?: 0
|
||||||
|
binding.progressPlayhead.setProgressCompat(playheadProgress, false)
|
||||||
|
binding.progressPlayhead.visibility = if (playheadProgress <= 0)
|
||||||
|
View.GONE else View.VISIBLE
|
||||||
|
|
||||||
|
// add watched icon to episode, if the episode id is present in playheads and fullyWatched
|
||||||
|
val watchedImage: Drawable? = if (playhead?.fullyWatched == true) {
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
binding.imageWatched.setImageDrawable(watchedImage)
|
||||||
|
|
||||||
binding.imageEpisode.setOnClickListener {
|
binding.imageEpisode.setOnClickListener {
|
||||||
onImageClick?.invoke(
|
onClickListener.onClick(episode)
|
||||||
episodes[bindingAdapterPosition].seasonId,
|
|
||||||
episodes[bindingAdapterPosition].id
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class PlayerEpisodeViewHolder(val binding: ItemEpisodePlayerBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
// -1, since position should never be < 0
|
||||||
|
fun bind(episode: Episode, currentSelected: Int) {
|
||||||
|
val context = binding.root.context
|
||||||
|
|
||||||
|
val titleText = if (episode.episodeNumber != null) {
|
||||||
|
// for tv shows add ep prefix and episode number
|
||||||
|
if (episode.isDubbed) {
|
||||||
|
context.getString(R.string.component_episode_title, episode.episode, episode.title)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.component_episode_title_sub, episode.episode, episode.title)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
episode.title
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.textEpisodeTitle2.text = titleText
|
||||||
|
binding.textEpisodeDesc2.text = episode.description.ifEmpty { "" }
|
||||||
|
|
||||||
|
if (episode.images.thumbnail[0][0].source.isNotEmpty()) {
|
||||||
|
Glide.with(context).load(episode.images.thumbnail[0][0].source)
|
||||||
|
.apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(10, 0)))
|
||||||
|
.into(binding.imageEpisode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide the play icon, if it's the current episode
|
||||||
|
binding.imageEpisodePlay.visibility = if (currentSelected == bindingAdapterPosition) {
|
||||||
|
View.GONE
|
||||||
|
} else {
|
||||||
|
View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSelected != bindingAdapterPosition) {
|
||||||
|
binding.imageEpisode.setOnClickListener {
|
||||||
|
onClickListener.onClick(episode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnClickListener(val clickListener: (episode: Episode) -> Unit) {
|
||||||
|
fun onClick(episode: Episode) = clickListener(episode)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ViewType {
|
||||||
|
MEDIA_FRAGMENT,
|
||||||
|
PLAYER
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,72 +0,0 @@
|
||||||
package org.mosad.teapod.util.adapter
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.request.RequestOptions
|
|
||||||
import jp.wasabeef.glide.transformations.RoundedCornersTransformation
|
|
||||||
import org.mosad.teapod.R
|
|
||||||
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
|
|
||||||
import org.mosad.teapod.parser.crunchyroll.Episodes
|
|
||||||
|
|
||||||
class PlayerEpisodeItemAdapter(private val episodes: Episodes) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() {
|
|
||||||
|
|
||||||
var onImageClick: ((seasonId: String, episodeId: String) -> Unit)? = null
|
|
||||||
var currentSelected: Int = -1 // -1, since position should never be < 0
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeViewHolder {
|
|
||||||
return EpisodeViewHolder(ItemEpisodePlayerBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: EpisodeViewHolder, position: Int) {
|
|
||||||
val context = holder.binding.root.context
|
|
||||||
val ep = episodes.items[position]
|
|
||||||
|
|
||||||
val titleText = if (ep.episodeNumber != null) {
|
|
||||||
// for tv shows add ep prefix and episode number
|
|
||||||
if (ep.isDubbed) {
|
|
||||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ep.title
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.binding.textEpisodeTitle2.text = titleText
|
|
||||||
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)
|
|
||||||
.apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(10, 0)))
|
|
||||||
.into(holder.binding.imageEpisode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide the play icon, if it's the current episode
|
|
||||||
holder.binding.imageEpisodePlay.visibility = if (currentSelected == position) {
|
|
||||||
View.GONE
|
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return episodes.items.size
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class EpisodeViewHolder(val binding: ItemEpisodePlayerBinding) : RecyclerView.ViewHolder(binding.root) {
|
|
||||||
init {
|
|
||||||
binding.imageEpisode.setOnClickListener {
|
|
||||||
// don't execute, if it's the current episode
|
|
||||||
if (currentSelected != bindingAdapterPosition) {
|
|
||||||
onImageClick?.invoke(
|
|
||||||
episodes.items[bindingAdapterPosition].seasonId,
|
|
||||||
episodes.items[bindingAdapterPosition].id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue