From de15a34365b47ca2ef4a4815498c769f8b771c82 Mon Sep 17 00:00:00 2001 From: Karim Abou Zeid Date: Tue, 17 Mar 2015 15:09:44 +0100 Subject: [PATCH] Playlists [beta] --- app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 5 + .../java/com/kabouzeid/gramophone/App.java | 2 + .../adapter/PlayingQueueAdapter.java | 4 +- .../gramophone/adapter/PlaylistAdapter.java | 63 ++++++++ .../songadapter/PlaylistSongAdapter.java | 142 ++++++++++++++++++ .../gramophone/helper/MusicPlayerRemote.java | 6 + .../gramophone/loader/PlaylistLoader.java | 57 +++++++ .../gramophone/loader/PlaylistSongLoader.java | 69 +++++++++ .../kabouzeid/gramophone/misc/AppKeys.java | 1 + .../kabouzeid/gramophone/model/Playlist.java | 16 ++ .../gramophone/model/PlaylistSong.java | 19 +++ .../gramophone/service/MusicService.java | 10 ++ .../ui/activities/MainActivity.java | 40 +---- .../ui/activities/PlaylistDetailActivity.java | 87 +++++++++++ .../fragments/NavigationDrawerFragment.java | 1 - .../PlaylistViewFragment.java | 59 ++++++++ .../kabouzeid/gramophone/util/MusicUtil.java | 62 ++++++++ .../gramophone/util/NavigationUtil.java | 18 +++ .../gramophone/util/PlaylistsUtil.java | 99 ++++++++++++ .../ic_queue_music_white_48dp.png | Bin 0 -> 409 bytes .../ic_queue_music_white_48dp.png | Bin 0 -> 333 bytes .../ic_queue_music_white_48dp.png | Bin 0 -> 538 bytes .../ic_queue_music_white_48dp.png | Bin 0 -> 747 bytes .../ic_queue_music_white_48dp.png | Bin 0 -> 1005 bytes .../res/layout/activity_playlist_detail.xml | 43 ++++++ .../res/layout/fragment_playlist_view.xml | 16 ++ .../main/res/layout/item_list_playlist.xml | 31 ++-- .../res/layout/item_list_playlist_song.xml | 31 ++++ .../main/res/menu/menu_playlist_detail.xml | 20 +++ app/src/main/res/menu/menu_playlist_song.xml | 40 +++++ app/src/main/res/values-w820dp/dimens.xml | 6 + app/src/main/res/values/strings.xml | 2 + 33 files changed, 896 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/model/PlaylistSong.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java create mode 100644 app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java create mode 100644 app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png create mode 100644 app/src/main/res/layout/activity_playlist_detail.xml create mode 100644 app/src/main/res/layout/fragment_playlist_view.xml create mode 100644 app/src/main/res/layout/item_list_playlist_song.xml create mode 100644 app/src/main/res/menu/menu_playlist_detail.xml create mode 100644 app/src/main/res/menu/menu_playlist_song.xml create mode 100644 app/src/main/res/values-w820dp/dimens.xml diff --git a/app/build.gradle b/app/build.gradle index 9f059cfa..1679a730 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -52,12 +52,13 @@ dependencies { compile 'com.nhaarman.listviewanimations:lib-core:3.1.0@aar' compile 'com.nhaarman.listviewanimations:lib-manipulation:3.1.0@aar' compile 'com.nhaarman.listviewanimations:lib-core-slh:3.1.0@aar' - compile 'com.nineoldandroids:library:2.4.0+' + compile 'com.nineoldandroids:library:2.4.0' compile 'com.melnykov:floatingactionbutton:1.2.0' compile 'com.github.ksoichiro:android-observablescrollview:1.5.0' compile 'com.mcxiaoke.volley:library:1.0.+' compile 'com.squareup.picasso:picasso:2.5.0' compile 'com.squareup:otto:1.3.6' + compile 'com.squareup.okhttp:okhttp:2.2.0' compile('com.crashlytics.sdk.android:crashlytics:2.2.1@aar') { transitive = true; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 40f993f2..d42e7a3a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,6 +70,11 @@ android:name="android.appwidget.provider" android:resource="@xml/music_player_widget_info" /> + + + diff --git a/app/src/main/java/com/kabouzeid/gramophone/App.java b/app/src/main/java/com/kabouzeid/gramophone/App.java index 12029193..91a1012b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/App.java +++ b/app/src/main/java/com/kabouzeid/gramophone/App.java @@ -12,6 +12,7 @@ import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.misc.AppKeys; import com.squareup.otto.Bus; import com.squareup.otto.ThreadEnforcer; +import com.squareup.picasso.Picasso; import io.fabric.sdk.android.Fabric; @@ -31,6 +32,7 @@ public class App extends Application { super.onCreate(); Fabric.with(this, new Crashlytics()); MusicPlayerRemote.init(this); + //Picasso.with(this).setIndicatorsEnabled(true);// debug only } public int getAppTheme() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlayingQueueAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlayingQueueAdapter.java index 9e23a60a..e02e838c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlayingQueueAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlayingQueueAdapter.java @@ -30,7 +30,7 @@ public class PlayingQueueAdapter extends ArrayAdapter { private Activity activity; public PlayingQueueAdapter(Activity activity, List playList) { - super(activity, R.layout.item_list_playlist, playList); + super(activity, R.layout.item_list_playlist_song, playList); this.activity = activity; } @@ -38,7 +38,7 @@ public class PlayingQueueAdapter extends ArrayAdapter { public View getView(final int position, View convertView, ViewGroup parent) { final Song song = getItem(position); if (convertView == null) { - convertView = LayoutInflater.from(activity).inflate(R.layout.item_list_playlist, parent, false); + convertView = LayoutInflater.from(activity).inflate(R.layout.item_list_playlist_song, parent, false); } final TextView title = (TextView) convertView.findViewById(R.id.song_title); final ImageView playingIndicator = (ImageView) convertView.findViewById(R.id.playing_indicator); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java new file mode 100644 index 00000000..11d85724 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java @@ -0,0 +1,63 @@ +package com.kabouzeid.gramophone.adapter; + +import android.app.Activity; +import android.support.v4.util.Pair; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; +import com.kabouzeid.gramophone.util.NavigationUtil; + +import java.util.List; + +/** + * Created by karim on 16.03.15. + */ +public class PlaylistAdapter extends RecyclerView.Adapter { + public static final String TAG = PlaylistAdapter.class.getSimpleName(); + protected Activity activity; + protected List dataSet; + + public PlaylistAdapter(Activity activity, List objects) { + this.activity = activity; + dataSet = objects; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(activity).inflate(R.layout.item_list_playlist, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + holder.playlistName.setText(dataSet.get(position).playlistName); + } + + @Override + public int getItemCount() { + return dataSet.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ + public TextView playlistName; + + public ViewHolder(View itemView) { + super(itemView); + playlistName = (TextView) itemView.findViewById(R.id.playlist_name); + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + Pair[] sharedViews = null; + if (activity instanceof AbsFabActivity) sharedViews = ((AbsFabActivity)activity).getSharedViewsWithFab(sharedViews); + NavigationUtil.goToPlaylist(activity, dataSet.get(getPosition()).id, sharedViews); + } + } +} 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 new file mode 100644 index 00000000..20c651db --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/songadapter/PlaylistSongAdapter.java @@ -0,0 +1,142 @@ +package com.kabouzeid.gramophone.adapter.songadapter; + +import android.app.Activity; +import android.content.Intent; +import android.support.v4.util.Pair; +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.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.helper.SongDetailDialogHelper; +import com.kabouzeid.gramophone.loader.SongFilePathLoader; +import com.kabouzeid.gramophone.misc.AppKeys; +import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.PlaylistSong; +import com.kabouzeid.gramophone.model.Song; +import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; +import com.kabouzeid.gramophone.ui.activities.tageditor.SongTagEditorActivity; +import com.kabouzeid.gramophone.util.MusicUtil; +import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PlaylistsUtil; +import com.squareup.picasso.Picasso; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by karim on 27.11.14. + */ +public class PlaylistSongAdapter extends RecyclerView.Adapter { + public static final String TAG = AlbumSongAdapter.class.getSimpleName(); + protected Activity activity; + protected List dataSet; + + public PlaylistSongAdapter(Activity activity, List objects) { + this.activity = activity; + dataSet = objects; + } + + @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); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + final Song song = dataSet.get(position); + + holder.songTitle.setText(song.title); + holder.songInfo.setText(song.artistName); + + Picasso.with(activity) + .load(MusicUtil.getAlbumArtUri(song.albumId)) + .placeholder(R.drawable.default_album_art) + .into(holder.albumArt); + } + + @Override + public int getItemCount() { + return dataSet.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + TextView songTitle; + TextView songInfo; + ImageView overflowButton; + ImageView albumArt; + + public ViewHolder(View itemView) { + super(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); + overflowButton = (ImageView) itemView.findViewById(R.id.menu); + overflowButton.setOnClickListener(this); + itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + MusicPlayerRemote.openQueue((List) (List)dataSet, getAdapterPosition(), true); + } + }); + } + + @Override + public void onClick(View v) { + PopupMenu popupMenu = new PopupMenu(activity, v); + popupMenu.inflate(R.menu.menu_playlist_song); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_delete_from_playlist: + int position = getAdapterPosition(); + PlaylistsUtil.removeFromPlaylist(activity, dataSet.remove(position)); + notifyItemRemoved(position); + return true; + case R.id.action_play_next: + MusicPlayerRemote.playNext(dataSet.get(getAdapterPosition())); + return true; + case R.id.action_add_to_current_playing: + MusicPlayerRemote.enqueue(dataSet.get(getAdapterPosition())); + return true; + case R.id.action_tag_editor: + Intent intent = new Intent(activity, SongTagEditorActivity.class); + intent.putExtra(AppKeys.E_ID, dataSet.get(getAdapterPosition()).id); + activity.startActivity(intent); + return true; + case R.id.action_details: + String songFilePath = SongFilePathLoader.getSongFilePath(activity, dataSet.get(getPosition()).id); + File songFile = new File(songFilePath); + SongDetailDialogHelper.getDialog(activity, songFile).show(); + 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; + case R.id.action_go_to_artist: + Pair[] artistPairs = null; + if (activity instanceof AbsFabActivity) + artistPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(artistPairs); + NavigationUtil.goToArtist(activity, dataSet.get(getAdapterPosition()).artistId, artistPairs); + return true; + } + return false; + } + }); + popupMenu.show(); + } + } +} 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 7eb397e6..48ccbfbc 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java @@ -230,6 +230,12 @@ public class MusicPlayerRemote { } } + public static void removeFromQueue(Song song) { + if (musicService != null) { + musicService.removeSong(song); + } + } + public static void removeFromQueue(int position) { if (musicService != null) { musicService.removeSong(position); diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java new file mode 100644 index 00000000..944cd9cd --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java @@ -0,0 +1,57 @@ +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.PlaylistsColumns; + +import com.kabouzeid.gramophone.model.Playlist; + +import java.util.ArrayList; +import java.util.List; + +public class PlaylistLoader { + public static Playlist getPlaylist(final Context context, final int playlistID) { + Playlist playlist = new Playlist(); + Cursor cursor = makePlaylistCursor(context, BaseColumns._ID + "=" + playlistID); + + if (cursor != null && cursor.moveToFirst()) { + final int id = cursor.getInt(0); + final String name = cursor.getString(1); + playlist = new Playlist(id, name); + } + if (cursor != null) { + cursor.close(); + } + return playlist; + } + + public static List getAllPlaylists(final Context context) { + List playlists = new ArrayList<>(); + Cursor cursor = makePlaylistCursor(context, null); + + if (cursor != null && cursor.moveToFirst()) { + do { + final int id = cursor.getInt(0); + final String name = cursor.getString(1); + final Playlist playlist = new Playlist(id, name); + playlists.add(playlist); + } while (cursor.moveToNext()); + } + if (cursor != null) { + cursor.close(); + } + return playlists; + } + + public static Cursor makePlaylistCursor(final Context context, final String selection) { + return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + new String[]{ + /* 0 */ + BaseColumns._ID, + /* 1 */ + PlaylistsColumns.NAME + }, selection, null, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java new file mode 100644 index 00000000..d1a51c42 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java @@ -0,0 +1,69 @@ +package com.kabouzeid.gramophone.loader; + +import android.content.Context; +import android.database.Cursor; +import android.provider.MediaStore; +import android.provider.MediaStore.Audio.AudioColumns; + +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; + +public class PlaylistSongLoader { + + public static List getPlaylistSongList(final Context context, final int playlistID) { + List songs = new ArrayList<>(); + Cursor cursor = makePlaylistSongCursor(context, playlistID); + + if (cursor != null && cursor.moveToFirst()) { + do { + final int id = cursor.getInt(0); + final String songName = cursor.getString(1); + final String artist = cursor.getString(2); + final String album = cursor.getString(3); + final long duration = cursor.getLong(4); + final int trackNumber = cursor.getInt(5); + final int albumId = cursor.getInt(6); + final int artistId = cursor.getInt(7); + final int idInPlaylist = cursor.getInt(8); + + final PlaylistSong song = new PlaylistSong(id, albumId, artistId, songName, artist, album, duration, trackNumber, playlistID, idInPlaylist); + + songs.add(song); + } while (cursor.moveToNext()); + } + if (cursor != null) { + cursor.close(); + } + return songs; + } + + public static Cursor makePlaylistSongCursor(final Context context, final int playlistID) { + return context.getContentResolver().query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistID), + new String[]{ + /* 0 */ + MediaStore.Audio.Playlists.Members.AUDIO_ID, + /* 1 */ + AudioColumns.TITLE, + /* 2 */ + AudioColumns.ARTIST, + /* 3 */ + AudioColumns.ALBUM, + /* 4 */ + AudioColumns.DURATION, + /* 5 */ + AudioColumns.TRACK, + /* 6 */ + AudioColumns.ALBUM_ID, + /* 7 */ + AudioColumns.ARTIST_ID, + /* 8 */ + MediaStore.Audio.Playlists.Members._ID + }, (AudioColumns.IS_MUSIC + "=1") + " AND " + AudioColumns.TITLE + " != ''", null, + MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/AppKeys.java b/app/src/main/java/com/kabouzeid/gramophone/misc/AppKeys.java index 305a4a97..d362c421 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/AppKeys.java +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/AppKeys.java @@ -21,6 +21,7 @@ public final class AppKeys { public static final String E_ALBUM = "com.kabouzeid.gramophone.ALBUM"; public static final String E_ARTIST = "com.kabouzeid.gramophone.ARTIST"; public static final String E_SONG = "com.kabouzeid.gramophone.SONG"; + public static final String E_PLAYLIST = "com.kabouzeid.gramophone.PLAYLIST"; public static final String E_TAG_EDIT_MODE = "com.kabouzeid.gramophone.TAG_EDIT_MODE"; public static final String E_ID = "com.kabouzeid.gramophone.ID"; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java new file mode 100644 index 00000000..03f11de5 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java @@ -0,0 +1,16 @@ +package com.kabouzeid.gramophone.model; + +public class Playlist { + public int id; + public String playlistName; + + public Playlist(final int id, final String playlistName) { + this.id = id; + this.playlistName = playlistName; + } + + public Playlist() { + this.id = -1; + this.playlistName = ""; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/PlaylistSong.java b/app/src/main/java/com/kabouzeid/gramophone/model/PlaylistSong.java new file mode 100644 index 00000000..866eb8eb --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/model/PlaylistSong.java @@ -0,0 +1,19 @@ +package com.kabouzeid.gramophone.model; + +public class PlaylistSong extends Song{ + public int playlistId; + public 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) { + super(id, albumId, artistId, title, artistName, albumName, duration, trackNumber); + this.playlistId = playlistId; + this.idInPlayList = idInPlayList; + } + + public PlaylistSong() { + super(); + playlistId = -1; + id = -1; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java index e20fdf8d..6601beae 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -499,6 +499,16 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe saveState(); } + public void removeSong(Song song){ + while (playingQueue.contains(song)){ + playingQueue.remove(song); + } + while (originalPlayingQueue.contains(song)){ + originalPlayingQueue.remove(song); + } + saveState(); + } + public void moveSong(int from, int to) { final int currentPosition = getPosition(); Song songToMove = playingQueue.remove(from); 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 a179cfed..5180b7ab 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 @@ -36,6 +36,7 @@ import com.kabouzeid.gramophone.ui.fragments.NavigationDrawerFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.AlbumViewFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.ArtistViewFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.AbsMainActivityFragment; +import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.PlaylistViewFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.SongViewFragment; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; @@ -261,41 +262,11 @@ public class MainActivity extends AbsFabActivity super.onBackPressed(); } - public static class PlaceholderFragmentAbs extends AbsMainActivityFragment { - - public PlaceholderFragmentAbs() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_place_holder, container, false); - TextView text = (TextView) rootView.findViewById(R.id.text); - text.setText("Coming soon!"); - return rootView; - } - - @Override - public void enableViews() { - - } - - @Override - public void disableViews() { - - } - - @Override - public boolean areViewsEnabled() { - return false; - } - } - private class MainActivityViewPagerAdapter extends FragmentPagerAdapter { private String[] titles; - private SparseArray pages; //TODO check if this must be static + private SparseArray pages; private Context context; public MainActivityViewPagerAdapter(Activity activity) { @@ -306,7 +277,6 @@ public class MainActivity extends AbsFabActivity context.getResources().getString(R.string.songs), context.getResources().getString(R.string.albums), context.getResources().getString(R.string.artists), - context.getResources().getString(R.string.genres), context.getResources().getString(R.string.playlists) }; } @@ -321,11 +291,9 @@ public class MainActivity extends AbsFabActivity case 2: return pages.get(position, new ArtistViewFragment()); case 3: - //TODO genres - case 4: - //TODO playlists + return pages.get(position, new PlaylistViewFragment()); } - return pages.get(position, new PlaceholderFragmentAbs()); + return null; } @Override 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 new file mode 100644 index 00000000..070ecfbc --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java @@ -0,0 +1,87 @@ +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 com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.adapter.songadapter.PlaylistSongAdapter; +import com.kabouzeid.gramophone.loader.PlaylistLoader; +import com.kabouzeid.gramophone.loader.PlaylistSongLoader; +import com.kabouzeid.gramophone.misc.AppKeys; +import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.PlaylistSong; +import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; +import com.kabouzeid.gramophone.util.NavigationUtil; + +import java.util.List; + +public class PlaylistDetailActivity extends AbsFabActivity { + public static final String TAG = PlaylistDetailActivity.class.getSimpleName(); + private RecyclerView recyclerView; + private Playlist playlist; + + @Override + protected void onCreate(Bundle savedInstanceState) { + setUpTranslucence(false, false); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_playlist_detail); + + getIntentExtras(); + setUpToolBar(); + + recyclerView = (RecyclerView) findViewById(R.id.recycler_view); + List songs = PlaylistSongLoader.getPlaylistSongList(this, playlist.id); + PlaylistSongAdapter adapter = new PlaylistSongAdapter(this, songs); + recyclerView.setLayoutManager(new GridLayoutManager(this, 1)); + recyclerView.setAdapter(adapter); + } + + private void getIntentExtras() { + Bundle intentExtras = getIntent().getExtras(); + final int playlistId = intentExtras.getInt(AppKeys.E_PLAYLIST); + playlist = PlaylistLoader.getPlaylist(this, playlistId); + if (playlist == null) { + finish(); + } + } + + private void setUpToolBar() { + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + getSupportActionBar().setTitle(playlist.playlistName); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @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 android.R.id.home: + super.onBackPressed(); + return true; + case R.id.action_settings: + 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; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/NavigationDrawerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/NavigationDrawerFragment.java index aa99d370..b2f492b5 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/NavigationDrawerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/NavigationDrawerFragment.java @@ -138,7 +138,6 @@ public class NavigationDrawerFragment extends Fragment { navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.songs), R.drawable.ic_my_library_music_white_24dp)); navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.albums), R.drawable.ic_album_white_24dp)); navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.artists), R.drawable.ic_person_white_24dp)); - navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.genres), R.drawable.ic_my_library_music_white_24dp)); navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.playlists), R.drawable.ic_queue_music_white_24dp)); drawerAdapter = new NavigationDrawerItemAdapter(getActivity(), R.id.navigation_drawer, navigationDrawerItems); 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 new file mode 100644 index 00000000..caf221f8 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivityfragments/PlaylistViewFragment.java @@ -0,0 +1,59 @@ +package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments; + + +import android.os.Bundle; +import android.app.Fragment; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.adapter.PlaylistAdapter; +import com.kabouzeid.gramophone.adapter.songadapter.SongAdapter; +import com.kabouzeid.gramophone.loader.PlaylistLoader; +import com.kabouzeid.gramophone.loader.SongLoader; +import com.kabouzeid.gramophone.model.Playlist; +import com.kabouzeid.gramophone.model.Song; + +import java.util.List; + +public class PlaylistViewFragment extends AbsMainActivityFragment { + public static final String TAG = PlaylistViewFragment.class.getSimpleName(); + + private RecyclerView recyclerView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_playlist_view, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); + setUpRecyclerView(); + } + + private void setUpRecyclerView() { + List playlists = PlaylistLoader.getAllPlaylists(getActivity()); + PlaylistAdapter adapter = new PlaylistAdapter(getActivity(), playlists); + + recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 1)); + recyclerView.setAdapter(adapter); + recyclerView.setPadding(0, getTopPadding(), 0, getBottomPadding()); + } + + @Override + public void enableViews() { + super.enableViews(); + recyclerView.setEnabled(true); + } + + @Override + public void disableViews() { + super.disableViews(); + recyclerView.setEnabled(false); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java index 6a79ebfa..af2fd93e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java @@ -4,15 +4,24 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; +import android.provider.MediaStore; import android.util.Log; +import android.widget.Toast; + +import com.kabouzeid.gramophone.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.loader.SongLoader; +import com.kabouzeid.gramophone.model.Song; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.List; /** * Created by karim on 29.12.14. @@ -86,4 +95,57 @@ public class MusicUtil { } return albumArtDir; } + + public static void deleteTracks(final Context context, final List songs) { + final String[] projection = new String[] { + BaseColumns._ID, MediaStore.MediaColumns.DATA + }; + final StringBuilder selection = new StringBuilder(); + selection.append(BaseColumns._ID + " IN ("); + for (int i = 0; i < songs.size(); i++) { + selection.append(songs.get(i).id); + if (i < songs.size() - 1) { + selection.append(","); + } + } + selection.append(")"); + final Cursor cursor = context.getContentResolver().query( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), + null, null); + if (cursor != null) { + // Step 1: Remove selected tracks from the current playlist, as well + // as from the album art cache + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + final int id = cursor.getInt(0); + final Song song = SongLoader.getSong(context, id); + MusicPlayerRemote.removeFromQueue(song); + } + + // Step 2: Remove selected tracks from the database + context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + selection.toString(), null); + + // Step 3: Remove files from card + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + final String name = cursor.getString(1); + final File f = new File(name); + try { // File.delete can throw a security exception + if (!f.delete()) { + // I'm not sure if we'd ever get here (deletion would + // have to fail, but no exception thrown) + Log.e("MusicUtils", "Failed to delete file " + name); + } + cursor.moveToNext(); + } catch (final SecurityException ex) { + cursor.moveToNext(); + } + } + cursor.close(); + } + context.getContentResolver().notifyChange(Uri.parse("content://media"), null); + Toast.makeText(context, "Deleted " + songs.size() + " songs", Toast.LENGTH_SHORT).show(); + //TODO add resource string + } } 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 4cc45fe1..51699525 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java @@ -16,6 +16,7 @@ import com.kabouzeid.gramophone.misc.AppKeys; 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; /** * Created by karim on 12.03.15. @@ -61,6 +62,23 @@ public class NavigationUtil { } } + public static void goToPlaylist(final Activity activity, final int playlistId, 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); + if (sharedViews != null) { + ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, + sharedViews + ); + ActivityCompat.startActivity(activity, intent, optionsCompat.toBundle()); + } else { + activity.startActivity(intent); + } + } + } + public static void openCurrentPlayingIfPossible(final Activity activity, final Pair[] sharedViews) { if (activity instanceof MusicControllerActivity) { activity.onBackPressed(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java new file mode 100644 index 00000000..a3f538c5 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java @@ -0,0 +1,99 @@ +package com.kabouzeid.gramophone.util; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; +import android.widget.Toast; + +import com.kabouzeid.gramophone.model.PlaylistSong; +import com.kabouzeid.gramophone.model.Song; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by karim on 16.03.15. + */ +public class PlaylistsUtil { + public static int createPlaylist(final Context context, final String name) { + if (name != null && name.length() > 0) { + final ContentResolver resolver = context.getContentResolver(); + final String[] projection = new String[]{ + MediaStore.Audio.PlaylistsColumns.NAME + }; + final String selection = MediaStore.Audio.PlaylistsColumns.NAME + " = '" + name + "'"; + Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + projection, selection, null, null); + if (cursor.getCount() <= 0) { + final ContentValues values = new ContentValues(1); + values.put(MediaStore.Audio.PlaylistsColumns.NAME, name); + final Uri uri = resolver.insert(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + values); + return Integer.parseInt(uri.getLastPathSegment()); + } + cursor.close(); + } + return -1; + } + + public static void deletePlaylist(final Context context, final int playlistId) { + final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); + context.getContentResolver().delete(uri, null, null); + } + + public static void addToPlaylist(final Context context, final Song song, final long playlistId) { + List helperList = new ArrayList<>(); + helperList.add(song); + addToPlaylist(context, helperList, playlistId); + } + + public static void addToPlaylist(final Context context, final List songs, final long playlistId) { + final ContentResolver resolver = context.getContentResolver(); + + final String[] projection = new String[]{ + MediaStore.Audio.PlaylistsColumns.NAME + }; + + final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); + Cursor cursor = resolver.query(uri, projection, null, null, null); + cursor.moveToFirst(); + final String playlistName = cursor.getString(0); + cursor.close(); + + ContentValues[] contentValues = new ContentValues[songs.size()]; + for (int i = 0; i < songs.size(); i++) { + contentValues[i] = new ContentValues(); + contentValues[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(i).id); + } + + resolver.bulkInsert(uri, contentValues); + Toast.makeText(context, "Added " + contentValues.length + " songs to playlist " + playlistName, Toast.LENGTH_SHORT).show(); + //TODO add string resource + } + + 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); + } + + public static void removeFromPlaylist(final Context context, final List songs) { + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( + "external", songs.get(0).playlistId); + String selectionArgs[] = new String[songs.size()]; + for (int i = 0; i < selectionArgs.length; i++) { + selectionArgs[i] = String.valueOf(songs.get(i).idInPlayList); + } + String selection = MediaStore.Audio.Playlists.Members._ID + " in ("; + for (String selectionArg : selectionArgs) selection += "?, "; + selection = selection.substring(0, selection.length() - 2) + ")"; + + context.getContentResolver().delete(uri, selection, selectionArgs); + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..15d7528e9dfd96a2309a1610981ef2131c9c48cc GIT binary patch literal 409 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84i*LmhW}5h&oD4BC?tCX`7$t6sWC7#v@kII zVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3`|Mh?k)`f+xyS#XJBC9EbxddW?mb6i}Gh{o`7vlp266kJTXx6C9X0Rlr0*(9&7K&$ERyTaTNx`Yf=I zP8ItxXW0bKB{5>jrkW{Cn$t7eScEhd#-!~mRqnay$lS&}EuZI@zydL~jOpIHt}<-C zcbw_#rD&G-##atqJJ`AFq`A+j^L`u`UL4)~d1{QA-&@NoKP{fk+plpi(EIh-tF^V; zcPLt0M$0^Ix^_-%^^OHUkL$kped9xD5Hn!&&zUNC1@pbY~915=W>y9>kr_Wm>b85kHi3p^r=85p>Q zL70(Y)*J~228N@aE{-7)hm-%DcZfbteB6)f;A=5LGMR@ zn&Q>qiyWI8E_ujpZ7AY)oBZ;@dG-@*1qLaGF2)(mNA(P61l(teYb2$J2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ae058bb47adff235f38a47e90a03f106c99b7d4f GIT binary patch literal 538 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4i*Lm29JsRH#0CWC?tCX`7$t6sWC7#v@kII zVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3`|Mh?k)`f+xyS#XJBC9EbxddW?ud5QptRUo&Z1wcDS~Aad()}~Nm$Vo5 zFSytIg<fQ?f>)XFw@z-TupH=;R?`K~7G`1zOwwZSq-w*g* z<^9p-!u6f*`j=W@~%jD&-ux!Em#aSH10jru)LjN7+wodwXqP9qa#8 z^KGJbv!X9N-?7{3>O#IPnwbmMS!MlRZ_^#s%C&DU8_3TPobZytezQpZ6PL|Fr63Vc LS3j3^P6dR literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..1462ad09c5262fa788bc3d8b3849e38cade9d02c GIT binary patch literal 747 zcmeAS@N?(olHy`uVBq!ia0y~yV3+{H94rhB48Hza#taM$3dtTpz6=aiY77hwEes65 z7#J8DUNA6}8Za=tN?>5Hn!&&zUNC1@pbY~915=W>y9>kr_Wm>b85kHi3p^r=85p>Q zL70(Y)*J~22Bu_B7srr_TW{~$>N^L@9RGMh-35TVuX(kC^G= z#ILKPO>JK|9lw=*`if74L#$RtFt`1`cY7bsxBjW`P$_D9vysd9Zj#*+f7i9Ycd_j} zKjE9{^Y=<`=Ph5Ox$Wn%vs>SHsq-vYH2WD>s`%;G3m#vOWMnK|dFj%zJl+%E#k3X>zzuGknen4aJ8Tl2?WkNqp#U3Uqexw!2{ z_te0wk4(0^XqSiK(d_3$|FH5RBy)<&$ z&sm9#C!hEnoFaYUitouaGw0k5=SiOUZ{EfA>mT%HI0X6j?+T1;zsV=6_&&)`|0yRp is=)+ie2X~RF|1FY^z`&L4siws1_n=8KbLh*2~7YR4;uym literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..416195b42e4fd2d81aa0a49fe200179c78b35a64 GIT binary patch literal 1005 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE694rhB3_q?e>0w}CP)PO&@?~JCQe$9fXklRZ z#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7?_g0-CY>|xA&jf&%nUIS>O>_%)r1c z48n{Iv*t)JFfgZjx;TbZ+ukq> zYa8R`xPLM4;*xFJpyxGrQS<}dwGl67?B{qjp;cv8tXQ(Ohx_f=xzEq-`B}IAQSzMg zd$xZrE7QHQA#B_EV1)(-HHuN#{%y<>s{iYqj53e?~HU3cK!eD@M#7yH^T zK}(v=>NcMdlCL~%vj5wk^DCcnF6cH=o^$ux^U!+*c0X7r1UJYXlX&XDmG(ey`U0J6 z=Vad%8}waocoUk`HTfRv)>VmGFKz#RWbXMisc%P~^XAfD^2`4UB=6&vddB)YcnAMU zPKQpf2}h6D-?{am_j$;!>;G;r<}LquPW_+iiuRugxAmqkV3YEGzRq0VctQ7i&OOf` z^V}$W+OXW7ReIx>diw+w?{M#hVzBV#&ze%d}-% z(l$QxeY+QEy}ieJH2SLQinv7ksI7Vqd&4%HR@E?cJG#dUEQp)m)Muyeus5x_&+c(< z#$Km=lHrdzw9hsC|33FSWBPTkOU}o3@K=cCJmD-q)O+RVbcORR<`&O#GnD`Gd}%!L zQJ}g^p_Wbqb$bvUks6ogMQu z^MrHHchd*);uZTIUvDx!rMUiI4a0##HOtM5t> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playlist_view.xml b/app/src/main/res/layout/fragment_playlist_view.xml new file mode 100644 index 00000000..7af63040 --- /dev/null +++ b/app/src/main/res/layout/fragment_playlist_view.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/layout/item_list_playlist.xml b/app/src/main/res/layout/item_list_playlist.xml index cc9eab18..fa26b08a 100644 --- a/app/src/main/res/layout/item_list_playlist.xml +++ b/app/src/main/res/layout/item_list_playlist.xml @@ -3,29 +3,28 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="64dp" - android:descendantFocusability="blocksDescendants" + android:background="?rect_selector" android:orientation="horizontal" - > + android:paddingLeft="16dp" + android:paddingRight="16dp"> - + + - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_playlist_song.xml b/app/src/main/res/layout/item_list_playlist_song.xml new file mode 100644 index 00000000..cc9eab18 --- /dev/null +++ b/app/src/main/res/layout/item_list_playlist_song.xml @@ -0,0 +1,31 @@ + + + + + + + + + \ 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 new file mode 100644 index 00000000..41708f2c --- /dev/null +++ b/app/src/main/res/menu/menu_playlist_detail.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_playlist_song.xml b/app/src/main/res/menu/menu_playlist_song.xml new file mode 100644 index 00000000..8056befd --- /dev/null +++ b/app/src/main/res/menu/menu_playlist_song.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..63fc8164 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09d361a9..ca9908da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -74,5 +74,7 @@ Added 1 title to the playing queue. Added titles to the playing queue. + PlaylistDetailActivity + Delete from playlist