From 35bc02e6d97e5114ab457aa751e8ab0f49e532a9 Mon Sep 17 00:00:00 2001 From: antonio Date: Sun, 19 Mar 2023 18:23:52 +0100 Subject: [PATCH] First experiment with on-scrolling pagination on track list by genre --- .../PaginationScrollListener.java | 36 +++++++++++++++++++ .../play/repository/SongRepository.java | 19 +++------- .../ui/fragment/SongListPageFragment.java | 23 +++++++++++- .../play/viewmodel/SongListPageViewModel.java | 21 ++++++++++- 4 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/com/cappielloantonio/play/helper/recyclerview/PaginationScrollListener.java diff --git a/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/PaginationScrollListener.java b/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/PaginationScrollListener.java new file mode 100644 index 00000000..20cec7ee --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/PaginationScrollListener.java @@ -0,0 +1,36 @@ +package com.cappielloantonio.play.helper.recyclerview; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + + +public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener { + private static final String TAG = "PaginationScrollListener"; + + private LinearLayoutManager layoutManager; + + protected PaginationScrollListener(LinearLayoutManager layoutManager) { + this.layoutManager = layoutManager; + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + int visibleItemCount = layoutManager.getChildCount(); + int totalItemCount = layoutManager.getItemCount(); + int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition(); + + if (!isLoading()) { + if (firstVisibleItemPosition >= 0 && (visibleItemCount + firstVisibleItemPosition) >= totalItemCount) { + loadMoreItems(); + } + } + } + + protected abstract void loadMoreItems(); + + public abstract boolean isLoading(); + +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/repository/SongRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/SongRepository.java index 5b082d76..06d262fc 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/SongRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/SongRepository.java @@ -11,7 +11,6 @@ import com.cappielloantonio.play.subsonic.models.Child; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; import retrofit2.Call; @@ -171,27 +170,19 @@ public class SongRepository { }); } - public MutableLiveData> getSongsByGenre(String id) { + public MutableLiveData> getSongsByGenre(String id, int page) { MutableLiveData> songsByGenre = new MutableLiveData<>(); + Log.d(TAG, "onScrolled PAGE: " + page); + App.getSubsonicClientInstance(false) .getAlbumSongListClient() - .getSongsByGenre(id, 500, 0) + .getSongsByGenre(id, 100, 100 * page) .enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSongsByGenre() != null) { - List newSongs = response.body().getSubsonicResponse().getSongsByGenre().getSongs(); - List songs = songsByGenre.getValue(); - - if (songs == null) songs = new ArrayList<>(); - songs.addAll(newSongs); - Collections.shuffle(songs); - - LinkedHashSet hashSet = new LinkedHashSet<>(songs); - ArrayList songsWithoutDuplicates = new ArrayList<>(hashSet); - - songsByGenre.setValue(songsWithoutDuplicates); + songsByGenre.setValue(response.body().getSubsonicResponse().getSongsByGenre().getSongs()); } } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java index 1c174e5a..dd75b7a4 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/SongListPageFragment.java @@ -18,6 +18,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.cappielloantonio.play.R; import com.cappielloantonio.play.databinding.FragmentSongListPageBinding; +import com.cappielloantonio.play.helper.recyclerview.PaginationScrollListener; import com.cappielloantonio.play.interfaces.ClickCallback; import com.cappielloantonio.play.service.MediaManager; import com.cappielloantonio.play.service.MediaService; @@ -32,6 +33,8 @@ import java.util.Collections; @UnstableApi public class SongListPageFragment extends Fragment implements ClickCallback { + private static final String TAG = "SongListPageFragment"; + private FragmentSongListPageBinding bind; private MainActivity activity; private SongListPageViewModel songListPageViewModel; @@ -40,6 +43,8 @@ public class SongListPageFragment extends Fragment implements ClickCallback { private ListenableFuture mediaBrowserListenableFuture; + private boolean isLoading = true; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { activity = (MainActivity) getActivity(); @@ -163,7 +168,23 @@ public class SongListPageFragment extends Fragment implements ClickCallback { songHorizontalAdapter = new SongHorizontalAdapter(this, true); bind.songListRecyclerView.setAdapter(songHorizontalAdapter); - songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs)); + songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> { + isLoading = false; + songHorizontalAdapter.setItems(songs); + }); + + bind.songListRecyclerView.addOnScrollListener(new PaginationScrollListener((LinearLayoutManager) bind.songListRecyclerView.getLayoutManager()) { + @Override + protected void loadMoreItems() { + isLoading = true; + songListPageViewModel.getSongsByPage(getViewLifecycleOwner()); + } + + @Override + public boolean isLoading() { + return isLoading; + } + }); } private void initializeMediaBrowser() { diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/SongListPageViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/SongListPageViewModel.java index b624109e..c460d4c9 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/SongListPageViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/SongListPageViewModel.java @@ -5,6 +5,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -48,7 +49,7 @@ public class SongListPageViewModel extends AndroidViewModel { switch (title) { case Constants.MEDIA_BY_GENRE: - songList = songRepository.getSongsByGenre(genre.getGenre()); + songList = songRepository.getSongsByGenre(genre.getGenre(), 0); break; case Constants.MEDIA_BY_ARTIST: songList = artistRepository.getTopSongs(artist.getName(), 50); @@ -67,6 +68,24 @@ public class SongListPageViewModel extends AndroidViewModel { return songList; } + public void getSongsByPage(LifecycleOwner owner) { + switch (title) { + case Constants.MEDIA_BY_GENRE: + int page = (songList.getValue() != null ? songList.getValue().size() : 0) / 100; + songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> { + List currentMedia = songList.getValue(); + currentMedia.addAll(children); + songList.setValue(currentMedia); + }); + break; + case Constants.MEDIA_BY_ARTIST: + case Constants.MEDIA_BY_GENRES: + case Constants.MEDIA_BY_YEAR: + case Constants.MEDIA_STARRED: + break; + } + } + public String getFiltersTitle() { return TextUtils.join(", ", filterNames); }