diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java index dfb0565b..900a4bee 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java @@ -7,6 +7,7 @@ import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.subsonic.base.ApiResponse; import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.SubsonicResponse; +import com.cappielloantonio.tempo.util.Constants; import java.util.ArrayList; import java.util.Collections; @@ -56,16 +57,26 @@ public class SongRepository { /** * Used by ViewModels. Updates the LiveData list incrementally as songs are found. */ - public MutableLiveData> getInstantMix(String id, int count) { + public MutableLiveData> getInstantMix(String id, Constants.SEEDTYPE type, int count) { MutableLiveData> instantMix = new MutableLiveData<>(new ArrayList<>()); - performSmartMix(id, count, songs -> { + performSmartMix(id, type, count, songs -> { List current = instantMix.getValue(); if (current != null) { for (Child s : songs) { if (!current.contains(s)) current.add(s); } - instantMix.postValue(current); + + if (current.size() < count / 2) { + fillWithRandom(count - current.size(), remainder -> { + for (Child r : remainder) { + if (!current.contains(r)) current.add(r); + } + instantMix.postValue(current); + }); + } else { + instantMix.postValue(current); + } } }); @@ -75,57 +86,79 @@ public class SongRepository { /** * Overloaded method used by other Repositories */ - public void getInstantMix(String id, int count, MediaCallbackInternal callback) { - performSmartMix(id, count, callback); + public void getInstantMix(String id, Constants.SEEDTYPE type, int count, MediaCallbackInternal callback) { + performSmartMix(id, type, count, callback); } - private void performSmartMix(final String id, final int count, final MediaCallbackInternal callback) { - App.getSubsonicClientInstance(false) - .getBrowsingClient() - .getSimilarSongs(id, count) - .enqueue(new Callback() { - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - List songs = extractSongs(response, "similarSongs"); - if (!songs.isEmpty()) { - callback.onSongsAvailable(songs); - } - if (songs.size() < count / 2) { - fetchContextAndSeed(id, count, callback); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - fetchContextAndSeed(id, count, callback); - } - }); + private void performSmartMix(final String id, final Constants.SEEDTYPE type, final int count, final MediaCallbackInternal callback) { + switch (type) { + case ARTIST: + fetchSimilarByArtist(id, count, callback); + break; + case ALBUM: + fetchAlbumSongsThenSimilar(id, count, callback); + break; + case TRACK: + fetchSingleTrackThenSimilar(id, count, callback); + break; + } } - private void fetchContextAndSeed(final String id, final int count, final MediaCallbackInternal callback) { - App.getSubsonicClientInstance(false) - .getBrowsingClient() - .getAlbum(id) - .enqueue(new Callback() { - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) { - List albumSongs = response.body().getSubsonicResponse().getAlbum().getSongs(); - if (albumSongs != null && !albumSongs.isEmpty()) { - callback.onSongsAvailable(new ArrayList<>(albumSongs)); - String seedArtistId = albumSongs.get(0).getArtistId(); - fetchSimilarByArtist(seedArtistId, count, callback); - return; - } - } - fillWithRandom(count, callback); + private void fetchAlbumSongsThenSimilar(String albumId, int count, MediaCallbackInternal callback) { + App.getSubsonicClientInstance(false).getBrowsingClient().getAlbum(albumId).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) { + List albumSongs = response.body().getSubsonicResponse().getAlbum().getSongs(); + if (albumSongs != null && !albumSongs.isEmpty()) { + callback.onSongsAvailable(new ArrayList<>(albumSongs)); + fetchSimilarByArtist(albumSongs.get(0).getArtistId(), count, callback); + return; } + } + fillWithRandom(count, callback); + } + @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + fillWithRandom(count, callback); + } + }); + } - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - fillWithRandom(count, callback); + private void fetchSingleTrackThenSimilar(String trackId, int count, MediaCallbackInternal callback) { + App.getSubsonicClientInstance(false).getBrowsingClient().getSong(trackId).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null) { + Child song = response.body().getSubsonicResponse().getSong(); + if (song != null) { + callback.onSongsAvailable(Collections.singletonList(song)); + fetchSimilarOnly(trackId, count, callback); + return; } - }); + } + fillWithRandom(count, callback); + } + @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + fillWithRandom(count, callback); + } + }); + } + + private void fetchSimilarOnly(String id, int count, MediaCallbackInternal callback) { + App.getSubsonicClientInstance(false).getBrowsingClient().getSimilarSongs(id, count).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + List songs = extractSongs(response, "similarSongs"); + if (!songs.isEmpty()) { + callback.onSongsAvailable(songs); + } else { + fillWithRandom(count, callback); + } + } + @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + fillWithRandom(count, callback); + } + }); } private void fetchSimilarByArtist(String artistId, final int count, final MediaCallbackInternal callback) { @@ -138,9 +171,14 @@ public class SongRepository { List similar = extractSongs(response, "similarSongs2"); if (!similar.isEmpty()) { callback.onSongsAvailable(similar); + } else { + fillWithRandom(count, callback); } } - @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) {} + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + fillWithRandom(count, callback); + } }); } diff --git a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java index 02cbd239..e7b6c6e6 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java +++ b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java @@ -26,6 +26,7 @@ import com.cappielloantonio.tempo.repository.SongRepository; import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation; import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode; +import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel; @@ -442,7 +443,7 @@ public class MediaManager { if (mediaItem != null && Preferences.isContinuousPlayEnabled() && Preferences.isInstantMixUsable()) { Preferences.setLastInstantMix(); - LiveData> instantMix = getSongRepository().getInstantMix(mediaItem.mediaId, 10); + LiveData> instantMix = getSongRepository().getInstantMix(mediaItem.mediaId, Constants.SEEDTYPE.TRACK, 10); instantMix.observeForever(new Observer>() { @Override public void onChanged(List media) { diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java index 0a9892fc..e0f13edd 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java @@ -24,6 +24,7 @@ import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Playlist; import com.cappielloantonio.tempo.subsonic.models.Share; +import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Preferences; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; @@ -223,7 +224,7 @@ public class HomeViewModel extends AndroidViewModel { public LiveData> getMediaInstantMix(LifecycleOwner owner, Child media) { mediaInstantMix.setValue(Collections.emptyList()); - songRepository.getInstantMix(media.getId(), 20).observe(owner, mediaInstantMix::postValue); + songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, mediaInstantMix::postValue); return mediaInstantMix; } diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java index df571690..8b631977 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java @@ -278,7 +278,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { public LiveData> getMediaInstantMix(LifecycleOwner owner, Child media) { instantMix.setValue(Collections.emptyList()); - songRepository.getInstantMix(media.getId(), 20).observe(owner, instantMix::postValue); + songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, instantMix::postValue); return instantMix; } diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java index de379dcd..6ea4a9ab 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java @@ -21,6 +21,7 @@ import com.cappielloantonio.tempo.subsonic.models.AlbumID3; import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Share; +import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.NetworkUtil; @@ -128,7 +129,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel { public LiveData> getInstantMix(LifecycleOwner owner, Child media) { instantMix.setValue(Collections.emptyList()); - songRepository.getInstantMix(media.getId(), 20).observe(owner, instantMix::postValue); + songRepository.getInstantMix(media.getId(), Constants.SEEDTYPE.TRACK, 20).observe(owner, instantMix::postValue); return instantMix; }