diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt index 6957cf2fa..191529cca 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt @@ -43,6 +43,7 @@ class SearchAdapter(private val listener: SelectableListListener) : is Album -> AlbumViewHolder.VIEW_TYPE is Artist -> ArtistViewHolder.VIEW_TYPE is Genre -> GenreViewHolder.VIEW_TYPE + is Playlist -> PlaylistViewHolder.VIEW_TYPE is BasicHeader -> BasicHeaderViewHolder.VIEW_TYPE else -> super.getItemViewType(position) } @@ -53,6 +54,7 @@ class SearchAdapter(private val listener: SelectableListListener) : AlbumViewHolder.VIEW_TYPE -> AlbumViewHolder.from(parent) ArtistViewHolder.VIEW_TYPE -> ArtistViewHolder.from(parent) GenreViewHolder.VIEW_TYPE -> GenreViewHolder.from(parent) + PlaylistViewHolder.VIEW_TYPE -> PlaylistViewHolder.from(parent) BasicHeaderViewHolder.VIEW_TYPE -> BasicHeaderViewHolder.from(parent) else -> error("Invalid item type $viewType") } @@ -64,6 +66,7 @@ class SearchAdapter(private val listener: SelectableListListener) : is Album -> (holder as AlbumViewHolder).bind(item, listener) is Artist -> (holder as ArtistViewHolder).bind(item, listener) is Genre -> (holder as GenreViewHolder).bind(item, listener) + is Playlist -> (holder as PlaylistViewHolder).bind(item, listener) is BasicHeader -> (holder as BasicHeaderViewHolder).bind(item) } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchEngine.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchEngine.kt index fa3cf2dea..ee83b5418 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchEngine.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchEngine.kt @@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Music +import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.info.Name @@ -33,8 +34,6 @@ import org.oxycblt.auxio.music.info.Name * Implements the fuzzy-ish searching algorithm used in the search view. * * @author Alexander Capehart - * - * TODO: Add playlists */ interface SearchEngine { /** @@ -53,12 +52,14 @@ interface SearchEngine { * @param albums A list of [Album]s, null if empty. * @param artists A list of [Artist]s, null if empty. * @param genres A list of [Genre]s, null if empty. + * @param playlists A list of [Playlist], null if empty. */ data class Items( val songs: List?, val albums: List?, val artists: List?, - val genres: List? + val genres: List?, + val playlists: List? ) } @@ -72,7 +73,8 @@ class SearchEngineImpl @Inject constructor(@ApplicationContext private val conte }, albums = items.albums?.searchListImpl(query), artists = items.artists?.searchListImpl(query), - genres = items.genres?.searchListImpl(query)) + genres = items.genres?.searchListImpl(query), + playlists = items.playlists?.searchListImpl(query)) /** * Search a given [Music] list. diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt index 68a0b3b7d..e34dcf800 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt @@ -34,6 +34,7 @@ import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.device.DeviceLibrary +import org.oxycblt.auxio.music.user.UserLibrary import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.util.logD @@ -73,7 +74,7 @@ constructor( } override fun onMusicChanges(changes: MusicRepository.Changes) { - if (changes.deviceLibrary && musicRepository.deviceLibrary != null) { + if (changes.deviceLibrary || changes.userLibrary) { search(lastQuery) } } @@ -90,7 +91,8 @@ constructor( lastQuery = query val deviceLibrary = musicRepository.deviceLibrary - if (query.isNullOrEmpty() || deviceLibrary == null) { + val userLibrary = musicRepository.userLibrary + if (query.isNullOrEmpty() || deviceLibrary == null || userLibrary == null) { logD("Search query is not applicable.") _searchResults.value = listOf() return @@ -101,11 +103,16 @@ constructor( // Searching is time-consuming, so do it in the background. currentSearchJob = viewModelScope.launch { - _searchResults.value = searchImpl(deviceLibrary, query).also { yield() } + _searchResults.value = + searchImpl(deviceLibrary, userLibrary, query).also { yield() } } } - private suspend fun searchImpl(deviceLibrary: DeviceLibrary, query: String): List { + private suspend fun searchImpl( + deviceLibrary: DeviceLibrary, + userLibrary: UserLibrary, + query: String + ): List { val filterMode = searchSettings.searchFilterMode val items = @@ -115,33 +122,40 @@ constructor( deviceLibrary.songs, deviceLibrary.albums, deviceLibrary.artists, - deviceLibrary.genres) + deviceLibrary.genres, + userLibrary.playlists) } else { SearchEngine.Items( songs = if (filterMode == MusicMode.SONGS) deviceLibrary.songs else null, albums = if (filterMode == MusicMode.ALBUMS) deviceLibrary.albums else null, artists = if (filterMode == MusicMode.ARTISTS) deviceLibrary.artists else null, - genres = if (filterMode == MusicMode.GENRES) deviceLibrary.genres else null) + genres = if (filterMode == MusicMode.GENRES) deviceLibrary.genres else null, + playlists = + if (filterMode == MusicMode.PLAYLISTS) userLibrary.playlists else null) } val results = searchEngine.search(items, query) return buildList { - results.artists?.let { artists -> + results.artists?.let { add(BasicHeader(R.string.lbl_artists)) - addAll(SORT.artists(artists)) + addAll(SORT.artists(it)) } - results.albums?.let { albums -> + results.albums?.let { add(BasicHeader(R.string.lbl_albums)) - addAll(SORT.albums(albums)) + addAll(SORT.albums(it)) } - results.genres?.let { genres -> + results.playlists?.let { + add(BasicHeader(R.string.lbl_playlists)) + addAll(SORT.playlists(it)) + } + results.genres?.let { add(BasicHeader(R.string.lbl_genres)) - addAll(SORT.genres(genres)) + addAll(SORT.genres(it)) } - results.songs?.let { songs -> + results.songs?.let { add(BasicHeader(R.string.lbl_songs)) - addAll(SORT.songs(songs)) + addAll(SORT.songs(it)) } } } @@ -158,7 +172,7 @@ constructor( MusicMode.ALBUMS -> R.id.option_filter_albums MusicMode.ARTISTS -> R.id.option_filter_artists MusicMode.GENRES -> R.id.option_filter_genres - MusicMode.PLAYLISTS -> R.id.option_filter_all // TODO: Handle + MusicMode.PLAYLISTS -> R.id.option_filter_playlists // Null maps to filtering nothing. null -> R.id.option_filter_all } @@ -175,6 +189,7 @@ constructor( R.id.option_filter_albums -> MusicMode.ALBUMS R.id.option_filter_artists -> MusicMode.ARTISTS R.id.option_filter_genres -> MusicMode.GENRES + R.id.option_filter_playlists -> MusicMode.PLAYLISTS // Null maps to filtering nothing. R.id.option_filter_all -> null else -> error("Invalid option ID provided") diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml index 09ee14a7c..0e82cb0f1 100644 --- a/app/src/main/res/menu/menu_search.xml +++ b/app/src/main/res/menu/menu_search.xml @@ -28,6 +28,10 @@ android:id="@+id/option_filter_genres" android:title="@string/lbl_genres" app:showAsAction="never" /> +