diff --git a/.idea/misc.xml b/.idea/misc.xml index 22db2b6e..ef098457 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -88,6 +88,7 @@ + diff --git a/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java b/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java index 29c5283f..a544a8e9 100644 --- a/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java @@ -2,7 +2,6 @@ package com.cappielloantonio.play.adapter; import android.content.Context; import android.os.Bundle; -import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,7 +12,6 @@ import androidx.annotation.NonNull; import androidx.navigation.Navigation; import androidx.recyclerview.widget.RecyclerView; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.cappielloantonio.play.R; import com.cappielloantonio.play.glide.CustomGlideRequest; import com.cappielloantonio.play.model.Playlist; @@ -23,6 +21,7 @@ import com.cappielloantonio.play.util.MusicUtil; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class PlaylistAdapter extends RecyclerView.Adapter { private static final String TAG = "PlaylistAdapter"; @@ -30,14 +29,16 @@ public class PlaylistAdapter extends RecyclerView.Adapter playlists; - public PlaylistAdapter(MainActivity activity, Context context) { + public PlaylistAdapter(MainActivity activity, Context context, boolean isDownloaded) { this.activity = activity; this.context = context; this.mInflater = LayoutInflater.from(context); this.playlists = new ArrayList<>(); + this.isDownloaded = isDownloaded; } @NonNull @@ -58,6 +59,8 @@ public class PlaylistAdapter extends RecyclerView.Adapter playlists; private List playlistsFull; - public PlaylistCatalogueAdapter(MainActivity activity, Context context) { + public PlaylistCatalogueAdapter(MainActivity activity, Context context, boolean isOffline) { this.activity = activity; this.context = context; this.mInflater = LayoutInflater.from(context); this.playlists = new ArrayList<>(); + this.isOffline = isOffline; } @NonNull @@ -133,6 +137,8 @@ public class PlaylistCatalogueAdapter extends RecyclerView.Adapter> getAll(String server); - @Query("SELECT * FROM download WHERE server=:server GROUP BY artistName LIMIT :size") + @Query("SELECT * FROM download WHERE server=:server AND playlistId IS NULL GROUP BY artistName LIMIT :size") LiveData> getSampleArtist(int size, String server); - @Query("SELECT * FROM download WHERE server=:server GROUP BY albumName LIMIT :size") + @Query("SELECT * FROM download WHERE server=:server AND playlistId IS NULL GROUP BY albumName LIMIT :size") LiveData> getSampleAlbum(int size, String server); + @Query("SELECT * FROM download WHERE server=:server AND playlistId IS NOT NULL GROUP BY playlistId LIMIT :size") + LiveData> getSamplePlaylist(int size, String server); + @Query("SELECT * FROM download WHERE server=:server LIMIT :size") LiveData> getSample(int size, String server); @@ -30,14 +33,20 @@ public interface DownloadDao { @Query("SELECT * FROM download WHERE server=:server AND albumId=:albumId") LiveData> getAllFromAlbum(String server, String albumId); + @Query("SELECT * FROM download WHERE server=:server AND playlistId=:playlistId") + LiveData> getAllFromPlaylist(String server, String playlistId); + + @Query("SELECT * FROM download WHERE server=:server AND playlistId IS NOT NULL GROUP BY playlistId") + LiveData> getAllPlaylists(String server); + @Insert(onConflict = OnConflictStrategy.REPLACE) void insert(Download download); @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List downloads); - @Query("DELETE FROM download WHERE id = :id") - void delete(String id); + @Query("DELETE FROM download WHERE songId = :songId") + void delete(String songId); @Query("DELETE FROM download WHERE server=:server") void deleteAll(String server); diff --git a/app/src/main/java/com/cappielloantonio/play/model/Download.java b/app/src/main/java/com/cappielloantonio/play/model/Download.java index 5e2cfe43..18c0f41c 100644 --- a/app/src/main/java/com/cappielloantonio/play/model/Download.java +++ b/app/src/main/java/com/cappielloantonio/play/model/Download.java @@ -9,11 +9,16 @@ import com.cappielloantonio.play.App; import com.cappielloantonio.play.util.MusicUtil; import com.cappielloantonio.play.util.PreferenceUtil; +import java.util.UUID; + @Entity(tableName = "download") public class Download { @NonNull @PrimaryKey @ColumnInfo(name = "id") + private String id; + + @ColumnInfo(name = "songId") private String songID; @ColumnInfo(name = "title") @@ -43,7 +48,14 @@ public class Download { @ColumnInfo(name = "server") private String server; - public Download(@NonNull String songID, String title, String albumId, String albumName, String artistId, String artistName, int trackNumber, String primary, long duration, String server) { + @ColumnInfo(name = "playlistId") + private String playlistId; + + @ColumnInfo(name = "playlistName") + private String playlistName; + + public Download(@NonNull String id, String songID, String title, String albumId, String albumName, String artistId, String artistName, int trackNumber, String primary, long duration, String server, String playlistId, String playlistName) { + this.id = id; this.songID = songID; this.title = title; this.albumId = albumId; @@ -54,9 +66,12 @@ public class Download { this.primary = primary; this.duration = duration; this.server = server; + this.playlistId = playlistId; + this.playlistName = playlistName; } - public Download(Song song) { + public Download(Song song, String playlistId, String playlistName) { + this.id = UUID.randomUUID().toString(); this.songID = song.getId(); this.title = song.getTitle(); this.albumId = song.getAlbumId(); @@ -67,14 +82,24 @@ public class Download { this.primary = song.getPrimary(); this.duration = song.getDuration(); this.server = PreferenceUtil.getInstance(App.getInstance()).getServerId(); + this.playlistId = playlistId; + this.playlistName = playlistName; } @NonNull + public String getId() { + return id; + } + + public void setId(@NonNull String id) { + this.id = id; + } + public String getSongID() { return songID; } - public void setSongID(@NonNull String songID) { + public void setSongID(String songID) { this.songID = songID; } @@ -149,4 +174,20 @@ public class Download { public void setServer(String server) { this.server = server; } + + public String getPlaylistId() { + return playlistId; + } + + public void setPlaylistId(String playlistId) { + this.playlistId = playlistId; + } + + public String getPlaylistName() { + return playlistName; + } + + public void setPlaylistName(String playlistName) { + this.playlistName = playlistName; + } } diff --git a/app/src/main/java/com/cappielloantonio/play/model/Playlist.java b/app/src/main/java/com/cappielloantonio/play/model/Playlist.java index 221827cb..3723d9da 100644 --- a/app/src/main/java/com/cappielloantonio/play/model/Playlist.java +++ b/app/src/main/java/com/cappielloantonio/play/model/Playlist.java @@ -4,6 +4,9 @@ import android.os.Parcel; import android.os.Parcelable; public class Playlist implements Parcelable { + public static final String ALL = "ALL"; + public static final String DOWNLOADED = "DOWNLOADED"; + private String id; private String name; private String primary; @@ -20,6 +23,11 @@ public class Playlist implements Parcelable { this.duration = playlist.getDuration(); } + public Playlist(String id, String name) { + this.id = id; + this.name = name; + } + public String getId() { return id; } diff --git a/app/src/main/java/com/cappielloantonio/play/repository/DownloadRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/DownloadRepository.java index dd6fe0a6..86a8eb08 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/DownloadRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/DownloadRepository.java @@ -26,10 +26,11 @@ public class DownloadRepository { return downloadDao.getAll(PreferenceUtil.getInstance(App.getInstance()).getServerId()); } - public LiveData> getLiveDownloadSample(int size, boolean isArtist, boolean isAlbum, boolean isTrack) { + public LiveData> getLiveDownloadSample(int size, boolean isArtist, boolean isAlbum, boolean isTrack, boolean isPlaylist) { if (isArtist) return downloadDao.getSampleArtist(size, PreferenceUtil.getInstance(App.getInstance()).getServerId()); else if (isAlbum) return downloadDao.getSampleAlbum(size, PreferenceUtil.getInstance(App.getInstance()).getServerId()); else if (isTrack) return downloadDao.getSample(size, PreferenceUtil.getInstance(App.getInstance()).getServerId()); + else if (isPlaylist) return downloadDao.getSamplePlaylist(size, PreferenceUtil.getInstance(App.getInstance()).getServerId()); else return downloadDao.getSample(size, PreferenceUtil.getInstance(App.getInstance()).getServerId()); } @@ -41,6 +42,14 @@ public class DownloadRepository { return downloadDao.getAllFromAlbum(PreferenceUtil.getInstance(App.getInstance()).getServerId(), albumId); } + public LiveData> getLiveDownloadFromPlaylist(String playlistId) { + return downloadDao.getAllFromPlaylist(PreferenceUtil.getInstance(App.getInstance()).getServerId(), playlistId); + } + + public LiveData> getLivePlaylist() { + return downloadDao.getAllPlaylists(PreferenceUtil.getInstance(App.getInstance()).getServerId()); + } + public void insert(Download download) { InsertThreadSafe insert = new InsertThreadSafe(downloadDao, download); Thread thread = new Thread(insert); diff --git a/app/src/main/java/com/cappielloantonio/play/service/DownloadTracker.java b/app/src/main/java/com/cappielloantonio/play/service/DownloadTracker.java index b39d5c68..145c84c3 100644 --- a/app/src/main/java/com/cappielloantonio/play/service/DownloadTracker.java +++ b/app/src/main/java/com/cappielloantonio/play/service/DownloadTracker.java @@ -87,17 +87,18 @@ public class DownloadTracker { return new DownloadRequest.Builder(id, uri).build(); } - public void download(List songs) { + public void download(List songs, String playlistId, String playlistName) { DownloadRepository downloadRepository = new DownloadRepository(App.getInstance()); for (Song song : songs) { if(isDownloaded(song)) { + downloadRepository.insert(MappingUtil.mapToDownload(song, playlistId, playlistName)); continue; } MediaItem mediaItem = MusicUtil.getMediaItemFromSong(song); DownloadService.sendAddDownload(context, DownloaderService.class, getDownloadRequest(song.getId(), checkNotNull(mediaItem.playbackProperties).uri), false); - downloadRepository.insert(MappingUtil.mapToDownload(song)); + downloadRepository.insert(MappingUtil.mapToDownload(song, playlistId, playlistName)); } } @@ -111,7 +112,7 @@ public class DownloadTracker { if (download != null && download.state != Download.STATE_FAILED) { DownloadService.sendRemoveDownload(context, DownloaderService.class, download.request.id, false); - downloadRepository.delete(MappingUtil.mapToDownload(song)); + downloadRepository.delete(MappingUtil.mapToDownload(song, null, null)); } } } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java index 16773cd0..7661eddc 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/AlbumPageFragment.java @@ -91,14 +91,11 @@ public class AlbumPageFragment extends Fragment { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_download_album: - albumPageViewModel.getAlbumSongLiveList(requireActivity()).observe(requireActivity(), songs -> { - DownloadUtil.getDownloadTracker(requireContext()).download(songs); - }); - return true; - default: - break; + if (item.getItemId() == R.id.action_download_album) { + albumPageViewModel.getAlbumSongLiveList(requireActivity()).observe(requireActivity(), songs -> { + DownloadUtil.getDownloadTracker(requireContext()).download(songs, null, null); + }); + return true; } return false; diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/DownloadFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/DownloadFragment.java index 0d46e942..20db2a5d 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/DownloadFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/DownloadFragment.java @@ -12,35 +12,27 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.PagerSnapHelper; import androidx.recyclerview.widget.SnapHelper; import androidx.viewpager2.widget.ViewPager2; import com.cappielloantonio.play.R; -import com.cappielloantonio.play.adapter.AlbumAdapter; import com.cappielloantonio.play.adapter.AlbumHorizontalAdapter; -import com.cappielloantonio.play.adapter.ArtistAdapter; import com.cappielloantonio.play.adapter.ArtistHorizontalAdapter; -import com.cappielloantonio.play.adapter.DiscoverSongAdapter; -import com.cappielloantonio.play.adapter.SimilarTrackAdapter; +import com.cappielloantonio.play.adapter.PlaylistAdapter; import com.cappielloantonio.play.adapter.SongHorizontalAdapter; -import com.cappielloantonio.play.adapter.TrackAdapter; -import com.cappielloantonio.play.adapter.YearAdapter; import com.cappielloantonio.play.databinding.FragmentDownloadBinding; -import com.cappielloantonio.play.databinding.FragmentHomeBinding; -import com.cappielloantonio.play.helper.recyclerview.CustomLinearSnapHelper; import com.cappielloantonio.play.helper.recyclerview.DotsIndicatorDecoration; import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Artist; +import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.ui.activity.MainActivity; -import com.cappielloantonio.play.util.MappingUtil; import com.cappielloantonio.play.util.UIUtil; import com.cappielloantonio.play.viewmodel.DownloadViewModel; -import com.cappielloantonio.play.viewmodel.HomeViewModel; import java.util.Objects; @@ -54,6 +46,7 @@ public class DownloadFragment extends Fragment { private ArtistHorizontalAdapter downloadedArtistAdapter; private AlbumHorizontalAdapter downloadedAlbumAdapter; private SongHorizontalAdapter downloadedTrackAdapter; + private PlaylistAdapter playlistAdapter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -89,6 +82,8 @@ public class DownloadFragment extends Fragment { initDownloadedArtistView(); initDownloadedAlbumView(); initDownloadedSongView(); + initDownloadedPlaylistSlideView(); + initPlaceholder(); } @Override @@ -138,6 +133,12 @@ public class DownloadFragment extends Fragment { bundle.putString(Song.DOWNLOADED, Song.DOWNLOADED); activity.navController.navigate(R.id.action_downloadFragment_to_songListPageFragment, bundle); }); + + bind.downloadedPlaylistTextViewClickable.setOnClickListener(v -> { + Bundle bundle = new Bundle(); + bundle.putString(Playlist.DOWNLOADED, Playlist.DOWNLOADED); + activity.navController.navigate(R.id.action_downloadFragment_to_playlistCatalogueFragment, bundle); + }); } private void initAppBar() { @@ -155,12 +156,10 @@ public class DownloadFragment extends Fragment { if (bind != null) bind.downloadDownloadedArtistPlaceholder.placeholder.setVisibility(View.VISIBLE); if (bind != null) bind.downloadDownloadedArtistSector.setVisibility(View.GONE); } else { - if (bind != null && !artists.isEmpty()) bind.emptyDownloadLayout.setVisibility(View.GONE); - if (bind != null && !artists.isEmpty()) bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE); - if (bind != null) bind.downloadDownloadedArtistPlaceholder.placeholder.setVisibility(View.GONE); if (bind != null) bind.downloadDownloadedArtistSector.setVisibility(!artists.isEmpty() ? View.VISIBLE : View.GONE); - if (bind != null) bind.downloadedArtistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(artists.size(), 5), GridLayoutManager.HORIZONTAL, false)); + if (bind != null) + bind.downloadedArtistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(artists.size(), 5), GridLayoutManager.HORIZONTAL, false)); downloadedArtistAdapter.setItems(artists); } @@ -189,12 +188,10 @@ public class DownloadFragment extends Fragment { if (bind != null) bind.downloadDownloadedAlbumPlaceholder.placeholder.setVisibility(View.VISIBLE); if (bind != null) bind.downloadDownloadedAlbumSector.setVisibility(View.GONE); } else { - if (bind != null && !albums.isEmpty()) bind.emptyDownloadLayout.setVisibility(View.GONE); - if (bind != null && !albums.isEmpty()) bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE); - if (bind != null) bind.downloadDownloadedAlbumPlaceholder.placeholder.setVisibility(View.GONE); if (bind != null) bind.downloadDownloadedAlbumSector.setVisibility(!albums.isEmpty() ? View.VISIBLE : View.GONE); - if (bind != null) bind.downloadedAlbumRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(albums.size(), 5), GridLayoutManager.HORIZONTAL, false)); + if (bind != null) + bind.downloadedAlbumRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(albums.size(), 5), GridLayoutManager.HORIZONTAL, false)); downloadedAlbumAdapter.setItems(albums); } @@ -223,12 +220,10 @@ public class DownloadFragment extends Fragment { if (bind != null) bind.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.VISIBLE); if (bind != null) bind.downloadDownloadedTracksSector.setVisibility(View.GONE); } else { - if (bind != null && !songs.isEmpty()) bind.emptyDownloadLayout.setVisibility(View.GONE); - if (bind != null && !songs.isEmpty()) bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE); - if (bind != null) bind.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.GONE); if (bind != null) bind.downloadDownloadedTracksSector.setVisibility(!songs.isEmpty() ? View.VISIBLE : View.GONE); - if (bind != null) bind.downloadedTracksRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(songs.size(), 5), GridLayoutManager.HORIZONTAL, false)); + if (bind != null) + bind.downloadedTracksRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(songs.size(), 5), GridLayoutManager.HORIZONTAL, false)); downloadedTrackAdapter.setItems(songs); } @@ -246,4 +241,60 @@ public class DownloadFragment extends Fragment { requireContext().getResources().getColor(R.color.titleTextColor, null)) ); } + + private void initDownloadedPlaylistSlideView() { + bind.downloadedPlaylistViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); + + playlistAdapter = new PlaylistAdapter(activity, requireContext(), true); + bind.downloadedPlaylistViewPager.setAdapter(playlistAdapter); + bind.downloadedPlaylistViewPager.setOffscreenPageLimit(3); + downloadViewModel.getDownloadedPlaylists(requireActivity(), 5).observe(requireActivity(), playlists -> { + if (playlists == null) { + if (bind != null) bind.downloadDownloadedPlaylistPlaceholder.placeholder.setVisibility(View.VISIBLE); + if (bind != null) bind.downloadDownloadedPlaylistSector.setVisibility(View.GONE); + } else { + if (bind != null) bind.downloadDownloadedPlaylistPlaceholder.placeholder.setVisibility(View.GONE); + if (bind != null) bind.downloadDownloadedPlaylistSector.setVisibility(!playlists.isEmpty() ? View.VISIBLE : View.GONE); + + playlistAdapter.setItems(playlists); + } + }); + + setSlideViewOffset(20, 16); + } + + private void setSlideViewOffset(float pageOffset, float pageMargin) { + bind.downloadedPlaylistViewPager.setPageTransformer((page, position) -> { + float myOffset = position * -(2 * pageOffset + pageMargin); + if (bind.downloadedPlaylistViewPager.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) { + if (ViewCompat.getLayoutDirection(bind.downloadedPlaylistViewPager) == ViewCompat.LAYOUT_DIRECTION_RTL) { + page.setTranslationX(-myOffset); + } else { + page.setTranslationX(myOffset); + } + } else { + page.setTranslationY(myOffset); + } + }); + } + + private void initPlaceholder() { + FragmentActivity requiredActivity = getActivity(); + + if (requiredActivity != null) { + downloadViewModel.getDownloadedTracks(requiredActivity, 20).observe(requiredActivity, songs -> + downloadViewModel.getDownloadedAlbums(requiredActivity, 20).observe(requiredActivity, albums -> + downloadViewModel.getDownloadedArtists(requiredActivity, 20).observe(requiredActivity, artists -> { + if ((songs != null && !songs.isEmpty()) || (albums != null && !albums.isEmpty()) || (artists != null && !artists.isEmpty())) { + if (bind != null) bind.emptyDownloadLayout.setVisibility(View.GONE); + if (bind != null) bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE); + } else { + if (bind != null) bind.emptyDownloadLayout.setVisibility(View.VISIBLE); + if (bind != null) bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE); + } + }) + ) + ); + } + } } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/LibraryFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/LibraryFragment.java index 9cbf209d..67c96281 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/LibraryFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/LibraryFragment.java @@ -28,6 +28,7 @@ import com.cappielloantonio.play.adapter.PlaylistAdapter; import com.cappielloantonio.play.databinding.FragmentLibraryBinding; import com.cappielloantonio.play.helper.recyclerview.CustomLinearSnapHelper; import com.cappielloantonio.play.helper.recyclerview.DotsIndicatorDecoration; +import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.util.UIUtil; @@ -118,7 +119,11 @@ public class LibraryFragment extends Fragment { bind.albumCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_albumCatalogueFragment)); bind.artistCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_artistCatalogueFragment)); bind.genreCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_genreCatalogueFragment)); - bind.playlistCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_playlistCatalogueFragment)); + bind.playlistCatalogueTextViewClickable.setOnClickListener(v -> { + Bundle bundle = new Bundle(); + bundle.putString(Playlist.ALL, Playlist.ALL); + activity.navController.navigate(R.id.action_libraryFragment_to_playlistCatalogueFragment, bundle); + }); bind.albumCatalogueSampleTextViewRefreshable.setOnLongClickListener(view -> { libraryViewModel.refreshAlbumSample(requireActivity()); @@ -249,7 +254,7 @@ public class LibraryFragment extends Fragment { private void initPlaylistSlideView() { bind.playlistViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); - playlistAdapter = new PlaylistAdapter(activity, requireContext()); + playlistAdapter = new PlaylistAdapter(activity, requireContext(), false); bind.playlistViewPager.setAdapter(playlistAdapter); bind.playlistViewPager.setOffscreenPageLimit(3); libraryViewModel.getPlaylistSample().observe(requireActivity(), playlists -> { diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistCatalogueFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistCatalogueFragment.java index 8ca537f2..004b3277 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistCatalogueFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistCatalogueFragment.java @@ -23,6 +23,8 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.cappielloantonio.play.R; import com.cappielloantonio.play.adapter.PlaylistCatalogueAdapter; import com.cappielloantonio.play.databinding.FragmentPlaylistCatalogueBinding; +import com.cappielloantonio.play.model.Album; +import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.viewmodel.PlaylistCatalogueViewModel; @@ -39,8 +41,6 @@ public class PlaylistCatalogueFragment extends Fragment { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - - initData(); } @Override @@ -49,7 +49,9 @@ public class PlaylistCatalogueFragment extends Fragment { bind = FragmentPlaylistCatalogueBinding.inflate(inflater, container, false); View view = bind.getRoot(); + playlistCatalogueViewModel = new ViewModelProvider(requireActivity()).get(PlaylistCatalogueViewModel.class); + init(); initAppBar(); initPlaylistCatalogueView(); @@ -68,8 +70,12 @@ public class PlaylistCatalogueFragment extends Fragment { bind = null; } - private void initData() { - playlistCatalogueViewModel = new ViewModelProvider(requireActivity()).get(PlaylistCatalogueViewModel.class); + private void init() { + if (getArguments().getString(Playlist.ALL) != null) { + playlistCatalogueViewModel.setType(Playlist.ALL); + } else if (getArguments().getString(Playlist.DOWNLOADED) != null) { + playlistCatalogueViewModel.setType(Playlist.DOWNLOADED); + } } private void initAppBar() { @@ -100,9 +106,9 @@ public class PlaylistCatalogueFragment extends Fragment { bind.playlistCatalogueRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); bind.playlistCatalogueRecyclerView.setHasFixedSize(true); - playlistCatalogueAdapter = new PlaylistCatalogueAdapter(activity, requireContext()); + playlistCatalogueAdapter = new PlaylistCatalogueAdapter(activity, requireContext(), playlistCatalogueViewModel.getType().equals(Playlist.DOWNLOADED)); bind.playlistCatalogueRecyclerView.setAdapter(playlistCatalogueAdapter); - playlistCatalogueViewModel.getPlaylistList().observe(requireActivity(), playlist -> { + playlistCatalogueViewModel.getPlaylistList(requireActivity()).observe(requireActivity(), playlist -> { playlistCatalogueAdapter.setItems(playlist); }); diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistPageFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistPageFragment.java index fb82f347..7d296307 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistPageFragment.java @@ -1,5 +1,6 @@ package com.cappielloantonio.play.ui.fragment; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@ -81,12 +82,13 @@ public class PlaylistPageFragment extends Fragment { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_download_playlist: - DownloadUtil.getDownloadTracker(requireContext()).download(Objects.requireNonNull(playlistPageViewModel.getPlaylistSongLiveList().getValue())); - return true; - default: - break; + if (item.getItemId() == R.id.action_download_playlist) { + playlistPageViewModel.getPlaylistSongLiveList(requireActivity()).observe(requireActivity(), songs -> { + if (getContext() != null) { + DownloadUtil.getDownloadTracker(requireContext()).download(songs, playlistPageViewModel.getPlaylist().getId(), playlistPageViewModel.getPlaylist().getName()); + } + }); + return true; } return false; @@ -94,6 +96,7 @@ public class PlaylistPageFragment extends Fragment { private void init() { playlistPageViewModel.setPlaylist(getArguments().getParcelable("playlist_object")); + playlistPageViewModel.setOffline(getArguments().getBoolean("is_offline")); } private void initAppBar() { @@ -110,13 +113,18 @@ public class PlaylistPageFragment extends Fragment { bind.playlistSongCountLabel.setText("Song count: " + playlistPageViewModel.getPlaylist().getSongCount()); bind.playlistDurationLabel.setText("Playlist duration: " + MusicUtil.getReadableDurationString(playlistPageViewModel.getPlaylist().getDuration(), false)); + if (playlistPageViewModel.isOffline()) { + bind.playlistSongCountLabel.setVisibility(View.GONE); + bind.playlistDurationLabel.setVisibility(View.GONE); + } + bind.animToolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp()); Objects.requireNonNull(bind.animToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null)); } private void initMusicButton() { - playlistPageViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> { + playlistPageViewModel.getPlaylistSongLiveList(requireActivity()).observe(requireActivity(), songs -> { if (bind != null) { bind.playlistPagePlayButton.setOnClickListener(v -> { QueueRepository queueRepository = new QueueRepository(App.getInstance()); @@ -158,6 +166,6 @@ public class PlaylistPageFragment extends Fragment { songHorizontalAdapter = new SongHorizontalAdapter(activity, requireContext(), true); bind.songRecyclerView.setAdapter(songHorizontalAdapter); - playlistPageViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> songHorizontalAdapter.setItems(songs)); + playlistPageViewModel.getPlaylistSongLiveList(requireActivity()).observe(requireActivity(), songs -> songHorizontalAdapter.setItems(songs)); } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java index 95f0b5a8..d3dc0aee 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java @@ -140,9 +140,8 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements TextView removeAll = view.findViewById(R.id.remove_all_text_view); albumBottomSheetViewModel.getAlbumTracks().observe(requireActivity(), songs -> { - downloadAll.setOnClickListener(v -> { - DownloadUtil.getDownloadTracker(requireContext()).download(songs); + DownloadUtil.getDownloadTracker(requireContext()).download(songs, null, null); dismissBottomSheet(); }); diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java index 36c0cefa..9d62dc65 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java @@ -148,7 +148,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements TextView download = view.findViewById(R.id.download_text_view); download.setOnClickListener(v -> { - DownloadUtil.getDownloadTracker(requireContext()).download(Arrays.asList(song)); + DownloadUtil.getDownloadTracker(requireContext()).download(Arrays.asList(song), null, null); dismissBottomSheet(); }); diff --git a/app/src/main/java/com/cappielloantonio/play/util/MappingUtil.java b/app/src/main/java/com/cappielloantonio/play/util/MappingUtil.java index 0a104f9d..cd270d54 100644 --- a/app/src/main/java/com/cappielloantonio/play/util/MappingUtil.java +++ b/app/src/main/java/com/cappielloantonio/play/util/MappingUtil.java @@ -19,6 +19,7 @@ import com.cappielloantonio.play.subsonic.models.SimilarArtistID3; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class MappingUtil { public static ArrayList mapSong(List children) { @@ -144,18 +145,28 @@ public class MappingUtil { return artists; } + public static ArrayList mapDownloadToPlaylist(List downloads) { + ArrayList playlists = new ArrayList(); + + for (Download download : downloads) { + playlists.add(new Playlist(download.getPlaylistId(), download.getPlaylistName())); + } + + return playlists; + } + public static ArrayList mapToDownload(List songs) { ArrayList downloads = new ArrayList(); for (Song song : songs) { - downloads.add(new Download(song)); + downloads.add(new Download(song, null, null)); } return downloads; } - public static Download mapToDownload(Song song) { - return new Download(song); + public static Download mapToDownload(Song song, String playlistId, String playlistName) { + return new Download(song, playlistId, playlistName); } public static ArrayList mapGenre(List genreList) { diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/DownloadViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/DownloadViewModel.java index de4ea641..db4d3f10 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/DownloadViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/DownloadViewModel.java @@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData; import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Artist; +import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.repository.DownloadRepository; import com.cappielloantonio.play.util.MappingUtil; @@ -24,6 +25,7 @@ public class DownloadViewModel extends AndroidViewModel { private final MutableLiveData> downloadedArtistSample = new MutableLiveData<>(null); private final MutableLiveData> downloadedAlbumSample = new MutableLiveData<>(null); private final MutableLiveData> downloadedTrackSample = new MutableLiveData<>(null); + private final MutableLiveData> downloadedPlaylistSample = new MutableLiveData<>(null); public DownloadViewModel(@NonNull Application application) { super(application); @@ -32,17 +34,22 @@ public class DownloadViewModel extends AndroidViewModel { } public LiveData> getDownloadedArtists(LifecycleOwner owner, int size) { - downloadRepository.getLiveDownloadSample(size, true, false, false).observe(owner, downloads -> downloadedArtistSample.postValue(MappingUtil.mapDownloadToArtist(downloads))); + downloadRepository.getLiveDownloadSample(size, true, false, false, false).observe(owner, downloads -> downloadedArtistSample.postValue(MappingUtil.mapDownloadToArtist(downloads))); return downloadedArtistSample; } public LiveData> getDownloadedAlbums(LifecycleOwner owner, int size) { - downloadRepository.getLiveDownloadSample(size, false, true, false).observe(owner, downloads -> downloadedAlbumSample.postValue(MappingUtil.mapDownloadToAlbum(downloads))); + downloadRepository.getLiveDownloadSample(size, false, true, false, false).observe(owner, downloads -> downloadedAlbumSample.postValue(MappingUtil.mapDownloadToAlbum(downloads))); return downloadedAlbumSample; } public LiveData> getDownloadedTracks(LifecycleOwner owner, int size) { - downloadRepository.getLiveDownloadSample(size, false, false, true).observe(owner, downloads -> downloadedTrackSample.postValue(MappingUtil.mapDownloadToSong(downloads))); + downloadRepository.getLiveDownloadSample(size, false, false, true, false).observe(owner, downloads -> downloadedTrackSample.postValue(MappingUtil.mapDownloadToSong(downloads))); return downloadedTrackSample; } + + public LiveData> getDownloadedPlaylists(LifecycleOwner owner, int size) { + downloadRepository.getLiveDownloadSample(size, false, false, false, true).observe(owner, downloads -> downloadedPlaylistSample.postValue(MappingUtil.mapDownloadToPlaylist(downloads))); + return downloadedPlaylistSample; + } } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java index eac3554e..835bda2b 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java @@ -3,24 +3,57 @@ package com.cappielloantonio.play.viewmodel; import android.app.Application; import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.repository.DownloadRepository; import com.cappielloantonio.play.repository.PlaylistRepository; +import com.cappielloantonio.play.util.MappingUtil; +import java.util.ArrayList; import java.util.List; public class PlaylistCatalogueViewModel extends AndroidViewModel { private final PlaylistRepository playlistRepository; + private final DownloadRepository downloadRepository; + + private String type; + + private MutableLiveData> playlistList; public PlaylistCatalogueViewModel(@NonNull Application application) { super(application); playlistRepository = new PlaylistRepository(application); + downloadRepository = new DownloadRepository(application); } - public LiveData> getPlaylistList() { - return playlistRepository.getPlaylists(false, -1); + public LiveData> getPlaylistList(FragmentActivity activity) { + playlistList = new MutableLiveData<>(new ArrayList<>()); + + switch (type) { + case Playlist.ALL: + playlistRepository.getPlaylists(false, -1).observe(activity, playlists -> playlistList.postValue(playlists)); + break; + case Playlist.DOWNLOADED: + downloadRepository.getLivePlaylist().observe(activity, downloads -> playlistList.setValue(MappingUtil.mapDownloadToPlaylist(downloads))); + break; + } + + playlistRepository.getPlaylists(false, -1); + + return playlistList; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; } } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistPageViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistPageViewModel.java index 0193cdef..5b8f58e4 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistPageViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistPageViewModel.java @@ -3,28 +3,45 @@ package com.cappielloantonio.play.viewmodel; import android.app.Application; import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Playlist; import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.repository.DownloadRepository; import com.cappielloantonio.play.repository.PlaylistRepository; +import com.cappielloantonio.play.util.MappingUtil; import java.util.List; public class PlaylistPageViewModel extends AndroidViewModel { private final PlaylistRepository playlistRepository; + private final DownloadRepository downloadRepository; + + private MutableLiveData> playlistSongLiveList = new MutableLiveData<>(); private Playlist playlist; + private boolean isOffline; public PlaylistPageViewModel(@NonNull Application application) { super(application); playlistRepository = new PlaylistRepository(application); + downloadRepository = new DownloadRepository(application); } - public MutableLiveData> getPlaylistSongLiveList() { - return playlistRepository.getPlaylistSongs(playlist.getId()); + public LiveData> getPlaylistSongLiveList(FragmentActivity activity) { + if (isOffline) { + downloadRepository.getLiveDownloadFromPlaylist(playlist.getId()).observe(activity, downloads -> playlistSongLiveList.postValue(MappingUtil.mapDownloadToSong(downloads))); + } + else { + playlistSongLiveList = playlistRepository.getPlaylistSongs(playlist.getId()); + } + + return playlistSongLiveList; } public Playlist getPlaylist() { @@ -34,4 +51,12 @@ public class PlaylistPageViewModel extends AndroidViewModel { public void setPlaylist(Playlist playlist) { this.playlist = playlist; } + + public void setOffline(boolean offline) { + isOffline = offline; + } + + public boolean isOffline() { + return isOffline; + } } diff --git a/app/src/main/res/layout/fragment_download.xml b/app/src/main/res/layout/fragment_download.xml index c2f6ce31..1db03ee0 100644 --- a/app/src/main/res/layout/fragment_download.xml +++ b/app/src/main/res/layout/fragment_download.xml @@ -256,6 +256,61 @@ android:id="@+id/download_downloaded_tracks_placeholder" layout="@layout/item_placeholder_horizontal" android:visibility="gone" /> + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index bf532104..1976f35f 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -119,6 +119,12 @@ + +