diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt index 59257b8..91aeb6d 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt @@ -35,15 +35,14 @@ class MediaFragmentEpisodes : Fragment() { adapterRecEpisodes = EpisodeItemAdapter( model.currentEpisodesCrunchy, model.tmdbTVSeason.episodes, - model.currentPlayheads + model.currentPlayheads, + EpisodeItemAdapter.OnClickListener { episode -> + playEpisode(episode.seasonId, episode.id) + }, + EpisodeItemAdapter.ViewType.MEDIA_FRAGMENT ) 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 if (model.seasonsCrunchy.total < 2) { binding.buttonSeasonSelection.visibility = View.GONE diff --git a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt index a29af19..143432e 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt @@ -7,7 +7,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import org.mosad.teapod.databinding.PlayerEpisodesListBinding 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( context: Context, @@ -17,8 +17,6 @@ class EpisodesListPlayer @JvmOverloads constructor( ) : LinearLayout(context, attrs, defStyleAttr) { 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 init { @@ -28,11 +26,17 @@ class EpisodesListPlayer @JvmOverloads constructor( } model?.let { - adapterRecEpisodes = PlayerEpisodeItemAdapter(model.episodes) - adapterRecEpisodes.onImageClick = {_, episodeId -> - (this.parent as ViewGroup).removeView(this) - model.setCurrentEpisode(episodeId, startPlayback = true) - } + val adapterRecEpisodes = EpisodeItemAdapter( + model.episodes.items, + null, + 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 adapterRecEpisodes.currentSelected = model.currentEpisode.episodeNumber?.minus(1) ?: 0 diff --git a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt index 897fc47..e21e586 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt @@ -56,13 +56,13 @@ class LanguageSettingsPlayer @JvmOverloads constructor( setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) 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) setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_baseline_check_24, 0, 0, 0) compoundDrawablesRelative.getOrNull(0)?.setTint(Color.WHITE) compoundDrawablePadding = 12 } else { - setTextColor(context.resources.getColor(R.color.textPrimaryDark, context.theme)) + setTextColor(context.resources.getColor(R.color.textSecondaryDark, context.theme)) setPadding(75, 0, 0, 0) } diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt index 291f540..5eba924 100644 --- a/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt +++ b/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt @@ -13,69 +13,54 @@ 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, private val tmdbEpisodes: List?, - private val playheads: PlayheadsMap -) : RecyclerView.Adapter() { + private val playheads: PlayheadsMap, + private val onClickListener: OnClickListener, + private val viewType: ViewType +) : RecyclerView.Adapter() { - 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(ItemEpisodeBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + 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: EpisodeViewHolder, position: Int) { - val context = holder.binding.root.context - val ep = episodes[position] + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val episode = episodes[position] + val playhead = playheads[episode.id] + val tmdbEpisode = tmdbEpisodes?.getOrNull(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) + when (holder.itemViewType) { + ViewType.MEDIA_FRAGMENT.ordinal -> { + (holder as EpisodeViewHolder).bind(episode, playhead, tmdbEpisode) + } + ViewType.PLAYER.ordinal -> { + (holder as PlayerEpisodeViewHolder).bind(episode, currentSelected) } - } else { - ep.title } + } - holder.binding.textEpisodeTitle.text = titleText - holder.binding.textEpisodeDesc.text = if (ep.description.isNotEmpty()) { - ep.description - } else if (tmdbEpisodes != null && position < tmdbEpisodes.size){ - tmdbEpisodes[position].overview - } else { - "" + override fun getItemViewType(position: Int): Int { + return when (viewType) { + ViewType.MEDIA_FRAGMENT -> ViewType.MEDIA_FRAGMENT.ordinal + ViewType.PLAYER -> ViewType.PLAYER.ordinal } - - // 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 { @@ -84,14 +69,103 @@ class EpisodeItemAdapter( inner class EpisodeViewHolder(val binding: ItemEpisodeBinding) : 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 { - onImageClick?.invoke( - episodes[bindingAdapterPosition].seasonId, - episodes[bindingAdapterPosition].id - ) + onClickListener.onClick(episode) } } } + + 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 + } } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt deleted file mode 100644 index 8d42439..0000000 --- a/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt +++ /dev/null @@ -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() { - - 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 - ) - } - } - } - } -} \ No newline at end of file