First snapshot (beta 0.9)

- playlists working (not all features added yet)
- minor fixes and improvements
This commit is contained in:
Karim Abou Zeid 2015-03-18 15:32:35 +01:00
commit 11ee05ce64
30 changed files with 1055 additions and 126 deletions

View file

@ -17,14 +17,14 @@ repositories {
android {
compileSdkVersion 22
buildToolsVersion "21.1.2"
buildToolsVersion "22"
defaultConfig {
applicationId "com.kabouzeid.gramophone"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
versionName "0.9b"
}
compileOptions {

View file

@ -36,34 +36,37 @@ MERGED from com.crashlytics.sdk.android:crashlytics:2.2.1:9:5
ADDED from AndroidManifest.xml:9:22
application
ADDED from AndroidManifest.xml:11:5
MERGED from com.android.support:appcompat-v7:21.0.3:16:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:gridlayout-v7:21.0.3:16:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:recyclerview-v7:21.0.3:17:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:palette-v7:21.0.3:16:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:support-v13:21.0.3:16:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.afollestad:material-dialogs:0.6.4.1:12:5
MERGED from com.android.support:appcompat-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:recyclerview-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:appcompat-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:recyclerview-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:gridlayout-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:palette-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:support-v13:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:cardview-v7:22.0.0:22:5
MERGED from com.nhaarman.listviewanimations:lib-core:3.1.0:26:5
MERGED from com.nhaarman.listviewanimations:lib-manipulation:3.1.0:26:5
MERGED from com.nhaarman.listviewanimations:lib-core-slh:3.1.0:26:5
MERGED from com.melnykov:floatingactionbutton:1.2.0:12:5
MERGED from com.android.support:recyclerview-v7:21.0.3:17:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:recyclerview-v7:21.0.3:17:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.afollestad:material-dialogs:0.6.3.0:12:5
MERGED from com.android.support:appcompat-v7:21.0.3:16:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:recyclerview-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.android.support:recyclerview-v7:22.0.0:22:5
MERGED from com.android.support:support-v4:22.0.0:22:5
MERGED from com.crashlytics.sdk.android:crashlytics:2.2.1:11:5
MERGED from com.crashlytics.sdk.android:answers:1.1.1:9:5
MERGED from io.fabric.sdk.android:fabric:1.1.1:9:5
MERGED from io.fabric.sdk.android:fabric:1.1.1:9:5
MERGED from com.crashlytics.sdk.android:beta:1.1.1:9:5
MERGED from io.fabric.sdk.android:fabric:1.1.1:9:5
MERGED from com.android.support:support-v4:21.0.3:16:5
MERGED from com.android.support:support-v4:22.0.0:22:5
android:label
ADDED from AndroidManifest.xml:15:9
android:allowBackup
@ -74,7 +77,7 @@ MERGED from com.android.support:support-v4:21.0.3:16:5
ADDED from AndroidManifest.xml:16:9
android:name
ADDED from AndroidManifest.xml:12:9
activity#gramophone.ui.activities.MainActivity
activity#com.kabouzeid.gramophone.ui.activities.MainActivity
ADDED from AndroidManifest.xml:17:9
android:label
ADDED from AndroidManifest.xml:19:13
@ -90,27 +93,27 @@ category#android.intent.category.LAUNCHER
ADDED from AndroidManifest.xml:23:17
android:name
ADDED from AndroidManifest.xml:23:27
activity#gramophone.ui.activities.AlbumDetailActivity
activity#com.kabouzeid.gramophone.ui.activities.AlbumDetailActivity
ADDED from AndroidManifest.xml:26:9
android:name
ADDED from AndroidManifest.xml:26:19
activity#gramophone.ui.activities.ArtistDetailActivity
activity#com.kabouzeid.gramophone.ui.activities.ArtistDetailActivity
ADDED from AndroidManifest.xml:28:9
android:name
ADDED from AndroidManifest.xml:28:19
activity#gramophone.ui.activities.MusicControllerActivity
activity#com.kabouzeid.gramophone.ui.activities.MusicControllerActivity
ADDED from AndroidManifest.xml:30:9
android:parentActivityName
ADDED from AndroidManifest.xml:32:13
android:name
ADDED from AndroidManifest.xml:31:13
service#gramophone.service.MusicService
service#com.kabouzeid.gramophone.service.MusicService
ADDED from AndroidManifest.xml:35:9
android:enabled
ADDED from AndroidManifest.xml:37:13
android:name
ADDED from AndroidManifest.xml:36:13
receiver#gramophone.service.MediaButtonIntentReceiver
receiver#com.kabouzeid.gramophone.service.MediaButtonIntentReceiver
ADDED from AndroidManifest.xml:40:9
android:name
ADDED from AndroidManifest.xml:40:19
@ -126,7 +129,7 @@ ADDED from AndroidManifest.xml:46:9
ADDED from AndroidManifest.xml:48:13
android:name
ADDED from AndroidManifest.xml:47:13
activity#gramophone.ui.activities.tageditor.SongTagEditorActivity
activity#com.kabouzeid.gramophone.ui.activities.tageditor.SongTagEditorActivity
ADDED from AndroidManifest.xml:50:9
android:label
ADDED from AndroidManifest.xml:52:13
@ -134,42 +137,67 @@ ADDED from AndroidManifest.xml:50:9
ADDED from AndroidManifest.xml:53:13
android:name
ADDED from AndroidManifest.xml:51:13
activity#gramophone.ui.activities.tageditor.AlbumTagEditorActivity
activity#com.kabouzeid.gramophone.ui.activities.tageditor.AlbumTagEditorActivity
ADDED from AndroidManifest.xml:55:9
android:label
ADDED from AndroidManifest.xml:57:13
android:name
ADDED from AndroidManifest.xml:56:13
activity#gramophone.ui.activities.SearchActivity
activity#com.kabouzeid.gramophone.ui.activities.SearchActivity
ADDED from AndroidManifest.xml:59:9
android:label
ADDED from AndroidManifest.xml:61:13
android:name
ADDED from AndroidManifest.xml:60:13
receiver#com.kabouzeid.gramophone.widget.MusicPlayerWidget
ADDED from AndroidManifest.xml:64:9
android:name
ADDED from AndroidManifest.xml:64:19
intent-filter#android.appwidget.action.APPWIDGET_UPDATE
ADDED from AndroidManifest.xml:65:13
action#android.appwidget.action.APPWIDGET_UPDATE
ADDED from AndroidManifest.xml:66:17
android:name
ADDED from AndroidManifest.xml:66:25
meta-data#android.appwidget.provider
ADDED from AndroidManifest.xml:69:13
android:resource
ADDED from AndroidManifest.xml:71:17
android:name
ADDED from AndroidManifest.xml:70:17
activity#com.kabouzeid.gramophone.ui.activities.PlaylistDetailActivity
ADDED from AndroidManifest.xml:74:9
android:label
ADDED from AndroidManifest.xml:76:13
android:name
ADDED from AndroidManifest.xml:75:13
uses-sdk
INJECTED from AndroidManifest.xml:0:0 reason: use-sdk injection requested
MERGED from com.android.support:appcompat-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:gridlayout-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:recyclerview-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:palette-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:support-v13:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.afollestad:material-dialogs:0.6.4.1:8:5
MERGED from com.android.support:appcompat-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:recyclerview-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:appcompat-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:recyclerview-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:gridlayout-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:palette-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:support-v13:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.android.support:cardview-v7:22.0.0:20:5
MERGED from com.nhaarman.listviewanimations:lib-core:3.1.0:22:5
MERGED from com.nhaarman.listviewanimations:lib-manipulation:3.1.0:22:5
MERGED from com.nhaarman.listviewanimations:lib-core-slh:3.1.0:22:5
MERGED from com.melnykov:floatingactionbutton:1.2.0:8:5
MERGED from com.android.support:recyclerview-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.github.ksoichiro:android-observablescrollview:1.3.0:21:5
MERGED from com.android.support:recyclerview-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.afollestad:material-dialogs:0.6.3.0:8:5
MERGED from com.android.support:appcompat-v7:21.0.3:15:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:recyclerview-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.github.ksoichiro:android-observablescrollview:1.5.0:21:5
MERGED from com.android.support:recyclerview-v7:22.0.0:20:5
MERGED from com.android.support:support-v4:22.0.0:20:5
MERGED from com.crashlytics.sdk.android:crashlytics:2.2.1:7:5
MERGED from com.crashlytics.sdk.android:answers:1.1.1:7:5
MERGED from io.fabric.sdk.android:fabric:1.1.1:7:5
@ -177,7 +205,7 @@ MERGED from io.fabric.sdk.android:fabric:1.1.1:7:5
MERGED from com.crashlytics.sdk.android:beta:1.1.1:7:5
MERGED from io.fabric.sdk.android:fabric:1.1.1:7:5
MERGED from asia.ivity.android:drag-sort-listview:1.0:6:5
MERGED from com.android.support:support-v4:21.0.3:15:5
MERGED from com.android.support:support-v4:22.0.0:20:5
android:targetSdkVersion
INJECTED from AndroidManifest.xml:0:0
INJECTED from AndroidManifest.xml:0:0
@ -185,13 +213,14 @@ MERGED from com.android.support:support-v4:21.0.3:15:5
INJECTED from AndroidManifest.xml:0:0
INJECTED from AndroidManifest.xml:0:0
activity#android.support.v7.widget.TestActivity
ADDED from com.android.support:recyclerview-v7:21.0.3:18:9
MERGED from com.android.support:recyclerview-v7:21.0.3:18:9
MERGED from com.android.support:recyclerview-v7:21.0.3:18:9
ADDED from com.android.support:recyclerview-v7:22.0.0:23:9
MERGED from com.android.support:recyclerview-v7:22.0.0:23:9
MERGED from com.android.support:recyclerview-v7:22.0.0:23:9
MERGED from com.android.support:recyclerview-v7:22.0.0:23:9
android:label
ADDED from com.android.support:recyclerview-v7:21.0.3:18:19
ADDED from com.android.support:recyclerview-v7:22.0.0:25:13
android:name
ADDED from com.android.support:recyclerview-v7:21.0.3:18:60
ADDED from com.android.support:recyclerview-v7:22.0.0:24:13
activity#com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeTouchListenerTestActivity
ADDED from com.nhaarman.listviewanimations:lib-manipulation:3.1.0:27:9
android:name

View file

@ -10,8 +10,10 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.SongDetailDialogHelper;
import com.kabouzeid.gramophone.loader.SongFilePathLoader;
@ -62,6 +64,12 @@ public class PlayingQueueAdapter extends ArrayAdapter<Song> {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete_from_disk:
Toast.makeText(activity, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(activity, song).show();
return true;
case R.id.action_remove_from_playing_queue:
MusicPlayerRemote.removeFromQueue(position);
notifyDataSetChanged();

View file

@ -8,10 +8,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity;
import com.kabouzeid.gramophone.util.NavigationUtil;
import com.kabouzeid.gramophone.util.PlaylistsUtil;
import java.util.List;
@ -36,7 +40,7 @@ public class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapter.ViewHo
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.playlistName.setText(dataSet.get(position).playlistName);
holder.playlistName.setText(dataSet.get(position).name);
}
@Override
@ -44,20 +48,46 @@ public class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapter.ViewHo
return dataSet.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public TextView playlistName;
public ViewHolder(View itemView) {
super(itemView);
playlistName = (TextView) itemView.findViewById(R.id.playlist_name);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(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);
if (activity instanceof AbsFabActivity)
sharedViews = ((AbsFabActivity) activity).getSharedViewsWithFab(sharedViews);
NavigationUtil.goToPlaylist(activity, dataSet.get(getAdapterPosition()).id, sharedViews);
}
@Override
public boolean onLongClick(View view) {
final Playlist playlist = dataSet.get(getAdapterPosition());
new MaterialDialog.Builder(activity)
.title(activity.getResources().getString(R.string.delete_playlist) + playlist.name)
.positiveText(activity.getResources().getString(R.string.ok))
.negativeText(activity.getResources().getString(R.string.cancel))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
super.onPositive(dialog);
dialog.dismiss();
PlaylistsUtil.deletePlaylist(activity, playlist.id);
}
@Override
public void onNegative(MaterialDialog dialog) {
super.onNegative(dialog);
dialog.dismiss();
}
}).show();
return true;
}
}
}

View file

@ -11,8 +11,10 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.SongDetailDialogHelper;
import com.kabouzeid.gramophone.loader.SongFilePathLoader;
@ -22,6 +24,7 @@ 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 java.io.File;
import java.util.List;
@ -75,7 +78,7 @@ public class AlbumSongAdapter extends RecyclerView.Adapter<AlbumSongAdapter.View
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MusicPlayerRemote.openQueue(dataSet, getPosition(), true);
MusicPlayerRemote.openQueue(dataSet, getAdapterPosition(), true);
}
});
}
@ -88,18 +91,24 @@ public class AlbumSongAdapter extends RecyclerView.Adapter<AlbumSongAdapter.View
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete_from_disk:
Toast.makeText(activity, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(activity, dataSet.get(getAdapterPosition())).show();
return true;
case R.id.action_play_next:
MusicPlayerRemote.playNext(dataSet.get(getPosition()));
MusicPlayerRemote.playNext(dataSet.get(getAdapterPosition()));
return true;
case R.id.action_add_to_current_playing:
MusicPlayerRemote.enqueue(dataSet.get(getPosition()));
MusicPlayerRemote.enqueue(dataSet.get(getAdapterPosition()));
case R.id.action_tag_editor:
Intent intent = new Intent(activity, SongTagEditorActivity.class);
intent.putExtra(AppKeys.E_ID, dataSet.get(getPosition()).id);
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);
String songFilePath = SongFilePathLoader.getSongFilePath(activity, dataSet.get(getAdapterPosition()).id);
File songFile = new File(songFilePath);
SongDetailDialogHelper.getDialog(activity, songFile).show();
return true;
@ -107,13 +116,13 @@ public class AlbumSongAdapter extends RecyclerView.Adapter<AlbumSongAdapter.View
Pair[] albumPairs = null;
if (activity instanceof AbsFabActivity)
albumPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(albumPairs);
NavigationUtil.goToAlbum(activity, dataSet.get(getPosition()).albumId, 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(getPosition()).artistId, artistPairs);
NavigationUtil.goToArtist(activity, dataSet.get(getAdapterPosition()).artistId, artistPairs);
return true;
}
return false;

View file

@ -11,8 +11,10 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.SongDetailDialogHelper;
import com.kabouzeid.gramophone.loader.SongFilePathLoader;
@ -67,6 +69,12 @@ public class ArtistSongAdapter extends ArrayAdapter<Song> {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete_from_disk:
Toast.makeText(activity, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(activity, song).show();
return true;
case R.id.action_play_next:
MusicPlayerRemote.playNext(song);
return true;

View file

@ -11,12 +11,15 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
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.misc.DragSortRecycler;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.PlaylistSong;
import com.kabouzeid.gramophone.model.Song;
@ -97,6 +100,12 @@ public class PlaylistSongAdapter extends RecyclerView.Adapter<PlaylistSongAdapte
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete_from_disk:
Toast.makeText(activity, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(activity, dataSet.get(getAdapterPosition())).show();
return true;
case R.id.action_delete_from_playlist:
int position = getAdapterPosition();
PlaylistsUtil.removeFromPlaylist(activity, dataSet.remove(position));
@ -114,7 +123,7 @@ public class PlaylistSongAdapter extends RecyclerView.Adapter<PlaylistSongAdapte
activity.startActivity(intent);
return true;
case R.id.action_details:
String songFilePath = SongFilePathLoader.getSongFilePath(activity, dataSet.get(getPosition()).id);
String songFilePath = SongFilePathLoader.getSongFilePath(activity, dataSet.get(getAdapterPosition()).id);
File songFile = new File(songFilePath);
SongDetailDialogHelper.getDialog(activity, songFile).show();
return true;

View file

@ -11,8 +11,10 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.SongDetailDialogHelper;
import com.kabouzeid.gramophone.loader.SongFilePathLoader;
@ -93,6 +95,12 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete_from_disk:
Toast.makeText(activity, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(activity, dataSet.get(getAdapterPosition())).show();
return true;
case R.id.action_play_next:
MusicPlayerRemote.playNext(dataSet.get(getPosition()));
return true;

View file

@ -0,0 +1,49 @@
package com.kabouzeid.gramophone.helper;
import android.content.Context;
import android.view.View;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.loader.PlaylistLoader;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.util.PlaylistsUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Created by karim on 17.03.15.
*/
public class AddToPlaylistDialogHelper {
public static MaterialDialog getDialog(final Context context, final Song song) {
List<Song> tmpSong = new ArrayList<>();
tmpSong.add(song);
return getDialog(context, tmpSong);
}
public static MaterialDialog getDialog(final Context context, final List<Song> songs) {
final List<Playlist> playlists = PlaylistLoader.getAllPlaylists(context);
CharSequence[] playlistNames = new CharSequence[playlists.size() + 1];
playlistNames[0] = context.getResources().getString(R.string.action_new_playlist);
for (int i = 1; i < playlistNames.length; i++) {
playlistNames[i] = playlists.get(i-1).name;
}
return new MaterialDialog.Builder(context)
.items(playlistNames)
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence charSequence) {
if (i == 0) {
materialDialog.dismiss();
CreatePlaylistDialogHelper.getDialog(context, songs).show();
} else {
materialDialog.dismiss();
PlaylistsUtil.addToPlaylist(context, songs, playlists.get(i - 1).id);
}
}
})
.build();
}
}

View file

@ -0,0 +1,67 @@
package com.kabouzeid.gramophone.helper;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.util.PlaylistsUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Created by karim on 17.03.15.
*/
public class CreatePlaylistDialogHelper {
public static MaterialDialog getDialog(final Context context, final Song song) {
List<Song> tmpSong = new ArrayList<>();
tmpSong.add(song);
return getDialog(context, tmpSong);
}
public static MaterialDialog getDialog(final Context context, final List<Song> songs) {
final EditText editText = new EditText(context);
ViewGroup layout = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.dialog_create_playlist, null);
if (editText.getParent() != null) {((ViewGroup) editText.getParent()).removeView(editText);}
layout.addView(editText, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
return new MaterialDialog.Builder(context)
.title(context.getResources().getString(R.string.action_new_playlist))
.customView(layout, false)
.positiveText(context.getResources().getString(R.string.ok))
.negativeText(context.getResources().getString(R.string.cancel))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
super.onPositive(dialog);
final String playlistName = editText.getText().toString();
if (!playlistName.trim().equals("")) {
dialog.dismiss();
final int playlistId = PlaylistsUtil.createPlaylist(context, playlistName);
if (playlistId != -1) {
if (songs != null) {
PlaylistsUtil.addToPlaylist(context, songs, playlistId);
}
}
}
}
@Override
public void onNegative(MaterialDialog dialog) {
super.onNegative(dialog);
dialog.dismiss();
}
}
)
.build();
}
public static MaterialDialog getDialog(final Context context) {
return getDialog(context, (List<Song>) null);
}
}

View file

@ -8,6 +8,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.PlayingQueueAdapter;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.util.PlaylistsUtil;
import com.mobeta.android.dslv.DragSortListView;
import java.util.List;
@ -16,8 +17,8 @@ import java.util.List;
* Created by karim on 24.01.15.
*/
public class PlayingQueueDialogHelper {
public static MaterialDialog getDialog(Activity activity) {
List<Song> playingQueue = MusicPlayerRemote.getPlayingQueue();
public static MaterialDialog getDialog(final Activity activity) {
final List<Song> playingQueue = MusicPlayerRemote.getPlayingQueue();
if (playingQueue.isEmpty()) {
return null;
}
@ -26,7 +27,7 @@ public class PlayingQueueDialogHelper {
.title(activity.getResources().getString(R.string.label_current_playing_queue))
.customView(R.layout.dialog_playlist, false)
.positiveText(activity.getResources().getString(R.string.close))
.negativeText(activity.getResources().getString(R.string.save_as_playlist))
.neutralText(activity.getResources().getString(R.string.save_as_playlist))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
@ -35,8 +36,10 @@ public class PlayingQueueDialogHelper {
}
@Override
public void onNegative(MaterialDialog dialog) {
public void onNeutral(MaterialDialog dialog) {
super.onNegative(dialog);
dialog.dismiss();
AddToPlaylistDialogHelper.getDialog(activity, playingQueue).show();
}
})
.build();

View file

@ -87,7 +87,7 @@ public class LastFMAlbumInfoUtil {
public static void downloadAlbumInfoJSON(final Context context, final String album, final String artist, final Response.Listener<JSONObject> callbackSuccess, final Response.ErrorListener callbackError) {
App app = (App) context.getApplicationContext();
String albumUrl = LastFMAlbumInfoUtil.getAlbumUrl(album, artist);
JsonObjectRequest albumInfoJSONRequest = new JsonObjectRequest(0, albumUrl, null, new Response.Listener<JSONObject>() {
JsonObjectRequest albumInfoJSONRequest = new JsonObjectRequest(0, albumUrl, (JSONObject)null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
LastFMAlbumInfoUtil.saveAlbumJSONDataToCacheAndDisk(context, album, artist, response);

View file

@ -38,7 +38,7 @@ public class LastFMArtistBiographyLoader {
private static void downloadArtistBio(final Context context, final String artist, final ArtistBioLoaderCallback callback) {
App app = (App) context.getApplicationContext();
String artistUrl = LastFMArtistInfoUtil.getArtistUrl(artist);
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, null, new Response.Listener<JSONObject>() {
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, (JSONObject)null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
LastFMArtistInfoUtil.saveArtistJSONDataToCacheAndDisk(context, artist, response);

View file

@ -1,7 +1,6 @@
package com.kabouzeid.gramophone.lastfm.artist;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
@ -101,7 +100,7 @@ public class LastFMArtistInfoUtil {
public static void downloadArtistJSON(final Context context, final String artist, final Response.Listener<JSONObject> callback) {
App app = (App) context.getApplicationContext();
String artistUrl = LastFMArtistInfoUtil.getArtistUrl(artist);
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, null, new Response.Listener<JSONObject>() {
JsonObjectRequest artistInfoJSONRequest = new JsonObjectRequest(0, artistUrl, (JSONObject)null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
LastFMArtistInfoUtil.saveArtistJSONDataToCacheAndDisk(context, artist, response);

View file

@ -0,0 +1,469 @@
/*
* DragSortRecycler
*
* Added drag and drop functionality to your RecyclerView
*
*
* Copyright 2014 Emile Belanger.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kabouzeid.gramophone.misc;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.support.annotation.Nullable;
import java.lang.reflect.Modifier;
public class DragSortRecycler extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener {
final String TAG = "DragSortRecycler";
final boolean DEBUG = false;
private int dragHandleWidth = 0;
private int selectedDragItemPos = -1;
private int fingerAnchorY;
private int fingerY;
private int fingerOffsetInViewY;
private float autoScrollWindow = 0.1f;
private float autoScrollSpeed = 0.5f;
private BitmapDrawable floatingItem;
private Rect floatingItemStatingBounds;
private Rect floatingItemBounds;
private float floatingItemAlpha = 0.5f;
private int floatingItemBgColor = 0;
private int viewHandleId = -1;
OnItemMovedListener moveInterface;
private boolean isDragging;
@Nullable
OnDragStateChangedListener dragStateChangedListener;
public interface OnItemMovedListener
{
public void onItemMoved(int from, int to);
}
public interface OnDragStateChangedListener {
public void onDragStart();
public void onDragStop();
}
private void debugLog(String log)
{
if (DEBUG)
Log.d(TAG, log);
}
public RecyclerView.OnScrollListener getScrollListener()
{
return scrollListener;
}
/*
* Set the item move interface
*/
public void setOnItemMovedListener(OnItemMovedListener swif)
{
moveInterface = swif;
}
public void setViewHandleId(int id)
{
viewHandleId = id;
}
public void setLeftDragArea(int w)
{
dragHandleWidth = w;
}
public void setFloatingAlpha(float a)
{
floatingItemAlpha = a;
}
public void setFloatingBgColor(int c)
{
floatingItemBgColor = c;
}
/*
Set the window at top and bottom of list, must be between 0 and 0.5
For example 0.1 uses the top and bottom 10% of the lists for scrolling
*/
public void setAutoScrollWindow(float w)
{
autoScrollWindow = w;
}
/*
Set the autoscroll speed, default is 0.5
*/
public void setAutoScrollSpeed(float speed)
{
autoScrollSpeed = speed;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView rv, RecyclerView.State state) {
super.getItemOffsets(outRect, view, rv, state);
debugLog("getItemOffsets");
debugLog("View top = " + view.getTop());
if (selectedDragItemPos != -1)
{
int itemPos = rv.getChildPosition(view);
debugLog("itemPos =" + itemPos);
if(!canDragOver(itemPos)) {
return;
}
//Movement of finger
float totalMovement = fingerY-fingerAnchorY;
if (itemPos == selectedDragItemPos)
{
view.setVisibility(View.INVISIBLE);
}
else
{
//Make view visible incase invisible
view.setVisibility(View.VISIBLE);
//Find middle of the floatingItem
float floatMiddleY = floatingItemBounds.top + floatingItemBounds.height()/2;
//Moving down the list
//These will auto-animate if the device continually sends touch motion events
// if (totalMovment>0)
{
if ((itemPos > selectedDragItemPos) && (view.getTop() < floatMiddleY))
{
float amountUp = (floatMiddleY - view.getTop()) / (float)view.getHeight();
// amountUp *= 0.5f;
if (amountUp > 1)
amountUp = 1;
outRect.top = -(int)(floatingItemBounds.height()*amountUp);
outRect.bottom = (int)(floatingItemBounds.height()*amountUp);
}
}//Moving up the list
// else if (totalMovment < 0)
{
if((itemPos < selectedDragItemPos) && (view.getBottom() > floatMiddleY))
{
float amountDown = ((float)view.getBottom() - floatMiddleY) / (float)view.getHeight();
// amountDown *= 0.5f;
if (amountDown > 1)
amountDown = 1;
outRect.top = (int)(floatingItemBounds.height()*amountDown);
outRect.bottom = -(int)(floatingItemBounds.height()*amountDown);
}
}
}
}
else
{
outRect.top = 0;
outRect.bottom = 0;
//Make view visible incase invisible
view.setVisibility(View.VISIBLE);
}
}
/**
* Find the new position by scanning through the items on
* screen and finding the positional relationship.
* This *seems* to work, another method would be to use
* getItemOffsets, but I think that could miss items?..
*/
private int getNewPostion(RecyclerView rv)
{
int itemsOnScreen = rv.getLayoutManager().getChildCount();
float floatMiddleY = floatingItemBounds.top + floatingItemBounds.height()/2;
int above=0;
int below = Integer.MAX_VALUE;
for (int n=0;n < itemsOnScreen;n++) //Scan though items on screen, however they may not
{ // be in order!
View view = rv.getLayoutManager().getChildAt(n);
if (view.getVisibility() != View.VISIBLE)
continue;
int itemPos = rv.getChildPosition(view);
if (itemPos == selectedDragItemPos) //Don't check against itself!
continue;
float viewMiddleY = view.getTop() + view.getHeight()/2;
if (floatMiddleY > viewMiddleY) //Is above this item
{
if (itemPos > above)
above = itemPos;
}
else if (floatMiddleY <= viewMiddleY) //Is below this item
{
if (itemPos < below)
below = itemPos;
}
}
debugLog("above = " + above + " below = " + below);
if (below != Integer.MAX_VALUE) {
if (below < selectedDragItemPos) //Need to count itself
below++;
return below - 1;
}
else
{
if (above < selectedDragItemPos)
above++;
return above;
}
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
debugLog("onInterceptTouchEvent");
//if (e.getAction() == MotionEvent.ACTION_DOWN)
{
View itemView = rv.findChildViewUnder(e.getX(), e.getY());
if (itemView==null)
return false;
boolean dragging = false;
if ((dragHandleWidth > 0 ) && (e.getX() < dragHandleWidth))
{
dragging = true;
}
else if (viewHandleId != -1)
{
//Find the handle in the list item
View handleView = itemView.findViewById(viewHandleId);
if (handleView == null)
{
Log.e(TAG, "The view ID " + viewHandleId + " was not found in the RecycleView item");
return false;
}
//View should be visible to drag
if(handleView.getVisibility()!=View.VISIBLE) {
return false;
}
//We need to find the relative position of the handle to the parent view
//Then we can work out if the touch is within the handle
int[] parentItemPos = new int[2];
itemView.getLocationInWindow(parentItemPos);
int[] handlePos = new int[2];
handleView.getLocationInWindow(handlePos);
int xRel = handlePos[0] - parentItemPos[0];
int yRel = handlePos[1] - parentItemPos[1];
Rect touchBounds = new Rect(itemView.getLeft() + xRel, itemView.getTop() + yRel,
itemView.getLeft() + xRel + handleView.getWidth(),
itemView.getTop() + yRel + handleView.getHeight()
);
if (touchBounds.contains((int)e.getX(), (int)e.getY()))
dragging = true;
debugLog("parentItemPos = " + parentItemPos[0] + " " + parentItemPos[1]);
debugLog("handlePos = " + handlePos[0] + " " + handlePos[1]);
}
if (dragging)
{
debugLog("Started Drag");
setIsDragging(true);
floatingItem = createFloatingBitmap(itemView);
fingerAnchorY = (int)e.getY();
fingerOffsetInViewY = fingerAnchorY - itemView.getTop();
fingerY = fingerAnchorY;
selectedDragItemPos = rv.getChildPosition(itemView);
debugLog("selectedDragItemPos = " + selectedDragItemPos);
return true;
}
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
debugLog("onTouchEvent");
if ((e.getAction() == MotionEvent.ACTION_UP) ||
(e.getAction() == MotionEvent.ACTION_CANCEL))
{
if ((e.getAction() == MotionEvent.ACTION_UP) && selectedDragItemPos != -1)
{
int newPos = getNewPostion(rv);
if (moveInterface != null)
moveInterface.onItemMoved(selectedDragItemPos, newPos);
}
setIsDragging(false);
selectedDragItemPos = -1;
floatingItem = null;
rv.invalidateItemDecorations();
return;
}
fingerY = (int)e.getY();
if (floatingItem!=null)
{
floatingItemBounds.top = fingerY - fingerOffsetInViewY;
if (floatingItemBounds.top < -floatingItemStatingBounds.height()/2) //Allow half the view out the top
floatingItemBounds.top = -floatingItemStatingBounds.height()/2;
floatingItemBounds.bottom = floatingItemBounds.top + floatingItemStatingBounds.height();
floatingItem.setBounds(floatingItemBounds);
}
//Do auto scrolling at end of list
float scrollAmount=0;
if (fingerY > (rv.getHeight() * (1-autoScrollWindow)))
{
scrollAmount = (fingerY - (rv.getHeight() * (1-autoScrollWindow)));
}
else if (fingerY < (rv.getHeight() * autoScrollWindow))
{
scrollAmount = (fingerY - (rv.getHeight() * autoScrollWindow));
}
debugLog("Scroll: " + scrollAmount);
scrollAmount *= autoScrollSpeed;
rv.scrollBy(0, (int)scrollAmount);
rv.invalidateItemDecorations();// Redraw
}
private void setIsDragging(final boolean dragging) {
if(dragging != isDragging) {
isDragging = dragging;
if(dragStateChangedListener != null) {
if (isDragging) {
dragStateChangedListener.onDragStart();
} else {
dragStateChangedListener.onDragStop();
}
}
}
}
public void setOnDragStateChangedListener(final OnDragStateChangedListener dragStateChangedListener) {
this.dragStateChangedListener = dragStateChangedListener;
}
Paint bgColor = new Paint();
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (floatingItem != null) {
floatingItem.setAlpha((int)(255 * floatingItemAlpha));
bgColor.setColor(floatingItemBgColor);
c.drawRect(floatingItemBounds,bgColor);
floatingItem.draw(c);
}
}
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
debugLog("Scrolled: " + dx + " " + dy);
fingerAnchorY -= dy;
}
};
/**
*
*
* @param position
* @return True if we can drag the item over this position, False if not.
*/
protected boolean canDragOver(int position) {
return true;
}
private BitmapDrawable createFloatingBitmap(View v)
{
floatingItemStatingBounds = new Rect(v.getLeft(), v.getTop(),v.getRight(), v.getBottom());
floatingItemBounds = new Rect(floatingItemStatingBounds);
Bitmap bitmap = Bitmap.createBitmap(floatingItemStatingBounds.width(),
floatingItemStatingBounds.height(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
BitmapDrawable retDrawable = new BitmapDrawable(v.getResources(), bitmap);
retDrawable.setBounds(floatingItemBounds);
return retDrawable;
}
}

View file

@ -0,0 +1,22 @@
package com.kabouzeid.gramophone.model;
/**
* Created by karim on 17.03.15.
*/
public class DataBaseChangedEvent {
public static final int PLAYLISTS_CHANGED = 0;
public static final int ALBUMS_CHANGED = 1;
public static final int ARTISTS_CHANGED = 2;
public static final int SONGS_CHANGED = 3;
public static final int DATABASE_CHANGED = 4;
private int action;
public DataBaseChangedEvent(int action) {
this.action = action;
}
public int getAction() {
return action;
}
}

View file

@ -2,15 +2,15 @@ package com.kabouzeid.gramophone.model;
public class Playlist {
public int id;
public String playlistName;
public String name;
public Playlist(final int id, final String playlistName) {
public Playlist(final int id, final String name) {
this.id = id;
this.playlistName = playlistName;
this.name = name;
}
public Playlist() {
this.id = -1;
this.playlistName = "";
this.name = "";
}
}

View file

@ -15,6 +15,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;
import com.kabouzeid.gramophone.App;
@ -264,6 +265,7 @@ public class AlbumDetailActivity extends AbsFabActivity {
NavigationUtil.openPlayingQueueDialog(this);
return true;
case R.id.action_settings:
Toast.makeText(this, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_current_playing:
NavigationUtil.openCurrentPlayingIfPossible(this, getSharedViewsWithFab(null));

View file

@ -326,6 +326,7 @@ public class ArtistDetailActivity extends AbsFabActivity {
Toast.makeText(ArtistDetailActivity.this, getResources().getString(R.string.updating), Toast.LENGTH_SHORT).show();
setUpArtistImageAndApplyPalette(true);
case R.id.action_settings:
Toast.makeText(this, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_current_playing:
NavigationUtil.openCurrentPlayingIfPossible(this, getSharedViewsWithFab(null));

View file

@ -26,6 +26,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AboutDeveloperDialogHelper;
import com.kabouzeid.gramophone.helper.CreatePlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.PlayingQueueDialogHelper;
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
@ -58,6 +59,7 @@ public class MainActivity extends AbsFabActivity
private MainActivityViewPagerAdapter viewPagerAdapter;
private ViewPager viewPager;
private SlidingTabLayout slidingTabLayout;
private int currentPage = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -78,6 +80,7 @@ public class MainActivity extends AbsFabActivity
viewPagerAdapter = new MainActivityViewPagerAdapter(this);
viewPager.setAdapter(viewPagerAdapter);
int startPosition = PreferenceUtils.getInstace(this).getStartPage();
currentPage = startPosition;
viewPager.setCurrentItem(startPosition);
navigationDrawerFragment.setItemChecked(startPosition);
@ -93,6 +96,8 @@ public class MainActivity extends AbsFabActivity
public void onPageSelected(final int position) {
PreferenceUtils.getInstace(MainActivity.this).setStartPage(position);
navigationDrawerFragment.setItemChecked(position);
currentPage = position;
invalidateOptionsMenu();
}
@Override
@ -210,7 +215,14 @@ public class MainActivity extends AbsFabActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.drawer, menu);
switch (currentPage){
case 3:
getMenuInflater().inflate(R.menu.menu_playlists, menu);
break;
default:
getMenuInflater().inflate(R.menu.drawer, menu);
break;
}
restoreActionBar();
return true;
}
@ -229,10 +241,17 @@ public class MainActivity extends AbsFabActivity
}
int id = item.getItemId();
switch (id) {
case R.id.action_licenses:
Toast.makeText(this, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_new_playlist:
CreatePlaylistDialogHelper.getDialog(this).show();
return true;
case R.id.action_search:
startActivity(new Intent(MainActivity.this, SearchActivity.class));
return true;
case R.id.action_settings:
Toast.makeText(this, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_about:
AboutDeveloperDialogHelper.getDialog(this).show();
@ -277,7 +296,7 @@ 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.playlists)
context.getResources().getString(R.string.playlists) + " BETA"
};
}

View file

@ -18,10 +18,9 @@ import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.AddToPlaylistDialogHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.PlayingQueueDialogHelper;
import com.kabouzeid.gramophone.helper.SongDetailDialogHelper;
import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistImageUrlLoader;
import com.kabouzeid.gramophone.loader.SongFilePathLoader;
@ -388,6 +387,12 @@ public class MusicControllerActivity extends AbsFabActivity {
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_settings:
Toast.makeText(this, "This feature is not available yet", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_add_to_playlist:
AddToPlaylistDialogHelper.getDialog(this, song).show();
return true;
case android.R.id.home:
super.onBackPressed();
return true;

View file

@ -12,10 +12,12 @@ 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.misc.DragSortRecycler;
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 com.kabouzeid.gramophone.util.PlaylistsUtil;
import java.util.List;
@ -34,10 +36,27 @@ public class PlaylistDetailActivity extends AbsFabActivity {
setUpToolBar();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
List<PlaylistSong> songs = PlaylistSongLoader.getPlaylistSongList(this, playlist.id);
PlaylistSongAdapter adapter = new PlaylistSongAdapter(this, songs);
final List<PlaylistSong> songs = PlaylistSongLoader.getPlaylistSongList(this, playlist.id);
final PlaylistSongAdapter adapter = new PlaylistSongAdapter(this, songs);
recyclerView.setLayoutManager(new GridLayoutManager(this, 1));
recyclerView.setAdapter(adapter);
DragSortRecycler dragSortRecycler = new DragSortRecycler();
dragSortRecycler.setViewHandleId(R.id.album_art);
dragSortRecycler.setOnItemMovedListener(new DragSortRecycler.OnItemMovedListener() {
@Override
public void onItemMoved(int from, int to) {
PlaylistSong song = songs.remove(from);
songs.add(to, song);
adapter.notifyDataSetChanged();
PlaylistsUtil.moveItem(PlaylistDetailActivity.this, playlist.id, from, to);
}
});
recyclerView.addItemDecoration(dragSortRecycler);
recyclerView.addOnItemTouchListener(dragSortRecycler);
recyclerView.setOnScrollListener(dragSortRecycler.getScrollListener());
}
private void getIntentExtras() {
@ -51,7 +70,7 @@ public class PlaylistDetailActivity extends AbsFabActivity {
private void setUpToolBar() {
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
getSupportActionBar().setTitle(playlist.playlistName);
getSupportActionBar().setTitle(playlist.name);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

View file

@ -2,20 +2,19 @@ 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.App;
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.DataBaseChangedEvent;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.Song;
import com.squareup.otto.Subscribe;
import java.util.List;
@ -37,12 +36,27 @@ public class PlaylistViewFragment extends AbsMainActivityFragment {
}
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());
setUpAdapter();
}
private void setUpAdapter(){
if(recyclerView != null) {
List<Playlist> playlists = PlaylistLoader.getAllPlaylists(getActivity());
PlaylistAdapter adapter = new PlaylistAdapter(getActivity(), playlists);
recyclerView.setAdapter(adapter);
}
}
@Subscribe
public void onDataBaseEvent(DataBaseChangedEvent event) {
switch (event.getAction()) {
case DataBaseChangedEvent.PLAYLISTS_CHANGED:
case DataBaseChangedEvent.DATABASE_CHANGED:
setUpAdapter();
break;
}
}
@Override
@ -56,4 +70,16 @@ public class PlaylistViewFragment extends AbsMainActivityFragment {
super.disableViews();
recyclerView.setEnabled(false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.bus.register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
App.bus.unregister(this);
}
}

View file

@ -5,9 +5,13 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.widget.Toast;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
import com.kabouzeid.gramophone.model.PlaylistSong;
import com.kabouzeid.gramophone.model.Song;
@ -18,6 +22,9 @@ import java.util.List;
* Created by karim on 16.03.15.
*/
public class PlaylistsUtil {
public static final String MUSIC_ONLY_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1"
+ " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"; //$NON-NLS-2$
public static int createPlaylist(final Context context, final String name) {
if (name != null && name.length() > 0) {
final ContentResolver resolver = context.getContentResolver();
@ -32,55 +39,93 @@ public class PlaylistsUtil {
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();
if (uri != null) {
Toast.makeText(context, context.getResources().getString(R.string.created_playlist) + name, Toast.LENGTH_SHORT).show();
App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED));
return Integer.parseInt(uri.getLastPathSegment());
}
}
cursor.close();
}
Toast.makeText(context, context.getResources().getString(R.string.create_playlist_failed) + name, Toast.LENGTH_SHORT).show();
return -1;
}
public static void deletePlaylist(final Context context, final int playlistId) {
public static void clearPlaylist(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) {
public static void deletePlaylist(final Context context, final int playlistId) {
final Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
String where = MediaStore.Audio.Playlists._ID + "=?";
String[] whereVal = {String.valueOf(playlistId)};
context.getContentResolver().delete(uri, where, whereVal);
Toast.makeText(context, context.getResources().getString(R.string.deleted_playlist) + getNameForPlaylist(context, playlistId), Toast.LENGTH_SHORT).show();
App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED));
}
public static void addToPlaylist(final Context context, final Song song, final int 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) {
public static void addToPlaylist(final Context context, final List<Song> songs, final int playlistId) {
final int size = songs.size();
final ContentResolver resolver = context.getContentResolver();
final String[] projection = new String[]{
MediaStore.Audio.PlaylistsColumns.NAME
"max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")",
};
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();
Cursor cursor = null;
int base = 0;
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);
try {
cursor = resolver.query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
base = cursor.getInt(0) + 1;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
resolver.bulkInsert(uri, contentValues);
Toast.makeText(context, "Added " + contentValues.length + " songs to playlist " + playlistName, Toast.LENGTH_SHORT).show();
//TODO add string resource
int numinserted = 0;
for (int offSet = 0; offSet < size; offSet += 1000) {
numinserted += resolver.bulkInsert(uri, makeInsertItems(songs, offSet, 1000, base));
}
Toast.makeText(context, context.getResources().getString(R.string.inserted_titles_to_playlist_1) + numinserted + context.getResources().getString(R.string.inserted_titles_to_playlist_2) + getNameForPlaylist(context, playlistId) + ".", Toast.LENGTH_SHORT).show();
App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED));
}
public static ContentValues[] makeInsertItems(final List<Song> songs, final int offset, int len, final int base) {
if (offset + len > songs.size()) {
len = songs.size() - offset;
}
ContentValues[] contentValues = new ContentValues[len];
for (int i = 0; i < len; i++) {
contentValues[i] = new ContentValues();
contentValues[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + offset + i);
contentValues[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(offset + i).id);
}
return contentValues;
}
public static void removeFromPlaylist(final Context context, final PlaylistSong song) {
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
"external", song.playlistId);
String selection = MediaStore.Audio.Playlists.Members._ID+ " =?";
String selection = MediaStore.Audio.Playlists.Members._ID + " =?";
String[] selectionArgs = new String[]{String.valueOf(song.idInPlayList)};
context.getContentResolver().delete(uri, selection, selectionArgs);
App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED));
}
public static void removeFromPlaylist(final Context context, final List<PlaylistSong> songs) {
@ -95,5 +140,47 @@ public class PlaylistsUtil {
selection = selection.substring(0, selection.length() - 2) + ")";
context.getContentResolver().delete(uri, selection, selectionArgs);
App.bus.post(new DataBaseChangedEvent(DataBaseChangedEvent.PLAYLISTS_CHANGED));
}
public static int getSongCountForPlaylist(final Context context, final long playlistId) {
Cursor c = context.getContentResolver().query(
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
new String[]{BaseColumns._ID}, MUSIC_ONLY_SELECTION, null, null);
if (c != null) {
int count = 0;
if (c.moveToFirst()) {
count = c.getCount();
}
c.close();
return count;
}
return 0;
}
public static void moveItem(final Context context, int playlistId, int from, int to) {
MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(),
playlistId, from, to);
}
public static String getNameForPlaylist(final Context context, final int id) {
Cursor cursor = context.getContentResolver().query(
MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.PlaylistsColumns.NAME},
BaseColumns._ID + "=?",
new String[]{String.valueOf(id)},
null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getString(0);
}
} finally {
cursor.close();
}
}
return "";
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start"
android:paddingLeft="@dimen/md_dialog_frame_margin"
android:paddingRight="@dimen/md_dialog_frame_margin">
</LinearLayout>

View file

@ -4,13 +4,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:elevation="3dp"
android:foreground="?rect_selector">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:gravity="center"
android:orientation="vertical">

View file

@ -10,10 +10,10 @@
app:showAsAction="always"/>
<item
android:id="@+id/action_search"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
android:title="@string/action_search"
app:showAsAction="ifRoom"/>
android:id="@+id/action_search"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
android:title="@string/action_search"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_playing_queue"
@ -29,8 +29,14 @@
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="101"
android:id="@+id/action_licenses"
android:title="Open Source Licenses"
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="102"
android:title="@string/action_about"
app:showAsAction="never"/>
</menu>

View file

@ -1,8 +0,0 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never"/>
</menu>

View file

@ -0,0 +1,45 @@
<?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_current_playing"
android:icon="@drawable/music_box"
android:title="@string/action_current_playing"
app:showAsAction="always"/>
<item
android:id="@+id/action_search"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
android:title="@string/action_search"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_new_playlist"
android:title="@string/action_new_playlist"
app:showAsAction="never"/>
<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:icon="@drawable/settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never"/>
<item
android:id="@+id/action_licenses"
android:orderInCategory="101"
android:title="Open Source Licenses"
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="102"
android:title="@string/action_about"
app:showAsAction="never"/>
</menu>

View file

@ -73,8 +73,16 @@
<string name="loading">Loading…</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_2"> titles to the playing queue.</string>
<string name="added_titles_to_playing_queue_2">\u0020titles to the playing queue.</string>
<string name="title_activity_playlist_detail">PlaylistDetailActivity</string>
<string name="action_delete_from_playlist">Delete from playlist</string>
<string name="inserted_titles_to_playlist_1">Inserted\u0020</string>
<string name="inserted_titles_to_playlist_2">\u0020titles to playlist\u0020</string>
<string name="action_new_playlist">New playlist</string>
<string name="cancel">Cancel</string>
<string name="created_playlist">Created playlist\u0020</string>
<string name="deleted_playlist">Deleted playlist\u0020</string>
<string name="create_playlist_failed">Could not create playlist\u0020</string>
<string name="delete_playlist">Delete playlist\u0020</string>
</resources>