diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2aef90ab..cec4f813 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + @@ -18,7 +18,6 @@ android:supportsRtl="true" android:theme="@style/Theme.MaterialMusic.Light" tools:ignore="UnusedAttribute"> - @@ -30,12 +29,11 @@ - + - @@ -140,7 +138,9 @@ - + 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 6c0e8007..73b8af88 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/AbsMultiSelectAdapter.java @@ -29,6 +29,10 @@ public abstract class AbsMultiSelectAdapter(checked)); cab.finish(); - uncheckAll(); + unCheckAll(); return true; } @Override public boolean onCabFinished(MaterialCab materialCab) { - uncheckAll(); + unCheckAll(); return true; } 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 60ac2d8a..b9342b0d 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java @@ -8,6 +8,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; @@ -21,7 +22,9 @@ import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.loader.PlaylistLoader; import com.kabouzeid.gramophone.loader.PlaylistSongLoader; import com.kabouzeid.gramophone.model.DataBaseChangedEvent; +import com.kabouzeid.gramophone.model.LastAddedPlaylist; import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.SmartPlaylist; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; import com.kabouzeid.gramophone.util.NavigationUtil; @@ -30,12 +33,19 @@ import com.squareup.otto.Subscribe; import java.util.ArrayList; import java.util.List; +import butterknife.ButterKnife; +import butterknife.InjectView; + /** * @author Karim Abou Zeid (kabouzeid) */ public class PlaylistAdapter extends AbsMultiSelectAdapter { public static final String TAG = PlaylistAdapter.class.getSimpleName(); + + private int VIEW_TYPE_SMART = 0; + private int VIEW_TYPE_DEFAULT = 1; + protected final AppCompatActivity activity; protected List dataSet; @@ -46,12 +56,15 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter(); + dataSet.add(new LastAddedPlaylist(activity)); + dataSet.addAll(PlaylistLoader.getAllPlaylists(activity)); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity).inflate(R.layout.item_list_playlist, parent, false); + int layoutRes = viewType == VIEW_TYPE_DEFAULT ? R.layout.item_list_playlist : R.layout.item_list_smart_playlist; + View view = LayoutInflater.from(activity).inflate(layoutRes, parent, false); return new ViewHolder(view); } @@ -60,6 +73,19 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter getSongList(List playlists) { final ArrayList songs = new ArrayList<>(); for (Playlist playlist : playlists) { - songs.addAll(PlaylistSongLoader.getPlaylistSongList(activity, playlist.id)); + if (playlist instanceof SmartPlaylist) { + songs.addAll(((SmartPlaylist) playlist).getSongs(activity)); + } else { + 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; + @InjectView(R.id.playlist_name) + TextView playlistName; + @InjectView(R.id.menu) + View menu; + @InjectView(R.id.playlist_icon) + ImageView icon; + View view; public ViewHolder(View itemView) { super(itemView); + ButterKnife.inject(this, itemView); view = itemView; - playlistName = (TextView) itemView.findViewById(R.id.playlist_name); - menu = itemView.findViewById(R.id.menu); view.setOnClickListener(this); view.setOnLongClickListener(this); menu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { PopupMenu popupMenu = new PopupMenu(activity, view); - popupMenu.inflate(R.menu.menu_item_playlist); + popupMenu.inflate(getItemViewType() == VIEW_TYPE_SMART ? R.menu.menu_item_smart_playlist : R.menu.menu_item_playlist); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -132,7 +165,8 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter extends AbsMultiSelectAdapter implements PlaylistSongViewHolder.onViewHolderClickListener, PlaylistSongViewHolder.onViewHolderLongClickListener, PlaylistSongViewHolder.onViewHolderMenuItemClickListener { + + public static final String TAG = AlbumSongAdapter.class.getSimpleName(); + protected final AppCompatActivity activity; + protected ArrayList dataSet; + + public AbsPlaylistSongAdapter(AppCompatActivity activity, ArrayList objects, @Nullable CabHolder cabHolder) { + super(activity, cabHolder, R.menu.menu_playlists_songs_selection); + setMultiSelectMenuRes(getMultiSelectMenuRes()); + this.activity = activity; + dataSet = objects; + } + + public void updateDataSet(ArrayList objects) { + dataSet = objects; + notifyDataSetChanged(); + } + + protected int getMultiSelectMenuRes() { + return R.menu.menu_playlists_songs_selection; + } + + protected int getSongMenuRes() { + return R.menu.menu_item_playlist_song; + } + + @Override + public PlaylistSongViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(activity).inflate(R.layout.item_list_song, parent, false); + return new PlaylistSongViewHolder(this, view, getSongMenuRes()); + } + + @Override + public void onBindViewHolder(final PlaylistSongViewHolder holder, int position) { + final S song = dataSet.get(position); + + holder.view.setActivated(isChecked(song)); + holder.songTitle.setText(song.title); + holder.songInfo.setText(song.artistName); + ImageLoader.getInstance().displayImage( + MusicUtil.getAlbumArtUri(song.albumId).toString(), + holder.albumArt, + new DisplayImageOptions.Builder() + .cacheInMemory(true) + .showImageOnFail(R.drawable.default_album_art) + .resetViewBeforeLoading(true) + .build() + ); + } + + @Override + public int getItemCount() { + return dataSet.size(); + } + + @Override + protected S getIdentifier(int position) { + return dataSet.get(position); + } + + @Override + protected void onMultipleItemAction(MenuItem menuItem, ArrayList selection) { + switch (menuItem.getItemId()) { + case R.id.action_delete_from_playlist: + onDeleteFromPlaylist(selection); + break; + case R.id.action_add_to_playlist: + onAddToPlaylist(selection); + break; + case R.id.action_add_to_current_playing: + onAddToCurrentPlaying(selection); + break; + } + } + + @Override + public void onClick(View v, int adapterPosition) { + if (isInQuickSelectMode()) { + toggleChecked(adapterPosition); + } else { + //noinspection unchecked + MusicPlayerRemote.openQueue((ArrayList) (List) dataSet, adapterPosition, true); + } + } + + @Override + public boolean onLongClick(View v, int adapterPosition) { + toggleChecked(adapterPosition); + return true; + } + + @Override + public boolean onMenuItemClick(MenuItem item, PlaylistSongViewHolder viewHolder, int adapterPosition) { + switch (item.getItemId()) { + case R.id.action_delete_from_playlist: + onDeleteFromPlaylist(dataSet.get(adapterPosition)); + return true; + case R.id.action_go_to_album: + Pair[] albumPairs = new Pair[]{ + Pair.create(viewHolder.albumArt, activity.getString(R.string.transition_album_cover)) + }; + if (activity instanceof AbsFabActivity) + albumPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(albumPairs); + NavigationUtil.goToAlbum(activity, dataSet.get(adapterPosition).albumId, albumPairs); + return true; + } + return MenuItemClickHelper.handleSongMenuClick(activity, dataSet.get(adapterPosition), item); + } + + protected void onDeleteFromPlaylist(S song) { + + } + + protected void onDeleteFromPlaylist(ArrayList songs) { + + } + + protected void onAddToPlaylist(ArrayList songs) { + //noinspection unchecked + AddToPlaylistDialog.create((ArrayList) (List) songs).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); + } + + protected void onAddToCurrentPlaying(ArrayList songs) { + //noinspection unchecked + MusicPlayerRemote.enqueue((ArrayList) (List) songs); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java index 84eaf6a9..3406f289 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java @@ -1,165 +1,32 @@ package com.kabouzeid.gramophone.adapter.songadapter; import android.support.annotation.Nullable; -import android.support.v4.util.Pair; import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.TextView; -import com.kabouzeid.gramophone.R; -import com.kabouzeid.gramophone.adapter.AbsMultiSelectAdapter; -import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog; import com.kabouzeid.gramophone.dialogs.RemoveFromPlaylistDialog; -import com.kabouzeid.gramophone.helper.MenuItemClickHelper; -import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.model.PlaylistSong; -import com.kabouzeid.gramophone.model.Song; -import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; -import com.kabouzeid.gramophone.util.MusicUtil; -import com.kabouzeid.gramophone.util.NavigationUtil; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; -import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class PlaylistSongAdapter extends AbsMultiSelectAdapter { - - public static final String TAG = AlbumSongAdapter.class.getSimpleName(); - protected final AppCompatActivity activity; - protected ArrayList dataSet; +public class PlaylistSongAdapter extends AbsPlaylistSongAdapter { public PlaylistSongAdapter(AppCompatActivity activity, ArrayList objects, @Nullable CabHolder cabHolder) { - super(activity, cabHolder, R.menu.menu_playlists_songs_selection); - this.activity = activity; - dataSet = objects; - } - - public void updateDataSet(ArrayList objects) { - dataSet = objects; - notifyDataSetChanged(); + super(activity, objects, cabHolder); } @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(activity).inflate(R.layout.item_list_song, parent, false); - return new ViewHolder(view); + protected void onDeleteFromPlaylist(ArrayList songs) { + super.onDeleteFromPlaylist(songs); + RemoveFromPlaylistDialog.create(songs).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); } @Override - public void onBindViewHolder(final ViewHolder holder, int position) { - final PlaylistSong song = dataSet.get(position); - - holder.view.setActivated(isChecked(song)); - holder.songTitle.setText(song.title); - holder.songInfo.setText(song.artistName); - ImageLoader.getInstance().displayImage( - MusicUtil.getAlbumArtUri(song.albumId).toString(), - holder.albumArt, - new DisplayImageOptions.Builder() - .cacheInMemory(true) - .showImageOnFail(R.drawable.default_album_art) - .resetViewBeforeLoading(true) - .build() - ); - } - - @Override - public int getItemCount() { - return dataSet.size(); - } - - @Override - protected PlaylistSong getIdentifier(int position) { - return dataSet.get(position); - } - - @Override - protected void onMultipleItemAction(MenuItem menuItem, ArrayList selection) { - switch (menuItem.getItemId()) { - case R.id.action_delete_from_playlist: - RemoveFromPlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_FROM_PLAYLIST"); - break; - case R.id.action_add_to_playlist: - //noinspection unchecked - AddToPlaylistDialog.create((ArrayList) (List) selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); - break; - case R.id.action_add_to_current_playing: - //noinspection unchecked - MusicPlayerRemote.enqueue((ArrayList) (List) selection); - break; - } - } - - public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { - final TextView songTitle; - final TextView songInfo; - final ImageView overflowButton; - final ImageView albumArt; - final View view; - - public ViewHolder(View itemView) { - super(itemView); - view = itemView; - songTitle = (TextView) itemView.findViewById(R.id.song_title); - songInfo = (TextView) itemView.findViewById(R.id.song_info); - albumArt = (ImageView) itemView.findViewById(R.id.album_art); - view.setOnClickListener(this); - view.setOnLongClickListener(this); - overflowButton = (ImageView) itemView.findViewById(R.id.menu); - overflowButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - PopupMenu popupMenu = new PopupMenu(activity, v); - popupMenu.inflate(R.menu.menu_item_playlist_song); - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_delete_from_playlist: - RemoveFromPlaylistDialog.create(dataSet.get(getAdapterPosition())).show(activity.getSupportFragmentManager(), "DELETE_FROM_PLAYLIST"); - return true; - case R.id.action_go_to_album: - Pair[] albumPairs = new Pair[]{ - Pair.create(albumArt, activity.getResources().getString(R.string.transition_album_cover)) - }; - if (activity instanceof AbsFabActivity) - albumPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(albumPairs); - NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition()).albumId, albumPairs); - return true; - } - return MenuItemClickHelper.handleSongMenuClick(activity, dataSet.get(getAdapterPosition()), item); - } - }); - popupMenu.show(); - } - }); - } - - @Override - public void onClick(View v) { - if (isInQuickSelectMode()) { - toggleChecked(getAdapterPosition()); - } else { - //noinspection unchecked - MusicPlayerRemote.openQueue((ArrayList) (List) dataSet, getAdapterPosition(), true); - } - } - - @Override - public boolean onLongClick(View view) { - toggleChecked(getAdapterPosition()); - return true; - } + protected void onDeleteFromPlaylist(PlaylistSong song) { + super.onDeleteFromPlaylist(song); + RemoveFromPlaylistDialog.create(song).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongViewHolder.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongViewHolder.java new file mode 100644 index 00000000..eaa8e82b --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongViewHolder.java @@ -0,0 +1,71 @@ +package com.kabouzeid.gramophone.adapter.songadapter; + +import android.support.v7.widget.RecyclerView; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.TextView; + +import com.kabouzeid.gramophone.R; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class PlaylistSongViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + final TextView songTitle; + final TextView songInfo; + final ImageView overflowButton; + final ImageView albumArt; + final View view; + + final AbsPlaylistSongAdapter adapter; + + public PlaylistSongViewHolder(final AbsPlaylistSongAdapter adapter, View itemView, final int songMenu) { + super(itemView); + this.adapter = adapter; + view = itemView; + songTitle = (TextView) itemView.findViewById(R.id.song_title); + songInfo = (TextView) itemView.findViewById(R.id.song_info); + albumArt = (ImageView) itemView.findViewById(R.id.album_art); + view.setOnClickListener(this); + view.setOnLongClickListener(this); + overflowButton = (ImageView) itemView.findViewById(R.id.menu); + overflowButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu popupMenu = new PopupMenu(adapter.activity, v); + popupMenu.inflate(songMenu); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return adapter.onMenuItemClick(item, PlaylistSongViewHolder.this, getAdapterPosition()); + } + }); + popupMenu.show(); + } + }); + } + + @Override + public void onClick(View v) { + adapter.onClick(v, getAdapterPosition()); + } + + @Override + public boolean onLongClick(View v) { + return adapter.onLongClick(v, getAdapterPosition()); + } + + protected interface onViewHolderMenuItemClickListener { + boolean onMenuItemClick(MenuItem item, PlaylistSongViewHolder viewHolder, int adapterPosition); + } + + protected interface onViewHolderClickListener { + void onClick(View v, int adapterPosition); + } + + protected interface onViewHolderLongClickListener { + boolean onLongClick(View v, int adapterPosition); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/LastAddedSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/LastAddedSongAdapter.java new file mode 100644 index 00000000..b1d70c5d --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/LastAddedSongAdapter.java @@ -0,0 +1,28 @@ +package com.kabouzeid.gramophone.adapter.songadapter.smartplaylist; + +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; + +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.interfaces.CabHolder; +import com.kabouzeid.gramophone.model.LastAddedPlaylist; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class LastAddedSongAdapter extends SmartPlaylistSongAdapter { + + public LastAddedSongAdapter(AppCompatActivity activity, @Nullable CabHolder cabHolder) { + super(activity, new LastAddedPlaylist(activity), cabHolder); + } + + @Override + protected int getMultiSelectMenuRes() { + return R.menu.menu_last_added_playlist_songs_selection; + } + + @Override + protected int getSongMenuRes() { + return R.menu.menu_item_last_added_playlist_song; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/SmartPlaylistSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/SmartPlaylistSongAdapter.java new file mode 100644 index 00000000..1c307917 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/smartplaylist/SmartPlaylistSongAdapter.java @@ -0,0 +1,26 @@ +package com.kabouzeid.gramophone.adapter.songadapter.smartplaylist; + +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; + +import com.kabouzeid.gramophone.adapter.songadapter.AbsPlaylistSongAdapter; +import com.kabouzeid.gramophone.interfaces.CabHolder; +import com.kabouzeid.gramophone.model.SmartPlaylist; +import com.kabouzeid.gramophone.model.Song; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public abstract class SmartPlaylistSongAdapter extends AbsPlaylistSongAdapter { + private SmartPlaylist playlist; + + public SmartPlaylistSongAdapter(AppCompatActivity activity, SmartPlaylist playlist, @Nullable CabHolder cabHolder) { + super(activity, playlist.getSongs(activity), cabHolder); + this.playlist = playlist; + } + + public void updateDataSet() { + updateDataSet(playlist.getSongs(activity)); + } + +} 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 c9307c38..2cdd0489 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MenuItemClickHelper.java @@ -1,5 +1,6 @@ package com.kabouzeid.gramophone.helper; +import android.app.Activity; import android.content.Intent; import android.support.v4.util.Pair; import android.support.v7.app.AppCompatActivity; @@ -12,9 +13,11 @@ import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog; import com.kabouzeid.gramophone.dialogs.RenamePlaylistDialog; import com.kabouzeid.gramophone.dialogs.SongDetailDialog; import com.kabouzeid.gramophone.interfaces.PaletteColorHolder; +import com.kabouzeid.gramophone.loader.PlaylistSongLoader; import com.kabouzeid.gramophone.loader.SongFilePathLoader; import com.kabouzeid.gramophone.misc.AppKeys; import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.SmartPlaylist; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; import com.kabouzeid.gramophone.ui.activities.tageditor.SongTagEditorActivity; @@ -22,6 +25,7 @@ import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; import java.io.File; +import java.util.ArrayList; /** * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) @@ -78,6 +82,12 @@ public class MenuItemClickHelper { public static boolean handlePlaylistMenuClick(AppCompatActivity activity, Playlist playlist, MenuItem item) { switch (item.getItemId()) { + case R.id.action_play: + MusicPlayerRemote.openQueue(new ArrayList<>(getPlaylistSongs(activity, playlist)), 0, true); + return true; + case R.id.action_add_to_current_playing: + MusicPlayerRemote.enqueue(new ArrayList<>(getPlaylistSongs(activity, playlist))); + return true; case R.id.action_rename_playlist: RenamePlaylistDialog.create(playlist.id).show(activity.getSupportFragmentManager(), "RENAME_PLAYLIST"); return true; @@ -87,4 +97,10 @@ public class MenuItemClickHelper { } return false; } + + private static ArrayList getPlaylistSongs(Activity activity, Playlist playlist) { + return playlist instanceof SmartPlaylist ? + ((SmartPlaylist) playlist).getSongs(activity) : + PlaylistSongLoader.getPlaylistSongList(activity, playlist.id); + } } 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 261dfb1c..198e6d86 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java @@ -14,7 +14,6 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import java.util.ArrayList; -import java.util.List; import java.util.Random; /** @@ -207,7 +206,7 @@ public class MusicPlayerRemote { return false; } - public static boolean enqueue(List songs) { + public static boolean enqueue(ArrayList songs) { if (musicService != null) { musicService.addSongs(songs); 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()); diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java new file mode 100644 index 00000000..2359ba53 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java @@ -0,0 +1,55 @@ +package com.kabouzeid.gramophone.loader; + +import android.content.Context; +import android.database.Cursor; +import android.provider.BaseColumns; +import android.provider.MediaStore; +import android.provider.MediaStore.Audio.AudioColumns; + +import com.kabouzeid.gramophone.model.Song; +import com.kabouzeid.gramophone.util.PreferenceUtils; + +import java.util.ArrayList; + +public class LastAddedLoader { + + public static ArrayList getLastAddedSongs(Context context) { + return SongLoader.getSongs(makeLastAddedCursor(context)); + } + + public static Cursor makeLastAddedCursor(final Context context) { + long fourWeeksAgo = (System.currentTimeMillis() / 1000) - (4 * 3600 * 24 * 7); + // possible saved timestamp caused by user "clearing" the last added playlist + long cutoff = PreferenceUtils.getInstance(context).getLastAddedCutOff() / 1000; + if (cutoff < fourWeeksAgo) { + cutoff = fourWeeksAgo; + } + + //noinspection StringBufferReplaceableByString + final StringBuilder selection = new StringBuilder(); + selection.append(AudioColumns.IS_MUSIC + "=1"); + selection.append(" AND " + AudioColumns.TITLE + " != ''"); + selection.append(" AND " + MediaStore.Audio.Media.DATE_ADDED + ">"); + selection.append(cutoff); + + return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + new String[]{ + /* 0 */ + BaseColumns._ID, + /* 1 */ + MediaStore.Audio.AudioColumns.TITLE, + /* 2 */ + MediaStore.Audio.AudioColumns.ARTIST, + /* 3 */ + MediaStore.Audio.AudioColumns.ALBUM, + /* 4 */ + MediaStore.Audio.AudioColumns.DURATION, + /* 5 */ + MediaStore.Audio.AudioColumns.TRACK, + /* 6 */ + MediaStore.Audio.AudioColumns.ARTIST_ID, + /* 7 */ + MediaStore.Audio.AudioColumns.ALBUM_ID + }, selection.toString(), null, MediaStore.Audio.Media.DATE_ADDED + " DESC"); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/LastAddedPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/LastAddedPlaylist.java new file mode 100644 index 00000000..851408af --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/model/LastAddedPlaylist.java @@ -0,0 +1,23 @@ +package com.kabouzeid.gramophone.model; + +import android.content.Context; + +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.loader.LastAddedLoader; + +import java.util.ArrayList; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class LastAddedPlaylist extends SmartPlaylist { + + public LastAddedPlaylist(Context context) { + super(context.getString(R.string.last_added), R.drawable.ic_queue_white_24dp); + } + + @Override + public ArrayList getSongs(Context context) { + return LastAddedLoader.getLastAddedSongs(context); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/SmartPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/SmartPlaylist.java new file mode 100644 index 00000000..27325303 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/model/SmartPlaylist.java @@ -0,0 +1,50 @@ +package com.kabouzeid.gramophone.model; + +import android.content.Context; +import android.support.annotation.DrawableRes; + +import com.kabouzeid.gramophone.R; + +import java.util.ArrayList; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public abstract class SmartPlaylist extends Playlist { + private static final long serialVersionUID = 3013701295356403681L; + + @DrawableRes + public final int iconRes; + + public SmartPlaylist(final String name, final int iconRes) { + super(-1, name); + this.iconRes = iconRes; + } + + public SmartPlaylist() { + super(); + this.iconRes = R.drawable.ic_queue_music_white_24dp; + } + + public abstract ArrayList getSongs(Context context); + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + iconRes; + return result; + } + + @Override + public boolean equals(final Object obj) { + if (super.equals(obj)) { + if (getClass() != obj.getClass()) { + return false; + } + final SmartPlaylist other = (SmartPlaylist) obj; + return iconRes == other.iconRes; + } + return false; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java index f78a4945..6bfc2c6b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java @@ -59,7 +59,6 @@ import com.nostra13.universalimageloader.core.ImageLoader; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.List; import java.util.Set; import butterknife.ButterKnife; @@ -419,8 +418,9 @@ public class MainActivity extends AbsFabActivity final int id = (int) parseIdFromIntent(intent, "playlistId", "playlist"); if (id >= 0) { int position = intent.getIntExtra("position", 0); - //noinspection unchecked - MusicPlayerRemote.openQueue((ArrayList) (List) PlaylistSongLoader.getPlaylistSongList(this, id), position, true); + ArrayList songs = new ArrayList<>(); + songs.addAll(PlaylistSongLoader.getPlaylistSongList(this, id)); + MusicPlayerRemote.openQueue(songs, position, true); handled = true; } } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java index f8d6955c..a71e8882 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java @@ -13,9 +13,7 @@ import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.songadapter.PlaylistSongAdapter; import com.kabouzeid.gramophone.interfaces.CabHolder; -import com.kabouzeid.gramophone.loader.PlaylistLoader; import com.kabouzeid.gramophone.loader.PlaylistSongLoader; -import com.kabouzeid.gramophone.misc.AppKeys; import com.kabouzeid.gramophone.misc.DragSortRecycler; import com.kabouzeid.gramophone.model.DataBaseChangedEvent; import com.kabouzeid.gramophone.model.Playlist; @@ -32,6 +30,8 @@ public class PlaylistDetailActivity extends AbsFabActivity implements CabHolder public static final String TAG = PlaylistDetailActivity.class.getSimpleName(); + public static String EXTRA_PLAYLIST = "extra_playlist"; + private Playlist playlist; private MaterialCab cab; private PlaylistSongAdapter adapter; @@ -86,8 +86,10 @@ public class PlaylistDetailActivity extends AbsFabActivity implements CabHolder private void getIntentExtras() { Bundle intentExtras = getIntent().getExtras(); - final int playlistId = intentExtras.getInt(AppKeys.E_PLAYLIST); - playlist = PlaylistLoader.getPlaylist(this, playlistId); + try { + playlist = (Playlist) intentExtras.getSerializable(EXTRA_PLAYLIST); + } catch (ClassCastException ignored) { + } if (playlist == null) { finish(); } @@ -120,6 +122,9 @@ public class PlaylistDetailActivity extends AbsFabActivity implements CabHolder case R.id.action_playing_queue: NavigationUtil.openPlayingQueueDialog(this); return true; + case R.id.action_clear_playlist: + //TODO + return true; } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SmartPlaylistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SmartPlaylistDetailActivity.java new file mode 100644 index 00000000..181b64f9 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SmartPlaylistDetailActivity.java @@ -0,0 +1,162 @@ +package com.kabouzeid.gramophone.ui.activities; + +import android.os.Bundle; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import com.afollestad.materialcab.MaterialCab; +import com.kabouzeid.gramophone.App; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.adapter.songadapter.smartplaylist.LastAddedSongAdapter; +import com.kabouzeid.gramophone.adapter.songadapter.smartplaylist.SmartPlaylistSongAdapter; +import com.kabouzeid.gramophone.interfaces.CabHolder; +import com.kabouzeid.gramophone.model.DataBaseChangedEvent; +import com.kabouzeid.gramophone.model.SmartPlaylist; +import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; +import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PreferenceUtils; +import com.squareup.otto.Subscribe; + +import butterknife.ButterKnife; +import butterknife.InjectView; + +public class SmartPlaylistDetailActivity extends AbsFabActivity implements CabHolder { + + public static final String TAG = SmartPlaylistDetailActivity.class.getSimpleName(); + + @InjectView(R.id.recycler_view) + RecyclerView recyclerView; + @InjectView(R.id.toolbar) + Toolbar toolbar; + @InjectView(android.R.id.empty) + TextView empty; + + private SmartPlaylist playlist; + private MaterialCab cab; + private SmartPlaylistSongAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_playlist_detail); + ButterKnife.inject(this); + + getIntentExtras(); + + setUpRecyclerView(); + + checkIsEmpty(); + + setUpToolBar(); + + if (PreferenceUtils.getInstance(this).coloredNavigationBarPlaylist()) + setNavigationBarThemeColor(); + setStatusBarThemeColor(); + + App.bus.register(this); + } + + private void setUpRecyclerView() { + adapter = new LastAddedSongAdapter(this, this); + + recyclerView.setLayoutManager(new GridLayoutManager(this, 1)); + recyclerView.setAdapter(adapter); + } + + private void setUpToolBar() { + toolbar.setBackgroundColor(getThemeColorPrimary()); + setSupportActionBar(toolbar); + //noinspection ConstantConditions + getSupportActionBar().setTitle(playlist.name); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + private void getIntentExtras() { + Bundle intentExtras = getIntent().getExtras(); + try { + playlist = (SmartPlaylist) intentExtras.getSerializable(PlaylistDetailActivity.EXTRA_PLAYLIST); + } catch (ClassCastException ignored) { + } + if (playlist == null) { + finish(); + } + } + + @Override + public String getTag() { + return TAG; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_playlist_detail, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + switch (id) { + case R.id.action_equalizer: + NavigationUtil.openEqualizer(this); + return true; + case android.R.id.home: + super.onBackPressed(); + return true; + case R.id.action_current_playing: + NavigationUtil.openCurrentPlayingIfPossible(this, getSharedViewsWithFab(null)); + return true; + case R.id.action_playing_queue: + NavigationUtil.openPlayingQueueDialog(this); + return true; + case R.id.action_clear_playlist: + //TODO + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public MaterialCab openCab(final int menu, final MaterialCab.Callback callback) { + if (cab != null && cab.isActive()) cab.finish(); + cab = new MaterialCab(this, R.id.cab_stub) + .setMenu(menu) + .setBackgroundColor(PreferenceUtils.getInstance(this).getThemeColorPrimary()) + .start(callback); + return cab; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + App.bus.unregister(this); + } + + @Subscribe + public void onDataBaseEvent(DataBaseChangedEvent event) { + switch (event.getAction()) { + case DataBaseChangedEvent.PLAYLISTS_CHANGED: + case DataBaseChangedEvent.DATABASE_CHANGED: + adapter.updateDataSet(); + checkIsEmpty(); + break; + } + } + + @Override + public void onBackPressed() { + if (cab != null && cab.isActive()) cab.finish(); + else super.onBackPressed(); + } + + private void checkIsEmpty() { + empty.setVisibility( + adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE + ); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/AbsMainActivityRecyclerViewFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/AbsMainActivityRecyclerViewFragment.java index baddab7a..f8606821 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/AbsMainActivityRecyclerViewFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/AbsMainActivityRecyclerViewFragment.java @@ -1,6 +1,7 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments; import android.os.Bundle; +import android.support.annotation.LayoutRes; import android.support.annotation.StringRes; import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout.OnOffsetChangedListener; @@ -15,38 +16,49 @@ import android.widget.TextView; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.views.FastScroller; +import butterknife.ButterKnife; +import butterknife.InjectView; +import butterknife.Optional; + /** * @author Karim Abou Zeid (kabouzeid) */ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivityFragment implements OnOffsetChangedListener { public static final String TAG = AbsMainActivityRecyclerViewFragment.class.getSimpleName(); - private RecyclerView recyclerView; + + @InjectView(R.id.recycler_view) + RecyclerView recyclerView; + @Optional + @InjectView(android.R.id.empty) + TextView empty; + @Optional + @InjectView(R.id.fast_scroller) + FastScroller fastScroller; + private RecyclerView.Adapter mAdapter; - private FastScroller fastScroller; - private TextView empty; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false); + View view = inflater.inflate(getLayoutRes(), container, false); + ButterKnife.inject(this, view); + return view; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); - fastScroller = (FastScroller) view.findViewById(R.id.fast_scroller); - empty = (TextView) view.findViewById(android.R.id.empty); - - fastScroller.setRecyclerView(recyclerView); - fastScroller.setPressedHandleColor(getMainActivity().getThemeColorPrimary()); - fastScroller.setOnHandleTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - return false; - } - }); + if (fastScroller != null) { + fastScroller.setRecyclerView(recyclerView); + fastScroller.setPressedHandleColor(getMainActivity().getThemeColorPrimary()); + fastScroller.setOnHandleTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + }); + } getMainActivity().addOnAppBarOffsetChangedListener(this); @@ -75,9 +87,11 @@ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivit @Override public void onOffsetChanged(AppBarLayout appBarLayout, int i) { - FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) fastScroller.getLayoutParams(); - params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, getMainActivity().getTotalAppBarScrollingRange() + i); - fastScroller.setLayoutParams(params); + if (fastScroller != null) { + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) fastScroller.getLayoutParams(); + params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, getMainActivity().getTotalAppBarScrollingRange() + i); + fastScroller.setLayoutParams(params); + } } @Override @@ -93,10 +107,12 @@ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivit } private void showEmptyMessageIfEmpty() { - RecyclerView.Adapter adapter = getAdapter(); - if (adapter != null) { - empty.setText(getEmptyMessage()); - empty.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + if (empty != null) { + RecyclerView.Adapter adapter = getAdapter(); + if (adapter != null) { + empty.setText(getEmptyMessage()); + empty.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } } } @@ -105,7 +121,22 @@ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivit return R.string.empty; } + @LayoutRes + protected int getLayoutRes() { + return R.layout.fragment_main_activity_recycler_view; + } + + protected RecyclerView getRecyclerView() { + return recyclerView; + } + protected abstract RecyclerView.LayoutManager createLayoutManager(); protected abstract RecyclerView.Adapter createAdapter(); + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.reset(this); + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java index 98e20fb2..b2c70fca 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java @@ -15,10 +15,13 @@ import com.kabouzeid.gramophone.dialogs.PlayingQueueDialog; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble; import com.kabouzeid.gramophone.misc.AppKeys; +import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.SmartPlaylist; import com.kabouzeid.gramophone.ui.activities.AlbumDetailActivity; import com.kabouzeid.gramophone.ui.activities.ArtistDetailActivity; import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity; import com.kabouzeid.gramophone.ui.activities.PlaylistDetailActivity; +import com.kabouzeid.gramophone.ui.activities.SmartPlaylistDetailActivity; /** * @author Karim Abou Zeid (kabouzeid) @@ -63,12 +66,19 @@ public class NavigationUtil { } } - public static void goToPlaylist(final Activity activity, final int playlistId, final Pair[] sharedViews) { + public static void goToPlaylist(final Activity activity, final Playlist playlist, final Pair[] sharedViews) { if ((activity instanceof KabViewsDisableAble && ((KabViewsDisableAble) activity).areViewsEnabled()) || !(activity instanceof KabViewsDisableAble)) { if (activity instanceof KabViewsDisableAble) ((KabViewsDisableAble) activity).disableViews(); - final Intent intent = new Intent(activity, PlaylistDetailActivity.class); - intent.putExtra(AppKeys.E_PLAYLIST, playlistId); + + final Intent intent; + if (playlist instanceof SmartPlaylist) { + intent = new Intent(activity, SmartPlaylistDetailActivity.class); + } else { + intent = new Intent(activity, PlaylistDetailActivity.class); + } + intent.putExtra(PlaylistDetailActivity.EXTRA_PLAYLIST, playlist); + if (sharedViews != null) { @SuppressWarnings("unchecked") ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedViews 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 0a2be896..edafb2ac 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java @@ -24,9 +24,6 @@ import java.util.List; */ public class PlaylistsUtil { -// public static final String MUSIC_ONLY_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" -// + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"; //$NON-NLS-2$ - public static int createPlaylist(final Context context, final String name) { if (name != null && name.length() > 0) { final ContentResolver resolver = context.getContentResolver(); @@ -61,13 +58,13 @@ public class PlaylistsUtil { // context.getContentResolver().delete(uri, null, null); // } - 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); - App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED)); - } +// 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); +// 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; @@ -136,19 +133,20 @@ public class PlaylistsUtil { return contentValues; } - public static void removeFromPlaylist(final Context context, final PlaylistSong song) { - Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( - "external", song.playlistId); - String selection = MediaStore.Audio.Playlists.Members._ID + " =?"; - String[] selectionArgs = new String[]{String.valueOf(song.idInPlayList)}; - - context.getContentResolver().delete(uri, selection, selectionArgs); - App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED)); - } +// public static void removeFromPlaylist(final Context context, final PlaylistSong song) { +// Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( +// "external", song.playlistId); +// String selection = MediaStore.Audio.Playlists.Members._ID + " =?"; +// String[] selectionArgs = new String[]{String.valueOf(song.idInPlayList)}; +// +// context.getContentResolver().delete(uri, selection, selectionArgs); +// App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED)); +// } public static void removeFromPlaylist(final Context context, final List songs) { + final int playlistId = songs.get(0).playlistId; Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( - "external", songs.get(0).playlistId); + "external", playlistId); String selectionArgs[] = new String[songs.size()]; for (int i = 0; i < selectionArgs.length; i++) { selectionArgs[i] = String.valueOf(songs.get(i).idInPlayList); diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtils.java b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtils.java index 6aaaf4f8..9521de1b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtils.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtils.java @@ -43,6 +43,7 @@ public final class PreferenceUtils { public static final String FADE_PLAY_PAUSE = "fade_play_pause"; public static final String COLORED_NOTIFICATION = "colored_notification"; public static final String GAPLESS_PLAYBACK = "gapless_playback"; + public static final String LAST_ADDED_CUTOFF_TIMESTAMP = "last_added_cutoff_timestamp"; private static PreferenceUtils sInstance; @@ -283,4 +284,15 @@ public final class PreferenceUtils { public final int getAlbumGridColumnsLand() { return mPreferences.getInt(ALBUM_GRID_COLUMNS_LAND, 3); } + + public long getLastAddedCutOff() { + return mPreferences.getLong(LAST_ADDED_CUTOFF_TIMESTAMP, 0L); + } + + @SuppressLint("CommitPrefEdits") + public void setLastAddedCutoffTimestamp(final long timestamp) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putLong(LAST_ADDED_CUTOFF_TIMESTAMP, timestamp); + editor.commit(); + } } diff --git a/app/src/main/res/drawable-hdpi/ic_access_time_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_access_time_white_24dp.png new file mode 100644 index 00000000..4e90aabd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_access_time_white_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_trending_up_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_trending_up_white_24dp.png new file mode 100644 index 00000000..07ada8a9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_trending_up_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_access_time_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_access_time_white_24dp.png new file mode 100644 index 00000000..c25b7a9e Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_access_time_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_trending_up_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_trending_up_white_24dp.png new file mode 100644 index 00000000..a8590be5 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_trending_up_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_access_time_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_access_time_white_24dp.png new file mode 100644 index 00000000..e7c496e4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_access_time_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_trending_up_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_trending_up_white_24dp.png new file mode 100644 index 00000000..ecb316ff Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_trending_up_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_access_time_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_access_time_white_24dp.png new file mode 100644 index 00000000..266ca418 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_access_time_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_trending_up_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_trending_up_white_24dp.png new file mode 100644 index 00000000..3bf0adca Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_trending_up_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_access_time_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_access_time_white_24dp.png new file mode 100644 index 00000000..b812470e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_access_time_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_trending_up_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_trending_up_white_24dp.png new file mode 100644 index 00000000..834787a4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_trending_up_white_24dp.png differ diff --git a/app/src/main/res/layout/fragment_main_activity_recycler_view.xml b/app/src/main/res/layout/fragment_main_activity_recycler_view.xml index 65c24d6b..fd944ae2 100644 --- a/app/src/main/res/layout/fragment_main_activity_recycler_view.xml +++ b/app/src/main/res/layout/fragment_main_activity_recycler_view.xml @@ -4,7 +4,6 @@ android:layout_height="match_parent"> + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_album_song.xml b/app/src/main/res/layout/item_list_album_song.xml index cc9d1ab6..0908ace9 100644 --- a/app/src/main/res/layout/item_list_album_song.xml +++ b/app/src/main/res/layout/item_list_album_song.xml @@ -1,18 +1,21 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="72dp" + android:background="?card_color" + android:elevation="2dp" + android:foreground="?rect_selector" + tools:ignore="UnusedAttribute"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:descendantFocusability="blocksDescendants" + android:orientation="horizontal" + android:paddingLeft="16dp" + android:paddingStart="16dp" + tools:ignore="RtlSymmetry"> + android:background="?card_color" + android:elevation="2dp" + android:foreground="?rect_selector" + tools:ignore="UnusedAttribute"> + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="72dp" + android:background="?card_color" + android:elevation="2dp" + android:foreground="?rect_selector" + tools:ignore="UnusedAttribute"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:descendantFocusability="blocksDescendants" + android:orientation="horizontal" + android:paddingLeft="16dp" + android:paddingStart="16dp"> + android:background="?card_color" + android:elevation="2dp" + android:foreground="?rect_selector" + tools:ignore="UnusedAttribute"> diff --git a/app/src/main/res/layout/item_list_playlist_song.xml b/app/src/main/res/layout/item_list_playlist_song.xml index ff4ee99d..cbd7036e 100644 --- a/app/src/main/res/layout/item_list_playlist_song.xml +++ b/app/src/main/res/layout/item_list_playlist_song.xml @@ -3,9 +3,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="64dp" + android:background="?card_color" android:descendantFocusability="blocksDescendants" + android:elevation="2dp" android:orientation="horizontal" - tools:ignore="ContentDescription"> + tools:ignore="UnusedAttribute"> + tools:ignore="ContentDescription" /> - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_smart_playlist.xml b/app/src/main/res/layout/item_list_smart_playlist.xml new file mode 100644 index 00000000..066b1ba3 --- /dev/null +++ b/app/src/main/res/layout/item_list_smart_playlist.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_song.xml b/app/src/main/res/layout/item_list_song.xml index 05c44643..7421dea5 100644 --- a/app/src/main/res/layout/item_list_song.xml +++ b/app/src/main/res/layout/item_list_song.xml @@ -1,9 +1,12 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="72dp" + android:background="?card_color" + android:elevation="2dp" + android:foreground="?rect_selector" + tools:ignore="UnusedAttribute"> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_item_playlist.xml b/app/src/main/res/menu/menu_item_playlist.xml index 8d2f772b..b13d1de4 100644 --- a/app/src/main/res/menu/menu_item_playlist.xml +++ b/app/src/main/res/menu/menu_item_playlist.xml @@ -1,9 +1,16 @@ + + + + android:title="@string/rename_playlist_title" /> + android:title="@string/delete_playlist_title" /> \ No newline at end of file diff --git a/app/src/main/res/menu/menu_item_smart_playlist.xml b/app/src/main/res/menu/menu_item_smart_playlist.xml new file mode 100644 index 00000000..5a11aaf7 --- /dev/null +++ b/app/src/main/res/menu/menu_item_smart_playlist.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_last_added_playlist_songs_selection.xml b/app/src/main/res/menu/menu_last_added_playlist_songs_selection.xml new file mode 100644 index 00000000..f9fa21ff --- /dev/null +++ b/app/src/main/res/menu/menu_last_added_playlist_songs_selection.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_playlist_detail.xml b/app/src/main/res/menu/menu_playlist_detail.xml index eb30fa3e..efd710d6 100644 --- a/app/src/main/res/menu/menu_playlist_detail.xml +++ b/app/src/main/res/menu/menu_playlist_detail.xml @@ -9,6 +9,11 @@ android:title="@string/action_currently_playing" app:showAsAction="ifRoom" /> + + Phonograph \u0020Cookicons \u0020Karim Abou Zeid.\n\nGoogle+   Twitter\n\n + + SmartPlaylistDetailActivity + TagEditorActivity + AlbumTagEditorActivity + SettingsActivity + PlaylistActivity + SearchActivity \ 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 e56d9d04..7b450a00 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,9 +10,11 @@ "Settings" About Currently playing + Clear playlist Playing queue Search Play next + Play Add to playing queue Remove from playing queue Add to playlist… @@ -28,7 +30,6 @@ We were not able to find a matching biography for this artist. Biography No audio focus. - Tag editor Tag editor Album Artist @@ -40,7 +41,6 @@ The album title or the album artist text field is empty. Writing file Saving changes… - Album Tag Editor Details File name File path @@ -55,13 +55,11 @@ Save as playlist Icon by "Phonograph is a completely free material designed music player by" - Search No results Update artist image Updating… "Added 1 title to the playing queue." Added %1$d titles to the playing queue. - Playlist Activity Remove from playlist New playlist… Grid columns @@ -99,7 +97,6 @@ In which views the navigation bar should be colored. Colored navigation bar Start page - Settings Artist view Album view Playing view @@ -146,15 +143,18 @@ "Fades the song in/out on play/pause." "Eliminates the gap between two songs. Disabling this might fix playback issues." Album art in the now playing view is forced to be squared. - The toolbar is opaque and do not cover the album art. - The statusbar is opaque and do not cover the album art. + The toolbar is opaque and does not cover the album art. + The statusbar is opaque and does not cover the album art. The box below the album art where the song title and artist goes is larger in its height. - Uses a progressbar which do not cover the album art instead of the normal progress slider. + Uses a progressbar which does not cover the album art instead of the default progress slider. Displays a card below the playback controller buttons (play/pause etc.). "Unable to download the art for this album." Search your library… Rescanning media… - Other + Favorites + Last added + Recently played + My top tracks Download from Last.fm Pick from Local Storage