Skip to content

Commit

Permalink
feat: added the ability to pin playlists to the home screen
Browse files Browse the repository at this point in the history
  • Loading branch information
CappielloAntonio committed Jun 8, 2024
1 parent 078aa87 commit fb7296b
Show file tree
Hide file tree
Showing 12 changed files with 1,288 additions and 8 deletions.
1,065 changes: 1,065 additions & 0 deletions app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
import com.cappielloantonio.tempo.database.dao.DownloadDao;
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
import com.cappielloantonio.tempo.database.dao.PlaylistDao;
import com.cappielloantonio.tempo.database.dao.QueueDao;
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
import com.cappielloantonio.tempo.database.dao.ServerDao;
Expand All @@ -23,12 +24,13 @@
import com.cappielloantonio.tempo.model.RecentSearch;
import com.cappielloantonio.tempo.model.Server;
import com.cappielloantonio.tempo.model.SessionMediaItem;
import com.cappielloantonio.tempo.subsonic.models.Playlist;

@UnstableApi
@Database(
version = 9,
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class},
autoMigrations = {@AutoMigration(from = 8, to = 9)}
version = 10,
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class, Playlist.class},
autoMigrations = {@AutoMigration(from = 9, to = 10)}
)
@TypeConverters({DateConverters.class})
public abstract class AppDatabase extends RoomDatabase {
Expand Down Expand Up @@ -58,4 +60,6 @@ public static synchronized AppDatabase getInstance() {
public abstract FavoriteDao favoriteDao();

public abstract SessionMediaItemDao sessionMediaItemDao();

public abstract PlaylistDao playlistDao();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.cappielloantonio.tempo.repository;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.cappielloantonio.tempo.App;
import com.cappielloantonio.tempo.database.AppDatabase;
import com.cappielloantonio.tempo.database.dao.PlaylistDao;
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.Playlist;
Expand All @@ -17,6 +20,7 @@
import retrofit2.Response;

public class PlaylistRepository {
private final PlaylistDao playlistDao = AppDatabase.getInstance().playlistDao();
public MutableLiveData<List<Playlist>> getPlaylists(boolean random, int size) {
MutableLiveData<List<Playlist>> listLivePlaylists = new MutableLiveData<>(new ArrayList<>());

Expand Down Expand Up @@ -153,4 +157,50 @@ public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
}
});
}

public LiveData<List<Playlist>> getPinnedPlaylists() {
return playlistDao.getAll();
}

public void insert(Playlist playlist) {
InsertThreadSafe insert = new InsertThreadSafe(playlistDao, playlist);
Thread thread = new Thread(insert);
thread.start();
}

public void delete(Playlist playlist) {
DeleteThreadSafe delete = new DeleteThreadSafe(playlistDao, playlist);
Thread thread = new Thread(delete);
thread.start();
}

private static class InsertThreadSafe implements Runnable {
private final PlaylistDao playlistDao;
private final Playlist playlist;

public InsertThreadSafe(PlaylistDao playlistDao, Playlist playlist) {
this.playlistDao = playlistDao;
this.playlist = playlist;
}

@Override
public void run() {
playlistDao.insert(playlist);
}
}

private static class DeleteThreadSafe implements Runnable {
private final PlaylistDao playlistDao;
private final Playlist playlist;

public DeleteThreadSafe(PlaylistDao playlistDao, Playlist playlist) {
this.playlistDao = playlistDao;
this.playlist = playlist;
}

@Override
public void run() {
playlistDao.delete(playlist);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration;
import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
import com.cappielloantonio.tempo.model.Download;
import com.cappielloantonio.tempo.model.HomeSector;
import com.cappielloantonio.tempo.service.DownloaderManager;
Expand All @@ -44,12 +45,13 @@
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.DiscoverSongAdapter;
import com.cappielloantonio.tempo.ui.adapter.GridTrackAdapter;
import com.cappielloantonio.tempo.ui.adapter.PlaylistHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.ShareHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter;
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
import com.cappielloantonio.tempo.ui.dialog.HomeRearrangementDialog;
import com.cappielloantonio.tempo.ui.dialog.PlaylistEditorDialog;
import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.MappingUtil;
Expand Down Expand Up @@ -85,7 +87,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
private AlbumAdapter mostPlayedAlbumAdapter;
private AlbumHorizontalAdapter newReleasesAlbumAdapter;
private YearAdapter yearAdapter;
private GridTrackAdapter gridTrackAdapter;
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
private ShareHorizontalAdapter shareHorizontalAdapter;

private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
Expand Down Expand Up @@ -122,6 +124,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
initYearSongView();
initRecentAddedAlbumView();
initTopSongsView();
initPinnedPlaylistsView();
initSharesView();
initHomeReorganizer();

Expand Down Expand Up @@ -422,7 +425,8 @@ private void initTopSongsView() {
} else {
if (bind != null) bind.homeGridTracksSector.setVisibility(View.VISIBLE);
if (bind != null) bind.afterGridDivider.setVisibility(View.VISIBLE);
if (bind != null) bind.topSongsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(chronologies.size(), 5), GridLayoutManager.HORIZONTAL, false));
if (bind != null)
bind.topSongsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), UIUtil.getSpanCount(chronologies.size(), 5), GridLayoutManager.HORIZONTAL, false));

List<Child> topSongs = chronologies.stream()
.map(cronologia -> (Child) cronologia)
Expand Down Expand Up @@ -671,6 +675,26 @@ private void initRecentAddedAlbumView() {
recentAddedAlbumSnapHelper.attachToRecyclerView(bind.recentlyAddedAlbumsRecyclerView);
}

private void initPinnedPlaylistsView() {
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_PINNED_PLAYLISTS)) return;

bind.pinnedPlaylistsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.pinnedPlaylistsRecyclerView.setHasFixedSize(true);

playlistHorizontalAdapter = new PlaylistHorizontalAdapter(this);
bind.pinnedPlaylistsRecyclerView.setAdapter(playlistHorizontalAdapter);
homeViewModel.getPinnedPlaylists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), playlists -> {
if (playlists == null) {
if (bind != null) bind.pinnedPlaylistsSector.setVisibility(View.GONE);
} else {
if (bind != null)
bind.pinnedPlaylistsSector.setVisibility(!playlists.isEmpty() ? View.VISIBLE : View.GONE);

playlistHorizontalAdapter.setItems(playlists);
}
});
}

private void initSharesView() {
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_SHARED)) return;

Expand Down Expand Up @@ -708,7 +732,9 @@ private void initSharesView() {

private void initHomeReorganizer() {
final Handler handler = new Handler();
final Runnable runnable = () -> { if (bind != null) bind.homeSectorRearrangementButton.setVisibility(View.VISIBLE); };
final Runnable runnable = () -> {
if (bind != null) bind.homeSectorRearrangementButton.setVisibility(View.VISIBLE);
};
handler.postDelayed(runnable, 5000);

bind.homeSectorRearrangementButton.setOnClickListener(v -> {
Expand Down Expand Up @@ -789,6 +815,9 @@ public void reorder() {
case Constants.HOME_SECTOR_RECENTLY_ADDED:
bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector);
break;
case Constants.HOME_SECTOR_PINNED_PLAYLISTS:
bind.homeLinearLayoutContainer.addView(bind.pinnedPlaylistsSector);
break;
case Constants.HOME_SECTOR_SHARED:
bind.homeLinearLayoutContainer.addView(bind.sharesSector);
break;
Expand Down Expand Up @@ -824,6 +853,17 @@ private void showPopupMenu(View view, int menuResource) {
popup.show();
}

private void refreshPlaylistView() {
final Handler handler = new Handler();

final Runnable runnable = () -> {
if (getView() != null && bind != null && homeViewModel != null)
homeViewModel.getPinnedPlaylists(getViewLifecycleOwner());
};

handler.postDelayed(runnable, 100);
}

private void initializeMediaBrowser() {
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
}
Expand Down Expand Up @@ -922,6 +962,24 @@ public void onShareClick(Bundle bundle) {
startActivity(intent);
}

@Override
public void onPlaylistClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.playlistPageFragment, bundle);
}

@Override
public void onPlaylistLongClick(Bundle bundle) {
PlaylistEditorDialog dialog = new PlaylistEditorDialog(new PlaylistCallback() {
@Override
public void onDismiss() {
refreshPlaylistView();
}
});

dialog.setArguments(bundle);
dialog.show(activity.getSupportFragmentManager(), null);
}

@Override
public void onShareLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.playlist_page_menu, menu);
initMenuOption(menu);
}

@Override
Expand Down Expand Up @@ -115,6 +116,12 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
}
});
return true;
} else if (item.getItemId() == R.id.action_pin_playlist) {
playlistPageViewModel.setPinned(true);
return true;
} else if (item.getItemId() == R.id.action_unpin_playlist) {
playlistPageViewModel.setPinned(false);
return true;
}

return false;
Expand All @@ -124,6 +131,13 @@ private void init() {
playlistPageViewModel.setPlaylist(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT));
}

private void initMenuOption(Menu menu) {
playlistPageViewModel.isPinned(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), isPinned -> {
menu.findItem(R.id.action_unpin_playlist).setVisible(isPinned);
menu.findItem(R.id.action_pin_playlist).setVisible(!isPinned);
});
}

private void initAppBar() {
activity.setSupportActionBar(bind.animToolbar);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,6 @@ object Constants {
const val HOME_SECTOR_MOST_PLAYED = "HOME_SECTOR_MOST_PLAYED"
const val HOME_SECTOR_LAST_PLAYED = "HOME_SECTOR_LAST_PLAYED"
const val HOME_SECTOR_RECENTLY_ADDED = "HOME_SECTOR_RECENTLY_ADDED"
const val HOME_SECTOR_PINNED_PLAYLISTS = "HOME_SECTOR_PINNED_PLAYLISTS"
const val HOME_SECTOR_SHARED = "HOME_SECTOR_SHARED"
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ private List<HomeSector> fillStandardHomeSectorList() {
sectors.add(new HomeSector(Constants.HOME_SECTOR_MOST_PLAYED, getApplication().getString(R.string.home_title_most_played), true, 11));
sectors.add(new HomeSector(Constants.HOME_SECTOR_LAST_PLAYED, getApplication().getString(R.string.home_title_last_played), true, 12));
sectors.add(new HomeSector(Constants.HOME_SECTOR_RECENTLY_ADDED, getApplication().getString(R.string.home_title_recently_added), true, 13));
sectors.add(new HomeSector(Constants.HOME_SECTOR_SHARED, getApplication().getString(R.string.home_title_shares), true, 14));
sectors.add(new HomeSector(Constants.HOME_SECTOR_PINNED_PLAYLISTS, getApplication().getString(R.string.home_title_pinned_playlists), true, 14));
sectors.add(new HomeSector(Constants.HOME_SECTOR_SHARED, getApplication().getString(R.string.home_title_shares), true, 15));

return sectors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
import com.cappielloantonio.tempo.repository.ArtistRepository;
import com.cappielloantonio.tempo.repository.ChronologyRepository;
import com.cappielloantonio.tempo.repository.FavoriteRepository;
import com.cappielloantonio.tempo.repository.PlaylistRepository;
import com.cappielloantonio.tempo.repository.SharingRepository;
import com.cappielloantonio.tempo.repository.SongRepository;
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.Playlist;
import com.cappielloantonio.tempo.subsonic.models.Share;
import com.cappielloantonio.tempo.util.Preferences;
import com.google.common.reflect.TypeToken;
Expand All @@ -32,6 +34,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

public class HomeViewModel extends AndroidViewModel {
private static final String TAG = "HomeViewModel";
Expand All @@ -41,6 +44,7 @@ public class HomeViewModel extends AndroidViewModel {
private final ArtistRepository artistRepository;
private final ChronologyRepository chronologyRepository;
private final FavoriteRepository favoriteRepository;
private final PlaylistRepository playlistRepository;
private final SharingRepository sharingRepository;

private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
Expand All @@ -60,6 +64,7 @@ public class HomeViewModel extends AndroidViewModel {
private final MutableLiveData<List<Child>> mediaInstantMix = new MutableLiveData<>(null);
private final MutableLiveData<List<Child>> artistInstantMix = new MutableLiveData<>(null);
private final MutableLiveData<List<Child>> artistBestOf = new MutableLiveData<>(null);
private final MutableLiveData<List<Playlist>> pinnedPlaylists = new MutableLiveData<>(null);
private final MutableLiveData<List<Share>> shares = new MutableLiveData<>(null);

private List<HomeSector> sectors;
Expand All @@ -74,6 +79,7 @@ public HomeViewModel(@NonNull Application application) {
artistRepository = new ArtistRepository();
chronologyRepository = new ChronologyRepository();
favoriteRepository = new FavoriteRepository();
playlistRepository = new PlaylistRepository();
sharingRepository = new SharingRepository();

setOfflineFavorite();
Expand Down Expand Up @@ -224,6 +230,24 @@ public LiveData<List<Child>> getArtistBestOf(LifecycleOwner owner, ArtistID3 art
return artistBestOf;
}

public LiveData<List<Playlist>> getPinnedPlaylists(LifecycleOwner owner) {
pinnedPlaylists.setValue(Collections.emptyList());

playlistRepository.getPlaylists(false, -1).observe(owner, remotes -> {
playlistRepository.getPinnedPlaylists().observe(owner, locals -> {
if (remotes != null && locals != null) {
List<Playlist> toReturn = remotes.stream()
.filter(remote -> locals.stream().anyMatch(local -> local.getId().equals(remote.getId())))
.collect(Collectors.toList());

pinnedPlaylists.setValue(toReturn);
}
});
});

return pinnedPlaylists;
}

public LiveData<List<Share>> getShares(LifecycleOwner owner) {
if (shares.getValue() == null) {
sharingRepository.getShares().observe(owner, shares::postValue);
Expand Down
Loading

0 comments on commit fb7296b

Please sign in to comment.