From b4105b6cc07962023d83c6c58a07fa621cb48b24 Mon Sep 17 00:00:00 2001 From: Karim Abou Zeid Date: Mon, 25 May 2015 16:10:59 +0200 Subject: [PATCH] Playlists multi select --- .../adapter/AbsMultiSelectAdapter.java | 9 ++- .../gramophone/adapter/AlbumAdapter.java | 3 +- .../gramophone/adapter/ArtistAdapter.java | 3 +- .../gramophone/adapter/PlaylistAdapter.java | 70 ++++++++++++++++--- .../dialogs/DeletePlaylistDialog.java | 35 +++++++--- .../gramophone/dialogs/DeleteSongsDialog.java | 4 +- .../helper/MenuItemClickHelper.java | 2 +- .../gramophone/helper/MusicPlayerRemote.java | 2 +- .../kabouzeid/gramophone/model/Playlist.java | 44 +++++++++++- .../PlaylistViewFragment.java | 2 +- .../gramophone/util/PlaylistsUtil.java | 21 +++++- .../res/menu/menu_playlists_selection.xml | 23 ++++++ app/src/main/res/values/strings.xml | 4 ++ 13 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 app/src/main/res/menu/menu_playlists_selection.xml diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java index d2d07873..ed8c11f3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java @@ -1,5 +1,6 @@ package com.kabouzeid.gramophone.adapter; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; @@ -18,7 +19,7 @@ public abstract class AbsMultiSelectAdapter checked; private int menuRes; - public AbsMultiSelectAdapter(CabHolder cabHolder, int menuRes) { + public AbsMultiSelectAdapter(@Nullable CabHolder cabHolder, int menuRes) { this.cabHolder = cabHolder; checked = new ArrayList<>(); this.menuRes = menuRes; @@ -40,8 +41,10 @@ public abstract class AbsMultiSelectAdapter dataSet; - public ArtistAdapter(AppCompatActivity activity, CabHolder cabHolder) { + public ArtistAdapter(AppCompatActivity activity, @Nullable CabHolder cabHolder) { super(cabHolder, R.menu.menu_media_selection); this.activity = activity; loadDataSet(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java index f9162ed6..0403a623 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java @@ -1,5 +1,6 @@ package com.kabouzeid.gramophone.adapter; +import android.support.annotation.Nullable; import android.support.v4.util.Pair; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.RecyclerView; @@ -11,24 +12,32 @@ import android.widget.PopupMenu; import android.widget.TextView; import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog; +import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog; import com.kabouzeid.gramophone.helper.MenuItemClickHelper; +import com.kabouzeid.gramophone.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.loader.PlaylistLoader; +import com.kabouzeid.gramophone.loader.PlaylistSongLoader; import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; import com.kabouzeid.gramophone.util.NavigationUtil; +import java.util.ArrayList; import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class PlaylistAdapter extends RecyclerView.Adapter { +public class PlaylistAdapter extends AbsMultiSelectAdapter { public static final String TAG = PlaylistAdapter.class.getSimpleName(); protected final AppCompatActivity activity; protected List dataSet; - public PlaylistAdapter(AppCompatActivity activity) { + public PlaylistAdapter(AppCompatActivity activity, @Nullable CabHolder cabHolder) { + super(cabHolder, R.menu.menu_playlists_selection); this.activity = activity; loadDataSet(); } @@ -45,7 +54,9 @@ public class PlaylistAdapter extends RecyclerView.Adapter selection) { + switch (menuItem.getItemId()) { + case R.id.action_delete_playlist: + DeletePlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); + break; + case R.id.action_add_to_playlist: + AddToPlaylistDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); + break; + case R.id.action_add_to_current_playing: + MusicPlayerRemote.enqueue(getSongList(selection)); + break; + } + } + + private ArrayList getSongList(List playlists) { + final ArrayList songs = new ArrayList<>(); + for (Playlist playlist : playlists) { + songs.addAll(PlaylistSongLoader.getPlaylistSongList(activity, playlist.id)); + } + return songs; + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { public final TextView playlistName; private final View menu; + private final View view; public ViewHolder(View itemView) { super(itemView); + view = itemView; playlistName = (TextView) itemView.findViewById(R.id.playlist_name); menu = itemView.findViewById(R.id.menu); - itemView.setOnClickListener(this); + view.setOnClickListener(this); + view.setOnLongClickListener(this); menu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -81,10 +123,20 @@ public class PlaylistAdapter extends RecyclerView.Adapter list = new ArrayList<>(); + list.add(playlist); + return create(list); + } + + public static DeletePlaylistDialog create(ArrayList playlists) { DeletePlaylistDialog dialog = new DeletePlaylistDialog(); Bundle args = new Bundle(); - args.putLong("playlist_id", playlistId); + args.putSerializable("playlists", playlists); dialog.setArguments(args); return dialog; } @@ -26,11 +35,20 @@ public class DeletePlaylistDialog extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - long playlistId = getArguments().getLong("playlist_id"); + //noinspection unchecked + final ArrayList playlists = (ArrayList) getArguments().getSerializable("playlists"); + int title; + CharSequence content; + if (playlists.size() > 1) { + title = R.string.delete_playlists_title; + content = Html.fromHtml(getString(R.string.delete_x_playlists, playlists.size())); + } else { + title = R.string.delete_playlist_title; + content = Html.fromHtml(getString(R.string.delete_playlist_x, playlists.get(0).name)); + } return new MaterialDialog.Builder(getActivity()) - .title(R.string.delete_playlist_title) - .content(Html.fromHtml(getString(R.string.delete_playlist_x, - PlaylistsUtil.getNameForPlaylist(getActivity(), playlistId)))) + .title(title) + .content(content) .positiveText(R.string.delete_action) .negativeText(android.R.string.cancel) .callback(new MaterialDialog.ButtonCallback() { @@ -39,8 +57,7 @@ public class DeletePlaylistDialog extends DialogFragment { super.onPositive(dialog); if (getActivity() == null) return; - long playlistId = getArguments().getLong("playlist_id"); - PlaylistsUtil.deletePlaylist(getActivity(), playlistId); + PlaylistsUtil.deletePlaylists(getActivity(), playlists); } }).build(); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java index 9f5e8619..f27d6ef7 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java @@ -36,7 +36,7 @@ public class DeleteSongsDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { //noinspection unchecked - ArrayList songs = (ArrayList) getArguments().getSerializable("songs"); + final ArrayList songs = (ArrayList) getArguments().getSerializable("songs"); int title; CharSequence content; if (songs.size() > 1) { @@ -57,8 +57,6 @@ public class DeleteSongsDialog extends DialogFragment { super.onPositive(dialog); if (getActivity() == null) return; - //noinspection unchecked - ArrayList songs = (ArrayList) getArguments().getSerializable("songs"); MusicUtil.deleteTracks(getActivity(), songs); } }).build(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java index 508a471d..fbf0e9ee 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java @@ -75,7 +75,7 @@ public class MenuItemClickHelper { RenamePlaylistDialog.create(playlist.id).show(activity.getSupportFragmentManager(), "RENAME_PLAYLIST"); return true; case R.id.action_delete_playlist: - DeletePlaylistDialog.create(playlist.id).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); + DeletePlaylistDialog.create(playlist).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); return true; } return false; diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java index fe62786d..eb506f61 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java @@ -255,7 +255,7 @@ public class MusicPlayerRemote { public static void enqueue(List songs) { if (musicService != null) { musicService.addSongs(songs); - final String toast = songs.size() > 1 ? musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size()) : musicService.getResources().getString(R.string.added_title_to_playing_queue); + final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size()); Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java index 8905c4cf..dedc8b2f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java @@ -1,6 +1,16 @@ package com.kabouzeid.gramophone.model; -public class Playlist { +import android.text.TextUtils; + +import java.io.Serializable; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class Playlist implements Serializable { + + private static final long serialVersionUID = 3013703495354856981L; + public final int id; public final String name; @@ -13,4 +23,36 @@ public class Playlist { this.id = -1; this.name = ""; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (name == null ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Playlist other = (Playlist) obj; + if (id != other.id) { + return false; + } + return TextUtils.equals(name, other.name); + } + + @Override + public String toString() { + return name; + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java index 85db703a..8cc0bbfc 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java @@ -40,7 +40,7 @@ public class PlaylistViewFragment extends AbsMainActivityRecyclerViewFragment { @Override protected RecyclerView.Adapter createAdapter() { - PlaylistAdapter adapter = new PlaylistAdapter(getMainActivity()); + PlaylistAdapter adapter = new PlaylistAdapter(getMainActivity(), getMainActivity()); View v = getView(); if (v != null) { v.findViewById(android.R.id.empty).setVisibility( diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java index 8ed31d68..2a9544c0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java @@ -12,9 +12,11 @@ import android.widget.Toast; import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.DataBaseChangedEvent; +import com.kabouzeid.gramophone.model.Playlist; import com.kabouzeid.gramophone.model.PlaylistSong; import com.kabouzeid.gramophone.model.Song; +import java.util.ArrayList; import java.util.List; /** @@ -59,13 +61,26 @@ public class PlaylistsUtil { // context.getContentResolver().delete(uri, null, null); // } - public static void deletePlaylist(final Context context, final long playlistId) { + public static void deletePlaylists(final Context context, final long playlistId) { final Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; String where = MediaStore.Audio.Playlists._ID + "=?"; String[] whereVal = {String.valueOf(playlistId)}; context.getContentResolver().delete(uri, where, whereVal); - Toast.makeText(context, context.getResources().getString(R.string.deleted_playlist_x, - getNameForPlaylist(context, playlistId)), Toast.LENGTH_SHORT).show(); + App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED)); + } + + public static void deletePlaylists(final Context context, final ArrayList playlists) { + final Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; + final StringBuilder selection = new StringBuilder(); + selection.append(MediaStore.Audio.Playlists._ID + " IN ("); + for (int i = 0; i < playlists.size(); i++) { + selection.append(playlists.get(i).id); + if (i < playlists.size() - 1) { + selection.append(","); + } + } + selection.append(")"); + context.getContentResolver().delete(uri, selection.toString(), null); App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED)); } diff --git a/app/src/main/res/menu/menu_playlists_selection.xml b/app/src/main/res/menu/menu_playlists_selection.xml new file mode 100644 index 00000000..2ab37d2d --- /dev/null +++ b/app/src/main/res/menu/menu_playlists_selection.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ 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 9e2745d2..493162df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,6 +68,9 @@ %1$s? ]]> + %1$d playlists? + ]]> %1$s? ]]> @@ -78,6 +81,7 @@ Delete Songs Rename Playlist Delete Playlist + Delete Playlists Add to Playlist New Playlist