Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Quick Play #531

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9da7d0d
Add home screen "Quick Play" on long press
Natanel-Shitrit Sep 29, 2023
305ed02
Fix linting
Natanel-Shitrit Sep 29, 2023
ffb4bb8
Add "Quick Play" for "Latest" item in `HomeFragment`
Natanel-Shitrit Sep 29, 2023
0fb2a7d
Add "Quick Play" for season in `ShowFragment`
Natanel-Shitrit Sep 29, 2023
ebf1071
Add "Quick Play" for "Next Up" episode in `ShowFragment`
Natanel-Shitrit Sep 29, 2023
6273dbe
Add "Quick Play" for episode in `SeasonFragment`
Natanel-Shitrit Sep 29, 2023
2c02836
Add "Quick Play" for items in `FavoriteFragment`
Natanel-Shitrit Sep 29, 2023
17e792e
Add "Quick Play" for items in `CollectionFragment`
Natanel-Shitrit Sep 29, 2023
341c7af
Add "Quick Play" for items in `DownloadsFragment`
Natanel-Shitrit Sep 29, 2023
b1d5663
Add "Quick Play" for items in `SearchResultFragment`
Natanel-Shitrit Sep 29, 2023
111230f
Add "Quick Play" for items in `LibraryFragment`
Natanel-Shitrit Sep 29, 2023
1b735e7
Add navigations to player activity
Natanel-Shitrit Sep 29, 2023
26a62f4
Fix linting
Natanel-Shitrit Sep 29, 2023
7458c26
Add loading indicator to home item
Natanel-Shitrit Sep 30, 2023
39ee9d2
Trigger home page `loadingIndicator` when loading item
Natanel-Shitrit Oct 2, 2023
b82dba3
Revert "Add loading indicator to home item"
Natanel-Shitrit Oct 2, 2023
d04485e
Add loading indicator to the rest of the fragments
Natanel-Shitrit Oct 2, 2023
6106238
Fix linting issue
Natanel-Shitrit Oct 2, 2023
dadca4b
Add error snackbar
Natanel-Shitrit Oct 2, 2023
bd77a0f
Fix linting issues
Natanel-Shitrit Oct 2, 2023
0c8a395
Merge remote-tracking branch 'upstream/main' into feature/quick-play
Natanel-Shitrit Dec 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ private const val ITEM_VIEW_TYPE_EPISODE = 1

class EpisodeListAdapter(
private val onClickListener: (item: FindroidEpisode) -> Unit,
private val onLongClickListener: (item: FindroidEpisode) -> Unit,
) :
ListAdapter<EpisodeItem, RecyclerView.ViewHolder>(DiffCallback) {

Expand Down Expand Up @@ -111,6 +112,10 @@ class EpisodeListAdapter(
holder.itemView.setOnClickListener {
onClickListener(item.episode)
}
holder.itemView.setOnLongClickListener {
onLongClickListener(item.episode)
true
}
(holder as EpisodeViewHolder).bind(item.episode)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@ import dev.jdtech.jellyfin.models.FindroidItem

class FavoritesListAdapter(
private val onItemClickListener: (item: FindroidItem) -> Unit,
private val onItemLongClickListener: (item: FindroidItem) -> Unit = {},
) : ListAdapter<FavoriteSection, FavoritesListAdapter.SectionViewHolder>(DiffCallback) {
class SectionViewHolder(private var binding: FavoriteSectionBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
section: FavoriteSection,
onItemClickListener: (item: FindroidItem) -> Unit,
onItemLongClickListener: (item: FindroidItem) -> Unit,
) {
if (section.id == Constants.FAVORITE_TYPE_MOVIES || section.id == Constants.FAVORITE_TYPE_SHOWS) {
binding.itemsRecyclerView.adapter =
ViewItemListAdapter(onItemClickListener, fixedWidth = true)
ViewItemListAdapter(onItemClickListener, onItemLongClickListener, fixedWidth = true)
(binding.itemsRecyclerView.adapter as ViewItemListAdapter).submitList(section.items)
} else if (section.id == Constants.FAVORITE_TYPE_EPISODES) {
binding.itemsRecyclerView.adapter =
HomeEpisodeListAdapter(onItemClickListener)
HomeEpisodeListAdapter(onItemClickListener, onItemLongClickListener)
(binding.itemsRecyclerView.adapter as HomeEpisodeListAdapter).submitList(section.items)
}
binding.sectionName.text = section.name.asString(binding.root.resources)
Expand Down Expand Up @@ -57,6 +59,6 @@ class FavoritesListAdapter(

override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
val collection = getItem(position)
holder.bind(collection, onItemClickListener)
holder.bind(collection, onItemClickListener, onItemLongClickListener)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.core.R as CoreR

class HomeEpisodeListAdapter(private val onClickListener: (item: FindroidItem) -> Unit) : ListAdapter<FindroidItem, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) {
class HomeEpisodeListAdapter(
private val onClickListener: (item: FindroidItem) -> Unit,
private val onItemLongClickListener: (item: FindroidItem) -> Unit = {},
) : ListAdapter<FindroidItem, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) {
class EpisodeViewHolder(
private var binding: HomeEpisodeItemBinding,
private val parent: ViewGroup,
Expand All @@ -39,12 +42,24 @@ class HomeEpisodeListAdapter(private val onClickListener: (item: FindroidItem) -
binding.primaryName.text = item.name
binding.secondaryName.visibility = View.GONE
}

is FindroidEpisode -> {
binding.primaryName.text = item.seriesName
binding.secondaryName.text = if (item.indexNumberEnd == null) {
parent.resources.getString(CoreR.string.episode_name_extended, item.parentIndexNumber, item.indexNumber, item.name)
parent.resources.getString(
CoreR.string.episode_name_extended,
item.parentIndexNumber,
item.indexNumber,
item.name,
)
} else {
parent.resources.getString(CoreR.string.episode_name_extended_with_end, item.parentIndexNumber, item.indexNumber, item.indexNumberEnd, item.name)
parent.resources.getString(
CoreR.string.episode_name_extended_with_end,
item.parentIndexNumber,
item.indexNumber,
item.indexNumberEnd,
item.name,
)
}
}
}
Expand Down Expand Up @@ -79,6 +94,10 @@ class HomeEpisodeListAdapter(private val onClickListener: (item: FindroidItem) -
holder.itemView.setOnClickListener {
onClickListener(item)
}
holder.itemView.setOnLongClickListener {
onItemLongClickListener(item)
true
}
holder.bind(item)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.core.R as CoreR

class ViewItemListAdapter(
private val onClickListener: (item: FindroidItem) -> Unit,
private val onItemLongClickListener: (item: FindroidItem) -> Unit = {},
private val fixedWidth: Boolean = false,
) : ListAdapter<FindroidItem, ViewItemListAdapter.ItemViewHolder>(DiffCallback) {

Expand Down Expand Up @@ -65,6 +66,10 @@ class ViewItemListAdapter(
holder.itemView.setOnClickListener {
onClickListener(item)
}
holder.itemView.setOnLongClickListener {
onItemLongClickListener(item)
true
}
holder.bind(item, fixedWidth)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.core.R as CoreR

class ViewItemPagingAdapter(
private val onClickListener: (item: FindroidItem) -> Unit,
private val onLongClickListener: (item: FindroidItem) -> Unit,
private val fixedWidth: Boolean = false,
) : PagingDataAdapter<FindroidItem, ViewItemPagingAdapter.ItemViewHolder>(DiffCallback) {

Expand Down Expand Up @@ -67,6 +68,10 @@ class ViewItemPagingAdapter(
holder.itemView.setOnClickListener {
onClickListener(item)
}
holder.itemView.setOnLongClickListener {
onLongClickListener(item)
true
}
holder.bind(item, fixedWidth)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ private const val ITEM_VIEW_TYPE_OFFLINE_CARD = 2
class ViewListAdapter(
private val onClickListener: (view: View) -> Unit,
private val onItemClickListener: (item: FindroidItem) -> Unit,
private val onItemLongClickListener: (item: FindroidItem) -> Unit,
private val onOnlineClickListener: () -> Unit,
) : ListAdapter<HomeItem, RecyclerView.ViewHolder>(DiffCallback) {

Expand All @@ -29,11 +30,12 @@ class ViewListAdapter(
dataItem: HomeItem.ViewItem,
onClickListener: (view: View) -> Unit,
onItemClickListener: (item: FindroidItem) -> Unit,
onItemLongClickListener: (item: FindroidItem) -> Unit,
) {
val view = dataItem.view
binding.viewName.text = binding.viewName.context.resources.getString(CoreR.string.latest_library, view.name)
binding.itemsRecyclerView.adapter =
ViewItemListAdapter(onItemClickListener, fixedWidth = true)
ViewItemListAdapter(onItemClickListener, onItemLongClickListener, fixedWidth = true)
(binding.itemsRecyclerView.adapter as ViewItemListAdapter).submitList(view.items)
binding.viewAll.setOnClickListener {
onClickListener(view)
Expand All @@ -46,9 +48,10 @@ class ViewListAdapter(
fun bind(
section: HomeItem.Section,
onClickListener: (item: FindroidItem) -> Unit,
onItemLongClickListener: (item: FindroidItem) -> Unit,
) {
binding.sectionName.text = section.homeSection.name.asString(binding.sectionName.context.resources)
binding.itemsRecyclerView.adapter = HomeEpisodeListAdapter(onClickListener)
binding.itemsRecyclerView.adapter = HomeEpisodeListAdapter(onClickListener, onItemLongClickListener)
(binding.itemsRecyclerView.adapter as HomeEpisodeListAdapter).submitList(section.homeSection.items)
}
}
Expand Down Expand Up @@ -106,11 +109,11 @@ class ViewListAdapter(
when (holder.itemViewType) {
ITEM_VIEW_TYPE_NEXT_UP -> {
val view = getItem(position) as HomeItem.Section
(holder as NextUpViewHolder).bind(view, onItemClickListener)
(holder as NextUpViewHolder).bind(view, onItemClickListener, onItemLongClickListener)
}
ITEM_VIEW_TYPE_VIEW -> {
val view = getItem(position) as HomeItem.ViewItem
(holder as ViewViewHolder).bind(view, onClickListener, onItemClickListener)
(holder as ViewViewHolder).bind(view, onClickListener, onItemClickListener, onItemLongClickListener)
}
ITEM_VIEW_TYPE_OFFLINE_CARD -> {
(holder as OfflineCardViewHolder).bind(onOnlineClickListener)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.playerErrorDialogSnackbar
import dev.jdtech.jellyfin.viewmodels.CollectionViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch
import timber.log.Timber

@AndroidEntryPoint
class CollectionFragment : Fragment() {
private lateinit var binding: FragmentFavoriteBinding
private val viewModel: CollectionViewModel by viewModels()
private val playerViewModel: PlayerViewModel by viewModels()
private val args: CollectionFragmentArgs by navArgs()

private lateinit var errorDialog: ErrorDialogFragment
Expand All @@ -40,9 +44,13 @@ class CollectionFragment : Fragment() {
): View {
binding = FragmentFavoriteBinding.inflate(inflater, container, false)

binding.favoritesRecyclerView.adapter = FavoritesListAdapter { item ->
navigateToMediaItem(item)
}
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
onItemClickListener = { item -> navigateToMediaItem(item) },
onItemLongClickListener = { item ->
binding.loadingIndicator.isVisible = true
playerViewModel.loadPlayerItems(item)
},
)

viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
Expand Down Expand Up @@ -71,9 +79,26 @@ class CollectionFragment : Fragment() {
errorDialog.show(parentFragmentManager, ErrorDialogFragment.TAG)
}

playerViewModel.onPlaybackRequested(lifecycleScope) { playerItems ->
when (playerItems) {
is PlayerViewModel.PlayerItemError -> bindPlayerItemsError(playerItems)
is PlayerViewModel.PlayerItems -> bindPlayerItems(playerItems)
}
binding.loadingIndicator.isVisible = false
}

return binding.root
}

private fun bindPlayerItemsError(error: PlayerViewModel.PlayerItemError) {
Timber.e(error.error.message)
playerErrorDialogSnackbar(parentFragmentManager, binding.root, error)
}

private fun bindPlayerItems(items: PlayerViewModel.PlayerItems) {
navigateToPlayerActivity(items.items.toTypedArray())
}

private fun bindUiStateNormal(uiState: CollectionViewModel.UiState.Normal) {
uiState.apply {
binding.noFavoritesText.isVisible = collectionSections.isEmpty()
Expand Down Expand Up @@ -126,4 +151,14 @@ class CollectionFragment : Fragment() {
}
}
}

private fun navigateToPlayerActivity(
playerItems: Array<PlayerItem>,
) {
findNavController().navigate(
CollectionFragmentDirections.actionCollectionFragmentToPlayerActivity(
playerItems,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ import dev.jdtech.jellyfin.databinding.FragmentDownloadsBinding
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.utils.playerErrorDialogSnackbar
import dev.jdtech.jellyfin.utils.restart
import dev.jdtech.jellyfin.viewmodels.DownloadsEvent
import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
Expand All @@ -31,6 +34,7 @@ import dev.jdtech.jellyfin.core.R as CoreR
class DownloadsFragment : Fragment() {
private lateinit var binding: FragmentDownloadsBinding
private val viewModel: DownloadsViewModel by viewModels()
private val playerViewModel: PlayerViewModel by viewModels()

@Inject
lateinit var appPreferences: AppPreferences
Expand All @@ -42,9 +46,13 @@ class DownloadsFragment : Fragment() {
): View {
binding = FragmentDownloadsBinding.inflate(inflater, container, false)

binding.downloadsRecyclerView.adapter = FavoritesListAdapter { item ->
navigateToMediaItem(item)
}
binding.downloadsRecyclerView.adapter = FavoritesListAdapter(
onItemClickListener = { item -> navigateToMediaItem(item) },
onItemLongClickListener = { item ->
binding.loadingIndicator.isVisible = true
playerViewModel.loadPlayerItems(item)
},
)

viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
Expand Down Expand Up @@ -76,9 +84,26 @@ class DownloadsFragment : Fragment() {
}
}

playerViewModel.onPlaybackRequested(lifecycleScope) { playerItems ->
when (playerItems) {
is PlayerViewModel.PlayerItemError -> bindPlayerItemsError(playerItems)
is PlayerViewModel.PlayerItems -> bindPlayerItems(playerItems)
}
binding.loadingIndicator.isVisible = false
}

return binding.root
}

private fun bindPlayerItemsError(error: PlayerViewModel.PlayerItemError) {
Timber.e(error.error.message)
playerErrorDialogSnackbar(parentFragmentManager, binding.root, error)
}

private fun bindPlayerItems(items: PlayerViewModel.PlayerItems) {
navigateToPlayerActivity(items.items.toTypedArray())
}

override fun onResume() {
super.onResume()

Expand Down Expand Up @@ -120,4 +145,14 @@ class DownloadsFragment : Fragment() {
}
}
}

private fun navigateToPlayerActivity(
playerItems: Array<PlayerItem>,
) {
findNavController().navigate(
DownloadsFragmentDirections.actionDownloadsFragmentToPlayerActivity(
playerItems,
),
)
}
}
Loading
Loading