From 320e3b867890d9337b2611af7ce0108f8722dc5c Mon Sep 17 00:00:00 2001 From: CappielloAntonio Date: Sat, 31 Jul 2021 14:47:29 +0200 Subject: [PATCH] Implemented the new search engine --- .../play/repository/SearchingRepository.java | 152 +++++++++++++++--- .../api/searching/SearchingClient.java | 8 +- .../api/searching/SearchingService.java | 9 +- .../play/subsonic/models/SearchResult2.java | 91 +++-------- .../play/subsonic/models/SearchResult3.java | 89 +++------- .../subsonic/models/SubsonicResponse.java | 2 + .../play/ui/fragment/SearchFragment.java | 29 +--- .../play/viewmodel/SearchViewModel.java | 39 ++--- app/src/main/res/layout/fragment_search.xml | 31 ---- 9 files changed, 212 insertions(+), 238 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/play/repository/SearchingRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/SearchingRepository.java index dcecbc52..bd2bbc69 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/SearchingRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/SearchingRepository.java @@ -2,21 +2,154 @@ package com.cappielloantonio.play.repository; import android.app.Application; +import androidx.lifecycle.MutableLiveData; + +import com.cappielloantonio.play.App; import com.cappielloantonio.play.database.AppDatabase; import com.cappielloantonio.play.database.dao.RecentSearchDao; +import com.cappielloantonio.play.model.Album; +import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.RecentSearch; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.subsonic.models.AlbumID3; +import com.cappielloantonio.play.subsonic.models.ArtistID3; +import com.cappielloantonio.play.subsonic.models.Child; +import com.cappielloantonio.play.subsonic.models.ResponseStatus; +import com.cappielloantonio.play.subsonic.models.SubsonicResponse; +import com.cappielloantonio.play.util.MappingUtil; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + public class SearchingRepository { private RecentSearchDao recentSearchDao; + private Application application; + public SearchingRepository(Application application) { + this.application = application; + AppDatabase database = AppDatabase.getInstance(application); recentSearchDao = database.recentSearchDao(); } + public MutableLiveData> getSearchedSongs(String query) { + MutableLiveData> searchedSongs = new MutableLiveData<>(); + + App.getSubsonicClientInstance(application, false) + .getSearchingClient() + .search3(query, 20, 0, 0) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) { + List songs = new ArrayList<>(MappingUtil.mapSong(response.body().getSearchResult3().getSongs())); + searchedSongs.setValue(songs); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + + return searchedSongs; + } + + public MutableLiveData> getSearchedAlbums(String query) { + MutableLiveData> searchedAlbums = new MutableLiveData<>(); + + App.getSubsonicClientInstance(application, false) + .getSearchingClient() + .search3(query, 0, 20, 0) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) { + List albums = new ArrayList<>(MappingUtil.mapAlbum(response.body().getSearchResult3().getAlbums())); + searchedAlbums.setValue(albums); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + + return searchedAlbums; + } + + public MutableLiveData> getSearchedArtists(String query) { + MutableLiveData> searchedArtists = new MutableLiveData<>(); + + App.getSubsonicClientInstance(application, false) + .getSearchingClient() + .search3(query, 0, 0, 20) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) { + List artists = new ArrayList<>(MappingUtil.mapArtist(response.body().getSearchResult3().getArtists())); + searchedArtists.setValue(artists); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + + return searchedArtists; + } + + public MutableLiveData> getSuggestions(String query) { + MutableLiveData> suggestions = new MutableLiveData<>(new ArrayList()); + + App.getSubsonicClientInstance(application, false) + .getSearchingClient() + .search3(query, 5, 5, 5) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + List newSuggestions = new ArrayList(); + + if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) { + for(ArtistID3 artistID3 : response.body().getSearchResult3().getArtists()) { + newSuggestions.add(artistID3.getName()); + } + + for(AlbumID3 albumID3 : response.body().getSearchResult3().getAlbums()) { + newSuggestions.add(albumID3.getName()); + } + + for(Child song : response.body().getSearchResult3().getSongs()) { + newSuggestions.add(song.getTitle()); + } + + LinkedHashSet hashSet = new LinkedHashSet<>(newSuggestions); + ArrayList suggestionsWithoutDuplicates = new ArrayList<>(hashSet); + + suggestions.setValue(suggestionsWithoutDuplicates); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + + return suggestions; + } + public void insert(RecentSearch recentSearch) { InsertThreadSafe insert = new InsertThreadSafe(recentSearchDao, recentSearch); Thread thread = new Thread(insert); @@ -29,12 +162,6 @@ public class SearchingRepository { thread.start(); } - public void deleteAll() { - DeleteAllThreadSafe delete = new DeleteAllThreadSafe(recentSearchDao); - Thread thread = new Thread(delete); - thread.start(); - } - public List getRecentSearchSuggestion(int limit) { List recent = new ArrayList<>(); @@ -82,19 +209,6 @@ public class SearchingRepository { } } - private static class DeleteAllThreadSafe implements Runnable { - private RecentSearchDao recentSearchDao; - - public DeleteAllThreadSafe(RecentSearchDao recentSearchDao) { - this.recentSearchDao = recentSearchDao; - } - - @Override - public void run() { - recentSearchDao.deleteAll(); - } - } - private static class RecentThreadSafe implements Runnable { private RecentSearchDao recentSearchDao; private int limit; diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingClient.java b/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingClient.java index 7f9cb030..e589faa3 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingClient.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingClient.java @@ -32,14 +32,14 @@ public class SearchingClient { this.searchingService = retrofit.create(SearchingService.class); } - public Call search2(String query) { + public Call search2(String query, int songCount, int albumCount, int artistCount) { Log.d(TAG, "search2()"); - return searchingService.search2(subsonic.getParams(), query); + return searchingService.search2(subsonic.getParams(), query, songCount, albumCount, artistCount); } - public Call search3(String query) { + public Call search3(String query, int songCount, int albumCount, int artistCount) { Log.d(TAG, "search3()"); - return searchingService.search3(subsonic.getParams(), query); + return searchingService.search3(subsonic.getParams(), query, songCount, albumCount, artistCount); } private TikXml getParser() { diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingService.java b/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingService.java index 2a71806b..9e54bcbc 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingService.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/api/searching/SearchingService.java @@ -6,12 +6,13 @@ import java.util.Map; import retrofit2.Call; import retrofit2.http.GET; +import retrofit2.http.Query; import retrofit2.http.QueryMap; public interface SearchingService { - @GET("search2?query={query}") - Call search2(@QueryMap Map params, String query); + @GET("search2") + Call search2(@QueryMap Map params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount); - @GET("search3?query={query}") - Call search3(@QueryMap Map params, String query); + @GET("search3") + Call search3(@QueryMap Map params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount); } diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult2.java b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult2.java index 611a2f33..55fd8c3c 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult2.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult2.java @@ -1,91 +1,50 @@ package com.cappielloantonio.play.subsonic.models; +import com.tickaroo.tikxml.annotation.Element; +import com.tickaroo.tikxml.annotation.Xml; + import java.util.ArrayList; import java.util.List; +@Xml public class SearchResult2 { + @Element(name = "artist") protected List artists; + @Element(name = "album") protected List albums; + @Element(name = "song") protected List songs; - - /** - * Gets the value of the artists property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the artists property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getArtists().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Artist } - */ + public List getArtists() { if (artists == null) { - artists = new ArrayList(); + artists = new ArrayList<>(); } return this.artists; } - - /** - * Gets the value of the albums property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the albums property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getAlbums().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Child } - */ + public List getAlbums() { if (albums == null) { - albums = new ArrayList(); + albums = new ArrayList<>(); } return this.albums; } - - /** - * Gets the value of the songs property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the songs property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getSongs().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Child } - */ + public List getSongs() { if (songs == null) { - songs = new ArrayList(); + songs = new ArrayList<>(); } return this.songs; } + + public void setArtists(List artists) { + this.artists = artists; + } + + public void setAlbums(List albums) { + this.albums = albums; + } + + public void setSongs(List songs) { + this.songs = songs; + } } diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult3.java b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult3.java index 976fcf50..e099f80d 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult3.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SearchResult3.java @@ -1,91 +1,50 @@ package com.cappielloantonio.play.subsonic.models; +import com.tickaroo.tikxml.annotation.Element; +import com.tickaroo.tikxml.annotation.Xml; + import java.util.ArrayList; import java.util.List; +@Xml public class SearchResult3 { + @Element(name = "artist") protected List artists; + @Element(name = "album") protected List albums; + @Element(name = "song") protected List songs; - - /** - * Gets the value of the artists property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the artists property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getArtists().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link ArtistID3 } - */ + public List getArtists() { if (artists == null) { - artists = new ArrayList(); + artists = new ArrayList<>(); } return this.artists; } - - /** - * Gets the value of the albums property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the albums property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getAlbums().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link AlbumID3 } - */ + public List getAlbums() { if (albums == null) { - albums = new ArrayList(); + albums = new ArrayList<>(); } return this.albums; } - /** - * Gets the value of the songs property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the songs property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getSongs().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Child } - */ public List getSongs() { if (songs == null) { - songs = new ArrayList(); + songs = new ArrayList<>(); } return this.songs; } + + public void setArtists(List artists) { + this.artists = artists; + } + + public void setAlbums(List albums) { + this.albums = albums; + } + + public void setSongs(List songs) { + this.songs = songs; + } } diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java index 96163e6b..b178bcdf 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/models/SubsonicResponse.java @@ -44,7 +44,9 @@ public class SubsonicResponse { private PlaylistWithSongs playlist; @Element private Playlists playlists; + @Element private SearchResult3 searchResult3; + @Element private SearchResult2 searchResult2; private SearchResult searchResult; private NowPlaying nowPlaying; diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java index 0c8fb3ad..ed3b2f4a 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SearchFragment.java @@ -37,7 +37,6 @@ public class SearchFragment extends Fragment { private SongHorizontalAdapter songHorizontalAdapter; private AlbumAdapter albumAdapter; private ArtistAdapter artistAdapter; - private GenreCatalogueAdapter genreCatalogueAdapter; @Nullable @Override @@ -93,20 +92,6 @@ public class SearchFragment extends Fragment { artistAdapter = new ArtistAdapter(requireContext()); bind.searchResultArtistRecyclerView.setAdapter(artistAdapter); - - // Genres - bind.searchResultGenreRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); - bind.searchResultGenreRecyclerView.addItemDecoration(new GridItemDecoration(2, 16, false)); - bind.searchResultGenreRecyclerView.setHasFixedSize(true); - - genreCatalogueAdapter = new GenreCatalogueAdapter(activity, requireContext()); - genreCatalogueAdapter.setClickListener((view, position) -> { - Bundle bundle = new Bundle(); - bundle.putString(Song.BY_GENRE, Song.BY_GENRE); - bundle.putParcelable("genre_object", genreCatalogueAdapter.getItem(position)); - activity.navController.navigate(R.id.action_searchFragment_to_songListPageFragment, bundle); - }); - bind.searchResultGenreRecyclerView.setAdapter(genreCatalogueAdapter); } private void initSearchView() { @@ -119,7 +104,9 @@ public class SearchFragment extends Fragment { bind.persistentSearchView.setOnSearchQueryChangeListener((searchView, oldQuery, newQuery) -> { if (!newQuery.trim().equals("") && newQuery.trim().length() > 1) { - searchView.setSuggestions(SuggestionCreationUtil.asRegularSearchSuggestions(searchViewModel.getSearchSuggestion(newQuery)), false); + searchViewModel.getSearchSuggestion(newQuery).observe(requireActivity(), suggestions -> { + searchView.setSuggestions(SuggestionCreationUtil.asRegularSearchSuggestions(suggestions), false); + }); } else { setSuggestions(); } @@ -173,22 +160,18 @@ public class SearchFragment extends Fragment { } private void performSearch(String query) { - searchViewModel.searchSong(query, requireContext()).observe(requireActivity(), songs -> { + searchViewModel.searchSong(query).observe(requireActivity(), songs -> { if(bind != null) bind.searchSongSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE); songHorizontalAdapter.setItems(songs); }); - searchViewModel.searchAlbum(query, requireContext()).observe(requireActivity(), albums -> { + searchViewModel.searchAlbum(query).observe(requireActivity(), albums -> { if(bind != null) bind.searchAlbumSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE); albumAdapter.setItems(albums); }); - searchViewModel.searchArtist(query, requireContext()).observe(requireActivity(), artists -> { + searchViewModel.searchArtist(query).observe(requireActivity(), artists -> { if(bind != null) bind.searchArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE); artistAdapter.setItems(artists); }); - searchViewModel.searchGenre(query, requireContext()).observe(requireActivity(), genres -> { - if(bind != null) bind.searchGenreSector.setVisibility(!genres.isEmpty() ? View.VISIBLE : View.GONE); - genreCatalogueAdapter.setItems(genres); - }); bind.searchResultLayout.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/SearchViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/SearchViewModel.java index 98354e87..b07fa496 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/SearchViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/SearchViewModel.java @@ -6,6 +6,7 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Artist; @@ -33,10 +34,10 @@ public class SearchViewModel extends AndroidViewModel { private GenreRepository genreRepository; private SearchingRepository searchingRepository; - private LiveData> searchSong; - private LiveData> searchAlbum; - private LiveData> searchArtist; - private LiveData> searchGenre; + private LiveData> searchSong = new MutableLiveData<>(new ArrayList<>()); + private LiveData> searchAlbum = new MutableLiveData<>(new ArrayList<>()); + private LiveData> searchArtist = new MutableLiveData<>(new ArrayList<>()); + private LiveData> searchGenre = new MutableLiveData<>(new ArrayList<>()); public SearchViewModel(@NonNull Application application) { super(application); @@ -60,26 +61,21 @@ public class SearchViewModel extends AndroidViewModel { } } - public LiveData> searchSong(String title, Context context) { - // searchSong = songRepository.searchListLiveSong(title, PreferenceUtil.getInstance(context).getSearchElementPerCategory()); + public LiveData> searchSong(String title) { + searchSong = searchingRepository.getSearchedSongs(title); return searchSong; } - public LiveData> searchAlbum(String name, Context context) { - // searchAlbum = albumRepository.searchListLiveAlbum(name, PreferenceUtil.getInstance(context).getSearchElementPerCategory()); + public LiveData> searchAlbum(String name) { + searchAlbum = searchingRepository.getSearchedAlbums(name); return searchAlbum; } - public LiveData> searchArtist(String name, Context context) { - // searchArtist = artistRepository.searchListLiveArtist(name, PreferenceUtil.getInstance(context).getSearchElementPerCategory()); + public LiveData> searchArtist(String name) { + searchArtist = searchingRepository.getSearchedArtists(name); return searchArtist; } - public LiveData> searchGenre(String name, Context context) { - // searchGenre = genreRepository.searchListLiveGenre(name, PreferenceUtil.getInstance(context).getSearchElementPerCategory()); - return searchGenre; - } - public void insertNewSearch(String search) { searchingRepository.insert(new RecentSearch(search)); } @@ -88,17 +84,8 @@ public class SearchViewModel extends AndroidViewModel { searchingRepository.delete(new RecentSearch(search)); } - public List getSearchSuggestion(String query) { - ArrayList suggestions = new ArrayList<>(); - // suggestions.addAll(songRepository.getSearchSuggestion(query)); - // suggestions.addAll(albumRepository.getSearchSuggestion(query)); - // suggestions.addAll(artistRepository.getSearchSuggestion(query)); - // suggestions.addAll(genreRepository.getSearchSuggestion(query)); - - LinkedHashSet hashSet = new LinkedHashSet<>(suggestions); - ArrayList suggestionsWithoutDuplicates = new ArrayList<>(hashSet); - - return suggestionsWithoutDuplicates; + public LiveData> getSearchSuggestion(String query) { + return searchingRepository.getSuggestions(query); } public List getRecentSearchSuggestion() { diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 4b7da67d..80e161ce 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -139,37 +139,6 @@ android:paddingEnd="8dp" android:paddingBottom="8dp" /> - - - - - - - - \ No newline at end of file