322 lines
No EOL
12 KiB
Kotlin
322 lines
No EOL
12 KiB
Kotlin
package dev.jdtech.jellyfin.fragments
|
|
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.Toast
|
|
import androidx.core.content.ContextCompat
|
|
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 androidx.navigation.fragment.findNavController
|
|
import androidx.navigation.fragment.navArgs
|
|
import dagger.hilt.android.AndroidEntryPoint
|
|
import dev.jdtech.jellyfin.R
|
|
import dev.jdtech.jellyfin.adapters.PersonListAdapter
|
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
|
import dev.jdtech.jellyfin.bindBaseItemImage
|
|
import dev.jdtech.jellyfin.bindItemBackdropImage
|
|
import dev.jdtech.jellyfin.databinding.FragmentMediaInfoBinding
|
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
|
import dev.jdtech.jellyfin.dialogs.VideoVersionDialogFragment
|
|
import dev.jdtech.jellyfin.models.PlayerItem
|
|
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
|
|
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
|
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
|
import kotlinx.coroutines.launch
|
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
|
import org.jellyfin.sdk.model.serializer.toUUID
|
|
import timber.log.Timber
|
|
import java.util.UUID
|
|
|
|
@AndroidEntryPoint
|
|
class MediaInfoFragment : Fragment() {
|
|
|
|
private lateinit var binding: FragmentMediaInfoBinding
|
|
private val viewModel: MediaInfoViewModel by viewModels()
|
|
private val playerViewModel: PlayerViewModel by viewModels()
|
|
private val args: MediaInfoFragmentArgs by navArgs()
|
|
|
|
lateinit var errorDialog: ErrorDialogFragment
|
|
|
|
override fun onCreateView(
|
|
inflater: LayoutInflater, container: ViewGroup?,
|
|
savedInstanceState: Bundle?
|
|
): View {
|
|
binding = FragmentMediaInfoBinding.inflate(inflater, container, false)
|
|
|
|
return binding.root
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
viewModel.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
|
|
Timber.d("$uiState")
|
|
when (uiState) {
|
|
is MediaInfoViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
|
is MediaInfoViewModel.UiState.Loading -> bindUiStateLoading()
|
|
is MediaInfoViewModel.UiState.Error -> bindUiStateError(uiState)
|
|
}
|
|
}
|
|
if (!args.isOffline) {
|
|
viewModel.loadData(args.itemId, args.itemType)
|
|
} else {
|
|
viewModel.loadData(args.playerItem!!)
|
|
}
|
|
}
|
|
}
|
|
|
|
if(args.itemType != "Movie") {
|
|
binding.downloadButton.visibility = View.GONE
|
|
}
|
|
|
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
|
viewModel.loadData(args.itemId, args.itemType)
|
|
}
|
|
|
|
playerViewModel.onPlaybackRequested(lifecycleScope) { playerItems ->
|
|
when (playerItems) {
|
|
is PlayerViewModel.PlayerItemError -> bindPlayerItemsError(playerItems)
|
|
is PlayerViewModel.PlayerItems -> bindPlayerItems(playerItems)
|
|
}
|
|
}
|
|
|
|
binding.trailerButton.setOnClickListener {
|
|
if (viewModel.item?.remoteTrailers.isNullOrEmpty()) return@setOnClickListener
|
|
val intent = Intent(
|
|
Intent.ACTION_VIEW,
|
|
Uri.parse(viewModel.item?.remoteTrailers?.get(0)?.url)
|
|
)
|
|
startActivity(intent)
|
|
}
|
|
|
|
binding.nextUp.setOnClickListener {
|
|
navigateToEpisodeBottomSheetFragment(viewModel.nextUp!!)
|
|
}
|
|
|
|
binding.seasonsRecyclerView.adapter =
|
|
ViewItemListAdapter(ViewItemListAdapter.OnClickListener { season ->
|
|
navigateToSeasonFragment(season)
|
|
}, fixedWidth = true)
|
|
binding.peopleRecyclerView.adapter = PersonListAdapter { person ->
|
|
val uuid = person.id?.toUUID()
|
|
if (uuid != null) {
|
|
navigateToPersonDetail(uuid)
|
|
} else {
|
|
Toast.makeText(requireContext(), R.string.error_getting_person_id, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
|
|
binding.playButton.setOnClickListener {
|
|
binding.playButton.setImageResource(android.R.color.transparent)
|
|
binding.progressCircular.isVisible = true
|
|
viewModel.item?.let { item ->
|
|
if (!args.isOffline) {
|
|
playerViewModel.loadPlayerItems(item) {
|
|
VideoVersionDialogFragment(item, playerViewModel).show(
|
|
parentFragmentManager,
|
|
"videoversiondialog"
|
|
)
|
|
}
|
|
} else {
|
|
playerViewModel.loadOfflinePlayerItems(args.playerItem!!)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!args.isOffline) {
|
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
|
viewModel.loadData(args.itemId, args.itemType)
|
|
}
|
|
|
|
binding.checkButton.setOnClickListener {
|
|
when (viewModel.played) {
|
|
true -> {
|
|
viewModel.markAsUnplayed(args.itemId)
|
|
binding.checkButton.setImageResource(R.drawable.ic_check)
|
|
}
|
|
false -> {
|
|
viewModel.markAsPlayed(args.itemId)
|
|
binding.checkButton.setImageResource(R.drawable.ic_check_filled)
|
|
}
|
|
}
|
|
}
|
|
|
|
binding.favoriteButton.setOnClickListener {
|
|
when (viewModel.favorite) {
|
|
true -> {
|
|
viewModel.unmarkAsFavorite(args.itemId)
|
|
binding.favoriteButton.setImageResource(R.drawable.ic_heart)
|
|
}
|
|
false -> {
|
|
viewModel.markAsFavorite(args.itemId)
|
|
binding.favoriteButton.setImageResource(R.drawable.ic_heart_filled)
|
|
}
|
|
}
|
|
}
|
|
|
|
binding.downloadButton.setOnClickListener {
|
|
viewModel.loadDownloadRequestItem(args.itemId)
|
|
}
|
|
|
|
binding.deleteButton.isVisible = false
|
|
} else {
|
|
binding.favoriteButton.isVisible = false
|
|
binding.checkButton.isVisible = false
|
|
binding.downloadButton.isVisible = false
|
|
|
|
binding.deleteButton.setOnClickListener {
|
|
viewModel.deleteItem()
|
|
findNavController().navigate(R.id.downloadFragment)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun bindUiStateNormal(uiState: MediaInfoViewModel.UiState.Normal) {
|
|
uiState.apply {
|
|
binding.originalTitle.isVisible = item.originalTitle != item.name
|
|
if (item.remoteTrailers.isNullOrEmpty()) {
|
|
binding.trailerButton.isVisible = false
|
|
}
|
|
binding.communityRating.isVisible = item.communityRating != null
|
|
binding.actors.isVisible = actors.isNotEmpty()
|
|
|
|
// Check icon
|
|
val checkDrawable = when (played) {
|
|
true -> R.drawable.ic_check_filled
|
|
false -> R.drawable.ic_check
|
|
}
|
|
binding.checkButton.setImageResource(checkDrawable)
|
|
|
|
// Favorite icon
|
|
val favoriteDrawable = when (favorite) {
|
|
true -> R.drawable.ic_heart_filled
|
|
false -> R.drawable.ic_heart
|
|
}
|
|
binding.favoriteButton.setImageResource(favoriteDrawable)
|
|
|
|
// Download icon
|
|
val downloadDrawable = when (downloaded) {
|
|
true -> R.drawable.ic_download_filled
|
|
false -> R.drawable.ic_download
|
|
}
|
|
binding.downloadButton.setImageResource(downloadDrawable)
|
|
binding.name.text = item.name
|
|
binding.originalTitle.text = item.originalTitle
|
|
if (dateString.isEmpty()) {
|
|
binding.year.isVisible = false
|
|
} else {
|
|
binding.year.text = dateString
|
|
}
|
|
if (runTime.isEmpty()) {
|
|
binding.playtime.isVisible = false
|
|
} else {
|
|
binding.playtime.text = runTime
|
|
}
|
|
binding.officialRating.text = item.officialRating
|
|
binding.communityRating.text = item.communityRating.toString()
|
|
binding.genresLayout.isVisible = item.genres?.isNotEmpty() ?: false
|
|
binding.genres.text = genresString
|
|
binding.directorLayout.isVisible = director != null
|
|
binding.director.text = director?.name
|
|
binding.writersLayout.isVisible = writers.isNotEmpty()
|
|
binding.writers.text = writersString
|
|
binding.description.text = item.overview
|
|
binding.nextUpLayout.isVisible = nextUp != null
|
|
binding.nextUpName.text = String.format(getString(R.string.episode_name_extended), nextUp?.parentIndexNumber, nextUp?.indexNumber, nextUp?.name)
|
|
binding.seasonsLayout.isVisible = seasons.isNotEmpty()
|
|
val seasonsAdapter = binding.seasonsRecyclerView.adapter as ViewItemListAdapter
|
|
seasonsAdapter.submitList(seasons)
|
|
val actorsAdapter = binding.peopleRecyclerView.adapter as PersonListAdapter
|
|
actorsAdapter.submitList(actors)
|
|
bindItemBackdropImage(binding.itemBanner, item)
|
|
bindBaseItemImage(binding.nextUpImage, nextUp)
|
|
}
|
|
binding.loadingIndicator.isVisible = false
|
|
binding.mediaInfoScrollview.isVisible = true
|
|
binding.errorLayout.errorPanel.isVisible = false
|
|
}
|
|
|
|
private fun bindUiStateLoading() {
|
|
binding.loadingIndicator.isVisible = true
|
|
binding.errorLayout.errorPanel.isVisible = false
|
|
}
|
|
|
|
private fun bindUiStateError(uiState: MediaInfoViewModel.UiState.Error) {
|
|
val error = uiState.message ?: getString(R.string.unknown_error)
|
|
binding.loadingIndicator.isVisible = false
|
|
binding.mediaInfoScrollview.isVisible = false
|
|
binding.errorLayout.errorPanel.isVisible = true
|
|
checkIfLoginRequired(error)
|
|
}
|
|
|
|
private fun bindPlayerItems(items: PlayerViewModel.PlayerItems) {
|
|
navigateToPlayerActivity(items.items.toTypedArray())
|
|
binding.playButton.setImageDrawable(
|
|
ContextCompat.getDrawable(
|
|
requireActivity(),
|
|
R.drawable.ic_play
|
|
)
|
|
)
|
|
binding.progressCircular.visibility = View.INVISIBLE
|
|
}
|
|
|
|
private fun bindPlayerItemsError(error: PlayerViewModel.PlayerItemError) {
|
|
Timber.e(error.message)
|
|
binding.playerItemsError.visibility = View.VISIBLE
|
|
binding.playButton.setImageDrawable(
|
|
ContextCompat.getDrawable(
|
|
requireActivity(),
|
|
R.drawable.ic_play
|
|
)
|
|
)
|
|
binding.progressCircular.visibility = View.INVISIBLE
|
|
binding.playerItemsErrorDetails.setOnClickListener {
|
|
ErrorDialogFragment(error.message).show(parentFragmentManager, "errordialog")
|
|
}
|
|
}
|
|
|
|
private fun navigateToEpisodeBottomSheetFragment(episode: BaseItemDto) {
|
|
findNavController().navigate(
|
|
MediaInfoFragmentDirections.actionMediaInfoFragmentToEpisodeBottomSheetFragment(
|
|
episode.id
|
|
)
|
|
)
|
|
}
|
|
|
|
private fun navigateToSeasonFragment(season: BaseItemDto) {
|
|
findNavController().navigate(
|
|
MediaInfoFragmentDirections.actionMediaInfoFragmentToSeasonFragment(
|
|
season.seriesId!!,
|
|
season.id,
|
|
season.seriesName,
|
|
season.name
|
|
)
|
|
)
|
|
}
|
|
|
|
private fun navigateToPlayerActivity(
|
|
playerItems: Array<PlayerItem>,
|
|
) {
|
|
findNavController().navigate(
|
|
MediaInfoFragmentDirections.actionMediaInfoFragmentToPlayerActivity(
|
|
playerItems
|
|
)
|
|
)
|
|
}
|
|
|
|
private fun navigateToPersonDetail(personId: UUID) {
|
|
findNavController().navigate(
|
|
MediaInfoFragmentDirections.actionMediaInfoFragmentToPersonDetailFragment(personId)
|
|
)
|
|
}
|
|
} |