feat: added the ability to pin playlists to the home screen

This commit is contained in:
CappielloAntonio 2024-06-08 18:53:58 +02:00
parent 078aa87521
commit fb7296b467
12 changed files with 1288 additions and 8 deletions

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@ import com.cappielloantonio.tempo.database.converter.DateConverters;
import com.cappielloantonio.tempo.database.dao.ChronologyDao; import com.cappielloantonio.tempo.database.dao.ChronologyDao;
import com.cappielloantonio.tempo.database.dao.DownloadDao; import com.cappielloantonio.tempo.database.dao.DownloadDao;
import com.cappielloantonio.tempo.database.dao.FavoriteDao; 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.QueueDao;
import com.cappielloantonio.tempo.database.dao.RecentSearchDao; import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
import com.cappielloantonio.tempo.database.dao.ServerDao; import com.cappielloantonio.tempo.database.dao.ServerDao;
@ -23,12 +24,13 @@ import com.cappielloantonio.tempo.model.Queue;
import com.cappielloantonio.tempo.model.RecentSearch; import com.cappielloantonio.tempo.model.RecentSearch;
import com.cappielloantonio.tempo.model.Server; import com.cappielloantonio.tempo.model.Server;
import com.cappielloantonio.tempo.model.SessionMediaItem; import com.cappielloantonio.tempo.model.SessionMediaItem;
import com.cappielloantonio.tempo.subsonic.models.Playlist;
@UnstableApi @UnstableApi
@Database( @Database(
version = 9, version = 10,
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class}, entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class, Playlist.class},
autoMigrations = {@AutoMigration(from = 8, to = 9)} autoMigrations = {@AutoMigration(from = 9, to = 10)}
) )
@TypeConverters({DateConverters.class}) @TypeConverters({DateConverters.class})
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
@ -58,4 +60,6 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract FavoriteDao favoriteDao(); public abstract FavoriteDao favoriteDao();
public abstract SessionMediaItemDao sessionMediaItemDao(); public abstract SessionMediaItemDao sessionMediaItemDao();
public abstract PlaylistDao playlistDao();
} }

View file

@ -1,9 +1,12 @@
package com.cappielloantonio.tempo.repository; package com.cappielloantonio.tempo.repository;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.cappielloantonio.tempo.App; 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.base.ApiResponse;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.Playlist; import com.cappielloantonio.tempo.subsonic.models.Playlist;
@ -17,6 +20,7 @@ import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
public class PlaylistRepository { public class PlaylistRepository {
private final PlaylistDao playlistDao = AppDatabase.getInstance().playlistDao();
public MutableLiveData<List<Playlist>> getPlaylists(boolean random, int size) { public MutableLiveData<List<Playlist>> getPlaylists(boolean random, int size) {
MutableLiveData<List<Playlist>> listLivePlaylists = new MutableLiveData<>(new ArrayList<>()); MutableLiveData<List<Playlist>> listLivePlaylists = new MutableLiveData<>(new ArrayList<>());
@ -153,4 +157,50 @@ public class PlaylistRepository {
} }
}); });
} }
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);
}
}
} }

View file

@ -31,6 +31,7 @@ import com.cappielloantonio.tempo.databinding.FragmentHomeTabMusicBinding;
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper; import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration; import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration;
import com.cappielloantonio.tempo.interfaces.ClickCallback; import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.model.Download;
import com.cappielloantonio.tempo.model.HomeSector; import com.cappielloantonio.tempo.model.HomeSector;
import com.cappielloantonio.tempo.service.DownloaderManager; import com.cappielloantonio.tempo.service.DownloaderManager;
@ -44,12 +45,13 @@ import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter; import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter; import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.DiscoverSongAdapter; 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.ShareHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter; import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter;
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter; import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
import com.cappielloantonio.tempo.ui.adapter.YearAdapter; import com.cappielloantonio.tempo.ui.adapter.YearAdapter;
import com.cappielloantonio.tempo.ui.dialog.HomeRearrangementDialog; 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.Constants;
import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.MappingUtil;
@ -85,7 +87,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
private AlbumAdapter mostPlayedAlbumAdapter; private AlbumAdapter mostPlayedAlbumAdapter;
private AlbumHorizontalAdapter newReleasesAlbumAdapter; private AlbumHorizontalAdapter newReleasesAlbumAdapter;
private YearAdapter yearAdapter; private YearAdapter yearAdapter;
private GridTrackAdapter gridTrackAdapter; private PlaylistHorizontalAdapter playlistHorizontalAdapter;
private ShareHorizontalAdapter shareHorizontalAdapter; private ShareHorizontalAdapter shareHorizontalAdapter;
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture; private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
@ -122,6 +124,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
initYearSongView(); initYearSongView();
initRecentAddedAlbumView(); initRecentAddedAlbumView();
initTopSongsView(); initTopSongsView();
initPinnedPlaylistsView();
initSharesView(); initSharesView();
initHomeReorganizer(); initHomeReorganizer();
@ -422,7 +425,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
} else { } else {
if (bind != null) bind.homeGridTracksSector.setVisibility(View.VISIBLE); if (bind != null) bind.homeGridTracksSector.setVisibility(View.VISIBLE);
if (bind != null) bind.afterGridDivider.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() List<Child> topSongs = chronologies.stream()
.map(cronologia -> (Child) cronologia) .map(cronologia -> (Child) cronologia)
@ -671,6 +675,26 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
recentAddedAlbumSnapHelper.attachToRecyclerView(bind.recentlyAddedAlbumsRecyclerView); 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() { private void initSharesView() {
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_SHARED)) return; if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_SHARED)) return;
@ -708,7 +732,9 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
private void initHomeReorganizer() { private void initHomeReorganizer() {
final Handler handler = new Handler(); 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); handler.postDelayed(runnable, 5000);
bind.homeSectorRearrangementButton.setOnClickListener(v -> { bind.homeSectorRearrangementButton.setOnClickListener(v -> {
@ -789,6 +815,9 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
case Constants.HOME_SECTOR_RECENTLY_ADDED: case Constants.HOME_SECTOR_RECENTLY_ADDED:
bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector); bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector);
break; break;
case Constants.HOME_SECTOR_PINNED_PLAYLISTS:
bind.homeLinearLayoutContainer.addView(bind.pinnedPlaylistsSector);
break;
case Constants.HOME_SECTOR_SHARED: case Constants.HOME_SECTOR_SHARED:
bind.homeLinearLayoutContainer.addView(bind.sharesSector); bind.homeLinearLayoutContainer.addView(bind.sharesSector);
break; break;
@ -824,6 +853,17 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
popup.show(); 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() { private void initializeMediaBrowser() {
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync(); mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
} }
@ -922,6 +962,24 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
startActivity(intent); 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 @Override
public void onShareLongClick(Bundle bundle) { public void onShareLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle); Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);

View file

@ -60,6 +60,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.playlist_page_menu, menu); inflater.inflate(R.menu.playlist_page_menu, menu);
initMenuOption(menu);
} }
@Override @Override
@ -115,6 +116,12 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
} }
}); });
return true; 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; return false;
@ -124,6 +131,13 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
playlistPageViewModel.setPlaylist(requireArguments().getParcelable(Constants.PLAYLIST_OBJECT)); 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() { private void initAppBar() {
activity.setSupportActionBar(bind.animToolbar); activity.setSupportActionBar(bind.animToolbar);

View file

@ -105,5 +105,6 @@ object Constants {
const val HOME_SECTOR_MOST_PLAYED = "HOME_SECTOR_MOST_PLAYED" const val HOME_SECTOR_MOST_PLAYED = "HOME_SECTOR_MOST_PLAYED"
const val HOME_SECTOR_LAST_PLAYED = "HOME_SECTOR_LAST_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_RECENTLY_ADDED = "HOME_SECTOR_RECENTLY_ADDED"
const val HOME_SECTOR_PINNED_PLAYLISTS = "HOME_SECTOR_PINNED_PLAYLISTS"
const val HOME_SECTOR_SHARED = "HOME_SECTOR_SHARED" const val HOME_SECTOR_SHARED = "HOME_SECTOR_SHARED"
} }

View file

@ -70,7 +70,8 @@ public class HomeRearrangementViewModel extends AndroidViewModel {
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_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_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_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; return sectors;
} }

View file

@ -16,11 +16,13 @@ import com.cappielloantonio.tempo.repository.AlbumRepository;
import com.cappielloantonio.tempo.repository.ArtistRepository; import com.cappielloantonio.tempo.repository.ArtistRepository;
import com.cappielloantonio.tempo.repository.ChronologyRepository; import com.cappielloantonio.tempo.repository.ChronologyRepository;
import com.cappielloantonio.tempo.repository.FavoriteRepository; import com.cappielloantonio.tempo.repository.FavoriteRepository;
import com.cappielloantonio.tempo.repository.PlaylistRepository;
import com.cappielloantonio.tempo.repository.SharingRepository; import com.cappielloantonio.tempo.repository.SharingRepository;
import com.cappielloantonio.tempo.repository.SongRepository; import com.cappielloantonio.tempo.repository.SongRepository;
import com.cappielloantonio.tempo.subsonic.models.AlbumID3; import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
import com.cappielloantonio.tempo.subsonic.models.Child; 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.subsonic.models.Share;
import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.Preferences;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
@ -32,6 +34,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class HomeViewModel extends AndroidViewModel { public class HomeViewModel extends AndroidViewModel {
private static final String TAG = "HomeViewModel"; private static final String TAG = "HomeViewModel";
@ -41,6 +44,7 @@ public class HomeViewModel extends AndroidViewModel {
private final ArtistRepository artistRepository; private final ArtistRepository artistRepository;
private final ChronologyRepository chronologyRepository; private final ChronologyRepository chronologyRepository;
private final FavoriteRepository favoriteRepository; private final FavoriteRepository favoriteRepository;
private final PlaylistRepository playlistRepository;
private final SharingRepository sharingRepository; private final SharingRepository sharingRepository;
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null); private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
@ -60,6 +64,7 @@ public class HomeViewModel extends AndroidViewModel {
private final MutableLiveData<List<Child>> mediaInstantMix = new MutableLiveData<>(null); private final MutableLiveData<List<Child>> mediaInstantMix = new MutableLiveData<>(null);
private final MutableLiveData<List<Child>> artistInstantMix = 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<Child>> artistBestOf = new MutableLiveData<>(null);
private final MutableLiveData<List<Playlist>> pinnedPlaylists = new MutableLiveData<>(null);
private final MutableLiveData<List<Share>> shares = new MutableLiveData<>(null); private final MutableLiveData<List<Share>> shares = new MutableLiveData<>(null);
private List<HomeSector> sectors; private List<HomeSector> sectors;
@ -74,6 +79,7 @@ public class HomeViewModel extends AndroidViewModel {
artistRepository = new ArtistRepository(); artistRepository = new ArtistRepository();
chronologyRepository = new ChronologyRepository(); chronologyRepository = new ChronologyRepository();
favoriteRepository = new FavoriteRepository(); favoriteRepository = new FavoriteRepository();
playlistRepository = new PlaylistRepository();
sharingRepository = new SharingRepository(); sharingRepository = new SharingRepository();
setOfflineFavorite(); setOfflineFavorite();
@ -224,6 +230,24 @@ public class HomeViewModel extends AndroidViewModel {
return artistBestOf; 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) { public LiveData<List<Share>> getShares(LifecycleOwner owner) {
if (shares.getValue() == null) { if (shares.getValue() == null) {
sharingRepository.getShares().observe(owner, shares::postValue); sharingRepository.getShares().observe(owner, shares::postValue);

View file

@ -4,7 +4,9 @@ import android.app.Application;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.cappielloantonio.tempo.repository.PlaylistRepository; import com.cappielloantonio.tempo.repository.PlaylistRepository;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
@ -35,4 +37,22 @@ public class PlaylistPageViewModel extends AndroidViewModel {
public void setPlaylist(Playlist playlist) { public void setPlaylist(Playlist playlist) {
this.playlist = playlist; this.playlist = playlist;
} }
public LiveData<Boolean> isPinned(LifecycleOwner owner) {
MutableLiveData<Boolean> isPinnedLive = new MutableLiveData<>();
playlistRepository.getPinnedPlaylists().observe(owner, playlists -> {
isPinnedLive.postValue(playlists.stream().anyMatch(obj -> obj.getId().equals(playlist.getId())));
});
return isPinnedLive;
}
public void setPinned(boolean isNowPinned) {
if (isNowPinned) {
playlistRepository.insert(playlist);
} else {
playlistRepository.delete(playlist);
}
}
} }

View file

@ -720,6 +720,36 @@
android:paddingBottom="8dp" /> android:paddingBottom="8dp" />
</LinearLayout> </LinearLayout>
<!-- Playlists -->
<LinearLayout
android:id="@+id/pinned_playlists_sector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/pinned_playlists_text_view"
style="@style/TitleLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:text="@string/home_title_pinned_playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/pinned_playlists_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:paddingTop="8dp"
android:paddingBottom="8dp" />
</LinearLayout>
<!-- Shares --> <!-- Shares -->
<LinearLayout <LinearLayout
android:id="@+id/shares_sector" android:id="@+id/shares_sector"

View file

@ -6,4 +6,14 @@
android:icon="@drawable/ic_file_download" android:icon="@drawable/ic_file_download"
android:title="@string/menu_download_all_button" android:title="@string/menu_download_all_button"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/action_pin_playlist"
android:icon="@drawable/ic_add"
android:title="@string/menu_pin_button"
app:showAsAction="never" />
<item
android:id="@+id/action_unpin_playlist"
android:icon="@drawable/ic_close"
android:title="@string/menu_unpin_button"
app:showAsAction="never" />
</menu> </menu>

View file

@ -125,6 +125,7 @@
<string name="home_title_most_played_see_all_button">See all</string> <string name="home_title_most_played_see_all_button">See all</string>
<string name="home_title_new_releases">New releases</string> <string name="home_title_new_releases">New releases</string>
<string name="home_title_newest_podcasts">Newest podcasts</string> <string name="home_title_newest_podcasts">Newest podcasts</string>
<string name="home_title_pinned_playlists">Playlists</string>
<string name="home_title_podcast_channels">Channels</string> <string name="home_title_podcast_channels">Channels</string>
<string name="home_title_podcast_channels_see_all_button">See all</string> <string name="home_title_podcast_channels_see_all_button">See all</string>
<string name="home_title_radio_station">Radio stations</string> <string name="home_title_radio_station">Radio stations</string>
@ -175,6 +176,8 @@
<string name="menu_sort_name">Name</string> <string name="menu_sort_name">Name</string>
<string name="menu_sort_random">Random</string> <string name="menu_sort_random">Random</string>
<string name="menu_sort_recently_added">Recently added</string> <string name="menu_sort_recently_added">Recently added</string>
<string name="menu_pin_button">Add to home screen</string>
<string name="menu_unpin_button">Remove from home screen</string>
<string name="menu_sort_year">Year</string> <string name="menu_sort_year">Year</string>
<string name="player_playback_speed">%1$.2fx</string> <string name="player_playback_speed">%1$.2fx</string>
<string name="player_queue_clean_all_button">Clean play queue</string> <string name="player_queue_clean_all_button">Clean play queue</string>