Playlists [beta]
This commit is contained in:
parent
f7bc62a2df
commit
de15a34365
33 changed files with 896 additions and 56 deletions
|
|
@ -52,12 +52,13 @@ dependencies {
|
||||||
compile 'com.nhaarman.listviewanimations:lib-core:3.1.0@aar'
|
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-manipulation:3.1.0@aar'
|
||||||
compile 'com.nhaarman.listviewanimations:lib-core-slh: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.melnykov:floatingactionbutton:1.2.0'
|
||||||
compile 'com.github.ksoichiro:android-observablescrollview:1.5.0'
|
compile 'com.github.ksoichiro:android-observablescrollview:1.5.0'
|
||||||
compile 'com.mcxiaoke.volley:library:1.0.+'
|
compile 'com.mcxiaoke.volley:library:1.0.+'
|
||||||
compile 'com.squareup.picasso:picasso:2.5.0'
|
compile 'com.squareup.picasso:picasso:2.5.0'
|
||||||
compile 'com.squareup:otto:1.3.6'
|
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') {
|
compile('com.crashlytics.sdk.android:crashlytics:2.2.1@aar') {
|
||||||
transitive = true;
|
transitive = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,11 @@
|
||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/music_player_widget_info" />
|
android:resource="@xml/music_player_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.activities.PlaylistDetailActivity"
|
||||||
|
android:label="@string/title_activity_playlist_detail" >
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
import com.kabouzeid.gramophone.misc.AppKeys;
|
import com.kabouzeid.gramophone.misc.AppKeys;
|
||||||
import com.squareup.otto.Bus;
|
import com.squareup.otto.Bus;
|
||||||
import com.squareup.otto.ThreadEnforcer;
|
import com.squareup.otto.ThreadEnforcer;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
import io.fabric.sdk.android.Fabric;
|
import io.fabric.sdk.android.Fabric;
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ public class App extends Application {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
Fabric.with(this, new Crashlytics());
|
Fabric.with(this, new Crashlytics());
|
||||||
MusicPlayerRemote.init(this);
|
MusicPlayerRemote.init(this);
|
||||||
|
//Picasso.with(this).setIndicatorsEnabled(true);// debug only
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAppTheme() {
|
public int getAppTheme() {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class PlayingQueueAdapter extends ArrayAdapter<Song> {
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
|
|
||||||
public PlayingQueueAdapter(Activity activity, List<Song> playList) {
|
public PlayingQueueAdapter(Activity activity, List<Song> playList) {
|
||||||
super(activity, R.layout.item_list_playlist, playList);
|
super(activity, R.layout.item_list_playlist_song, playList);
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ public class PlayingQueueAdapter extends ArrayAdapter<Song> {
|
||||||
public View getView(final int position, View convertView, ViewGroup parent) {
|
public View getView(final int position, View convertView, ViewGroup parent) {
|
||||||
final Song song = getItem(position);
|
final Song song = getItem(position);
|
||||||
if (convertView == null) {
|
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 TextView title = (TextView) convertView.findViewById(R.id.song_title);
|
||||||
final ImageView playingIndicator = (ImageView) convertView.findViewById(R.id.playing_indicator);
|
final ImageView playingIndicator = (ImageView) convertView.findViewById(R.id.playing_indicator);
|
||||||
|
|
|
||||||
|
|
@ -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<PlaylistAdapter.ViewHolder> {
|
||||||
|
public static final String TAG = PlaylistAdapter.class.getSimpleName();
|
||||||
|
protected Activity activity;
|
||||||
|
protected List<Playlist> dataSet;
|
||||||
|
|
||||||
|
public PlaylistAdapter(Activity activity, List<Playlist> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<PlaylistSongAdapter.ViewHolder> {
|
||||||
|
public static final String TAG = AlbumSongAdapter.class.getSimpleName();
|
||||||
|
protected Activity activity;
|
||||||
|
protected List<PlaylistSong> dataSet;
|
||||||
|
|
||||||
|
public PlaylistSongAdapter(Activity activity, List<PlaylistSong> 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<Song>) (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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
public static void removeFromQueue(int position) {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService.removeSong(position);
|
musicService.removeSong(position);
|
||||||
|
|
|
||||||
|
|
@ -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<Playlist> getAllPlaylists(final Context context) {
|
||||||
|
List<Playlist> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<PlaylistSong> getPlaylistSongList(final Context context, final int playlistID) {
|
||||||
|
List<PlaylistSong> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ public final class AppKeys {
|
||||||
public static final String E_ALBUM = "com.kabouzeid.gramophone.ALBUM";
|
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_ARTIST = "com.kabouzeid.gramophone.ARTIST";
|
||||||
public static final String E_SONG = "com.kabouzeid.gramophone.SONG";
|
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_TAG_EDIT_MODE = "com.kabouzeid.gramophone.TAG_EDIT_MODE";
|
||||||
public static final String E_ID = "com.kabouzeid.gramophone.ID";
|
public static final String E_ID = "com.kabouzeid.gramophone.ID";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -499,6 +499,16 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
||||||
saveState();
|
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) {
|
public void moveSong(int from, int to) {
|
||||||
final int currentPosition = getPosition();
|
final int currentPosition = getPosition();
|
||||||
Song songToMove = playingQueue.remove(from);
|
Song songToMove = playingQueue.remove(from);
|
||||||
|
|
|
||||||
|
|
@ -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.AlbumViewFragment;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.ArtistViewFragment;
|
import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.ArtistViewFragment;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.mainactivityfragments.AbsMainActivityFragment;
|
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.ui.fragments.mainactivityfragments.SongViewFragment;
|
||||||
import com.kabouzeid.gramophone.util.MusicUtil;
|
import com.kabouzeid.gramophone.util.MusicUtil;
|
||||||
import com.kabouzeid.gramophone.util.NavigationUtil;
|
import com.kabouzeid.gramophone.util.NavigationUtil;
|
||||||
|
|
@ -261,41 +262,11 @@ public class MainActivity extends AbsFabActivity
|
||||||
super.onBackPressed();
|
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 class MainActivityViewPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private String[] titles;
|
private String[] titles;
|
||||||
|
|
||||||
private SparseArray<AbsMainActivityFragment> pages; //TODO check if this must be static
|
private SparseArray<AbsMainActivityFragment> pages;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public MainActivityViewPagerAdapter(Activity activity) {
|
public MainActivityViewPagerAdapter(Activity activity) {
|
||||||
|
|
@ -306,7 +277,6 @@ public class MainActivity extends AbsFabActivity
|
||||||
context.getResources().getString(R.string.songs),
|
context.getResources().getString(R.string.songs),
|
||||||
context.getResources().getString(R.string.albums),
|
context.getResources().getString(R.string.albums),
|
||||||
context.getResources().getString(R.string.artists),
|
context.getResources().getString(R.string.artists),
|
||||||
context.getResources().getString(R.string.genres),
|
|
||||||
context.getResources().getString(R.string.playlists)
|
context.getResources().getString(R.string.playlists)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -321,11 +291,9 @@ public class MainActivity extends AbsFabActivity
|
||||||
case 2:
|
case 2:
|
||||||
return pages.get(position, new ArtistViewFragment());
|
return pages.get(position, new ArtistViewFragment());
|
||||||
case 3:
|
case 3:
|
||||||
//TODO genres
|
return pages.get(position, new PlaylistViewFragment());
|
||||||
case 4:
|
|
||||||
//TODO playlists
|
|
||||||
}
|
}
|
||||||
return pages.get(position, new PlaceholderFragmentAbs());
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -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<PlaylistSong> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.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.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.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));
|
navigationDrawerItems.add(new NavigationDrawerItem(getString(R.string.playlists), R.drawable.ic_queue_music_white_24dp));
|
||||||
|
|
||||||
drawerAdapter = new NavigationDrawerItemAdapter(getActivity(), R.id.navigation_drawer, navigationDrawerItems);
|
drawerAdapter = new NavigationDrawerItemAdapter(getActivity(), R.id.navigation_drawer, navigationDrawerItems);
|
||||||
|
|
|
||||||
|
|
@ -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<Playlist> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,15 +4,24 @@ import android.content.ContentResolver;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
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.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by karim on 29.12.14.
|
* Created by karim on 29.12.14.
|
||||||
|
|
@ -86,4 +95,57 @@ public class MusicUtil {
|
||||||
}
|
}
|
||||||
return albumArtDir;
|
return albumArtDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteTracks(final Context context, final List<Song> 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import com.kabouzeid.gramophone.misc.AppKeys;
|
||||||
import com.kabouzeid.gramophone.ui.activities.AlbumDetailActivity;
|
import com.kabouzeid.gramophone.ui.activities.AlbumDetailActivity;
|
||||||
import com.kabouzeid.gramophone.ui.activities.ArtistDetailActivity;
|
import com.kabouzeid.gramophone.ui.activities.ArtistDetailActivity;
|
||||||
import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity;
|
import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity;
|
||||||
|
import com.kabouzeid.gramophone.ui.activities.PlaylistDetailActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by karim on 12.03.15.
|
* 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) {
|
public static void openCurrentPlayingIfPossible(final Activity activity, final Pair[] sharedViews) {
|
||||||
if (activity instanceof MusicControllerActivity) {
|
if (activity instanceof MusicControllerActivity) {
|
||||||
activity.onBackPressed();
|
activity.onBackPressed();
|
||||||
|
|
|
||||||
|
|
@ -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<Song> helperList = new ArrayList<>();
|
||||||
|
helperList.add(song);
|
||||||
|
addToPlaylist(context, helperList, playlistId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addToPlaylist(final Context context, final List<Song> 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<PlaylistSong> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_queue_music_white_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 409 B |
BIN
app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_queue_music_white_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 333 B |
BIN
app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_queue_music_white_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 538 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_queue_music_white_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 747 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_queue_music_white_48dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1,005 B |
43
app/src/main/res/layout/activity_playlist_detail.xml
Normal file
43
app/src/main/res/layout/activity_playlist_detail.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?colorPrimary"
|
||||||
|
android:elevation="@dimen/toolbar_elevation"
|
||||||
|
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:divider="@null"
|
||||||
|
android:dividerHeight="0px"
|
||||||
|
android:scrollbars="vertical"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="right|bottom"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.melnykov.fab.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
style="@style/PlayPauseFab"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
16
app/src/main/res/layout/fragment_playlist_view.xml
Normal file
16
app/src/main/res/layout/fragment_playlist_view.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fragment_song_view"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:dividerHeight="0px"
|
||||||
|
android:divider="@null"
|
||||||
|
android:scrollbars="vertical"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -3,29 +3,28 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
android:descendantFocusability="blocksDescendants"
|
android:background="?rect_selector"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
>
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/song_title"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@drawable/ic_queue_music_white_24dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/playlist_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_weight="1"
|
android:layout_marginRight="16dp"
|
||||||
android:fontFamily="sans-serif"
|
android:fontFamily="sans-serif"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/playing_indicator"
|
|
||||||
style="@style/PlayingIndicator"
|
|
||||||
android:layout_marginRight="-8dp"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/menu"
|
|
||||||
style="@style/OverFlowButton"
|
|
||||||
android:layout_gravity="center_vertical"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
31
app/src/main/res/layout/item_list_playlist_song.xml
Normal file
31
app/src/main/res/layout/item_list_playlist_song.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/song_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playing_indicator"
|
||||||
|
style="@style/PlayingIndicator"
|
||||||
|
android:layout_marginRight="-8dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/menu"
|
||||||
|
style="@style/OverFlowButton"
|
||||||
|
android:layout_gravity="center_vertical"/>
|
||||||
|
</LinearLayout>
|
||||||
20
app/src/main/res/menu/menu_playlist_detail.xml
Normal file
20
app/src/main/res/menu/menu_playlist_detail.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context="com.kabouzeid.gramophone.ui.activities.PlaylistDetailActivity">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_current_playing"
|
||||||
|
android:icon="@drawable/music_box"
|
||||||
|
android:title="@string/action_current_playing"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_playing_queue"
|
||||||
|
android:icon="@drawable/ic_queue_music_white_24dp"
|
||||||
|
android:title="@string/action_playing_queue"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item android:id="@+id/action_settings"
|
||||||
|
android:title="@string/action_settings"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
</menu>
|
||||||
40
app/src/main/res/menu/menu_playlist_song.xml
Normal file
40
app/src/main/res/menu/menu_playlist_song.xml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_from_playlist"
|
||||||
|
android:title="@string/action_delete_from_playlist"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/action_play_next"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_current_playing"
|
||||||
|
android:title="@string/action_add_to_playing_queue"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_go_to_album"
|
||||||
|
android:title="@string/action_go_to_album"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_go_to_artist"
|
||||||
|
android:title="@string/action_go_to_artist"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_tag_editor"
|
||||||
|
android:title="@string/action_tag_editor"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_details"
|
||||||
|
android:title="@string/action_details"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_from_disk"
|
||||||
|
android:title="@string/action_delete_from_disk"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
</menu>
|
||||||
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
||||||
|
|
@ -74,5 +74,7 @@
|
||||||
<string name="added_title_to_playing_queue">Added 1 title to the playing queue.</string>
|
<string name="added_title_to_playing_queue">Added 1 title to the playing queue.</string>
|
||||||
<string name="added_titles_to_playing_queue_1">Added </string>
|
<string name="added_titles_to_playing_queue_1">Added </string>
|
||||||
<string name="added_titles_to_playing_queue_2"> titles to the playing queue.</string>
|
<string name="added_titles_to_playing_queue_2"> titles to the playing queue.</string>
|
||||||
|
<string name="title_activity_playlist_detail">PlaylistDetailActivity</string>
|
||||||
|
<string name="action_delete_from_playlist">Delete from playlist</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue