From 72712d6feb15f2b5a523d7990e4e1bf7dd1e448c Mon Sep 17 00:00:00 2001 From: CappielloAntonio Date: Fri, 26 Nov 2021 15:57:36 +0100 Subject: [PATCH] Dynamically added views of pinned playlists to the homepage --- .../play/ui/fragment/HomeFragment.java | 60 ++++++++++++++++- .../cappielloantonio/play/util/CacheUtil.java | 66 +++++++++++++++++++ .../play/viewmodel/HomeViewModel.java | 18 +++++ .../res/layout/generic_playlist_sector.xml | 48 ++++++++++++++ .../res/layout/item_home_similar_track.xml | 13 +--- app/src/main/res/navigation/nav_graph.xml | 3 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 195 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/cappielloantonio/play/util/CacheUtil.java create mode 100644 app/src/main/res/layout/generic_playlist_sector.xml diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java index 181524c0..21a80ac6 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java @@ -7,6 +7,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,6 +17,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.PagerSnapHelper; +import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SnapHelper; import androidx.viewpager2.widget.ViewPager2; @@ -26,15 +28,16 @@ import com.cappielloantonio.play.adapter.ArtistHorizontalAdapter; import com.cappielloantonio.play.adapter.DiscoverSongAdapter; import com.cappielloantonio.play.adapter.SimilarTrackAdapter; import com.cappielloantonio.play.adapter.SongHorizontalAdapter; -import com.cappielloantonio.play.adapter.TrackAdapter; import com.cappielloantonio.play.adapter.YearAdapter; 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.MusicUtil; import com.cappielloantonio.play.util.UIUtil; import com.cappielloantonio.play.viewmodel.HomeViewModel; @@ -97,6 +100,7 @@ public class HomeFragment extends Fragment { initStarredArtistsView(); initYearSongView(); initRecentAddedAlbumView(); + initPinnedPlaylistsView(); } @Override @@ -479,7 +483,59 @@ public class HomeFragment extends Fragment { } } - public void addPinnedPlaylistsView() { + public void initPinnedPlaylistsView() { + homeViewModel.getPinnedPlaylistList(requireActivity()).observe(requireActivity(), playlists -> { + if (bind != null && playlists != null) { + for (Playlist playlist : playlists) { + int playlistViewHashCode = playlist.getId().hashCode(); + if (requireView().findViewById(playlistViewHashCode) == null) { + View genericPlaylistView = activity.getLayoutInflater().inflate(R.layout.generic_playlist_sector, null); + genericPlaylistView.setId(playlistViewHashCode); + genericPlaylistView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + TextView genericPlaylistTitleTextView = genericPlaylistView.findViewById(R.id.generic_playlist_title_text_view); + TextView genericPlaylistCickableTextView = genericPlaylistView.findViewById(R.id.generic_playlist_text_view_clickable); + RecyclerView genericPlaylistRecyclerView = genericPlaylistView.findViewById(R.id.generic_playlist_recycler_view); + + genericPlaylistTitleTextView.setText(MusicUtil.getReadableString(playlist.getName())); + genericPlaylistRecyclerView.setHasFixedSize(true); + + SongHorizontalAdapter trackAdapter = new SongHorizontalAdapter(activity, requireContext(), true); + genericPlaylistRecyclerView.setAdapter(trackAdapter); + + homeViewModel.getPlaylistSongLiveList(playlist.getId()).observe(requireActivity(), songs -> { + if (songs.size() > 0) { + int songsNumber = Math.min(20, songs.size()); + + genericPlaylistRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(songsNumber, 5), GridLayoutManager.HORIZONTAL, false)); + trackAdapter.setItems(songs.subList(0, songsNumber)); + } + }); + + genericPlaylistCickableTextView.setOnClickListener(view -> { + Bundle bundle = new Bundle(); + bundle.putParcelable("playlist_object", playlist); + bundle.putBoolean("is_offline", false); + activity.navController.navigate(R.id.action_homeFragment_to_playlistPageFragment, bundle); + }); + + SnapHelper genericPlaylistSnapHelper = new PagerSnapHelper(); + genericPlaylistSnapHelper.attachToRecyclerView(genericPlaylistRecyclerView); + + genericPlaylistRecyclerView.addItemDecoration( + new DotsIndicatorDecoration( + getResources().getDimensionPixelSize(R.dimen.radius), + getResources().getDimensionPixelSize(R.dimen.radius) * 4, + getResources().getDimensionPixelSize(R.dimen.dots_height), + requireContext().getResources().getColor(R.color.titleTextColor, null), + requireContext().getResources().getColor(R.color.titleTextColor, null)) + ); + + + bind.homeLinearLayoutContainer.addView(genericPlaylistView); + } + } + } + }); } } diff --git a/app/src/main/java/com/cappielloantonio/play/util/CacheUtil.java b/app/src/main/java/com/cappielloantonio/play/util/CacheUtil.java new file mode 100644 index 00000000..f03d2d39 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/util/CacheUtil.java @@ -0,0 +1,66 @@ +package com.cappielloantonio.play.util; + +import android.content.Context; + +import com.google.android.exoplayer2.database.DatabaseProvider; +import com.google.android.exoplayer2.database.ExoDatabaseProvider; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.SimpleCache; + +import java.io.File; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookiePolicy; + +public final class CacheUtil { + + private static final String CACHE_CONTENT_DIRECTORY = "cache"; + + private static HttpDataSource.Factory httpDataSourceFactory; + private static File cacheDirectory; + private static SimpleCache simpleCache; + private static ExoDatabaseProvider databaseProvider; + + public static synchronized HttpDataSource.Factory getHttpDataSourceFactory() { + if (httpDataSourceFactory == null) { + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); + CookieHandler.setDefault(cookieManager); + httpDataSourceFactory = new DefaultHttpDataSourceFactory(); + } + return httpDataSourceFactory; + } + + public static synchronized SimpleCache getCache(Context context) { + if (simpleCache == null) { + File downloadContentDirectory = new File(getCacheDirectory(context), CACHE_CONTENT_DIRECTORY); + + long cacheSize = PreferenceUtil.getInstance(context).getMediaCacheSize(); + LeastRecentlyUsedCacheEvictor cacheEvictor = new LeastRecentlyUsedCacheEvictor(cacheSize); + ExoDatabaseProvider databaseProvider = getDatabaseProvider(context); + + simpleCache = new SimpleCache(downloadContentDirectory, cacheEvictor, databaseProvider); + } + return simpleCache; + } + + private static synchronized ExoDatabaseProvider getDatabaseProvider(Context context) { + if (databaseProvider == null) { + databaseProvider = new ExoDatabaseProvider(context); + } + return databaseProvider; + } + + private static synchronized File getCacheDirectory(Context context) { + if (cacheDirectory == null) { + cacheDirectory = context.getExternalFilesDir(null); + if (cacheDirectory == null) { + cacheDirectory = context.getFilesDir(); + } + } + return cacheDirectory; + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/HomeViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/HomeViewModel.java index 63f3148c..33db5f52 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/HomeViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/HomeViewModel.java @@ -3,6 +3,7 @@ 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.LifecycleOwner; import androidx.lifecycle.LiveData; @@ -10,12 +11,17 @@ 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.AlbumRepository; import com.cappielloantonio.play.repository.ArtistRepository; +import com.cappielloantonio.play.repository.PlaylistRepository; import com.cappielloantonio.play.repository.SongRepository; +import com.cappielloantonio.play.util.MappingUtil; +import java.util.HashSet; import java.util.List; +import java.util.TreeSet; public class HomeViewModel extends AndroidViewModel { private static final String TAG = "HomeViewModel"; @@ -23,6 +29,7 @@ public class HomeViewModel extends AndroidViewModel { private final SongRepository songRepository; private final AlbumRepository albumRepository; private final ArtistRepository artistRepository; + private final PlaylistRepository playlistRepository; private final MutableLiveData> dicoverSongSample = new MutableLiveData<>(null); private final MutableLiveData> starredTracksSample = new MutableLiveData<>(null); @@ -34,6 +41,7 @@ public class HomeViewModel extends AndroidViewModel { private final MutableLiveData> starredAlbums = new MutableLiveData<>(null); private final MutableLiveData> starredArtists = new MutableLiveData<>(null); private final MutableLiveData> recentlyAddedAlbumSample = new MutableLiveData<>(null); + private final MutableLiveData> pinnedPlaylists = new MutableLiveData<>(null); public HomeViewModel(@NonNull Application application) { super(application); @@ -41,6 +49,7 @@ public class HomeViewModel extends AndroidViewModel { songRepository = new SongRepository(application); albumRepository = new AlbumRepository(application); artistRepository = new ArtistRepository(application); + playlistRepository = new PlaylistRepository(application); songRepository.getRandomSample(10, null, null).observeForever(dicoverSongSample::postValue); songRepository.getStarredSongs(true, 10).observeForever(starredTracksSample::postValue); @@ -89,6 +98,15 @@ public class HomeViewModel extends AndroidViewModel { return recentlyPlayedAlbumSample; } + public LiveData> getPinnedPlaylistList(LifecycleOwner owner) { + playlistRepository.getPinnedPlaylists().observe(owner, pinnedPlaylists::postValue); + return pinnedPlaylists; + } + + public LiveData> getPlaylistSongLiveList(String playlistId) { + return playlistRepository.getPlaylistSongs(playlistId); + } + public void refreshDiscoverySongSample(LifecycleOwner owner) { songRepository.getRandomSample(10, null, null).observe(owner, dicoverSongSample::postValue); } diff --git a/app/src/main/res/layout/generic_playlist_sector.xml b/app/src/main/res/layout/generic_playlist_sector.xml new file mode 100644 index 00000000..2b9bd638 --- /dev/null +++ b/app/src/main/res/layout/generic_playlist_sector.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_similar_track.xml b/app/src/main/res/layout/item_home_similar_track.xml index 0a5d499a..08fd459c 100644 --- a/app/src/main/res/layout/item_home_similar_track.xml +++ b/app/src/main/res/layout/item_home_similar_track.xml @@ -14,16 +14,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - \ 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 4343cc4e..b2156df1 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -62,6 +62,9 @@ + Select two or more filters Filter Filter Genres + See all Genre Catalogue Browse Genres Start mix from a song you liked