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 00000000..15d7528e
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png
new file mode 100644
index 00000000..44a35a7b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png differ
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 00000000..ae058bb4
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png differ
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 00000000..1462ad09
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png differ
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 00000000..416195b4
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png differ
diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml
new file mode 100644
index 00000000..6dd63109
--- /dev/null
+++ b/app/src/main/res/layout/activity_playlist_detail.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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