/** * Teapod * * Copyright 2020-2022 * * 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 import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.children import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.bumptech.glide.Glide import com.facebook.shimmer.ShimmerFrameLayout import kotlinx.coroutines.launch import org.mosad.teapod.R import org.mosad.teapod.databinding.FragmentHomeBinding 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.playerIntent import org.mosad.teapod.util.setDrawableTop import org.mosad.teapod.util.showFragment import org.mosad.teapod.util.toItemMediaList class HomeFragment : Fragment() { private val classTag = javaClass.name private val model: HomeViewModel by viewModels() private lateinit var binding: FragmentHomeBinding private val itemOffset = 21 private val playerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { model.updateUpNextItems() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = FragmentHomeBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.recyclerUpNext.adapter = MediaEpisodeListAdapter( MediaEpisodeListAdapter.OnClickListener { playerResult.launch(playerIntent(it.panel.episodeMetadata.seasonId, it.panel.id)) }, itemOffset ) binding.recyclerWatchlist.adapter = MediaItemListAdapter( MediaItemListAdapter.OnClickListener { activity?.showFragment(MediaFragment(it.id)) }, itemOffset ) binding.recyclerRecommendations.adapter = MediaItemListAdapter( MediaItemListAdapter.OnClickListener { activity?.showFragment(MediaFragment(it.id)) }, itemOffset ) binding.recyclerNewTitles.adapter = MediaItemListAdapter( MediaItemListAdapter.OnClickListener { activity?.showFragment(MediaFragment(it.id)) }, itemOffset ) binding.recyclerTopTen.adapter = MediaItemListAdapter( MediaItemListAdapter.OnClickListener { activity?.showFragment(MediaFragment(it.id)) }, itemOffset ) 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 } // set the shimmer items size as it's depending on the screen size setShimmerLayoutItemSize(binding.shimmerLayoutUpNext) setShimmerLayoutItemSize(binding.shimmerLayoutWatchlist) setShimmerLayoutItemSize(binding.shimmerLayoutRecommendations) setShimmerLayoutItemSize(binding.shimmerLayoutNewTitles) setShimmerLayoutItemSize(binding.shimmerLayoutTopTen) viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState -> when (uiState) { is HomeViewModel.UiState.Normal -> bindUiStateNormal(uiState) is HomeViewModel.UiState.Loading -> bindUiStateLoading() is HomeViewModel.UiState.Error -> bindUiStateError(uiState) } } } } } private fun bindUiStateNormal(uiState: HomeViewModel.UiState.Normal) { 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()) val adapterRecommendations = binding.recyclerRecommendations.adapter as MediaItemListAdapter adapterRecommendations.submitList(uiState.recommendationsItems.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)) } binding.buttonPlayHighlight.setOnClickListener { val panel = uiState.highlightItemUpNext.panel playerResult.launch(playerIntent(panel.episodeMetadata.seasonId, panel.id)) } // disable the shimmer effect disableShimmer() // make highlights layout visible again binding.linearHighlight.isVisible = true } private fun bindUiStateLoading() { // hide highlights layout binding.linearHighlight.isVisible = false binding.shimmerLayoutUpNext.startShimmer() binding.shimmerLayoutWatchlist.startShimmer() binding.shimmerLayoutRecommendations.startShimmer() binding.shimmerLayoutNewTitles.startShimmer() binding.shimmerLayoutTopTen.startShimmer() } private fun setShimmerLayoutItemSize(shimmerLayout: ShimmerFrameLayout) { (shimmerLayout.children.first() as? LinearLayout)?.children?.forEach { child -> child.layoutParams.apply { width = (resources.displayMetrics.widthPixels / requireContext().resources.getInteger(R.integer.item_media_columns)) - itemOffset } } } private fun bindUiStateError(uiState: HomeViewModel.UiState.Error) { // currently not used Log.e(classTag, "A error occurred while loading a UiState: ${uiState.message}") } /** * Disable the shimmer effect for all shimmer layouts and hide them. */ private fun disableShimmer() { binding.shimmerLayoutHighlight.apply { stopShimmer() isVisible = false } binding.shimmerLayoutUpNext.apply { stopShimmer() isVisible = false } binding.shimmerLayoutWatchlist.apply { stopShimmer() isVisible = false } binding.shimmerLayoutRecommendations.apply { stopShimmer() isVisible = false } binding.shimmerLayoutNewTitles.apply { stopShimmer() isVisible = false } binding.shimmerLayoutTopTen.apply { stopShimmer() isVisible = false } } }