Added playlist creator and editor

This commit is contained in:
CappielloAntonio 2021-08-12 12:50:42 +02:00
parent 00b85e5bc2
commit d2092f5239
14 changed files with 589 additions and 1 deletions

View file

@ -0,0 +1,91 @@
package com.cappielloantonio.play.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.model.Playlist;
import com.cappielloantonio.play.ui.fragment.dialog.PlaylistChooserDialog;
import com.cappielloantonio.play.util.MusicUtil;
import com.cappielloantonio.play.viewmodel.PlaylistChooserViewModel;
import java.util.ArrayList;
import java.util.List;
public class PlaylistHorizontalAdapter extends RecyclerView.Adapter<PlaylistHorizontalAdapter.ViewHolder> {
private static final String TAG = "PlaylistHorizontalAdapter";
private List<Playlist> playlists;
private LayoutInflater mInflater;
private Context context;
private PlaylistChooserViewModel playlistChooserViewModel;
private PlaylistChooserDialog playlistChooserDialog;
public PlaylistHorizontalAdapter(Context context, PlaylistChooserViewModel playlistChooserViewModel, PlaylistChooserDialog playlistChooserDialog) {
this.context = context;
this.mInflater = LayoutInflater.from(context);
this.playlists = new ArrayList<>();
this.playlistChooserViewModel = playlistChooserViewModel;
this.playlistChooserDialog = playlistChooserDialog;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_horizontal_playlist, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Playlist playlist = playlists.get(position);
holder.playlistTitle.setText(MusicUtil.getReadableString(playlist.getName()));
holder.playlistTrackCount.setText(MusicUtil.getReadableString(playlist.getSongCount() + " tracks"));
holder.playlistDuration.setText(MusicUtil.getReadableDurationString(playlist.getDuration(), false));
}
@Override
public int getItemCount() {
return playlists.size();
}
public void setItems(List<Playlist> playlists) {
this.playlists = playlists;
notifyDataSetChanged();
}
public Playlist getItem(int id) {
return playlists.get(id);
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView playlistTitle;
TextView playlistTrackCount;
TextView playlistDuration;
ViewHolder(View itemView) {
super(itemView);
playlistTitle = itemView.findViewById(R.id.playlist_dialog_title_text_view);
playlistTrackCount = itemView.findViewById(R.id.playlist_dialog_count_text_view);
playlistDuration = itemView.findViewById(R.id.playlist_dialog_duration_text_view);
itemView.setOnClickListener(this);
playlistTitle.setSelected(true);
}
@Override
public void onClick(View view) {
playlistChooserViewModel.addSongToPlaylist(playlists.get(getBindingAdapterPosition()).getId());
playlistChooserDialog.dismiss();
}
}
}

View file

@ -77,4 +77,42 @@ public class PlaylistRepository {
return listLivePlaylistSongs;
}
public void addSongToPlaylist(String playlistId, String songId) {
App.getSubsonicClientInstance(application, false)
.getPlaylistClient()
.updatePlaylist(playlistId, null, true, songId, null)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
}
});
}
public void createPlaylist(String name, String songId) {
App.getSubsonicClientInstance(application, false)
.getPlaylistClient()
.createPlaylist(null, name, songId)
.enqueue(new Callback<SubsonicResponse>() {
@Override
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
}
}
@Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
}
});
}
}

View file

@ -40,6 +40,21 @@ public class PlaylistClient {
return playlistService.getPlaylist(subsonic.getParams(), id);
}
public Call<SubsonicResponse> createPlaylist(String playlistId, String name, String songId) {
Log.d(TAG, "createPlaylist()");
return playlistService.createPlaylist(subsonic.getParams(), playlistId, name, songId);
}
public Call<SubsonicResponse> updatePlaylist(String playlistId, String name, boolean isPublic, String songIdToAdd, String songIndexToRemove) {
Log.d(TAG, "updatePlaylist()");
return playlistService.updatePlaylist(subsonic.getParams(), playlistId, name, isPublic, songIdToAdd, songIndexToRemove);
}
public Call<SubsonicResponse> deletePlaylist(String id) {
Log.d(TAG, "deletePlaylist()");
return playlistService.deletePlaylist(subsonic.getParams(), id);
}
private OkHttpClient getOkHttpClient() {
return new OkHttpClient.Builder()
.addInterceptor(getHttpLoggingInterceptor())

View file

@ -15,4 +15,13 @@ public interface PlaylistService {
@GET("getPlaylist")
Call<SubsonicResponse> getPlaylist(@QueryMap Map<String, String> params, @Query("id") String id);
@GET("createPlaylist")
Call<SubsonicResponse> createPlaylist(@QueryMap Map<String, String> params, @Query("playlistId") String playlistId, @Query("name") String name, @Query("songId") String songId);
@GET("updatePlaylist")
Call<SubsonicResponse> updatePlaylist(@QueryMap Map<String, String> params, @Query("playlistId") String playlistId, @Query("name") String name, @Query("public") boolean isPublic, @Query("songIdToAdd") String songIdToAdd, @Query("songIndexToRemove") String songIndexToRemove);
@GET("deletePlaylist")
Call<SubsonicResponse> deletePlaylist(@QueryMap Map<String, String> params, @Query("id") String id);
}

View file

@ -25,6 +25,8 @@ import com.cappielloantonio.play.repository.QueueRepository;
import com.cappielloantonio.play.repository.SongRepository;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.ui.fragment.dialog.PlaylistChooserDialog;
import com.cappielloantonio.play.ui.fragment.dialog.ServerSignupDialog;
import com.cappielloantonio.play.util.DownloadUtil;
import com.cappielloantonio.play.util.MusicUtil;
import com.cappielloantonio.play.viewmodel.SongBottomSheetViewModel;
@ -49,6 +51,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
private TextView playNext;
private TextView addToQueue;
private TextView download;
private TextView addToPlaylist;
private TextView goToAlbum;
private TextView goToArtist;
@ -139,6 +142,18 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
dismissBottomSheet();
});
addToPlaylist = view.findViewById(R.id.add_to_playlist_text_view);
addToPlaylist.setOnClickListener(v -> {
Bundle bundle = new Bundle();
bundle.putParcelable("song_object", song);
PlaylistChooserDialog dialog = new PlaylistChooserDialog();
dialog.setArguments(bundle);
dialog.show(requireActivity().getSupportFragmentManager(), null);
dismissBottomSheet();
});
goToAlbum = view.findViewById(R.id.go_to_album_text_view);
goToAlbum.setOnClickListener(v -> {
songBottomSheetViewModel.getAlbum().observe(requireActivity(), album -> {

View file

@ -0,0 +1,103 @@
package com.cappielloantonio.play.ui.fragment.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.adapter.PlaylistHorizontalAdapter;
import com.cappielloantonio.play.databinding.DialogPlaylistChooserBinding;
import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.viewmodel.PlaylistChooserViewModel;
import java.util.Objects;
public class PlaylistChooserDialog extends DialogFragment {
private static final String TAG = "ServerSignupDialog";
private DialogPlaylistChooserBinding bind;
private MainActivity activity;
private Context context;
private PlaylistChooserViewModel playlistChooserViewModel;
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
activity = (MainActivity) getActivity();
context = requireContext();
bind = DialogPlaylistChooserBinding.inflate(LayoutInflater.from(requireContext()));
playlistChooserViewModel = new ViewModelProvider(requireActivity()).get(PlaylistChooserViewModel.class);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);
builder.setView(bind.getRoot())
.setTitle("Add to a playlist")
.setNeutralButton("Create", (dialog, id) -> {
})
.setNegativeButton("Cancel", (dialog, id) -> dialog.cancel());
return builder.create();
}
@Override
public void onStart() {
super.onStart();
initPlaylistView();
setSongInfo();
setButtonAction();
}
private void setSongInfo() {
if (getArguments() != null) {
playlistChooserViewModel.setSongToAdd(getArguments().getParcelable("song_object"));
} else {
playlistChooserViewModel.setSongToAdd(null);
}
}
private void setButtonAction() {
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
Bundle bundle = new Bundle();
bundle.putParcelable("song_object", playlistChooserViewModel.getSongToAdd());
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
dialog.setArguments(bundle);
dialog.show(requireActivity().getSupportFragmentManager(), null);
Objects.requireNonNull(getDialog()).dismiss();
});
}
private void initPlaylistView() {
bind.playlistDialogRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.playlistDialogRecyclerView.setHasFixedSize(true);
playlistHorizontalAdapter = new PlaylistHorizontalAdapter(requireContext(), playlistChooserViewModel, this);
bind.playlistDialogRecyclerView.setAdapter(playlistHorizontalAdapter);
playlistChooserViewModel.getPlaylistList().observe(requireActivity(), playlists -> {
if(playlists.size() > 0) {
if (bind != null) bind.noPlaylistsCreatedTextView.setVisibility(View.GONE);
if (bind != null) bind.playlistDialogRecyclerView.setVisibility(View.VISIBLE);
playlistHorizontalAdapter.setItems(playlists);
}
else {
if (bind != null) bind.noPlaylistsCreatedTextView.setVisibility(View.VISIBLE);
if (bind != null) bind.playlistDialogRecyclerView.setVisibility(View.GONE);
}
});
}
}

View file

@ -0,0 +1,88 @@
package com.cappielloantonio.play.ui.fragment.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.databinding.DialogPlaylistEditorBinding;
import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.viewmodel.PlaylistEditorViewModel;
import java.util.Objects;
public class PlaylistEditorDialog extends DialogFragment {
private static final String TAG = "ServerSignupDialog";
private DialogPlaylistEditorBinding bind;
private MainActivity activity;
private Context context;
private PlaylistEditorViewModel playlistEditorViewModel;
private String playlistName;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
activity = (MainActivity) getActivity();
context = requireContext();
bind = DialogPlaylistEditorBinding.inflate(LayoutInflater.from(requireContext()));
playlistEditorViewModel = new ViewModelProvider(requireActivity()).get(PlaylistEditorViewModel.class);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);
builder.setView(bind.getRoot())
.setTitle("Create playlist")
.setPositiveButton("Save", (dialog, id) -> {
})
.setNegativeButton("Cancel", (dialog, id) -> dialog.cancel());
return builder.create();
}
@Override
public void onStart() {
super.onStart();
setSongInfo();
setButtonAction();
}
private void setSongInfo() {
if (getArguments() != null) {
playlistEditorViewModel.setSongToAdd(getArguments().getParcelable("song_object"));
} else {
playlistEditorViewModel.setSongToAdd(null);
}
}
private void setButtonAction() {
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
if (validateInput()) {
playlistEditorViewModel.createPlaylist(playlistName);
Objects.requireNonNull(getDialog()).dismiss();
}
});
}
private boolean validateInput() {
playlistName = bind.playlistNameTextView.getText().toString().trim();
if (TextUtils.isEmpty(playlistName)) {
bind.playlistNameTextView.setError("Required");
return false;
}
return true;
}
}

View file

@ -0,0 +1,45 @@
package com.cappielloantonio.play.viewmodel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.cappielloantonio.play.model.Playlist;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.PlaylistRepository;
import java.util.List;
public class PlaylistChooserViewModel extends AndroidViewModel {
private PlaylistRepository playlistRepository;
private MutableLiveData<List<Playlist>> playlists;
private Song toAdd;
public PlaylistChooserViewModel(@NonNull Application application) {
super(application);
playlistRepository = new PlaylistRepository(application);
playlists = playlistRepository.getPlaylists(false, -1);
}
public LiveData<List<Playlist>> getPlaylistList() {
return playlists;
}
public void addSongToPlaylist(String playlistId) {
playlistRepository.addSongToPlaylist(playlistId, toAdd.getId());
}
public void setSongToAdd(Song song) {
toAdd = song;
}
public Song getSongToAdd() {
return toAdd;
}
}

View file

@ -0,0 +1,38 @@
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.Server;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.PlaylistRepository;
import com.cappielloantonio.play.repository.ServerRepository;
import java.util.List;
public class PlaylistEditorViewModel extends AndroidViewModel {
private PlaylistRepository playlistRepository;
private Song toAdd;
public PlaylistEditorViewModel(@NonNull Application application) {
super(application);
playlistRepository = new PlaylistRepository(application);
}
public void createPlaylist(String name) {
playlistRepository.createPlaylist(name, toAdd.getId());
}
public void updatePlaylist(String playlistId) {
}
public void setSongToAdd(Song song) {
toAdd = song;
}
}