teapod/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt

179 lines
7.1 KiB
Kotlin

package org.mosad.teapod.util.adapter
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
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.ItemEpisodeBinding
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
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.util.tmdb.TMDBTVEpisode
class EpisodeItemAdapter(
private val episodes: List<Episode>,
private val tmdbEpisodes: List<TMDBTVEpisode>?,
private val playheads: PlayheadsMap,
private val onClickListener: OnClickListener,
private val viewType: ViewType
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var currentSelected: Int = -1 // -1, since position should never be < 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
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: RecyclerView.ViewHolder, position: Int) {
val episode = episodes[position]
val playhead = playheads[episode.id]
val tmdbEpisode = tmdbEpisodes?.getOrNull(position)
when (holder.itemViewType) {
ViewType.MEDIA_FRAGMENT.ordinal -> {
(holder as EpisodeViewHolder).bind(episode, playhead, tmdbEpisode)
}
ViewType.PLAYER.ordinal -> {
(holder as PlayerEpisodeViewHolder).bind(episode, playhead, currentSelected)
}
}
}
override fun getItemViewType(position: Int): Int {
return when (viewType) {
ViewType.MEDIA_FRAGMENT -> ViewType.MEDIA_FRAGMENT.ordinal
ViewType.PLAYER -> ViewType.PLAYER.ordinal
}
}
override fun getItemCount(): Int {
return episodes.size
}
inner class EpisodeViewHolder(val binding: ItemEpisodeBinding) :
RecyclerView.ViewHolder(binding.root) {
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 {
onClickListener.onClick(episode)
}
}
}
inner class PlayerEpisodeViewHolder(val binding: ItemEpisodePlayerBinding) :
RecyclerView.ViewHolder(binding.root) {
// -1, since position should never be < 0
fun bind(episode: Episode, playhead: PlayheadObject?, 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)
}
// 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
// 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
}
}