diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/PlaylistRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/PlaylistRepository.java index bcf0c732..ee378c93 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/repository/PlaylistRepository.java +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/PlaylistRepository.java @@ -73,6 +73,29 @@ public class PlaylistRepository { return listLivePlaylistSongs; } + public MutableLiveData isSongInPlaylist(String playlistId, String songId) { + MutableLiveData songInPlayList = new MutableLiveData<>(); + + App.getSubsonicClientInstance(false) + .getPlaylistClient() + .getPlaylist(playlistId) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylist() != null && response.body().getSubsonicResponse().getPlaylist().getEntries() != null) { + List songs = response.body().getSubsonicResponse().getPlaylist().getEntries(); + songInPlayList.setValue(songs.stream().anyMatch(s -> s.getId().equals(songId))); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + } + }); + + return songInPlayList; + } + public void addSongToPlaylist(String playlistId, ArrayList songsId) { App.getSubsonicClientInstance(false) .getPlaylistClient() diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java index f0266c84..f8459b11 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.databinding.DialogPlaylistChooserBinding; import com.cappielloantonio.tempo.interfaces.ClickCallback; +import com.cappielloantonio.tempo.interfaces.DialogClickCallback; import com.cappielloantonio.tempo.subsonic.models.Playlist; import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogHorizontalAdapter; import com.cappielloantonio.tempo.util.Constants; @@ -36,7 +37,8 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba return new MaterialAlertDialogBuilder(getActivity()) .setView(bind.getRoot()) .setTitle(R.string.playlist_chooser_dialog_title) - .setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> { }) + .setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> { + }) .setNegativeButton(R.string.playlist_chooser_dialog_negative_button, (dialog, id) -> dialog.cancel()) .create(); } @@ -98,7 +100,23 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba @Override public void onPlaylistClick(Bundle bundle) { Playlist playlist = bundle.getParcelable(Constants.PLAYLIST_OBJECT); - playlistChooserViewModel.addSongToPlaylist(playlist.getId()); - dismiss(); + + playlistChooserViewModel.isSongInPlaylist(Objects.requireNonNull(playlist).getId(), requireActivity()).observe(requireActivity(), songInPlaylist -> { + if (songInPlaylist) { + playlistChooserViewModel.addSongToPlaylist(playlist.getId()); + dismiss(); + } else { + PlaylistDuplicateSongDialog playlistDuplicateSongDialog = new PlaylistDuplicateSongDialog(playlist, + playlistChooserViewModel.getSongToAdd(), + new DialogClickCallback() { + @Override + public void onPositiveClick() { + playlistChooserViewModel.addSongToPlaylist(playlist.getId()); + dismiss(); + } + }); + playlistDuplicateSongDialog.show(getActivity().getSupportFragmentManager(), null); + } + }); } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistDuplicateSongDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistDuplicateSongDialog.java new file mode 100644 index 00000000..d1f07d01 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistDuplicateSongDialog.java @@ -0,0 +1,70 @@ +package com.cappielloantonio.tempo.ui.dialog; + +import android.app.Dialog; +import android.os.Bundle; +import android.widget.Button; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.databinding.DialogPlaylistDuplicateSongBinding; +import com.cappielloantonio.tempo.interfaces.DialogClickCallback; +import com.cappielloantonio.tempo.subsonic.models.Child; +import com.cappielloantonio.tempo.subsonic.models.Playlist; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +public class PlaylistDuplicateSongDialog extends DialogFragment { + + private final Playlist playlist; + + private final Child song; + + private final DialogClickCallback dialogClickCallback; + + public PlaylistDuplicateSongDialog(Playlist playlist, Child song, DialogClickCallback dialogClickCallback) { + this.playlist = playlist; + this.song = song; + this.dialogClickCallback = dialogClickCallback; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + DialogPlaylistDuplicateSongBinding bind = DialogPlaylistDuplicateSongBinding.inflate(getLayoutInflater()); + bind.playlistDuplicateSongDialogSummary + .setText(getResources().getString(R.string.playlist_duplicate_song_dialog_summary, song.getTitle(), playlist.getName())); + + return new MaterialAlertDialogBuilder(requireContext()) + .setView(bind.getRoot()) + .setTitle(R.string.playlist_duplicate_song_dialog_title) + .setPositiveButton(R.string.playlist_duplicate_song_dialog_positive_button, null) + .setNegativeButton(R.string.playlist_duplicate_song_dialog_negative_button, null) + .create(); + } + + @Override + public void onResume() { + super.onResume(); + setButtonAction(); + } + + private void setButtonAction() { + androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog(); + + if (dialog != null) { + Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE); + positiveButton.setOnClickListener(v -> { + dialogClickCallback.onPositiveClick(); + dialog.dismiss(); + }); + + Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE); + negativeButton.setOnClickListener(v -> { + dialogClickCallback.onNegativeClick(); + dialog.dismiss(); + }); + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java index cb8833fe..bb74100b 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java @@ -33,6 +33,13 @@ public class PlaylistChooserViewModel extends AndroidViewModel { return playlists; } + public LiveData isSongInPlaylist(String playlistId, LifecycleOwner owner) { + MutableLiveData songs = new MutableLiveData<>(); + + playlistRepository.isSongInPlaylist(playlistId, toAdd.getId()).observe(owner, songs::postValue); + return songs; + } + public void addSongToPlaylist(String playlistId) { playlistRepository.addSongToPlaylist(playlistId, new ArrayList(Collections.singletonList(toAdd.getId()))); } diff --git a/app/src/main/res/layout/dialog_playlist_duplicate_song.xml b/app/src/main/res/layout/dialog_playlist_duplicate_song.xml new file mode 100644 index 00000000..0cddd1c5 --- /dev/null +++ b/app/src/main/res/layout/dialog_playlist_duplicate_song.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5c627b9a..7138fde7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -406,4 +406,8 @@ Tempo wird den Server bitten, die Datei zu transkodieren. Der vom Benutzer gewünschte Codec ist %1$s, die Bitrate wird dieselbe wie bei der original Datei sein. Die potentielle Transkodierung der Datei in das gewünschte Format ist vom Server abhängig. Dieser kann die Operation gegebenenfalls nicht unterstützen. Tempo wird den Server bitten, die Bitrate der Datei zu erändern. Die vom Benutzer gewünschte Bitrate ist %1$s, der Codec der Originaldatei wird nicht verändert. Änderungen an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen. Die Anwendung wird den Server bitten die Datei zu transkodieren und die Bitrate zu verändern. Der vom Benutzer gewünschte Codec ist %1$s, mit der Bitrate %2$s. Änderungen am Codec und an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen. + Doppelter Track + Der Track \"%1$s\" existiert bereits in der Playlist \"%2$s\" + Trotzdem hinzufügen + Nicht hinzufügen \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5fd1ac33..c3c1c763 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -415,4 +415,8 @@ unDraw A special thanks goes to unDraw without whose illustrations we could not have made this application more beautiful. https://undraw.co/ + Duplicate song + The song \"%1$s\" already exists in playlist \"%2$s\". + Add anyway + Don\'t add