port HomeFragment to ViewModel and Kotlin flow; update gradle wrapper
This commit is contained in:
parent
aa49169034
commit
f852600dc7
|
@ -1,34 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Teapod
|
||||||
|
*
|
||||||
|
* Copyright 2020-2022 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
package org.mosad.teapod.ui.activity.main.fragments
|
package org.mosad.teapod.ui.activity.main.fragments
|
||||||
|
|
||||||
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
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.joinAll
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.FragmentHomeBinding
|
import org.mosad.teapod.databinding.FragmentHomeBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
|
import org.mosad.teapod.ui.activity.main.viewmodel.HomeViewModel
|
||||||
import org.mosad.teapod.parser.crunchyroll.Item
|
import org.mosad.teapod.util.adapter.MediaItemListAdapter
|
||||||
import org.mosad.teapod.parser.crunchyroll.SortBy
|
|
||||||
import org.mosad.teapod.util.adapter.MediaItemAdapter
|
|
||||||
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.showFragment
|
||||||
import org.mosad.teapod.util.toItemMediaList
|
import org.mosad.teapod.util.toItemMediaList
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class HomeFragment : Fragment() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
|
private val classTag = javaClass.name
|
||||||
|
private val model: HomeViewModel by viewModels()
|
||||||
private lateinit var binding: FragmentHomeBinding
|
private lateinit var binding: FragmentHomeBinding
|
||||||
private lateinit var adapterUpNext: MediaItemAdapter
|
|
||||||
private lateinit var adapterWatchlist: MediaItemAdapter
|
|
||||||
private lateinit var adapterNewTitles: MediaItemAdapter
|
|
||||||
private lateinit var adapterTopTen: MediaItemAdapter
|
|
||||||
|
|
||||||
private lateinit var highlightMedia: Item
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -38,84 +57,43 @@ class HomeFragment : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
context?.let {
|
|
||||||
initHighlight()
|
|
||||||
initRecyclerViews()
|
|
||||||
initActions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initHighlight() {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val newTitles = Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 10)
|
|
||||||
// FIXME crashes on newTitles.items.size == 0
|
|
||||||
highlightMedia = newTitles.items[Random.nextInt(newTitles.items.size)]
|
|
||||||
|
|
||||||
// add media item to gui
|
|
||||||
binding.textHighlightTitle.text = highlightMedia.title
|
|
||||||
Glide.with(requireContext()).load(highlightMedia.images.poster_wide[0][3].source)
|
|
||||||
.into(binding.imageHighlight)
|
|
||||||
|
|
||||||
// TODO watchlist indicator
|
|
||||||
// if (StorageController.myList.contains(0)) {
|
|
||||||
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_check_24)
|
|
||||||
// } else {
|
|
||||||
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_add_24)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Suspend, since adapters need to be initialized before we can initialize the actions.
|
|
||||||
*/
|
|
||||||
private suspend fun initRecyclerViews() {
|
|
||||||
binding.recyclerWatchlist.addItemDecoration(MediaItemDecoration(9))
|
|
||||||
binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9))
|
||||||
|
binding.recyclerWatchlist.addItemDecoration(MediaItemDecoration(9))
|
||||||
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
|
||||||
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
|
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
|
||||||
|
|
||||||
val asyncJobList = arrayListOf<Job>()
|
binding.recyclerNewEpisodes.adapter = MediaItemListAdapter(
|
||||||
|
MediaItemListAdapter.OnClickListener {
|
||||||
|
activity?.showFragment(MediaFragment(it.id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// continue watching
|
binding.recyclerWatchlist.adapter = MediaItemListAdapter(
|
||||||
val upNextJob = lifecycleScope.launch {
|
MediaItemListAdapter.OnClickListener {
|
||||||
// TODO create EpisodeItemAdapter, which will start the playback of the selected episode immediately
|
activity?.showFragment(MediaFragment(it.id))
|
||||||
adapterUpNext = MediaItemAdapter(Crunchyroll.upNextAccount().items
|
}
|
||||||
.filter { !it.fullyWatched }.toItemMediaList())
|
)
|
||||||
binding.recyclerNewEpisodes.adapter = adapterUpNext
|
|
||||||
|
binding.recyclerNewTitles.adapter = MediaItemListAdapter(
|
||||||
|
MediaItemListAdapter.OnClickListener {
|
||||||
|
activity?.showFragment(MediaFragment(it.id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.recyclerTopTen.adapter = MediaItemListAdapter(
|
||||||
|
MediaItemListAdapter.OnClickListener {
|
||||||
|
activity?.showFragment(MediaFragment(it.id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.textHighlightMyList.setOnClickListener {
|
||||||
|
model.toggleHighlightWatchlist()
|
||||||
|
|
||||||
|
// disable the watchlist button until the result has been loaded
|
||||||
|
binding.textHighlightMyList.isClickable = false
|
||||||
|
// TODO since this might take a few seconds show a loading animation for the watchlist button
|
||||||
}
|
}
|
||||||
asyncJobList.add(upNextJob)
|
|
||||||
|
|
||||||
// watchlist
|
|
||||||
val watchlistJob = lifecycleScope.launch {
|
|
||||||
adapterWatchlist = MediaItemAdapter(Crunchyroll.watchlist(50).toItemMediaList())
|
|
||||||
binding.recyclerWatchlist.adapter = adapterWatchlist
|
|
||||||
}
|
|
||||||
asyncJobList.add(watchlistJob)
|
|
||||||
|
|
||||||
// new simulcasts
|
|
||||||
val simulcastsJob = lifecycleScope.launch {
|
|
||||||
// val latestSeasonTag = Crunchyroll.seasonList().items.first().id
|
|
||||||
// val newSimulcasts = Crunchyroll.browse(seasonTag = latestSeasonTag, n = 50)
|
|
||||||
val newSimulcasts = Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50)
|
|
||||||
|
|
||||||
adapterNewTitles = MediaItemAdapter(newSimulcasts.toItemMediaList())
|
|
||||||
binding.recyclerNewTitles.adapter = adapterNewTitles
|
|
||||||
}
|
|
||||||
asyncJobList.add(simulcastsJob)
|
|
||||||
|
|
||||||
// newly added / top ten
|
|
||||||
val newlyAddedJob = lifecycleScope.launch {
|
|
||||||
adapterTopTen = MediaItemAdapter(Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).toItemMediaList())
|
|
||||||
binding.recyclerTopTen.adapter = adapterTopTen
|
|
||||||
}
|
|
||||||
asyncJobList.add(newlyAddedJob)
|
|
||||||
|
|
||||||
asyncJobList.joinAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initActions() {
|
|
||||||
binding.buttonPlayHighlight.setOnClickListener {
|
binding.buttonPlayHighlight.setOnClickListener {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
@ -126,37 +104,57 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textHighlightMyList.setOnClickListener {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
// TODO implement
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
// if (StorageController.myList.contains(0)) {
|
model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
|
||||||
// StorageController.myList.remove(0)
|
when (uiState) {
|
||||||
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_add_24)
|
is HomeViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
||||||
// } else {
|
is HomeViewModel.UiState.Loading -> bindUiStateLoading()
|
||||||
// StorageController.myList.add(0)
|
is HomeViewModel.UiState.Error -> bindUiStateError(uiState)
|
||||||
// binding.textHighlightMyList.setDrawableTop(R.drawable.ic_baseline_check_24)
|
}
|
||||||
// }
|
}
|
||||||
// StorageController.saveMyList(requireContext())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
binding.textHighlightInfo.setOnClickListener {
|
|
||||||
activity?.showFragment(MediaFragment(highlightMedia.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
adapterUpNext.onItemClick = { id, _ ->
|
|
||||||
activity?.showFragment(MediaFragment(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
adapterWatchlist.onItemClick = { id, _ ->
|
|
||||||
activity?.showFragment(MediaFragment(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
adapterNewTitles.onItemClick = { id, _ ->
|
|
||||||
activity?.showFragment(MediaFragment(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
adapterTopTen.onItemClick = { id, _ ->
|
|
||||||
activity?.showFragment(MediaFragment(id)) //(mediaId))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun bindUiStateNormal(uiState: HomeViewModel.UiState.Normal) {
|
||||||
|
val adapterUpNext = binding.recyclerNewEpisodes.adapter as MediaItemListAdapter
|
||||||
|
adapterUpNext.submitList(uiState.upNextItems.filter { !it.fullyWatched }.toItemMediaList())
|
||||||
|
|
||||||
|
val adapterWatchlist = binding.recyclerWatchlist.adapter as MediaItemListAdapter
|
||||||
|
adapterWatchlist.submitList(uiState.watchlistItems.toItemMediaList())
|
||||||
|
|
||||||
|
val adapterNewTitles = binding.recyclerNewTitles.adapter as MediaItemListAdapter
|
||||||
|
adapterNewTitles.submitList(uiState.recentlyAddedItems.toItemMediaList())
|
||||||
|
|
||||||
|
val adapterTopTen = binding.recyclerTopTen.adapter as MediaItemListAdapter
|
||||||
|
adapterTopTen.submitList(uiState.topTenItems.toItemMediaList())
|
||||||
|
|
||||||
|
// highlight item
|
||||||
|
binding.textHighlightTitle.text = uiState.highlightItem.title
|
||||||
|
Glide.with(requireContext()).load(uiState.highlightItem.images.poster_wide[0][3].source)
|
||||||
|
.into(binding.imageHighlight)
|
||||||
|
|
||||||
|
val iconHighlightWatchlist = if (uiState.highlightIsWatchlist) {
|
||||||
|
R.drawable.ic_baseline_check_24
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_baseline_add_24
|
||||||
|
}
|
||||||
|
binding.textHighlightMyList.setDrawableTop(iconHighlightWatchlist)
|
||||||
|
binding.textHighlightMyList.isClickable = true
|
||||||
|
|
||||||
|
binding.textHighlightInfo.setOnClickListener {
|
||||||
|
activity?.showFragment(MediaFragment(uiState.highlightItem.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindUiStateLoading() {
|
||||||
|
// currently not used
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindUiStateError(uiState: HomeViewModel.UiState.Error) {
|
||||||
|
// currently not used
|
||||||
|
Log.e(classTag, "A error occurred while loading a UiState: ${uiState.message}")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* Teapod
|
||||||
|
*
|
||||||
|
* Copyright 2020-2022 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.teapod.ui.activity.main.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val uiState = MutableStateFlow<UiState>(UiState.Loading)
|
||||||
|
|
||||||
|
sealed class UiState {
|
||||||
|
object Loading : UiState()
|
||||||
|
data class Normal(
|
||||||
|
val upNextItems: List<ContinueWatchingItem>,
|
||||||
|
val watchlistItems: List<Item>,
|
||||||
|
val recentlyAddedItems: List<Item>,
|
||||||
|
val topTenItems: List<Item>,
|
||||||
|
val highlightItem: Item,
|
||||||
|
val highlightIsWatchlist:Boolean
|
||||||
|
) : UiState()
|
||||||
|
data class Error(val message: String?) : UiState()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
|
||||||
|
scope.launch { uiState.collect { collector(it) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
uiState.emit(UiState.Loading)
|
||||||
|
try {
|
||||||
|
// run the loading in parallel to speed up the process
|
||||||
|
|
||||||
|
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount().items }
|
||||||
|
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(50).items }
|
||||||
|
val recentlyAddedJob = viewModelScope.async {
|
||||||
|
Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).items
|
||||||
|
}
|
||||||
|
val topTenJob = viewModelScope.async {
|
||||||
|
Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).items
|
||||||
|
}
|
||||||
|
|
||||||
|
val recentlyAddedItems = recentlyAddedJob.await()
|
||||||
|
// FIXME crashes on newTitles.items.size == 0
|
||||||
|
val highlightItem = recentlyAddedItems[Random.nextInt(recentlyAddedItems.size)]
|
||||||
|
val highlightItemIsWatchlist = Crunchyroll.isWatchlist(highlightItem.id)
|
||||||
|
|
||||||
|
uiState.emit(UiState.Normal(
|
||||||
|
upNextJob.await(), watchlistJob.await(), recentlyAddedJob.await(),
|
||||||
|
topTenJob.await(), highlightItem, highlightItemIsWatchlist
|
||||||
|
))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
uiState.emit(UiState.Error(e.message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the watchlist state of the highlight media.
|
||||||
|
*/
|
||||||
|
fun toggleHighlightWatchlist() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
uiState.update { currentUiState ->
|
||||||
|
if (currentUiState is UiState.Normal) {
|
||||||
|
if (currentUiState.highlightIsWatchlist) {
|
||||||
|
Crunchyroll.deleteWatchlist(currentUiState.highlightItem.id)
|
||||||
|
} else {
|
||||||
|
Crunchyroll.postWatchlist(currentUiState.highlightItem.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the watchlist after a item has been added/removed
|
||||||
|
val watchlistItems = Crunchyroll.watchlist(50).items
|
||||||
|
|
||||||
|
currentUiState.copy(
|
||||||
|
watchlistItems = watchlistItems,
|
||||||
|
highlightIsWatchlist = !currentUiState.highlightIsWatchlist)
|
||||||
|
} else {
|
||||||
|
currentUiState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,13 @@ fun Collection<Item>.toItemMediaList(): List<ItemMedia> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmName("toItemMediaListItem")
|
||||||
|
fun List<Item>.toItemMediaList(): List<ItemMedia> {
|
||||||
|
return this.map {
|
||||||
|
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JvmName("toItemMediaListContinueWatchingItem")
|
@JvmName("toItemMediaListContinueWatchingItem")
|
||||||
fun Collection<ContinueWatchingItem>.toItemMediaList(): List<ItemMedia> {
|
fun Collection<ContinueWatchingItem>.toItemMediaList(): List<ItemMedia> {
|
||||||
return items.map {
|
return items.map {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.mosad.teapod.util.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
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.databinding.ItemMediaBinding
|
||||||
|
import org.mosad.teapod.util.ItemMedia
|
||||||
|
|
||||||
|
class MediaItemListAdapter(private val onClickListener: OnClickListener) : ListAdapter<ItemMedia, MediaItemListAdapter.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: 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object DiffCallback : DiffUtil.ItemCallback<ItemMedia>() {
|
||||||
|
override fun areItemsTheSame(oldItem: ItemMedia, newItem: ItemMedia): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: ItemMedia, newItem: ItemMedia): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnClickListener(val clickListener: (item: ItemMedia) -> Unit) {
|
||||||
|
fun onClick(item: ItemMedia) = clickListener(item)
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
Loading…
Reference in New Issue