From afe47270d80c154828adcaa66708cad5c3fd2207 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Fri, 11 Oct 2024 11:10:48 +0200 Subject: [PATCH 1/3] Discover: extract AddShowPopupMenu, add add show menu item Also update watchlist actions visibility on refresh depending on Trakt sign-in state. --- .../shows/search/discover/AddFragment.kt | 55 ++++++++------ .../shows/search/discover/AddShowPopupMenu.kt | 73 +++++++++++++++++++ .../search/discover/ShowsDiscoverAdapter.kt | 54 ++++++++------ .../search/discover/ShowsDiscoverFragment.kt | 26 +++---- .../shows/search/discover/TraktAddFragment.kt | 49 +++---------- app/src/main/res/menu/add_show_popup_menu.xml | 8 +- 6 files changed, 163 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddFragment.kt index 8cf293c5ea..09f78e4341 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddFragment.kt @@ -156,13 +156,13 @@ abstract class AddFragment : Fragment() { activity: Activity, objects: List, private val itemClickListener: ItemClickListener, - private val enableMoreOptions: Boolean + private val showWatchlistActions: Boolean ) : ArrayAdapter(activity, 0, objects) { interface ItemClickListener { fun onItemClick(item: SearchResult) fun onAddClick(item: SearchResult) - fun onMoreOptionsClick(view: View, showTmdbId: Int) + fun onMoreOptionsClick(view: View, show: SearchResult) } private fun getItemForShowTmdbId(showTmdbId: Int): SearchResult? { @@ -208,7 +208,7 @@ abstract class AddFragment : Fragment() { } val item = getItem(position) - holder.bindTo(item, context, enableMoreOptions) + holder.bindTo(item, context, showWatchlistActions) return view } @@ -231,39 +231,46 @@ abstract class AddFragment : Fragment() { } } - private fun onMoreOptionsClick() { + private fun onMoreOptionsClick(anchor: View) { item?.let { - itemClickListener.onMoreOptionsClick( - binding.buttonItemAddMoreOptions, - it.tmdbId - ) + itemClickListener.onMoreOptionsClick(anchor, it) } } - fun bindTo(item: SearchResult?, context: Context, enableMoreOptions: Boolean) { + fun bindTo(item: SearchResult?, context: Context, showWatchlistActions: Boolean) { this.item = item - if (enableMoreOptions) { - binding.root.setContextAndLongClickListener { - onMoreOptionsClick() - } - binding.buttonItemAddMoreOptions.setOnClickListener { - onMoreOptionsClick() - } - } else { - // Remove listener so there is no long press feedback - binding.root.setContextAndLongClickListener(null) - binding.buttonItemAddMoreOptions.setOnClickListener(null) - } - binding.buttonItemAddMoreOptions.visibility = - if (enableMoreOptions) View.VISIBLE else View.GONE - if (item == null) { binding.addIndicatorAddShow.isGone = true binding.textViewAddTitle.text = null binding.textViewAddDescription.text = null binding.imageViewAddPoster.setImageDrawable(null) } else { + val canBeAdded = item.state == SearchResult.STATE_ADD + // If not added, always display add action on long press for accessibility + binding.root.apply { + if (canBeAdded) { + setContextAndLongClickListener { + onMoreOptionsClick(binding.root) + } + } else { + // Remove listener so there is no long press feedback + setContextAndLongClickListener(null) + } + } + // Only display more options button when displaying remove from watchlist action + binding.buttonItemAddMoreOptions.apply { + if (showWatchlistActions) { + setOnClickListener { + onMoreOptionsClick(binding.buttonItemAddMoreOptions) + } + isVisible = true + } else { + setOnClickListener(null) + isGone = true + } + } + // add indicator val showTitle = item.title binding.addIndicatorAddShow.apply { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt new file mode 100644 index 0000000000..24f50324b1 --- /dev/null +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Uwe Trottmann + +package com.battlelancer.seriesguide.shows.search.discover + +import android.content.Context +import android.os.AsyncTask +import android.view.MenuItem +import android.view.View +import androidx.appcompat.widget.PopupMenu +import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.shows.search.discover.AddFragment.OnAddingShowEvent +import com.battlelancer.seriesguide.util.TaskManager +import com.battlelancer.seriesguide.util.tasks.AddShowToWatchlistTask +import com.battlelancer.seriesguide.util.tasks.RemoveShowFromWatchlistTask +import org.greenrobot.eventbus.EventBus + +/** + * A [PopupMenu] with menu items to add a show and add to or remove a show from the Trakt watchlist. + * + * Hides add show item by default based on [show] state. + * Use methods to hide not useful watchlist options. + */ +class AddShowPopupMenu( + private val context: Context, + private val show: SearchResult, + anchor: View +) : PopupMenu(anchor.context, anchor), PopupMenu.OnMenuItemClickListener { + + init { + inflate(R.menu.add_show_popup_menu) + if (show.state != SearchResult.STATE_ADD) { + menu.findItem(R.id.menu_action_add_show_add).isVisible = false + } + setOnMenuItemClickListener(this) + } + + override fun onMenuItemClick(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.menu_action_add_show_add -> { + // post so other fragments can display a progress indicator for that show + EventBus.getDefault().post(OnAddingShowEvent(show.tmdbId)) + TaskManager.getInstance().performAddTask(context, show) + true + } + + R.id.menu_action_add_show_watchlist_add -> { + @Suppress("DEPRECATION") // AsyncTask + AddShowToWatchlistTask(context, show.tmdbId) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) + true + } + + R.id.menu_action_add_show_watchlist_remove -> { + @Suppress("DEPRECATION") // AsyncTask + RemoveShowFromWatchlistTask(context, show.tmdbId) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) + true + } + + else -> false + } + } + + fun hideAddToWatchlistAction() { + menu.findItem(R.id.menu_action_add_show_watchlist_add).isVisible = false + } + + fun hideRemoveFromWatchlistAction() { + menu.findItem(R.id.menu_action_add_show_watchlist_remove).isVisible = false + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt index aacdf19e23..e3a0820c55 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt @@ -29,13 +29,12 @@ import com.battlelancer.seriesguide.util.ViewTools.setContextAndLongClickListene */ class ShowsDiscoverAdapter( private val context: Context, - private val itemClickListener: ItemClickListener, - private val showMenuWatchlist: Boolean, - private val hideMenuWatchlistIfAdded: Boolean + private val itemClickListener: ItemClickListener ) : RecyclerView.Adapter() { private var emptyText: String = "" private var hasError: Boolean = false + private var showWatchlistActions: Boolean = false private val searchResults = mutableListOf() private val links: MutableList = mutableListOf() @@ -69,7 +68,8 @@ class ShowsDiscoverAdapter( fun updateSearchResults( newSearchResults: List?, emptyText: String, - hasError: Boolean + hasError: Boolean, + showWatchlistActions: Boolean ) { searchResults.clear() if (newSearchResults != null) { @@ -77,6 +77,7 @@ class ShowsDiscoverAdapter( } this.emptyText = emptyText this.hasError = hasError + this.showWatchlistActions = showWatchlistActions notifyDataSetChanged() } @@ -128,6 +129,7 @@ class ShowsDiscoverAdapter( VIEW_TYPE_LINK -> LinkViewHolder.inflate(parent, itemClickListener) VIEW_TYPE_HEADER -> HeaderViewHolder.inflate(parent, itemClickListener) VIEW_TYPE_SHOW -> ShowViewHolder.inflate(parent, itemClickListener) + VIEW_TYPE_EMPTY -> EmptyViewHolder.inflate(parent, itemClickListener) else -> throw IllegalArgumentException("View type $viewType is unknown") } @@ -145,7 +147,7 @@ class ShowsDiscoverAdapter( is ShowViewHolder -> { val item = getSearchResultFor(position) - holder.bindTo(context, item, showMenuWatchlist, hideMenuWatchlistIfAdded) + holder.bindTo(context, item, showWatchlistActions) } is EmptyViewHolder -> { @@ -159,7 +161,7 @@ class ShowsDiscoverAdapter( fun onHeaderButtonClick(anchor: View) fun onItemClick(item: SearchResult) fun onAddClick(item: SearchResult) - fun onMoreOptionsClick(view: View, showTmdbId: Int) + fun onMoreOptionsClick(view: View, show: SearchResult) fun onEmptyViewButtonClick() } @@ -265,38 +267,44 @@ class ShowsDiscoverAdapter( } } - private fun onMoreOptionsClick() { + private fun onMoreOptionsClick(anchor: View) { item?.let { - itemClickListener.onMoreOptionsClick(binding.buttonItemAddMoreOptions, it.tmdbId) + itemClickListener.onMoreOptionsClick(anchor, it) } } fun bindTo( context: Context, item: SearchResult, - showMenuWatchlist: Boolean, - hideMenuWatchlistIfAdded: Boolean + showWatchlistActions: Boolean, ) { this.item = item - // hide and disable watchlist menu if not useful - val showMenuWatchlistActual = showMenuWatchlist - && (!hideMenuWatchlistIfAdded || item.state != SearchResult.STATE_ADDED) - if (showMenuWatchlistActual) { + val canBeAdded = item.state == SearchResult.STATE_ADD + // If not added, always display add action on long press for accessibility + if (canBeAdded) { itemView.setContextAndLongClickListener { - onMoreOptionsClick() - } - binding.buttonItemAddMoreOptions.setOnClickListener { - onMoreOptionsClick() + onMoreOptionsClick(itemView) } - binding.buttonItemAddMoreOptions.isVisible = true } else { - // remove listener to prevent long press feedback + // Remove listener to prevent long press feedback itemView.setContextAndLongClickListener(null) - binding.buttonItemAddMoreOptions.setOnClickListener(null) - binding.buttonItemAddMoreOptions.isGone = true } - // display added indicator instead of add button if already added that show + // Only display more options button when displaying add to watchlist action, + // but only when a show is not added. + binding.buttonItemAddMoreOptions.apply { + if (showWatchlistActions && canBeAdded) { + setOnClickListener { + onMoreOptionsClick(binding.buttonItemAddMoreOptions) + } + isVisible = true + } else { + setOnClickListener(null) + isGone = true + } + } + + // add button/indicator binding.addIndicatorAddShow.setState(item.state) val showTitle = item.title binding.addIndicatorAddShow.setNameOfAssociatedItem(showTitle) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt index f9de516ae7..b286526b7e 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt @@ -73,10 +73,7 @@ class ShowsDiscoverFragment : BaseAddShowsFragment() { swipeRefreshLayout.setOnRefreshListener { refreshData() } ViewTools.setSwipeRefreshLayoutColors(requireActivity().theme, swipeRefreshLayout) - adapter = ShowsDiscoverAdapter( - requireContext(), discoverItemClickListener, - TraktCredentials.get(requireContext()).hasCredentials(), true - ) + adapter = ShowsDiscoverAdapter(requireContext(), discoverItemClickListener) val recyclerView = binding.recyclerViewShowsDiscover recyclerView.also { it.setHasFixedSize(true) @@ -87,7 +84,12 @@ class ShowsDiscoverFragment : BaseAddShowsFragment() { // observe results and loading state viewLifecycleOwner.lifecycleScope.launch { model.data.collectLatest { - adapter.updateSearchResults(it.searchResults, it.emptyText, !it.successful) + adapter.updateSearchResults( + it.searchResults, + it.emptyText, + hasError = !it.successful, + showWatchlistActions = TraktCredentials.get(requireContext()).hasCredentials() + ) } } viewLifecycleOwner.lifecycleScope.launch { @@ -196,14 +198,12 @@ class ShowsDiscoverFragment : BaseAddShowsFragment() { TaskManager.getInstance().performAddTask(context, item) } - override fun onMoreOptionsClick(view: View, showTmdbId: Int) { - PopupMenu(view.context, view).apply { - inflate(R.menu.add_show_popup_menu) - // only support adding shows to watchlist - menu.findItem(R.id.menu_action_show_watchlist_remove).isVisible = false - setOnMenuItemClickListener( - TraktAddFragment.AddItemMenuItemClickListener(requireContext(), showTmdbId) - ) + override fun onMoreOptionsClick(view: View, show: SearchResult) { + val isTraktConnected = TraktCredentials.get(requireContext()).hasCredentials() + AddShowPopupMenu(requireContext(), show, view).apply { + if (!isTraktConnected) hideAddToWatchlistAction() + // this does not know watchlist state, so only show the add to watchlist action + hideRemoveFromWatchlistAction() }.show() } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt index 753c295f8f..429fd01bde 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt @@ -3,8 +3,6 @@ package com.battlelancer.seriesguide.shows.search.discover -import android.content.Context -import android.os.AsyncTask import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -13,7 +11,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.GridView -import androidx.appcompat.widget.PopupMenu import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import androidx.loader.app.LoaderManager @@ -24,9 +21,7 @@ import com.battlelancer.seriesguide.ui.OverviewActivity import com.battlelancer.seriesguide.ui.widgets.EmptyView import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.ThemeUtils -import com.battlelancer.seriesguide.util.tasks.AddShowToWatchlistTask import com.battlelancer.seriesguide.util.tasks.BaseShowActionTask.ShowChangedEvent -import com.battlelancer.seriesguide.util.tasks.RemoveShowFromWatchlistTask import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -76,7 +71,7 @@ class TraktAddFragment : AddFragment() { // set initial view states setProgressVisible(visible = true, animate = false) - // setup adapter, enable context menu only for watchlist + // set up adapter adapter = AddAdapter( requireActivity(), ArrayList(), itemClickListener, listType == TraktAddLoader.Type.WATCHLIST @@ -118,42 +113,16 @@ class TraktAddFragment : AddFragment() { TaskManager.getInstance().performAddTask(requireContext(), item) } - override fun onMoreOptionsClick(view: View, showTmdbId: Int) { - val popupMenu = PopupMenu(view.context, view) - popupMenu.inflate(R.menu.add_show_popup_menu) - - // prevent adding shows to watchlist already on watchlist - if (listType == TraktAddLoader.Type.WATCHLIST) { - popupMenu.menu.findItem(R.id.menu_action_show_watchlist_add).isVisible = false - } - popupMenu.setOnMenuItemClickListener( - AddItemMenuItemClickListener(requireContext(), showTmdbId) - ) - popupMenu.show() - } - } - - class AddItemMenuItemClickListener( - private val context: Context, - private val showTmdbId: Int - ) : PopupMenu.OnMenuItemClickListener { - override fun onMenuItemClick(item: MenuItem): Boolean { - val itemId = item.itemId - if (itemId == R.id.menu_action_show_watchlist_add) { - @Suppress("DEPRECATION") // AsyncTask - AddShowToWatchlistTask(context, showTmdbId) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) - return true - } - if (itemId == R.id.menu_action_show_watchlist_remove) { - @Suppress("DEPRECATION") // AsyncTask - RemoveShowFromWatchlistTask(context, showTmdbId) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) - return true + override fun onMoreOptionsClick(view: View, show: SearchResult) { + AddShowPopupMenu(requireContext(), show, view).apply { + // For watchlist only show remove from, for other lists show no watchlist option + hideAddToWatchlistAction() + if (listType != TraktAddLoader.Type.WATCHLIST) { + hideRemoveFromWatchlistAction() + } + }.show() } - return false } - } private val optionsMenuProvider: MenuProvider = object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { diff --git a/app/src/main/res/menu/add_show_popup_menu.xml b/app/src/main/res/menu/add_show_popup_menu.xml index af46837142..897c8eda52 100644 --- a/app/src/main/res/menu/add_show_popup_menu.xml +++ b/app/src/main/res/menu/add_show_popup_menu.xml @@ -3,11 +3,15 @@ + + \ No newline at end of file From 55f69ba3034088c06a753e580caecdbb96c89f94 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Fri, 11 Oct 2024 13:53:09 +0200 Subject: [PATCH 2/3] Discover: extract item view holder and click listener --- .../search/discover/BaseAddShowsFragment.kt | 42 +----- .../discover/ItemAddShowClickListener.kt | 60 ++++++++ .../search/discover/ItemAddShowViewHolder.kt | 122 ++++++++++++++++ .../discover/SearchResultPagingAdapter.kt | 16 +- .../search/discover/SearchResultViewHolder.kt | 82 ----------- .../search/discover/ShowsDiscoverAdapter.kt | 96 +----------- .../search/discover/ShowsDiscoverFragment.kt | 137 +++++++----------- .../discover/ShowsDiscoverPagingFragment.kt | 7 +- .../search/similar/SimilarShowsAdapter.kt | 18 ++- .../search/similar/SimilarShowsFragment.kt | 7 +- 10 files changed, 271 insertions(+), 316 deletions(-) create mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt create mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt delete mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultViewHolder.kt diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseAddShowsFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseAddShowsFragment.kt index f41eb67ead..fafd18a221 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseAddShowsFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseAddShowsFragment.kt @@ -4,17 +4,9 @@ package com.battlelancer.seriesguide.shows.search.discover import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope import com.battlelancer.seriesguide.enums.NetworkResult -import com.battlelancer.seriesguide.provider.SgRoomDatabase import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.shows.tools.ShowTools2 -import com.battlelancer.seriesguide.ui.OverviewActivity -import com.battlelancer.seriesguide.util.TaskManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -65,36 +57,6 @@ abstract class BaseAddShowsFragment : Fragment() { } } - interface ItemClickListener { - fun onItemClick(item: SearchResult) - fun onAddClick(item: SearchResult) - } - - protected val itemClickListener = object : ItemClickListener { - override fun onItemClick(item: SearchResult) { - if (item.state != SearchResult.STATE_ADDING) { - if (item.state == SearchResult.STATE_ADDED) { - // Already in library, open it. - lifecycleScope.launch { - val showId = withContext(Dispatchers.IO) { - SgRoomDatabase.getInstance(requireContext()).sgShow2Helper() - .getShowIdByTmdbId(item.tmdbId) - } - if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { - startActivity(OverviewActivity.intentShow(requireContext(), showId)) - } - } - } else { - // Display more details in a dialog. - AddShowDialogFragment.show(parentFragmentManager, item) - } - } - } - - override fun onAddClick(item: SearchResult) { - EventBus.getDefault().post(AddFragment.OnAddingShowEvent(item.tmdbId)) - TaskManager.getInstance().performAddTask(context, item) - } - - } + protected val itemClickListener + get() = ItemAddShowClickListener(requireContext(), lifecycle, parentFragmentManager) } \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt new file mode 100644 index 0000000000..4f27b2f0bb --- /dev/null +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Uwe Trottmann + +package com.battlelancer.seriesguide.shows.search.discover + +import android.content.Context +import android.view.View +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.coroutineScope +import com.battlelancer.seriesguide.provider.SgRoomDatabase +import com.battlelancer.seriesguide.traktapi.TraktCredentials +import com.battlelancer.seriesguide.ui.OverviewActivity +import com.battlelancer.seriesguide.util.TaskManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.greenrobot.eventbus.EventBus + +open class ItemAddShowClickListener( + private val context: Context, + private val lifecycle: Lifecycle, + private val fragmentManager: FragmentManager +) : ItemAddShowViewHolder.ClickListener { + + override fun onItemClick(item: SearchResult) { + if (item.state != SearchResult.STATE_ADDING) { + if (item.state == SearchResult.STATE_ADDED) { + // Already in library, open it. + lifecycle.coroutineScope.launch { + val showId = withContext(Dispatchers.IO) { + SgRoomDatabase.getInstance(context).sgShow2Helper() + .getShowIdByTmdbId(item.tmdbId) + } + if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + context.startActivity(OverviewActivity.intentShow(context, showId)) + } + } + } else { + // Display more details in a dialog. + AddShowDialogFragment.show(fragmentManager, item) + } + } + } + + override fun onAddClick(item: SearchResult) { + EventBus.getDefault().post(AddFragment.OnAddingShowEvent(item.tmdbId)) + TaskManager.getInstance().performAddTask(context, item) + } + + override fun onMoreOptionsClick(view: View, show: SearchResult) { + val isTraktConnected = TraktCredentials.get(context).hasCredentials() + AddShowPopupMenu(context, show, view).apply { + // this does not know watchlist state, so if at all only show the add to item + hideRemoveFromWatchlistAction() + if (!isTraktConnected) hideAddToWatchlistAction() + }.show() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt new file mode 100644 index 0000000000..5807ac9a4a --- /dev/null +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Uwe Trottmann + +package com.battlelancer.seriesguide.shows.search.discover + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.TooltipCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.databinding.ItemAddshowBinding +import com.battlelancer.seriesguide.util.ImageTools +import com.battlelancer.seriesguide.util.ViewTools.setContextAndLongClickListener + +class ItemAddShowViewHolder( + private val binding: ItemAddshowBinding, + private val clickListener: ClickListener +) : RecyclerView.ViewHolder(binding.root) { + + private var item: SearchResult? = null + + init { + itemView.setOnClickListener { + item?.let { clickListener.onItemClick(it) } + } + binding.addIndicatorAddShow.setOnAddClickListener { + item?.let { clickListener.onAddClick(it) } + } + binding.buttonItemAddMoreOptions.also { + TooltipCompat.setTooltipText(it, it.contentDescription) + } + } + + private fun onMoreOptionsClick(anchor: View) { + item?.let { + clickListener.onMoreOptionsClick(anchor, it) + } + } + + // Nullable item to support placeholders of paging adapters + fun bindTo( + context: Context, + item: SearchResult?, + showWatchlistActions: Boolean + ) { + this.item = item + + if (item == null) { + // placeholder data + binding.textViewAddTitle.text = null + binding.textViewAddDescription.text = null + binding.addIndicatorAddShow.isGone = true + binding.imageViewAddPoster.setImageResource(R.drawable.ic_photo_gray_24dp) + + itemView.setContextAndLongClickListener(null) + binding.buttonItemAddMoreOptions.isGone = true + return + } + + // title and overview + val showTitle = item.title + binding.textViewAddTitle.text = showTitle + binding.textViewAddDescription.text = item.overview + + // add button/indicator + binding.addIndicatorAddShow.apply { + setState(item.state) + setNameOfAssociatedItem(showTitle) + isVisible = true + } + + // image + ImageTools.loadShowPosterResizeCrop( + context, + binding.imageViewAddPoster, + item.posterPath + ) + + // context/long press listener and more options button + val canBeAdded = item.state == SearchResult.STATE_ADD + // If not added, always display add action on long press for accessibility + if (canBeAdded) { + itemView.setContextAndLongClickListener { + onMoreOptionsClick(itemView) + } + } else { + // Remove listener to prevent long press feedback + itemView.setContextAndLongClickListener(null) + } + // Only display more options button when displaying watchlist actions + binding.buttonItemAddMoreOptions.apply { + if (showWatchlistActions) { + setOnClickListener { + onMoreOptionsClick(binding.buttonItemAddMoreOptions) + } + isVisible = true + } else { + setOnClickListener(null) + isGone = true + } + } + } + + companion object { + fun create(parent: ViewGroup, clickListener: ClickListener) = + ItemAddShowViewHolder( + ItemAddshowBinding.inflate(LayoutInflater.from(parent.context), parent, false), + clickListener + ) + } + + interface ClickListener { + fun onItemClick(item: SearchResult) + fun onAddClick(item: SearchResult) + fun onMoreOptionsClick(view: View, show: SearchResult) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultPagingAdapter.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultPagingAdapter.kt index 0551dbc8b6..9eb5381218 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultPagingAdapter.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultPagingAdapter.kt @@ -4,22 +4,24 @@ package com.battlelancer.seriesguide.shows.search.discover import android.annotation.SuppressLint +import android.content.Context import android.view.ViewGroup import androidx.paging.PagingDataAdapter -import androidx.recyclerview.widget.RecyclerView class SearchResultPagingAdapter( - private val itemClickListener: BaseAddShowsFragment.ItemClickListener -) : PagingDataAdapter( + private val context: Context, + private val itemClickListener: ItemAddShowViewHolder.ClickListener, + private val showWatchlistActions: Boolean +) : PagingDataAdapter( SearchResultDiffCallback() ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return SearchResultViewHolder.create(parent, itemClickListener) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemAddShowViewHolder { + return ItemAddShowViewHolder.create(parent, itemClickListener) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as SearchResultViewHolder).bindTo(getItem(position)) + override fun onBindViewHolder(holder: ItemAddShowViewHolder, position: Int) { + holder.bindTo(context, getItem(position), showWatchlistActions) } fun setStateForTmdbId(showTmdbId: Int, newState: Int) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultViewHolder.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultViewHolder.kt deleted file mode 100644 index 5b5790520b..0000000000 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultViewHolder.kt +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2018-2024 Uwe Trottmann - -package com.battlelancer.seriesguide.shows.search.discover - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.battlelancer.seriesguide.R -import com.battlelancer.seriesguide.util.ImageTools - -class SearchResultViewHolder( - itemView: View, - itemClickListener: BaseAddShowsFragment.ItemClickListener -) : RecyclerView.ViewHolder(itemView) { - - private val title = itemView.findViewById(R.id.textViewAddTitle) - private val description = itemView.findViewById(R.id.textViewAddDescription) - private val poster = itemView.findViewById(R.id.imageViewAddPoster) - private val addIndicator = itemView.findViewById(R.id.addIndicatorAddShow) - private val moreOptionsButton = itemView.findViewById(R.id.buttonItemAddMoreOptions) - private var item: SearchResult? = null - - init { - itemView.setOnClickListener { _ -> - item?.let { - itemClickListener.onItemClick(it) - } - } - addIndicator.setOnAddClickListener { _ -> - item?.let { - itemClickListener.onAddClick(it) - } - } - } - - fun bindTo(searchResult: SearchResult?) { - this.item = searchResult - - // hide more options button - moreOptionsButton.visibility = View.GONE - - // add indicator - if (searchResult != null) { - addIndicator.setState(searchResult.state) - addIndicator.setNameOfAssociatedItem(searchResult.title) - addIndicator.visibility = View.VISIBLE - } else { - addIndicator.visibility = View.GONE - } - - title.text = searchResult?.title - description.text = searchResult?.overview - - // only local shows will have a poster path set - // try to fall back to the TMDB poster for all others - val posterUrl = if (searchResult != null) { - ImageTools.posterUrlOrResolve( - searchResult.posterPath, - searchResult.tmdbId, - searchResult.language, - itemView.context - ) - } else null - ImageTools.loadShowPosterUrlResizeCrop(itemView.context, poster, posterUrl) - } - - companion object { - fun create( - parent: ViewGroup, - itemClickListener: BaseAddShowsFragment.ItemClickListener - ): SearchResultViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_addshow, parent, false) - return SearchResultViewHolder(view, itemClickListener) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt index e3a0820c55..2c7a2ec21b 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverAdapter.kt @@ -9,20 +9,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.TooltipCompat -import androidx.core.view.isGone -import androidx.core.view.isVisible import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.battlelancer.seriesguide.R -import com.battlelancer.seriesguide.databinding.ItemAddshowBinding import com.battlelancer.seriesguide.databinding.ItemDiscoverEmptyBinding import com.battlelancer.seriesguide.databinding.ItemDiscoverHeaderBinding import com.battlelancer.seriesguide.databinding.ItemDiscoverLinkBinding import com.battlelancer.seriesguide.traktapi.TraktCredentials import com.battlelancer.seriesguide.ui.AutoGridLayoutManager -import com.battlelancer.seriesguide.util.ImageTools import com.battlelancer.seriesguide.util.ViewTools -import com.battlelancer.seriesguide.util.ViewTools.setContextAndLongClickListener /** * Displays a set of links and if loaded a list of results, separated by a header. @@ -128,8 +123,7 @@ class ShowsDiscoverAdapter( return when (viewType) { VIEW_TYPE_LINK -> LinkViewHolder.inflate(parent, itemClickListener) VIEW_TYPE_HEADER -> HeaderViewHolder.inflate(parent, itemClickListener) - VIEW_TYPE_SHOW -> ShowViewHolder.inflate(parent, itemClickListener) - + VIEW_TYPE_SHOW -> ItemAddShowViewHolder.create(parent, itemClickListener) VIEW_TYPE_EMPTY -> EmptyViewHolder.inflate(parent, itemClickListener) else -> throw IllegalArgumentException("View type $viewType is unknown") } @@ -145,7 +139,7 @@ class ShowsDiscoverAdapter( holder.bindTo(DiscoverShowsLink.NEW_EPISODES) } - is ShowViewHolder -> { + is ItemAddShowViewHolder -> { val item = getSearchResultFor(position) holder.bindTo(context, item, showWatchlistActions) } @@ -156,12 +150,9 @@ class ShowsDiscoverAdapter( } } - interface ItemClickListener { + interface ItemClickListener : ItemAddShowViewHolder.ClickListener { fun onLinkClick(anchor: View, link: DiscoverShowsLink) fun onHeaderButtonClick(anchor: View) - fun onItemClick(item: SearchResult) - fun onAddClick(item: SearchResult) - fun onMoreOptionsClick(view: View, show: SearchResult) fun onEmptyViewButtonClick() } @@ -248,87 +239,6 @@ class ShowsDiscoverAdapter( } } - class ShowViewHolder( - private val binding: ItemAddshowBinding, - private val itemClickListener: ItemClickListener - ) : RecyclerView.ViewHolder(binding.root) { - - private var item: SearchResult? = null - - init { - itemView.setOnClickListener { - item?.let { itemClickListener.onItemClick(it) } - } - binding.addIndicatorAddShow.setOnAddClickListener { - item?.let { itemClickListener.onAddClick(it) } - } - binding.buttonItemAddMoreOptions.also { - TooltipCompat.setTooltipText(it, it.contentDescription) - } - } - - private fun onMoreOptionsClick(anchor: View) { - item?.let { - itemClickListener.onMoreOptionsClick(anchor, it) - } - } - - fun bindTo( - context: Context, - item: SearchResult, - showWatchlistActions: Boolean, - ) { - this.item = item - - val canBeAdded = item.state == SearchResult.STATE_ADD - // If not added, always display add action on long press for accessibility - if (canBeAdded) { - itemView.setContextAndLongClickListener { - onMoreOptionsClick(itemView) - } - } else { - // Remove listener to prevent long press feedback - itemView.setContextAndLongClickListener(null) - } - // Only display more options button when displaying add to watchlist action, - // but only when a show is not added. - binding.buttonItemAddMoreOptions.apply { - if (showWatchlistActions && canBeAdded) { - setOnClickListener { - onMoreOptionsClick(binding.buttonItemAddMoreOptions) - } - isVisible = true - } else { - setOnClickListener(null) - isGone = true - } - } - - // add button/indicator - binding.addIndicatorAddShow.setState(item.state) - val showTitle = item.title - binding.addIndicatorAddShow.setNameOfAssociatedItem(showTitle) - - // set text properties immediately - binding.textViewAddTitle.text = showTitle - binding.textViewAddDescription.text = item.overview - - ImageTools.loadShowPosterResizeCrop( - context, - binding.imageViewAddPoster, - item.posterPath - ) - } - - companion object { - fun inflate(parent: ViewGroup, itemClickListener: ItemClickListener) = - ShowViewHolder( - ItemAddshowBinding.inflate(LayoutInflater.from(parent.context), parent, false), - itemClickListener - ) - } - } - class EmptyViewHolder( private val binding: ItemDiscoverEmptyBinding, itemClickListener: ItemClickListener diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt index b286526b7e..dfed707f38 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverFragment.kt @@ -21,23 +21,19 @@ import com.battlelancer.seriesguide.databinding.FragmentShowsDiscoverBinding import com.battlelancer.seriesguide.shows.ShowsActivityImpl import com.battlelancer.seriesguide.shows.ShowsActivityViewModel import com.battlelancer.seriesguide.shows.ShowsSettings -import com.battlelancer.seriesguide.shows.search.discover.AddFragment.OnAddingShowEvent import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsActivity import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsFragment import com.battlelancer.seriesguide.streaming.WatchProviderFilterDialogFragment import com.battlelancer.seriesguide.traktapi.TraktCredentials -import com.battlelancer.seriesguide.ui.OverviewActivity import com.battlelancer.seriesguide.ui.dialogs.L10nDialogFragment import com.battlelancer.seriesguide.ui.dialogs.LanguagePickerDialogFragment import com.battlelancer.seriesguide.ui.dialogs.YearPickerDialogFragment -import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.Utils import com.battlelancer.seriesguide.util.ViewTools import com.battlelancer.seriesguide.util.findDialog import com.battlelancer.seriesguide.util.safeShow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -122,96 +118,69 @@ class ShowsDiscoverFragment : BaseAddShowsFragment() { ) } - private val discoverItemClickListener = object : ShowsDiscoverAdapter.ItemClickListener { - override fun onLinkClick(anchor: View, link: DiscoverShowsLink) { - val intent = - when (link) { - DiscoverShowsLink.POPULAR, - DiscoverShowsLink.NEW_EPISODES -> { - ShowsDiscoverPagingActivity.intentLink(requireContext(), link) - } - - DiscoverShowsLink.WATCHLIST, - DiscoverShowsLink.WATCHED, - DiscoverShowsLink.COLLECTION -> { - ShowsTraktActivity.intent(requireContext(), link) - } - - } - Utils.startActivityWithAnimation(activity, intent, anchor) - } - - override fun onHeaderButtonClick(anchor: View) { - val popupMenu = PopupMenu(anchor.context, anchor) - popupMenu.inflate(R.menu.new_episodes_filter_popup_menu) - popupMenu.setOnMenuItemClickListener { menuItem -> - when (menuItem.itemId) { - R.id.menu_action_new_episodes_filter_year -> { - YearPickerDialogFragment - .create(ShowsDiscoverSettings.getFirstReleaseYearRaw(requireContext())) - .also { yearPicker = it } - .apply { onPickedListener = firstReleaseYearPickedListener } - .safeShow(parentFragmentManager, TAG_YEAR_PICKER) - return@setOnMenuItemClickListener true - } + private val discoverItemClickListener: ShowsDiscoverAdapter.ItemClickListener + get() = object : + ItemAddShowClickListener(requireContext(), lifecycle, parentFragmentManager), + ShowsDiscoverAdapter.ItemClickListener { + override fun onLinkClick(anchor: View, link: DiscoverShowsLink) { + val intent = + when (link) { + DiscoverShowsLink.POPULAR, + DiscoverShowsLink.NEW_EPISODES -> { + ShowsDiscoverPagingActivity.intentLink(requireContext(), link) + } + + DiscoverShowsLink.WATCHLIST, + DiscoverShowsLink.WATCHED, + DiscoverShowsLink.COLLECTION -> { + ShowsTraktActivity.intent(requireContext(), link) + } - R.id.menu_action_new_episodes_filter_language -> { - LanguagePickerDialogFragment - .createForShows(ShowsDiscoverSettings.getOriginalLanguage(requireContext())) - .also { languagePicker = it } - .apply { onPickedListener = originalLanguagePickedListener } - .safeShow(parentFragmentManager, TAG_LANGUAGE_PICKER) - return@setOnMenuItemClickListener true } + Utils.startActivityWithAnimation(activity, intent, anchor) + } - R.id.menu_action_new_episodes_filter_providers -> { - WatchProviderFilterDialogFragment.showForShows(parentFragmentManager) - return@setOnMenuItemClickListener true + override fun onHeaderButtonClick(anchor: View) { + val popupMenu = PopupMenu(anchor.context, anchor) + popupMenu.inflate(R.menu.new_episodes_filter_popup_menu) + popupMenu.setOnMenuItemClickListener { menuItem -> + when (menuItem.itemId) { + R.id.menu_action_new_episodes_filter_year -> { + YearPickerDialogFragment + .create(ShowsDiscoverSettings.getFirstReleaseYearRaw(requireContext())) + .also { yearPicker = it } + .apply { onPickedListener = firstReleaseYearPickedListener } + .safeShow(parentFragmentManager, TAG_YEAR_PICKER) + return@setOnMenuItemClickListener true + } + + R.id.menu_action_new_episodes_filter_language -> { + LanguagePickerDialogFragment + .createForShows( + ShowsDiscoverSettings.getOriginalLanguage(requireContext()) + ) + .also { languagePicker = it } + .apply { onPickedListener = originalLanguagePickedListener } + .safeShow(parentFragmentManager, TAG_LANGUAGE_PICKER) + return@setOnMenuItemClickListener true + } + + R.id.menu_action_new_episodes_filter_providers -> { + WatchProviderFilterDialogFragment.showForShows(parentFragmentManager) + return@setOnMenuItemClickListener true + } + + else -> return@setOnMenuItemClickListener false } - - else -> return@setOnMenuItemClickListener false } + popupMenu.show() } - popupMenu.show() - } - override fun onItemClick(item: SearchResult) { - if (item.state != SearchResult.STATE_ADDING) { - if (item.state == SearchResult.STATE_ADDED) { - // already in library, open it - startActivity( - OverviewActivity.intentShowByTmdbId( - requireContext(), - item.tmdbId - ) - ) - } else { - // display more details in a dialog - AddShowDialogFragment.show(parentFragmentManager, item) - } + override fun onEmptyViewButtonClick() { + refreshData() } } - override fun onAddClick(item: SearchResult) { - // post to let other fragments know show is getting added - EventBus.getDefault().post(OnAddingShowEvent(item.tmdbId)) - TaskManager.getInstance().performAddTask(context, item) - } - - override fun onMoreOptionsClick(view: View, show: SearchResult) { - val isTraktConnected = TraktCredentials.get(requireContext()).hasCredentials() - AddShowPopupMenu(requireContext(), show, view).apply { - if (!isTraktConnected) hideAddToWatchlistAction() - // this does not know watchlist state, so only show the add to watchlist action - hideRemoveFromWatchlistAction() - }.show() - } - - override fun onEmptyViewButtonClick() { - refreshData() - } - } - private fun refreshData() { model.refreshData() } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt index 38009476f8..e16757e313 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt @@ -30,6 +30,7 @@ import com.battlelancer.seriesguide.databinding.FragmentShowsPopularBinding import com.battlelancer.seriesguide.shows.ShowsSettings import com.battlelancer.seriesguide.shows.search.TmdbIdExtractor import com.battlelancer.seriesguide.streaming.WatchProviderFilterDialogFragment +import com.battlelancer.seriesguide.traktapi.TraktCredentials import com.battlelancer.seriesguide.ui.AutoGridLayoutManager import com.battlelancer.seriesguide.ui.dialogs.L10nDialogFragment import com.battlelancer.seriesguide.ui.dialogs.LanguagePickerDialogFragment @@ -190,7 +191,11 @@ class ShowsDiscoverPagingFragment : BaseAddShowsFragment() { ) } - adapter = SearchResultPagingAdapter(itemClickListener) + adapter = SearchResultPagingAdapter( + requireContext(), + itemClickListener, + showWatchlistActions = TraktCredentials.get(requireContext()).hasCredentials() + ) binding.recyclerViewShowsPopular.adapter = adapter viewLifecycleOwner.lifecycleScope.launch { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsAdapter.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsAdapter.kt index e948ed5ab8..45d17d0ae9 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsAdapter.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsAdapter.kt @@ -3,25 +3,27 @@ package com.battlelancer.seriesguide.shows.search.similar +import android.content.Context import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.battlelancer.seriesguide.shows.search.discover.BaseAddShowsFragment +import com.battlelancer.seriesguide.shows.search.discover.ItemAddShowViewHolder import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.search.discover.SearchResultDiffCallback -import com.battlelancer.seriesguide.shows.search.discover.SearchResultViewHolder class SimilarShowsAdapter( - private val itemClickListener: BaseAddShowsFragment.ItemClickListener -) : ListAdapter( + private val context: Context, + private val itemClickListener: ItemAddShowViewHolder.ClickListener, + private val showWatchlistActions: Boolean +) : ListAdapter( SearchResultDiffCallback() ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchResultViewHolder { - return SearchResultViewHolder.create(parent, itemClickListener) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemAddShowViewHolder { + return ItemAddShowViewHolder.create(parent, itemClickListener) } - override fun onBindViewHolder(holder: SearchResultViewHolder, position: Int) { - holder.bindTo(getItem(position)) + override fun onBindViewHolder(holder: ItemAddShowViewHolder, position: Int) { + holder.bindTo(context, getItem(position), showWatchlistActions) } } \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt index bb983ca839..8969297ce3 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt @@ -20,6 +20,7 @@ import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.shows.search.discover.BaseAddShowsFragment import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.search.discover.ShowsDiscoverPagingActivity +import com.battlelancer.seriesguide.traktapi.TraktCredentials import com.battlelancer.seriesguide.ui.AutoGridLayoutManager import com.battlelancer.seriesguide.ui.widgets.EmptyView import com.battlelancer.seriesguide.util.ThemeUtils @@ -89,7 +90,11 @@ class SimilarShowsFragment : BaseAddShowsFragment() { ) } - adapter = SimilarShowsAdapter(itemClickListener) + adapter = SimilarShowsAdapter( + requireContext(), + itemClickListener, + showWatchlistActions = TraktCredentials.get(requireContext()).hasCredentials() + ) recyclerView.adapter = adapter similarShowsViewModel.resultLiveData.observe(viewLifecycleOwner) { From 920f52b360f61844dbb17c02b56f9d41aba306b7 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Fri, 11 Oct 2024 15:40:36 +0200 Subject: [PATCH 3/3] BaseSimilarActivity: display status message when adding to watchlist --- .../seriesguide/ui/BaseSimilarActivity.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt index 2984b1cc5a..901c7767d6 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt @@ -5,22 +5,25 @@ package com.battlelancer.seriesguide.ui import android.content.Intent import android.os.Bundle +import android.view.View import androidx.fragment.app.Fragment import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.databinding.ActivitySinglepaneBinding import com.battlelancer.seriesguide.util.commitReorderingAllowed /** - * Is a [SinglePaneActivity]. + * Is a [SinglePaneActivity]. And [BaseMessageActivity] for Trakt watchlist actions. */ -abstract class BaseSimilarActivity : BaseActivity() { +abstract class BaseSimilarActivity : BaseMessageActivity() { abstract val liftOnScrollTargetViewId: Int abstract val titleStringRes: Int abstract fun createFragment(tmdbId: Int, title: String?): Fragment + private lateinit var binding: ActivitySinglepaneBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val binding = SinglePaneActivity.onCreateFor(this) + binding = SinglePaneActivity.onCreateFor(this) binding.sgAppBarLayout.sgAppBarLayout.liftOnScrollTargetViewId = liftOnScrollTargetViewId setupActionBar() @@ -61,6 +64,9 @@ abstract class BaseSimilarActivity : BaseActivity() { } } + override val snackbarParentView: View + get() = binding.root + companion object { private const val EXTRA_TMDB_ID = "EXTRA_TMDB_ID" private const val EXTRA_TITLE = "EXTRA_TITLE"