diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt index cadba184..761a6788 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt @@ -10,6 +10,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint +import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.databinding.EpisodeBottomSheetBinding import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel import java.util.* @@ -40,6 +41,13 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() { } } + binding.favoriteButton.setOnClickListener { + when (viewModel.favorite.value) { + true -> viewModel.unmarkAsFavorite(args.episodeId) + false -> viewModel.markAsFavorite(args.episodeId) + } + } + viewModel.item.observe(viewLifecycleOwner, { episode -> if (episode.userData?.playedPercentage != null) { binding.progressBar.layoutParams.width = TypedValue.applyDimension( @@ -51,6 +59,15 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() { } }) + viewModel.favorite.observe(viewLifecycleOwner, { + val drawable = when (it) { + true -> R.drawable.ic_heart_filled + false -> R.drawable.ic_heart + } + + binding.favoriteButton.setImageResource(drawable) + }) + viewModel.loadEpisode(args.episodeId) return binding.root diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaInfoFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaInfoFragment.kt index 7e5608c9..e850e523 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaInfoFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaInfoFragment.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.viewModels 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.databinding.FragmentMediaInfoBinding @@ -64,6 +65,15 @@ class MediaInfoFragment : Fragment() { } }) + viewModel.favorite.observe(viewLifecycleOwner, { + val drawable = when (it) { + true -> R.drawable.ic_heart_filled + false -> R.drawable.ic_heart + } + + binding.favoriteButton.setImageResource(drawable) + }) + binding.trailerButton.setOnClickListener { val intent = Intent( Intent.ACTION_VIEW, @@ -101,6 +111,13 @@ class MediaInfoFragment : Fragment() { } } + binding.favoriteButton.setOnClickListener { + when (viewModel.favorite.value) { + true -> viewModel.unmarkAsFavorite(args.itemId) + false -> viewModel.markAsFavorite(args.itemId) + } + } + viewModel.loadData(args.itemId, args.itemType) } diff --git a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt index e338a363..ae27d4b9 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt @@ -25,4 +25,8 @@ interface JellyfinRepository { suspend fun postPlaybackStop(itemId: UUID, positionTicks: Long) suspend fun postPlaybackProgress(itemId: UUID, positionTicks: Long, isPaused: Boolean) + + suspend fun markAsFavorite(itemId: UUID) + + suspend fun unmarkAsFavorite(itemId: UUID) } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt index 9cbecb71..69d8f126 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt @@ -135,4 +135,16 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep jellyfinApi.playstateApi.onPlaybackProgress(jellyfinApi.userId!!, itemId, positionTicks = positionTicks, isPaused = isPaused) } } + + override suspend fun markAsFavorite(itemId: UUID) { + withContext(Dispatchers.IO) { + jellyfinApi.userLibraryApi.markFavoriteItem(jellyfinApi.userId!!, itemId) + } + } + + override suspend fun unmarkAsFavorite(itemId: UUID) { + withContext(Dispatchers.IO) { + jellyfinApi.userLibraryApi.unmarkFavoriteItem(jellyfinApi.userId!!, itemId) + } + } } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt index 31c5fe7a..8bea338d 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt @@ -34,15 +34,33 @@ constructor( private val _mediaSources = MutableLiveData>() val mediaSources: LiveData> = _mediaSources + private val _favorite = MutableLiveData() + val favorite: LiveData = _favorite + fun loadEpisode(episodeId: UUID) { viewModelScope.launch { _item.value = jellyfinRepository.getItem(episodeId) _runTime.value = "${_item.value?.runTimeTicks?.div(600000000)} min" _dateString.value = getDateString(_item.value!!) _mediaSources.value = jellyfinRepository.getMediaSources(episodeId) + _favorite.value = _item.value?.userData?.isFavorite } } + fun markAsFavorite(itemId: UUID) { + viewModelScope.launch { + jellyfinRepository.markAsFavorite(itemId) + } + _favorite.value = true + } + + fun unmarkAsFavorite(itemId: UUID) { + viewModelScope.launch { + jellyfinRepository.unmarkAsFavorite(itemId) + } + _favorite.value = false + } + private fun getDateString(item: BaseItemDto): String { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val instant = item.premiereDate?.toInstant(ZoneOffset.UTC) diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaInfoViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaInfoViewModel.kt index 15c5dcd0..dcb28bb8 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaInfoViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaInfoViewModel.kt @@ -56,6 +56,9 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() { private val _navigateToPlayer = MutableLiveData() val navigateToPlayer: LiveData = _navigateToPlayer + private val _favorite = MutableLiveData() + val favorite: LiveData = _favorite + fun loadData(itemId: UUID, itemType: String) { viewModelScope.launch { _item.value = jellyfinRepository.getItem(itemId) @@ -67,6 +70,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() { _genresString.value = _item.value?.genres?.joinToString(separator = ", ") _runTime.value = "${_item.value?.runTimeTicks?.div(600000000)} min" _dateString.value = getDateString(_item.value!!) + _favorite.value = _item.value?.userData?.isFavorite if (itemType == "Series") { _nextUp.value = getNextUp(itemId) _seasons.value = jellyfinRepository.getSeasons(itemId) @@ -110,6 +114,20 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() { } } + fun markAsFavorite(itemId: UUID) { + viewModelScope.launch { + jellyfinRepository.markAsFavorite(itemId) + } + _favorite.value = true + } + + fun unmarkAsFavorite(itemId: UUID) { + viewModelScope.launch { + jellyfinRepository.unmarkAsFavorite(itemId) + } + _favorite.value = false + } + private fun getDateString(item: BaseItemDto): String { val dateString: String = item.productionYear.toString() return when (item.status) { diff --git a/app/src/main/res/drawable/ic_heart_filled.xml b/app/src/main/res/drawable/ic_heart_filled.xml new file mode 100644 index 00000000..5f1451cb --- /dev/null +++ b/app/src/main/res/drawable/ic_heart_filled.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 0de0c363..31697c5b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -13,4 +13,5 @@ #EEF2F6 #FF000000 #FFFFFFFF + #EB5757 \ No newline at end of file