Implemented the new search engine

This commit is contained in:
CappielloAntonio 2021-07-31 14:47:29 +02:00
parent 05d2e0b9ec
commit 320e3b8678
9 changed files with 212 additions and 238 deletions

View file

@ -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<List<Song>> getSearchedSongs(String query) {
MutableLiveData<List<Song>> searchedSongs = new MutableLiveData<>();
App.getSubsonicClientInstance(application, false)
.getSearchingClient()
.search3(query, 20, 0, 0)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
List<Song> songs = new ArrayList<>(MappingUtil.mapSong(response.body().getSearchResult3().getSongs()));
searchedSongs.setValue(songs);
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
}
});
return searchedSongs;
}
public MutableLiveData<List<Album>> getSearchedAlbums(String query) {
MutableLiveData<List<Album>> searchedAlbums = new MutableLiveData<>();
App.getSubsonicClientInstance(application, false)
.getSearchingClient()
.search3(query, 0, 20, 0)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
List<Album> albums = new ArrayList<>(MappingUtil.mapAlbum(response.body().getSearchResult3().getAlbums()));
searchedAlbums.setValue(albums);
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
}
});
return searchedAlbums;
}
public MutableLiveData<List<Artist>> getSearchedArtists(String query) {
MutableLiveData<List<Artist>> searchedArtists = new MutableLiveData<>();
App.getSubsonicClientInstance(application, false)
.getSearchingClient()
.search3(query, 0, 0, 20)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
List<Artist> artists = new ArrayList<>(MappingUtil.mapArtist(response.body().getSearchResult3().getArtists()));
searchedArtists.setValue(artists);
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
}
});
return searchedArtists;
}
public MutableLiveData<List<String>> getSuggestions(String query) {
MutableLiveData<List<String>> suggestions = new MutableLiveData<>(new ArrayList());
App.getSubsonicClientInstance(application, false)
.getSearchingClient()
.search3(query, 5, 5, 5)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
List<String> 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<String> hashSet = new LinkedHashSet<>(newSuggestions);
ArrayList<String> suggestionsWithoutDuplicates = new ArrayList<>(hashSet);
suggestions.setValue(suggestionsWithoutDuplicates);
}
}
@Override
public void onFailure(Call<SubsonicResponse> 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<String> getRecentSearchSuggestion(int limit) {
List<String> 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;

View file

@ -32,14 +32,14 @@ public class SearchingClient {
this.searchingService = retrofit.create(SearchingService.class);
}
public Call<SubsonicResponse> search2(String query) {
public Call<SubsonicResponse> 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<SubsonicResponse> search3(String query) {
public Call<SubsonicResponse> 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() {

View file

@ -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<SubsonicResponse> search2(@QueryMap Map<String, String> params, String query);
@GET("search2")
Call<SubsonicResponse> search2(@QueryMap Map<String, String> params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount);
@GET("search3?query={query}")
Call<SubsonicResponse> search3(@QueryMap Map<String, String> params, String query);
@GET("search3")
Call<SubsonicResponse> search3(@QueryMap Map<String, String> params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount);
}

View file

@ -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<Artist> artists;
@Element(name = "album")
protected List<Child> albums;
@Element(name = "song")
protected List<Child> songs;
/**
* Gets the value of the artists property.
*
* <p>
* 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 <CODE>set</CODE> method for the artists property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getArtists().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Artist }
*/
public List<Artist> getArtists() {
if (artists == null) {
artists = new ArrayList<Artist>();
artists = new ArrayList<>();
}
return this.artists;
}
/**
* Gets the value of the albums property.
*
* <p>
* 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 <CODE>set</CODE> method for the albums property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAlbums().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Child }
*/
public List<Child> getAlbums() {
if (albums == null) {
albums = new ArrayList<Child>();
albums = new ArrayList<>();
}
return this.albums;
}
/**
* Gets the value of the songs property.
*
* <p>
* 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 <CODE>set</CODE> method for the songs property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getSongs().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Child }
*/
public List<Child> getSongs() {
if (songs == null) {
songs = new ArrayList<Child>();
songs = new ArrayList<>();
}
return this.songs;
}
public void setArtists(List<Artist> artists) {
this.artists = artists;
}
public void setAlbums(List<Child> albums) {
this.albums = albums;
}
public void setSongs(List<Child> songs) {
this.songs = songs;
}
}

View file

@ -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<ArtistID3> artists;
@Element(name = "album")
protected List<AlbumID3> albums;
@Element(name = "song")
protected List<Child> songs;
/**
* Gets the value of the artists property.
*
* <p>
* 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 <CODE>set</CODE> method for the artists property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getArtists().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ArtistID3 }
*/
public List<ArtistID3> getArtists() {
if (artists == null) {
artists = new ArrayList<ArtistID3>();
artists = new ArrayList<>();
}
return this.artists;
}
/**
* Gets the value of the albums property.
*
* <p>
* 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 <CODE>set</CODE> method for the albums property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAlbums().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link AlbumID3 }
*/
public List<AlbumID3> getAlbums() {
if (albums == null) {
albums = new ArrayList<AlbumID3>();
albums = new ArrayList<>();
}
return this.albums;
}
/**
* Gets the value of the songs property.
*
* <p>
* 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 <CODE>set</CODE> method for the songs property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getSongs().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Child }
*/
public List<Child> getSongs() {
if (songs == null) {
songs = new ArrayList<Child>();
songs = new ArrayList<>();
}
return this.songs;
}
public void setArtists(List<ArtistID3> artists) {
this.artists = artists;
}
public void setAlbums(List<AlbumID3> albums) {
this.albums = albums;
}
public void setSongs(List<Child> songs) {
this.songs = songs;
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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<List<Song>> searchSong;
private LiveData<List<Album>> searchAlbum;
private LiveData<List<Artist>> searchArtist;
private LiveData<List<Genre>> searchGenre;
private LiveData<List<Song>> searchSong = new MutableLiveData<>(new ArrayList<>());
private LiveData<List<Album>> searchAlbum = new MutableLiveData<>(new ArrayList<>());
private LiveData<List<Artist>> searchArtist = new MutableLiveData<>(new ArrayList<>());
private LiveData<List<Genre>> searchGenre = new MutableLiveData<>(new ArrayList<>());
public SearchViewModel(@NonNull Application application) {
super(application);
@ -60,26 +61,21 @@ public class SearchViewModel extends AndroidViewModel {
}
}
public LiveData<List<Song>> searchSong(String title, Context context) {
// searchSong = songRepository.searchListLiveSong(title, PreferenceUtil.getInstance(context).getSearchElementPerCategory());
public LiveData<List<Song>> searchSong(String title) {
searchSong = searchingRepository.getSearchedSongs(title);
return searchSong;
}
public LiveData<List<Album>> searchAlbum(String name, Context context) {
// searchAlbum = albumRepository.searchListLiveAlbum(name, PreferenceUtil.getInstance(context).getSearchElementPerCategory());
public LiveData<List<Album>> searchAlbum(String name) {
searchAlbum = searchingRepository.getSearchedAlbums(name);
return searchAlbum;
}
public LiveData<List<Artist>> searchArtist(String name, Context context) {
// searchArtist = artistRepository.searchListLiveArtist(name, PreferenceUtil.getInstance(context).getSearchElementPerCategory());
public LiveData<List<Artist>> searchArtist(String name) {
searchArtist = searchingRepository.getSearchedArtists(name);
return searchArtist;
}
public LiveData<List<Genre>> 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<String> getSearchSuggestion(String query) {
ArrayList<String> suggestions = new ArrayList<>();
// suggestions.addAll(songRepository.getSearchSuggestion(query));
// suggestions.addAll(albumRepository.getSearchSuggestion(query));
// suggestions.addAll(artistRepository.getSearchSuggestion(query));
// suggestions.addAll(genreRepository.getSearchSuggestion(query));
LinkedHashSet<String> hashSet = new LinkedHashSet<>(suggestions);
ArrayList<String> suggestionsWithoutDuplicates = new ArrayList<>(hashSet);
return suggestionsWithoutDuplicates;
public LiveData<List<String>> getSearchSuggestion(String query) {
return searchingRepository.getSuggestions(query);
}
public List<String> getRecentSearchSuggestion() {

View file

@ -139,37 +139,6 @@
android:paddingEnd="8dp"
android:paddingBottom="8dp" />
</LinearLayout>
<!-- Genre -->
<LinearLayout
android:id="@+id/search_genre_sector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="8dp"
android:visibility="gone">
<TextView
style="@style/HeadlineTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="20dp"
android:paddingEnd="16dp"
android:text="Genres" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_result_genre_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</RelativeLayout>