Prepared everything for multi-selection and implemented it in the song view for now.

This commit is contained in:
Karim Abou Zeid 2015-05-24 20:33:14 +02:00
commit 8503eb4e36
55 changed files with 348 additions and 108 deletions

View file

@ -0,0 +1,80 @@
package com.kabouzeid.gramophone.adapter;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import com.afollestad.materialcab.MaterialCab;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import java.util.ArrayList;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class AbsMultiSelectAdapter<VH extends RecyclerView.ViewHolder, I> extends RecyclerView.Adapter<VH> implements MaterialCab.Callback {
private final CabHolder cabHolder;
private MaterialCab cab;
private ArrayList<I> checked;
private int menuRes;
public AbsMultiSelectAdapter(CabHolder cabHolder, int menuRes) {
this.cabHolder = cabHolder;
checked = new ArrayList<>();
this.menuRes = menuRes;
}
protected void toggleChecked(final int position) {
if (cabHolder != null) {
openCabIfNecessary();
if (position > 0) {
I identifier = getIdentifier(position);
if (!checked.remove(identifier)) checked.add(identifier);
notifyItemChanged(position);
}
if (checked.isEmpty()) cab.finish();
}
}
private void openCabIfNecessary() {
if (cab == null || !cab.isActive()) {
cab = cabHolder.openCab(menuRes, this);
}
}
private void uncheckAll() {
checked.clear();
notifyDataSetChanged();
}
protected boolean isChecked(I identifier) {
return checked.contains(identifier);
}
protected boolean isInQuickSelectMode() {
return cab != null && cab.isActive();
}
@Override
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
return true;
}
@Override
public boolean onCabItemClicked(MenuItem menuItem) {
onMultipleItemAction(menuItem, new ArrayList<>(checked));
cab.finish();
uncheckAll();
return true;
}
@Override
public boolean onCabFinished(MaterialCab materialCab) {
uncheckAll();
return true;
}
protected abstract I getIdentifier(int position);
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection);
}

View file

@ -12,11 +12,16 @@ import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.afollestad.materialcab.MaterialCab;
import com.afollestad.materialdialogs.ThemeSingleton;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.AbsMultiSelectAdapter;
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.loader.SongLoader;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
import com.kabouzeid.gramophone.model.Song;
@ -28,12 +33,11 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.otto.Subscribe;
import java.util.ArrayList;
import java.util.HashSet;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, Song> implements MaterialCab.Callback {
public static final String TAG = AlbumSongAdapter.class.getSimpleName();
private static final int SHUFFLE_BUTTON = 0;
@ -41,11 +45,10 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
protected final AppCompatActivity activity;
protected ArrayList<Song> dataSet;
private HashSet<Integer> checked;
public SongAdapter(AppCompatActivity activity) {
public SongAdapter(AppCompatActivity activity, CabHolder cabHolder) {
super(cabHolder, R.menu.menu_media_selection);
this.activity = activity;
checked = new HashSet<>();
loadDataSet();
}
@ -80,7 +83,7 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
.resetViewBeforeLoading(true)
.build()
);
holder.view.setActivated(checked.contains(song.id));
holder.view.setActivated(isChecked(song));
} else {
holder.songTitle.setText(activity.getResources().getString(R.string.shuffle_all).toUpperCase());
holder.songTitle.setTextColor(ThemeSingleton.get().positiveColor);
@ -101,6 +104,26 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
return dataSet.size() + 1;
}
@Override
protected Song getIdentifier(int position) {
return dataSet.get(position - 1);
}
@Override
protected void onMultipleItemAction(MenuItem menuItem, ArrayList<Song> selection) {
switch (menuItem.getItemId()) {
case R.id.action_delete_from_disk:
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
break;
case R.id.action_add_to_playlist:
AddToPlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
break;
case R.id.action_add_to_current_playing:
MusicPlayerRemote.enqueue(selection);
break;
}
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final TextView songTitle;
final TextView songInfo;
@ -152,6 +175,8 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
public void onClick(View v) {
if (getItemViewType() == SHUFFLE_BUTTON) {
MusicPlayerRemote.shuffleAllSongs(activity);
} else if (isInQuickSelectMode()) {
toggleChecked(getAdapterPosition());
} else {
MusicPlayerRemote.openQueue(dataSet, getAdapterPosition() - 1, true);
}
@ -164,11 +189,6 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
}
}
private void toggleChecked(final int position) {
final int id = dataSet.get(position - 1).id;
if (!checked.add(id)) checked.remove(id);
notifyItemChanged(position);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {

View file

@ -21,6 +21,7 @@ import com.kabouzeid.gramophone.service.MusicService;
import com.kabouzeid.gramophone.util.InternalStorageUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
@ -251,6 +252,14 @@ public class MusicPlayerRemote {
}
}
public static void enqueue(List<Song> 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);
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
}
}
public static void removeFromQueue(Song song) {
if (musicService != null) {
musicService.removeSong(song);

View file

@ -0,0 +1,11 @@
package com.kabouzeid.gramophone.interfaces;
import com.afollestad.materialcab.MaterialCab;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public interface CabHolder {
MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback);
}

View file

@ -27,9 +27,8 @@ public class AlbumLoader {
final int artistId = cursor.getInt(3);
final int songCount = cursor.getInt(4);
final int year = cursor.getInt(5);
final String albumArtPath = cursor.getString(6);
final Album album = new Album(id, albumName, artist, artistId, songCount, year, albumArtPath);
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
albums.add(album);
} while (cursor.moveToNext());
}
@ -58,8 +57,6 @@ public class AlbumLoader {
MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
/* 5 */
MediaStore.Audio.AlbumColumns.FIRST_YEAR,
/* 6 */
MediaStore.Audio.AlbumColumns.ALBUM_ART
}, selection, values, PreferenceUtils.getInstance(context).getAlbumSortOrder());
}
@ -73,9 +70,8 @@ public class AlbumLoader {
final int artistId = cursor.getInt(3);
final int songCount = cursor.getInt(4);
final int year = cursor.getInt(5);
final String albumArtPath = cursor.getString(6);
album = new Album(id, albumName, artist, artistId, songCount, year, albumArtPath);
album = new Album(id, albumName, artist, artistId, songCount, year);
}
if (cursor != null) {
@ -95,9 +91,8 @@ public class AlbumLoader {
final int artistId = cursor.getInt(3);
final int songCount = cursor.getInt(4);
final int year = cursor.getInt(5);
final String albumArtPath = cursor.getString(6);
final Album album = new Album(id, albumName, artist, artistId, songCount, year, albumArtPath);
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
albums.add(album);
} while (cursor.moveToNext());
}

View file

@ -27,9 +27,8 @@ public class AlbumSongLoader {
final long duration = cursor.getLong(4);
final int trackNumber = cursor.getInt(5);
final int artistId = cursor.getInt(6);
final long dateModified = cursor.getInt(7);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, dateModified);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
songs.add(song);
} while (cursor.moveToNext());
}
@ -54,9 +53,7 @@ public class AlbumSongLoader {
/* 5 */
MediaStore.Audio.AudioColumns.TRACK,
/* 6 */
MediaStore.Audio.AudioColumns.ARTIST_ID,
/* 7 */
MediaStore.Audio.AudioColumns.DATE_MODIFIED
MediaStore.Audio.AudioColumns.ARTIST_ID
}, (MediaStore.Audio.AudioColumns.IS_MUSIC + "=1") + " AND " +
MediaStore.Audio.AudioColumns.TITLE + " != ''" + " AND " +
MediaStore.Audio.AudioColumns.ALBUM_ID + "=" + albumId, null,

View file

@ -26,9 +26,8 @@ public class ArtistAlbumLoader {
final String artist = cursor.getString(2);
final int songCount = cursor.getInt(3);
final int year = cursor.getInt(4);
final String albumArtPath = cursor.getString(5);
final Album album = new Album(id, albumName, artist, artistId, songCount, year, albumArtPath);
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
albums.add(album);
} while (cursor.moveToNext());
}
@ -51,8 +50,6 @@ public class ArtistAlbumLoader {
MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS,
/* 4 */
MediaStore.Audio.AlbumColumns.FIRST_YEAR,
/* 5 */
MediaStore.Audio.AlbumColumns.ALBUM_ART
}, null, null, PreferenceUtils.getInstance(context).getArtistAlbumSortOrder());
}
}

View file

@ -27,9 +27,8 @@ public class ArtistSongLoader {
final long duration = cursor.getLong(4);
final int trackNumber = cursor.getInt(5);
final int albumId = cursor.getInt(6);
final long dateModified = cursor.getInt(7);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, dateModified);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
songs.add(song);
} while (cursor.moveToNext());
}
@ -54,9 +53,7 @@ public class ArtistSongLoader {
/* 5 */
MediaStore.Audio.AudioColumns.TRACK,
/* 6 */
MediaStore.Audio.AudioColumns.ALBUM_ID,
/* 7 */
MediaStore.Audio.AudioColumns.DATE_MODIFIED
MediaStore.Audio.AudioColumns.ALBUM_ID
}, (MediaStore.Audio.AudioColumns.IS_MUSIC + "=1") + " AND " +
MediaStore.Audio.AudioColumns.TITLE + " != ''" + " AND " +
MediaStore.Audio.AudioColumns.ARTIST_ID + "=" + artistId, null,

View file

@ -26,9 +26,8 @@ public class PlaylistSongLoader {
final int albumId = cursor.getInt(6);
final int artistId = cursor.getInt(7);
final int idInPlaylist = cursor.getInt(8);
final long dateModified = cursor.getInt(9);
final PlaylistSong song = new PlaylistSong(id, albumId, artistId, songName, artist, album, duration, trackNumber, playlistID, idInPlaylist, dateModified);
final PlaylistSong song = new PlaylistSong(id, albumId, artistId, songName, artist, album, duration, trackNumber, playlistID, idInPlaylist);
songs.add(song);
} while (cursor.moveToNext());
@ -60,9 +59,7 @@ public class PlaylistSongLoader {
/* 7 */
AudioColumns.ARTIST_ID,
/* 8 */
MediaStore.Audio.Playlists.Members._ID,
/* 9 */
MediaStore.Audio.AudioColumns.DATE_MODIFIED
MediaStore.Audio.Playlists.Members._ID
}, (AudioColumns.IS_MUSIC + "=1") + " AND " + AudioColumns.TITLE + " != ''", null,
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
}

View file

@ -30,9 +30,8 @@ public class SongLoader {
final int trackNumber = cursor.getInt(5);
final int artistId = cursor.getInt(6);
final int albumId = cursor.getInt(7);
final long dateModified = cursor.getInt(8);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, dateModified);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
songs.add(song);
} while (cursor.moveToNext());
}
@ -68,9 +67,7 @@ public class SongLoader {
/* 6 */
MediaStore.Audio.AudioColumns.ARTIST_ID,
/* 7 */
MediaStore.Audio.AudioColumns.ALBUM_ID,
/* 8 */
MediaStore.Audio.AudioColumns.DATE_MODIFIED
MediaStore.Audio.AudioColumns.ALBUM_ID
}, finalSelection, values, PreferenceUtils.getInstance(context).getSongSortOrder());
}
@ -87,9 +84,8 @@ public class SongLoader {
final int trackNumber = cursor.getInt(5);
final int artistId = cursor.getInt(6);
final int albumId = cursor.getInt(7);
final long dateModified = cursor.getInt(8);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, dateModified);
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
songs.add(song);
} while (cursor.moveToNext());
}
@ -111,8 +107,7 @@ public class SongLoader {
final int trackNumber = cursor.getInt(5);
final int artistId = cursor.getInt(6);
final int albumId = cursor.getInt(7);
final long dateModified = cursor.getInt(8);
song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, dateModified);
song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
}
if (cursor != null) {
cursor.close();

View file

@ -11,17 +11,15 @@ public class Album {
public final String artistName;
public final int songCount;
public final int year;
public final String albumArtPath; //used as cache key
public Album(final int id, final String title, final String artistName, final int artistId,
final int songNumber, final int albumYear, final String albumArtPath) {
final int songNumber, final int albumYear) {
this.id = id;
this.title = title;
this.artistName = artistName;
this.artistId = artistId;
songCount = songNumber;
year = albumYear;
this.albumArtPath = albumArtPath != null ? albumArtPath : "";
}
public Album() {
@ -31,6 +29,5 @@ public class Album {
this.artistId = -1;
songCount = -1;
year = -1;
this.albumArtPath = "";
}
}

View file

@ -8,8 +8,8 @@ public class PlaylistSong extends Song {
public final int idInPlayList;
public PlaylistSong(final int id, final int albumId, final int artistId, final String title, final String artistName,
final String albumName, final long duration, final int trackNumber, final int playlistId, final int idInPlayList, final long dateModified) {
super(id, albumId, artistId, title, artistName, albumName, duration, trackNumber, dateModified);
final String albumName, final long duration, final int trackNumber, final int playlistId, final int idInPlayList) {
super(id, albumId, artistId, title, artistName, albumName, duration, trackNumber);
this.playlistId = playlistId;
this.idInPlayList = idInPlayList;
}

View file

@ -1,5 +1,7 @@
package com.kabouzeid.gramophone.model;
import android.text.TextUtils;
import java.io.Serializable;
/**
@ -17,10 +19,9 @@ public class Song implements Serializable {
public final String albumName;
public final long duration;
public final int trackNumber;
public final long dateModified; //used as cache key
public Song(final int id, final int albumId, final int artistId, final String title, final String artistName,
final String albumName, final long duration, final int trackNumber, final long dateModified) {
final String albumName, final long duration, final int trackNumber) {
this.id = id;
this.albumId = albumId;
this.artistId = artistId;
@ -29,7 +30,6 @@ public class Song implements Serializable {
this.albumName = albumName;
this.duration = duration;
this.trackNumber = trackNumber;
this.dateModified = dateModified;
}
public Song() {
@ -41,6 +41,49 @@ public class Song implements Serializable {
this.albumName = "";
this.duration = -1;
this.trackNumber = -1;
this.dateModified = -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (albumName == null ? 0 : albumName.hashCode());
result = prime * result + (artistName == null ? 0 : artistName.hashCode());
result = prime * result + (int) duration;
result = prime * result + id;
result = prime * result + (title == null ? 0 : title.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 Song other = (Song) obj;
if (id != other.id) {
return false;
}
if (!TextUtils.equals(albumName, other.albumName)) {
return false;
}
if (!TextUtils.equals(artistName, other.artistName)) {
return false;
}
if (duration != other.duration) {
return false;
}
return TextUtils.equals(title, other.title);
}
@Override
public String toString() {
return title;
}
}

View file

@ -42,6 +42,7 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener {
public static final String ACTION_TOGGLE_PLAYBACK = "com.kabouzeid.gramophone.action.TOGGLE_PLAYBACK";
@ -552,6 +553,18 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
saveState();
}
public void addSongs(int position, List<Song> songs) {
playingQueue.addAll(position, songs);
originalPlayingQueue.addAll(position, songs);
saveState();
}
public void addSongs(List<Song> songs) {
playingQueue.addAll(songs);
originalPlayingQueue.addAll(songs);
saveState();
}
public void removeSong(int position) {
if (getShuffleMode() == SHUFFLE_MODE_NONE) {
playingQueue.remove(position);

View file

@ -25,6 +25,7 @@ import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.FrameLayout;
import com.afollestad.materialcab.MaterialCab;
import com.afollestad.materialdialogs.ThemeSingleton;
import com.astuetz.PagerSlidingTabStrip;
import com.kabouzeid.gramophone.R;
@ -32,6 +33,7 @@ import com.kabouzeid.gramophone.adapter.PagerAdapter;
import com.kabouzeid.gramophone.dialogs.AboutDialog;
import com.kabouzeid.gramophone.dialogs.CreatePlaylistDialog;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
import com.kabouzeid.gramophone.loader.AlbumSongLoader;
import com.kabouzeid.gramophone.loader.ArtistSongLoader;
@ -57,7 +59,7 @@ import java.util.List;
import java.util.Set;
public class MainActivity extends AbsFabActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks, KabViewsDisableAble {
implements NavigationDrawerFragment.NavigationDrawerCallbacks, KabViewsDisableAble, CabHolder {
public static final String TAG = MainActivity.class.getSimpleName();
@ -69,6 +71,7 @@ public class MainActivity extends AbsFabActivity
private ViewPager viewPager;
private PagerSlidingTabStrip slidingTabLayout;
private int currentPage = -1;
private MaterialCab cab;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -553,4 +556,14 @@ public class MainActivity extends AbsFabActivity
});
return super.onMenuOpened(featureId, menu);
}
@Override
public MaterialCab openCab(final int menu, final MaterialCab.Callback callback) {
if (cab != null) cab.finish();
cab = new MaterialCab(this, R.id.cab_stub)
.setMenu(menu)
.setBackgroundColor(PreferenceUtils.getInstance(this).getThemeColorPrimary())
.start(callback);
return cab;
}
}

View file

@ -5,6 +5,7 @@ import android.support.v4.app.Fragment;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
import com.kabouzeid.gramophone.ui.activities.MainActivity;
import com.kabouzeid.gramophone.util.Util;
/**
@ -29,6 +30,10 @@ public abstract class AbsMainActivityFragment extends Fragment implements KabVie
return getResources().getDimensionPixelSize(R.dimen.bottom_offset_fab_activity);
}
protected MainActivity getMainActivity() {
return (MainActivity) getActivity();
}
@Override
public void enableViews() {
areViewsEnabled = true;

View file

@ -26,6 +26,6 @@ public class SongViewFragment extends AbsMainActivityRecyclerViewFragment {
@Override
protected RecyclerView.Adapter createAdapter() {
return new SongAdapter((AppCompatActivity) getActivity());
return new SongAdapter((AppCompatActivity) getActivity(), getMainActivity());
}
}