diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 68ba94e4..f15f38d3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,7 +16,8 @@ + android:label="@string/title_activity_main" + android:windowSoftInputMode="adjustPan"/> + navigateToMediaInfoFragment(item) + }, HomeEpisodeListAdapter.OnClickListener { item -> + navigateToEpisodeBottomSheetFragment(item) + }) + + viewModel.finishedLoading.observe(viewLifecycleOwner, { isFinished -> + binding.loadingIndicator.visibility = if (isFinished) View.GONE else View.VISIBLE + }) + + viewModel.error.observe(viewLifecycleOwner, { error -> + if (error) { + snackbar.show() + } + }) + + viewModel.sections.observe(viewLifecycleOwner, { sections -> + if (sections.isEmpty()) { + binding.noSearchResultsText.visibility = View.VISIBLE + } else { + binding.noSearchResultsText.visibility = View.GONE + } + }) + + viewModel.loadData(args.query) + + return binding.root + } + + private fun navigateToMediaInfoFragment(item: BaseItemDto) { + findNavController().navigate( + FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment( + item.id, + item.name, + item.type ?: "Unknown" + ) + ) + } + + private fun navigateToEpisodeBottomSheetFragment(episode: BaseItemDto) { + findNavController().navigate( + FavoriteFragmentDirections.actionFavoriteFragmentToEpisodeBottomSheetFragment( + episode.id + ) + ) + } +} \ No newline at end of file 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 e4648c27..64abe807 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt @@ -14,6 +14,8 @@ interface JellyfinRepository { suspend fun getFavoriteItems(): List + suspend fun getSearchItems(searchQuery: String): List + suspend fun getResumeItems(): List suspend fun getLatestMedia(parentId: UUID): List 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 18f7916b..6008226f 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt @@ -49,6 +49,19 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep return items } + override suspend fun getSearchItems(searchQuery: String): List { + val items: List + withContext(Dispatchers.IO) { + items = jellyfinApi.itemsApi.getItems( + jellyfinApi.userId!!, + searchTerm = searchQuery, + includeItemTypes = listOf("Movie", "Series", "Episode"), + recursive = true + ).content.items ?: listOf() + } + return items + } + override suspend fun getResumeItems(): List { val items: List withContext(Dispatchers.IO) { diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/SearchResultViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/SearchResultViewModel.kt new file mode 100644 index 00000000..f5f8f925 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/SearchResultViewModel.kt @@ -0,0 +1,82 @@ +package dev.jdtech.jellyfin.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import dev.jdtech.jellyfin.models.FavoriteSection +import dev.jdtech.jellyfin.repository.JellyfinRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.util.* +import javax.inject.Inject + +@HiltViewModel +class SearchResultViewModel +@Inject +constructor( + private val jellyfinRepository: JellyfinRepository +) : ViewModel() { + private val _sections = MutableLiveData>() + val sections: LiveData> = _sections + + private val _finishedLoading = MutableLiveData() + val finishedLoading: LiveData = _finishedLoading + + private val _error = MutableLiveData() + val error: LiveData = _error + + fun loadData(query: String) { + _error.value = false + _finishedLoading.value = false + viewModelScope.launch { + try { + val items = jellyfinRepository.getSearchItems(query) + + if (items.isEmpty()) { + _sections.value = listOf() + _finishedLoading.value = true + return@launch + } + + val tempSections = mutableListOf() + + withContext(Dispatchers.Default) { + FavoriteSection( + UUID.randomUUID(), + "Movies", + items.filter { it.type == "Movie" }).let { + if (it.items.isNotEmpty()) tempSections.add( + it + ) + } + FavoriteSection( + UUID.randomUUID(), + "Shows", + items.filter { it.type == "Series" }).let { + if (it.items.isNotEmpty()) tempSections.add( + it + ) + } + FavoriteSection( + UUID.randomUUID(), + "Episodes", + items.filter { it.type == "Episode" }).let { + if (it.items.isNotEmpty()) tempSections.add( + it + ) + } + } + + _sections.value = tempSections + } catch (e: Exception) { + Timber.e(e) + _error.value = true + } + _finishedLoading.value = true + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 00000000..00b5e5f3 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,21 @@ + + + + diff --git a/app/src/main/res/layout/favorite_section.xml b/app/src/main/res/layout/favorite_section.xml index 67fc3242..c8a67162 100644 --- a/app/src/main/res/layout/favorite_section.xml +++ b/app/src/main/res/layout/favorite_section.xml @@ -32,7 +32,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" - android:layoutAnimation="@anim/overview_media_animation" android:orientation="horizontal" android:paddingHorizontal="12dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" diff --git a/app/src/main/res/layout/fragment_search_result.xml b/app/src/main/res/layout/fragment_search_result.xml new file mode 100644 index 00000000..d250d75e --- /dev/null +++ b/app/src/main/res/layout/fragment_search_result.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/media_menu.xml b/app/src/main/res/menu/media_menu.xml new file mode 100644 index 00000000..9195a338 --- /dev/null +++ b/app/src/main/res/menu/media_menu.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index ae17c88f..7a2a1246 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -48,12 +48,15 @@ app:exitAnim="@anim/nav_default_exit_anim" app:popEnterAnim="@anim/nav_default_pop_enter_anim" app:popExitAnim="@anim/nav_default_pop_exit_anim" /> + + android:label="@string/title_settings" /> + app:nullable="true" /> + tools:layout="@layout/activity_player"> @@ -165,4 +168,19 @@ android:id="@+id/action_favoriteFragment_to_mediaInfoFragment" app:destination="@id/mediaInfoFragment" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 27989468..9dbea87d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,7 +40,8 @@ Latest %1$s Series poster You have no favorites - + Search + No search results Language Preferred audio language Preferred subtitle language