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 f8f78261..667f47f8 100644 --- a/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java +++ b/app/src/main/java/com/cappielloantonio/play/adapter/PlaylistAdapter.java @@ -5,14 +5,17 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.TextView; import androidx.navigation.Navigation; import androidx.recyclerview.widget.RecyclerView; import com.cappielloantonio.play.R; +import com.cappielloantonio.play.glide.CustomGlideRequest; import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.ui.activities.MainActivity; import java.util.ArrayList; import java.util.List; @@ -23,11 +26,13 @@ public class PlaylistAdapter extends RecyclerView.Adapter playlists; private LayoutInflater mInflater; private Context context; + private MainActivity activity; - public PlaylistAdapter(Context context) { + public PlaylistAdapter(MainActivity activity, Context context, List playlists) { + this.activity = activity; this.context = context; this.mInflater = LayoutInflater.from(context); - this.playlists = new ArrayList<>(); + this.playlists = playlists; } @Override @@ -41,6 +46,11 @@ public class PlaylistAdapter extends RecyclerView.Adapter { + + private List playlists; + private LayoutInflater mInflater; + private Context context; + + public PlaylistCatalogueAdapter(Context context) { + this.context = context; + this.mInflater = LayoutInflater.from(context); + this.playlists = new ArrayList<>(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.item_library_catalogue_playlist, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Playlist playlist = playlists.get(position); + + holder.textPlaylistName.setText(playlist.getName()); + + CustomGlideRequest.Builder + .from(context, playlist.getPrimary(), playlist.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY, CustomGlideRequest.PLAYLIST_PIC) + .build() + .into(holder.cover); + } + + @Override + public int getItemCount() { + return playlists.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + TextView textPlaylistName; + ImageView cover; + + ViewHolder(View itemView) { + super(itemView); + + textPlaylistName = itemView.findViewById(R.id.playlist_name_text); + cover = itemView.findViewById(R.id.playlist_cover_image_view); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + Bundle bundle = new Bundle(); + bundle.putParcelable("playlist_object", playlists.get(getBindingAdapterPosition())); + Navigation.findNavController(view).navigate(R.id.action_playlistCatalogueFragment_to_playlistPageFragment, bundle); + + } + } + + public Playlist getItem(int position) { + return playlists.get(position); + } + + public void setItems(List playlists) { + this.playlists = playlists; + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/database/dao/PlaylistDao.java b/app/src/main/java/com/cappielloantonio/play/database/dao/PlaylistDao.java index e1f026e6..06f64cef 100644 --- a/app/src/main/java/com/cappielloantonio/play/database/dao/PlaylistDao.java +++ b/app/src/main/java/com/cappielloantonio/play/database/dao/PlaylistDao.java @@ -8,6 +8,7 @@ import androidx.room.OnConflictStrategy; import androidx.room.Query; import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.model.Song; import java.util.List; @@ -21,4 +22,7 @@ public interface PlaylistDao { @Query("DELETE FROM playlist") void deleteAll(); + + @Query("SELECT * FROM playlist ORDER BY RANDOM() LIMIT :number") + List random(int number); } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/repository/PlaylistRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/PlaylistRepository.java index d45f1dd5..2c649fdc 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/PlaylistRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/PlaylistRepository.java @@ -6,7 +6,9 @@ import androidx.lifecycle.LiveData; import com.cappielloantonio.play.database.AppDatabase; import com.cappielloantonio.play.database.dao.PlaylistDao; +import com.cappielloantonio.play.database.dao.SongDao; import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.model.Song; import java.util.ArrayList; import java.util.List; @@ -65,4 +67,41 @@ public class PlaylistRepository { playlistDao.deleteAll(); } } + + public List getRandomSample(int number) { + List sample = new ArrayList<>(); + + PickRandomThreadSafe randomThread = new PickRandomThreadSafe(playlistDao, number); + Thread thread = new Thread(randomThread); + thread.start(); + + try { + thread.join(); + sample = randomThread.getSample(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return sample; + } + + private static class PickRandomThreadSafe implements Runnable { + private PlaylistDao playlistDao; + private int elementNumber; + private List sample; + + public PickRandomThreadSafe(PlaylistDao playlistDao, int number) { + this.playlistDao = playlistDao; + this.elementNumber = number; + } + + @Override + public void run() { + sample = playlistDao.random(elementNumber); + } + + public List getSample() { + return sample; + } + } } diff --git a/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java b/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java index 8267c9c5..ab08d9bc 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/activities/MainActivity.java @@ -244,7 +244,7 @@ public class MainActivity extends BaseActivity { if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) { navController.navigate(R.id.action_landingFragment_to_homeFragment); } else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.syncFragment) { - navController.navigate(R.id.action_syncFragment_to_libraryFragment); + navController.navigate(R.id.action_syncFragment_to_homeFragment); } else if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.loginFragment) { navController.navigate(R.id.action_loginFragment_to_homeFragment); } 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 e3deef9b..0b35c220 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 @@ -1,6 +1,7 @@ package com.cappielloantonio.play.ui.fragment; import android.os.Bundle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -9,19 +10,23 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.viewpager2.widget.ViewPager2; import com.cappielloantonio.play.R; import com.cappielloantonio.play.adapter.AlbumAdapter; import com.cappielloantonio.play.adapter.ArtistAdapter; +import com.cappielloantonio.play.adapter.DiscoverSongAdapter; import com.cappielloantonio.play.adapter.GenreAdapter; import com.cappielloantonio.play.adapter.PlaylistAdapter; import com.cappielloantonio.play.databinding.FragmentLibraryBinding; import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.ui.activities.MainActivity; +import com.cappielloantonio.play.util.MusicUtil; import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.viewmodel.LibraryViewModel; @@ -58,7 +63,7 @@ public class LibraryFragment extends Fragment { initAlbumView(); initArtistView(); initGenreView(); - initPlaylistView(); + initPlaylistSlideView(); initCatalogueSyncCheck(); } @@ -78,6 +83,7 @@ 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)); } private void initAlbumView() { @@ -115,16 +121,15 @@ public class LibraryFragment extends Fragment { }); } - private void initPlaylistView() { - bind.playlistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); - bind.playlistRecyclerView.setHasFixedSize(true); + private void initPlaylistSlideView() { + bind.playlistViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); - playlistAdapter = new PlaylistAdapter(requireContext()); - bind.playlistRecyclerView.setAdapter(playlistAdapter); - libraryViewModel.getPlaylistList().observe(requireActivity(), playlists -> { - if(bind != null) bind.libraryPlaylistSector.setVisibility(playlists.size() > 0 ? View.VISIBLE : View.GONE); - playlistAdapter.setItems(playlists); - }); + playlistAdapter = new PlaylistAdapter(activity, requireContext(), libraryViewModel.getPlaylistSample()); + bind.playlistViewPager.setAdapter(playlistAdapter); + bind.playlistViewPager.setOffscreenPageLimit(3); + setDiscoverSongSlideViewOffset(20, 16); + + Log.i(TAG, "initDiscoverSongSlideView: " + MusicUtil.getRandomSongNumber(requireContext(), 10, 3)); } private void initCatalogueSyncCheck() { @@ -137,4 +142,19 @@ public class LibraryFragment extends Fragment { .show(); } } + + private void setDiscoverSongSlideViewOffset(float pageOffset, float pageMargin) { + bind.playlistViewPager.setPageTransformer((page, position) -> { + float myOffset = position * -(2 * pageOffset + pageMargin); + if (bind.playlistViewPager.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) { + if (ViewCompat.getLayoutDirection(bind.playlistViewPager) == ViewCompat.LAYOUT_DIRECTION_RTL) { + page.setTranslationX(-myOffset); + } else { + page.setTranslationX(myOffset); + } + } else { + page.setTranslationY(myOffset); + } + }); + } } 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 new file mode 100644 index 00000000..fcb8c052 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlaylistCatalogueFragment.java @@ -0,0 +1,71 @@ +package com.cappielloantonio.play.ui.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.adapter.GenreCatalogueAdapter; +import com.cappielloantonio.play.adapter.PlaylistAdapter; +import com.cappielloantonio.play.adapter.PlaylistCatalogueAdapter; +import com.cappielloantonio.play.databinding.FragmentGenreCatalogueBinding; +import com.cappielloantonio.play.databinding.FragmentPlaylistCatalogueBinding; +import com.cappielloantonio.play.helper.recyclerview.GridItemDecoration; +import com.cappielloantonio.play.model.Song; +import com.cappielloantonio.play.ui.activities.MainActivity; +import com.cappielloantonio.play.viewmodel.GenreCatalogueViewModel; +import com.cappielloantonio.play.viewmodel.PlaylistCatalogueViewModel; + +import java.util.ArrayList; + +public class PlaylistCatalogueFragment extends Fragment { + private static final String TAG = "GenreCatalogueFragment";; + + private FragmentPlaylistCatalogueBinding bind; + private MainActivity activity; + private PlaylistCatalogueViewModel playlistCatalogueViewModel; + + private PlaylistCatalogueAdapter playlistCatalogueAdapter; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + activity = (MainActivity) getActivity(); + + bind = FragmentPlaylistCatalogueBinding.inflate(inflater, container, false); + View view = bind.getRoot(); + playlistCatalogueViewModel = new ViewModelProvider(requireActivity()).get(PlaylistCatalogueViewModel.class); + + initArtistCatalogueView(); + + return view; + } + + @Override + public void onStart() { + super.onStart(); + activity.setBottomNavigationBarVisibility(false); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + bind = null; + } + + private void initArtistCatalogueView() { + bind.playlistCatalogueRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + bind.playlistCatalogueRecyclerView.setHasFixedSize(true); + + playlistCatalogueAdapter = new PlaylistCatalogueAdapter(requireContext()); + bind.playlistCatalogueRecyclerView.setAdapter(playlistCatalogueAdapter); + playlistCatalogueViewModel.getPlaylistList().observe(requireActivity(), playlist -> { + playlistCatalogueAdapter.setItems(playlist); + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/GenreCatalogueViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/GenreCatalogueViewModel.java index 8f663d83..a8c1e9c8 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/GenreCatalogueViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/GenreCatalogueViewModel.java @@ -28,8 +28,4 @@ public class GenreCatalogueViewModel extends AndroidViewModel { genreList = genreRepository.getListLiveGenres(); return genreList; } - - public List getGenres() { - return genreRepository.getListGenre(); - } } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/LibraryViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/LibraryViewModel.java index 86ec6a71..f4d880e4 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/LibraryViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/LibraryViewModel.java @@ -10,6 +10,7 @@ import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Genre; import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.repository.AlbumRepository; import com.cappielloantonio.play.repository.ArtistRepository; import com.cappielloantonio.play.repository.GenreRepository; @@ -24,10 +25,10 @@ public class LibraryViewModel extends AndroidViewModel { private GenreRepository genreRepository; private PlaylistRepository playlistRepository; + private List playlistSample; private LiveData> sampleAlbum; private LiveData> sampleArtist; private LiveData> sampleGenres; - private LiveData> allPlaylist; public LibraryViewModel(@NonNull Application application) { super(application); @@ -42,11 +43,15 @@ public class LibraryViewModel extends AndroidViewModel { sampleAlbum = albumRepository.getListLiveSampleAlbum(); sampleArtist = artistRepository.getListLiveSampleArtist(); sampleGenres = genreRepository.getListLiveSampleGenre(); - allPlaylist = playlistRepository.getListLivePlaylists(); + playlistSample = playlistRepository.getRandomSample(5); } - public LiveData> getPlaylistList() { - return allPlaylist; + public List getPlaylistSample() { + if(playlistSample.isEmpty()) { + playlistSample = playlistRepository.getRandomSample(5); + } + + return playlistSample; } public LiveData> getAlbumSample() { diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java new file mode 100644 index 00000000..a88b3d1b --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PlaylistCatalogueViewModel.java @@ -0,0 +1,31 @@ +package com.cappielloantonio.play.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; + +import com.cappielloantonio.play.model.Genre; +import com.cappielloantonio.play.model.Playlist; +import com.cappielloantonio.play.repository.GenreRepository; +import com.cappielloantonio.play.repository.PlaylistRepository; + +import java.util.List; + +public class PlaylistCatalogueViewModel extends AndroidViewModel { + private PlaylistRepository playlistRepository; + + private LiveData> playlistList; + + public PlaylistCatalogueViewModel(@NonNull Application application) { + super(application); + + playlistRepository = new PlaylistRepository(application); + } + + public LiveData> getPlaylistList() { + playlistList = playlistRepository.getListLivePlaylists(); + return playlistList; + } +} diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 7e57ba7c..553fcb47 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -238,6 +238,7 @@ - + diff --git a/app/src/main/res/layout/fragment_playlist_catalogue.xml b/app/src/main/res/layout/fragment_playlist_catalogue.xml new file mode 100644 index 00000000..2ee599fc --- /dev/null +++ b/app/src/main/res/layout/fragment_playlist_catalogue.xml @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/item_library_catalogue_playlist.xml b/app/src/main/res/layout/item_library_catalogue_playlist.xml new file mode 100644 index 00000000..a52b268c --- /dev/null +++ b/app/src/main/res/layout/item_library_catalogue_playlist.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_library_playlist.xml b/app/src/main/res/layout/item_library_playlist.xml index b1ed9424..5fd2e60e 100644 --- a/app/src/main/res/layout/item_library_playlist.xml +++ b/app/src/main/res/layout/item_library_playlist.xml @@ -1,37 +1,42 @@ - + + android:layout_height="match_parent" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:backgroundTint="@color/cardColor" + app:cardCornerRadius="4dp"> - + android:layout_height="196dp" /> - + - - - - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index b21c6a11..a37dfbcf 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -89,6 +89,9 @@ + @@ -172,6 +175,15 @@ android:id="@+id/action_genreCatalogueFragment_to_filterFragment" app:destination="@id/filterFragment" /> + + +