From 3f5a6c5bfac12c3ac7cb281c0f08e083bfab537e Mon Sep 17 00:00:00 2001 From: Jarne Demeulemeester Date: Mon, 14 Jun 2021 16:41:54 +0200 Subject: [PATCH] Show collections on media fragment --- .../dev/jdtech/jellyfin/BindingAdapters.kt | 27 ++++++++- .../java/dev/jdtech/jellyfin/MainActivity.kt | 3 +- .../adapters/CollectionListAdapter.kt | 45 ++++++++++++++ .../jdtech/jellyfin/fragments/HomeFragment.kt | 6 +- .../jellyfin/fragments/MediaFragment.kt | 25 +++++++- .../jellyfin/viewmodels/MediaViewModel.kt | 41 +++++++++++++ .../viewmodels/MediaViewModelFactory.kt | 18 ++++++ app/src/main/res/layout/collection_item.xml | 34 +++++++++++ app/src/main/res/layout/fragment_media.xml | 59 +++++++++++++------ 9 files changed, 229 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/dev/jdtech/jellyfin/adapters/CollectionListAdapter.kt create mode 100644 app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModel.kt create mode 100644 app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModelFactory.kt create mode 100644 app/src/main/res/layout/collection_item.xml diff --git a/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt b/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt index 325fc38b..1def0be9 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt @@ -1,18 +1,19 @@ package dev.jdtech.jellyfin -import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.widget.ImageView import androidx.databinding.BindingAdapter import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import dev.jdtech.jellyfin.adapters.CollectionListAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewListAdapter +import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.database.Server import dev.jdtech.jellyfin.models.View import dev.jdtech.jellyfin.models.ViewItem +import org.jellyfin.sdk.model.api.BaseItemDto @BindingAdapter("servers") fun bindServers(recyclerView: RecyclerView, data: List?) { @@ -40,4 +41,26 @@ fun bindItemImage(imageView: ImageView, item: ViewItem) { .transition(DrawableTransitionOptions.withCrossFade()) .placeholder(R.color.neutral_800) .into(imageView) + + imageView.contentDescription = "${item.name} poster" +} + +@BindingAdapter("collections") +fun bindCollections(recyclerView: RecyclerView, data: List?) { + val adapter = recyclerView.adapter as CollectionListAdapter + adapter.submitList(data) +} + +@BindingAdapter("collectionImage") +fun bindCollectionImage(imageView: ImageView, item: BaseItemDto) { + val jellyfinApi = JellyfinApi.getInstance(imageView.context.applicationContext, "") + + Glide + .with(imageView.context) + .load(jellyfinApi.api.baseUrl.plus("/items/${item.id}/Images/Primary")) + .transition(DrawableTransitionOptions.withCrossFade()) + .placeholder(R.color.neutral_800) + .into(imageView) + + imageView.contentDescription = "${item.name} image" } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/MainActivity.kt b/app/src/main/java/dev/jdtech/jellyfin/MainActivity.kt index 2021efb0..68d1bd41 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/MainActivity.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/MainActivity.kt @@ -20,7 +20,8 @@ class MainActivity : AppCompatActivity() { val navView: BottomNavigationView = binding.navView - val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main) as NavHostFragment + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main) as NavHostFragment val navController = navHostFragment.navController navView.setupWithNavController(navController) diff --git a/app/src/main/java/dev/jdtech/jellyfin/adapters/CollectionListAdapter.kt b/app/src/main/java/dev/jdtech/jellyfin/adapters/CollectionListAdapter.kt new file mode 100644 index 00000000..ba580e3b --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/adapters/CollectionListAdapter.kt @@ -0,0 +1,45 @@ +package dev.jdtech.jellyfin.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import dev.jdtech.jellyfin.databinding.CollectionItemBinding +import org.jellyfin.sdk.model.api.BaseItemDto + +class CollectionListAdapter : + ListAdapter(DiffCallback) { + class ViewViewHolder(private var binding: CollectionItemBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(collection: BaseItemDto) { + binding.collection = collection + binding.executePendingBindings() + } + } + + companion object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: BaseItemDto, newItem: BaseItemDto): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: BaseItemDto, newItem: BaseItemDto): Boolean { + return oldItem == newItem + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewViewHolder { + return ViewViewHolder( + CollectionItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ViewViewHolder, position: Int) { + val collection = getItem(position) + holder.bind(collection) + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/HomeFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/HomeFragment.kt index d0735f39..0943a3fb 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/HomeFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/HomeFragment.kt @@ -1,13 +1,11 @@ package dev.jdtech.jellyfin.fragments import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider -import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.adapters.ViewListAdapter import dev.jdtech.jellyfin.databinding.FragmentHomeBinding import dev.jdtech.jellyfin.viewmodels.HomeViewModel @@ -18,9 +16,9 @@ class HomeFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val application = requireNotNull(this.activity).application - val binding = FragmentHomeBinding.inflate(inflater) + val binding = FragmentHomeBinding.inflate(inflater, container, false) val viewModelFactory = HomeViewModelFactory(application) val viewModel = ViewModelProvider(this, viewModelFactory).get(HomeViewModel::class.java) diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaFragment.kt index bb1fba0e..d51b8066 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/MediaFragment.kt @@ -5,14 +5,33 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import dev.jdtech.jellyfin.R +import androidx.lifecycle.ViewModelProvider +import dev.jdtech.jellyfin.adapters.CollectionListAdapter +import dev.jdtech.jellyfin.databinding.FragmentMediaBinding +import dev.jdtech.jellyfin.viewmodels.MediaViewModel +import dev.jdtech.jellyfin.viewmodels.MediaViewModelFactory class MediaFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_media, container, false) + ): View { + val application = requireNotNull(this.activity).application + val binding = FragmentMediaBinding.inflate(inflater, container, false) + val viewModelFactory = MediaViewModelFactory(application) + val viewModel = ViewModelProvider(this, viewModelFactory).get(MediaViewModel::class.java) + + binding.lifecycleOwner = this + binding.viewModel = viewModel + binding.viewsRecyclerView.adapter = CollectionListAdapter() + + viewModel.finishedLoading.observe(viewLifecycleOwner, { + if (it) { + binding.loadingIncicator.visibility = View.GONE + } + }) + + return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModel.kt new file mode 100644 index 00000000..4af78f72 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModel.kt @@ -0,0 +1,41 @@ +package dev.jdtech.jellyfin.viewmodels + +import android.app.Application +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dev.jdtech.jellyfin.api.JellyfinApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.jellyfin.sdk.model.api.BaseItemDto +import java.util.* + +class MediaViewModel( + val application: Application +) : ViewModel() { + private val jellyfinApi = JellyfinApi.getInstance(application, "") + + private val _collections = MutableLiveData>() + val collections : LiveData> = _collections + + private val _finishedLoading = MutableLiveData() + val finishedLoading: LiveData = _finishedLoading + + init { + viewModelScope.launch { + val items = getItems(jellyfinApi.userId!!) + _collections.value = items + _finishedLoading.value = true + } + } + + private suspend fun getItems(userId: UUID) : List? { + var items: List? + withContext(Dispatchers.IO) { + items = jellyfinApi.itemsApi.getItems(userId).content.items + } + return items + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModelFactory.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModelFactory.kt new file mode 100644 index 00000000..3229a73d --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/MediaViewModelFactory.kt @@ -0,0 +1,18 @@ +package dev.jdtech.jellyfin.viewmodels + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import java.lang.IllegalArgumentException + +class MediaViewModelFactory( + private val application: Application +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(MediaViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return MediaViewModel(application) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/collection_item.xml b/app/src/main/res/layout/collection_item.xml new file mode 100644 index 00000000..6567d0ec --- /dev/null +++ b/app/src/main/res/layout/collection_item.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_media.xml b/app/src/main/res/layout/fragment_media.xml index 3c64d3d8..a37743ec 100644 --- a/app/src/main/res/layout/fragment_media.xml +++ b/app/src/main/res/layout/fragment_media.xml @@ -1,23 +1,44 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + + + + + - + android:layout_height="match_parent" + tools:context=".fragments.MediaFragment"> + + + + + +