OSDN Git Service

Eleven: Add context menus to all top level fragments and playlists
authorLinus Lee <llee@cyngn.com>
Thu, 25 Sep 2014 07:02:30 +0000 (00:02 -0700)
committerlinus_lee <llee@cyngn.com>
Thu, 20 Nov 2014 20:03:04 +0000 (12:03 -0800)
This is part 1/2 - will add it to album detali/artist detail page after
Added helper pop up menu class
This also merged songfragment with basicsongframent because of the large similarities

https://cyanogen.atlassian.net/browse/MUSIC-22

Change-Id: I9951fe7238aaff7599f0cb6f655f0677f998bb6b

41 files changed:
res/drawable-hdpi/menu_button_light.png [moved from res/drawable-hdpi/menu_button.png with 100% similarity]
res/drawable-mdpi/menu_button_light.png [moved from res/drawable-mdpi/menu_button.png with 100% similarity]
res/drawable-xhdpi/menu_button.png [deleted file]
res/drawable-xhdpi/menu_button_light.png
res/drawable-xxhdpi/menu_button.png
res/drawable-xxhdpi/menu_item_button.png [deleted file]
res/layout/activity_album_detail.xml
res/layout/activity_player_fragment.xml
res/layout/album_detail_song.xml
res/layout/artist_detail_album.xml
res/layout/artist_detail_song.xml
res/layout/grid_items_normal.xml
res/layout/list_item_common.xml
res/layout/list_item_queue.xml
res/layout/list_item_simple.xml
res/layout/list_item_top_tracks.xml
src/com/cyngn/eleven/adapters/AlbumAdapter.java
src/com/cyngn/eleven/adapters/ArtistAdapter.java
src/com/cyngn/eleven/adapters/PlaylistAdapter.java
src/com/cyngn/eleven/adapters/ProfileSongAdapter.java
src/com/cyngn/eleven/adapters/SongAdapter.java
src/com/cyngn/eleven/adapters/SummarySearchAdapter.java
src/com/cyngn/eleven/loaders/LastAddedLoader.java
src/com/cyngn/eleven/sectionadapter/SectionAdapter.java
src/com/cyngn/eleven/ui/MusicHolder.java
src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java
src/com/cyngn/eleven/ui/activities/SearchActivity.java
src/com/cyngn/eleven/ui/fragments/AlbumFragment.java
src/com/cyngn/eleven/ui/fragments/ArtistFragment.java
src/com/cyngn/eleven/ui/fragments/AudioPlayerFragment.java
src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java
src/com/cyngn/eleven/ui/fragments/QueueFragment.java
src/com/cyngn/eleven/ui/fragments/RecentFragment.java
src/com/cyngn/eleven/ui/fragments/SongFragment.java
src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java
src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
src/com/cyngn/eleven/utils/MusicUtils.java
src/com/cyngn/eleven/utils/PopupMenuHelper.java [new file with mode: 0644]
src/com/cyngn/eleven/widgets/IPopupMenuCallback.java [new file with mode: 0644]
src/com/cyngn/eleven/widgets/PopupMenuButton.java [new file with mode: 0644]

diff --git a/res/drawable-xhdpi/menu_button.png b/res/drawable-xhdpi/menu_button.png
deleted file mode 100644 (file)
index 67fdc8c..0000000
Binary files a/res/drawable-xhdpi/menu_button.png and /dev/null differ
index 9112f57..67fdc8c 100644 (file)
Binary files a/res/drawable-xhdpi/menu_button_light.png and b/res/drawable-xhdpi/menu_button_light.png differ
index 055dfc2..8eac8ef 100644 (file)
Binary files a/res/drawable-xxhdpi/menu_button.png and b/res/drawable-xxhdpi/menu_button.png differ
diff --git a/res/drawable-xxhdpi/menu_item_button.png b/res/drawable-xxhdpi/menu_item_button.png
deleted file mode 100644 (file)
index 8eac8ef..0000000
Binary files a/res/drawable-xxhdpi/menu_item_button.png and /dev/null differ
index 1a37b99..876a29e 100644 (file)
@@ -19,7 +19,7 @@
             android:layout_alignParentRight="true"
             android:layout_marginBottom="40dp"
             android:gravity="center"
-            android:src="@drawable/menu_item_button" />
+            android:src="@drawable/menu_button" />
 
         <ImageView
             android:id="@+id/album_art"
index 9e438be..283e6ee 100644 (file)
@@ -79,7 +79,7 @@
                 android:layout_height="@dimen/audio_player_controls_top_button_height"
                 android:layout_weight="0"
                 android:scaleType="centerInside"
-                android:src="@drawable/menu_button" />
+                android:src="@drawable/menu_button_light" />
         </LinearLayout>
 
         <RelativeLayout
index 062128f..2420e31 100644 (file)
@@ -12,7 +12,7 @@
         android:layout_alignParentRight="true"
         android:layout_marginBottom="2dp"
         android:gravity="center"
-        android:src="@drawable/menu_item_button" />
+        android:src="@drawable/menu_button" />
 
     <TextView
         android:id="@+id/title"
index 93131a2..10ec00d 100644 (file)
@@ -22,7 +22,7 @@
         android:layout_marginBottom="1dp"
         android:layout_marginRight="-2dp"
         android:gravity="center"
-        android:src="@drawable/menu_item_button" />
+        android:src="@drawable/menu_button" />
 
     <TextView
         android:id="@+id/title"
index 4db05f2..28d193f 100644 (file)
@@ -20,7 +20,7 @@
         android:layout_alignParentRight="true"
         android:layout_marginBottom="2dp"
         android:gravity="center"
-        android:src="@drawable/menu_item_button" />
+        android:src="@drawable/menu_button" />
 
     <TextView
         android:id="@+id/title"
index f61af9a..91f9374 100644 (file)
             style="@style/ListItemMainText.Grid.Single"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentTop="true" />
+            android:layout_alignParentTop="true"
+            android:layout_toLeftOf="@+id/popup_menu_button"/>
 
         <TextView
             android:id="@+id/line_two"
             style="@style/ListItemSecondaryText.Grid.Single"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_below="@+id/line_one" />
+            android:layout_below="@+id/line_one"
+            android:layout_toLeftOf="@id/popup_menu_button" />
+
+        <com.cyngn.eleven.widgets.PopupMenuButton
+            android:id="@id/popup_menu_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:src="@drawable/menu_button" />
     </RelativeLayout>
 
 </RelativeLayout>
\ No newline at end of file
index 4ba3914..f9f09ee 100644 (file)
         style="@style/ListItemSecondaryText.Single"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
+        android:layout_toLeftOf="@+id/popup_menu_button"
         android:layout_centerVertical="true" />
+
+    <com.cyngn.eleven.widgets.PopupMenuButton
+        android:id="@id/popup_menu_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:src="@drawable/menu_button" />
+
 </merge>
\ No newline at end of file
index ddbc66f..5cd9111 100644 (file)
@@ -43,7 +43,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerVertical="true"
-        android:layout_alignParentRight="true"
+        android:layout_toLeftOf="@+id/popup_menu_button"
         android:paddingLeft="@dimen/list_item_progress_padding_left"
         android:paddingRight="@dimen/list_item_progress_padding_right">
         <include
             android:layout_centerVertical="true"
             layout="@layout/play_pause_progress_button"/>
     </FrameLayout>
+
+    <com.cyngn.eleven.widgets.PopupMenuButton
+        android:id="@id/popup_menu_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:src="@drawable/menu_button_light" />
 </RelativeLayout>
\ No newline at end of file
index 1516e09..39371e8 100644 (file)
             style="@style/ListItemSecondaryText.Single"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentRight="true"
+            android:layout_toLeftOf="@+id/popup_menu_button"
             android:layout_centerVertical="true" />
+
+        <com.cyngn.eleven.widgets.PopupMenuButton
+            android:id="@id/popup_menu_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:src="@drawable/menu_button" />
     </RelativeLayout>
 
     <ImageView
index a564963..9a8bfb6 100644 (file)
@@ -56,7 +56,7 @@
             android:layout_width="match_parent"
             android:layout_height="@dimen/list_item_image_height"
             android:layout_toRightOf="@+id/position_contanier"
-            android:layout_toLeftOf="@+id/line_one_right"
+            android:layout_toLeftOf="@+id/popup_menu_button"
             android:gravity="center_vertical"
             android:minHeight="@dimen/item_normal_height"
             android:paddingLeft="@dimen/list_preferred_item_padding" >
                 android:layout_below="@+id/line_one" />
         </RelativeLayout>
 
-        <TextView
-            android:id="@+id/line_one_right"
-            style="@style/ListItemSecondaryText.Single"
+        <com.cyngn.eleven.widgets.PopupMenuButton
+            android:id="@id/popup_menu_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentRight="true"
-            android:layout_centerVertical="true" />
+            android:layout_centerVertical="true"
+            android:src="@drawable/menu_button" />
     </RelativeLayout>
 
     <ImageView
index b37d5a0..d970d99 100644 (file)
@@ -29,6 +29,7 @@ import com.cyngn.eleven.ui.MusicHolder;
 import com.cyngn.eleven.ui.MusicHolder.DataHolder;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 /**
  * This {@link ArrayAdapter} is used to display all of the albums on a user's
@@ -36,7 +37,8 @@ import com.cyngn.eleven.utils.MusicUtils;
  * 
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class AlbumAdapter extends ArrayAdapter<Album> implements SectionAdapter.BasicAdapter {
+public class AlbumAdapter extends ArrayAdapter<Album>
+        implements SectionAdapter.BasicAdapter, IPopupMenuCallback {
 
     /**
      * Number of views (ImageView and TextView)
@@ -70,6 +72,11 @@ public class AlbumAdapter extends ArrayAdapter<Album> implements SectionAdapter.
     private DataHolder[] mData;
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    private IPopupMenuCallback.IListener mListener;
+
+    /**
      * Constructor of <code>AlbumAdapter</code>
      * 
      * @param context The {@link Context} to use.
@@ -98,6 +105,8 @@ public class AlbumAdapter extends ArrayAdapter<Album> implements SectionAdapter.
             convertView = LayoutInflater.from(getContext()).inflate(mLayoutId, parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+            // set the pop up menu listener
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
@@ -105,6 +114,8 @@ public class AlbumAdapter extends ArrayAdapter<Album> implements SectionAdapter.
         // Retrieve the data holder
         final DataHolder dataHolder = mData[position];
 
+        // Sets the position each time because of recycling
+        holder.mPopupMenuButton.get().setPosition(position);
         // Set each album name (line one)
         holder.mLineOne.get().setText(dataHolder.mLineOne);
         // Set the artist name (line two)
@@ -233,4 +244,9 @@ public class AlbumAdapter extends ArrayAdapter<Album> implements SectionAdapter.
     public void setTouchPlay(final boolean play) {
         mTouchPlay = play;
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IPopupMenuCallback.IListener listener) {
+        mListener = listener;
+    }
 }
index 2c90599..4a70418 100644 (file)
@@ -26,10 +26,12 @@ import com.cyngn.eleven.R;
 import com.cyngn.eleven.cache.ImageFetcher;
 import com.cyngn.eleven.model.Artist;
 import com.cyngn.eleven.sectionadapter.SectionAdapter;
+import com.cyngn.eleven.sectionadapter.SectionAdapter.BasicAdapter;
 import com.cyngn.eleven.ui.MusicHolder;
 import com.cyngn.eleven.ui.MusicHolder.DataHolder;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 /**
  * This {@link ArrayAdapter} is used to display all of the artists on a user's
@@ -40,7 +42,7 @@ import com.cyngn.eleven.utils.MusicUtils;
 /**
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionAdapter.BasicAdapter {
+public class ArtistAdapter extends ArrayAdapter<Artist> implements BasicAdapter, IPopupMenuCallback {
 
     /**
      * Number of views (ImageView and TextView)
@@ -68,6 +70,11 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionAdapte
     private DataHolder[] mData;
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    private IListener mListener;
+
+    /**
      * Constructor of <code>ArtistAdapter</code>
      * 
      * @param context The {@link Context} to use.
@@ -94,6 +101,9 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionAdapte
             convertView = LayoutInflater.from(getContext()).inflate(mLayoutId, parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+
+            // set the pop up menu listener
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
@@ -107,6 +117,8 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionAdapte
         holder.mLineTwo.get().setText(dataHolder.mLineTwo);
         // Asynchronously load the artist image into the adapter
         mImageFetcher.loadArtistImage(dataHolder.mLineOne, holder.mImage.get());
+        // because of recycling, we need to set the position each time
+        holder.mPopupMenuButton.get().setPosition(position);
 
         return convertView;
     }
@@ -221,4 +233,9 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionAdapte
 
         return  -1;
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IListener listener) {
+        mListener = listener;
+    }
 }
index ab2e5c6..b542344 100644 (file)
@@ -27,6 +27,7 @@ import com.cyngn.eleven.ui.MusicHolder;
 import com.cyngn.eleven.ui.MusicHolder.DataHolder;
 import com.cyngn.eleven.ui.fragments.PlaylistFragment;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 /**
  * This {@link ArrayAdapter} is used to display all of the playlists on a user's
@@ -34,7 +35,7 @@ import com.cyngn.eleven.utils.MusicUtils;
  * 
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class PlaylistAdapter extends ArrayAdapter<Playlist> {
+public class PlaylistAdapter extends ArrayAdapter<Playlist> implements IPopupMenuCallback {
 
     /**
      * Smart playlists and normal playlists
@@ -52,6 +53,11 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> {
     private DataHolder[] mData;
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    protected IListener mListener;
+
+    /**
      * Constructor of <code>PlaylistAdapter</code>
      * 
      * @param context The {@link Context} to use.
@@ -77,6 +83,9 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> {
             convertView = LayoutInflater.from(getContext()).inflate(layoutId, parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+
+            // set the pop up menu listener
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
@@ -84,6 +93,9 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> {
         // Retrieve the data holder
         final DataHolder dataHolder = mData[position];
 
+        // because of recycling, we need to set the position each time
+        holder.mPopupMenuButton.get().setPosition(position);
+
         // Set each playlist name (line one)
         holder.mLineOne.get().setText(dataHolder.mLineOne);
 
@@ -181,4 +193,8 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> {
         mData = null;
     }
 
+    @Override
+    public void setPopupMenuClickedListener(IListener listener) {
+        mListener = listener;
+    }
 }
index ab0fccf..52d0141 100644 (file)
@@ -27,6 +27,7 @@ import com.cyngn.eleven.ui.activities.PlaylistDetailActivity;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.Lists;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 import java.util.List;
 
@@ -37,7 +38,7 @@ import java.util.List;
  * 
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class ProfileSongAdapter extends ArrayAdapter<Song> {
+public class ProfileSongAdapter extends ArrayAdapter<Song> implements IPopupMenuCallback {
 
     /**
      * Default display setting: title/album
@@ -100,6 +101,11 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> {
     private List<Song> mCount = Lists.newArrayList();
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    private IListener mListener;
+
+    /**
      * Constructor of <code>ProfileSongAdapter</code>
      * 
      * @param activity The {@link Activity} to use
@@ -137,6 +143,9 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> {
             convertView = LayoutInflater.from(getContext()).inflate(mLayoutId, parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+
+            // set the pop up menu listener
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
@@ -144,6 +153,9 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> {
         // Retrieve the album
         final Song song = getItem(position - 1);
 
+        // because of recycling, we need to set the position each time
+        holder.mPopupMenuButton.get().setPosition(position);
+
         // Set each track name (line one)
         holder.mLineOne.get().setText(song.mSongName);
         // Set the line two
@@ -164,8 +176,6 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> {
                             MusicUtils.makeShortTimeString(getContext(), song.mDuration));
                 }
 
-                ;
-
                 holder.mLineTwo.get().setText(MusicUtils.makeCombinedString(getContext(),
                         song.mArtistName, song.mAlbumName));
 
@@ -275,4 +285,9 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> {
     public boolean isEmpty() {
         return (mCount == null || mCount.size() == 0);
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IPopupMenuCallback.IListener listener) {
+        mListener = listener;
+    }
 }
index d6a84fd..da012f9 100644 (file)
@@ -29,6 +29,7 @@ import com.cyngn.eleven.ui.fragments.QueueFragment;
 import com.cyngn.eleven.ui.fragments.SongFragment;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.PlayPauseProgressButton;
 
 /**
@@ -38,7 +39,8 @@ import com.cyngn.eleven.widgets.PlayPauseProgressButton;
  * 
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.BasicAdapter {
+public class SongAdapter extends ArrayAdapter<Song>
+        implements SectionAdapter.BasicAdapter, IPopupMenuCallback {
 
     /**
      * Number of views (TextView)
@@ -66,6 +68,11 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba
     private DataHolder[] mData;
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    private IPopupMenuCallback.IListener mListener;
+
+    /**
      * Constructor of <code>SongAdapter</code>
      * 
      * @param context The {@link Context} to use.
@@ -90,6 +97,8 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba
             convertView = LayoutInflater.from(getContext()).inflate(mLayoutId, parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
@@ -97,6 +106,8 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba
         // Retrieve the data holder
         final DataHolder dataHolder = mData[position];
 
+        // Sets the position each time because of recycling
+        holder.mPopupMenuButton.get().setPosition(position);
         // Set each song name (line one)
         holder.mLineOne.get().setText(dataHolder.mLineOne);
         // Set the album name (line two)
@@ -226,4 +237,9 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba
             notifyDataSetChanged();
         }
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IListener listener) {
+        mListener = listener;
+    }
 }
index c9137c1..8072f3f 100644 (file)
@@ -19,13 +19,15 @@ import com.cyngn.eleven.sectionadapter.SectionAdapter;
 import com.cyngn.eleven.ui.MusicHolder;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 import java.util.Locale;
 
 /**
  * Used to populate the list view with the search results.
  */
-public final class SummarySearchAdapter extends ArrayAdapter<SearchResult> implements SectionAdapter.BasicAdapter {
+public final class SummarySearchAdapter extends ArrayAdapter<SearchResult>
+        implements SectionAdapter.BasicAdapter, IPopupMenuCallback {
 
     /**
      * no-image list item type and with image type
@@ -48,6 +50,11 @@ public final class SummarySearchAdapter extends ArrayAdapter<SearchResult> imple
     private char[] mPrefix;
 
     /**
+     * Used to listen to the pop up menu callbacks
+     */
+    private IListener mListener;
+
+    /**
      * Constructor for <code>SearchAdapter</code>
      *
      * @param context The {@link Activity} to use.
@@ -73,10 +80,15 @@ public final class SummarySearchAdapter extends ArrayAdapter<SearchResult> imple
                     getViewResourceId(position), parent, false);
             holder = new MusicHolder(convertView);
             convertView.setTag(holder);
+            // set the pop up menu listener
+            holder.mPopupMenuButton.get().setPopupMenuClickedListener(mListener);
         } else {
             holder = (MusicHolder)convertView.getTag();
         }
 
+        // Sets the position each time because of recycling
+        holder.mPopupMenuButton.get().setPosition(position);
+
         final SearchResult item = getItem(position);
 
         switch (item.mType) {
@@ -90,7 +102,6 @@ public final class SummarySearchAdapter extends ArrayAdapter<SearchResult> imple
                 String albumCount = MusicUtils.makeLabel(getContext(), R.plurals.Nalbums, item.mAlbumCount);
                 // Album Name | Artist Name (line two)
                 holder.mLineTwo.get().setText(MusicUtils.makeCombinedString(getContext(), songCount, albumCount));
-
                 break;
             case Album:
                 // Asynchronously load the album images into the adapter
@@ -231,4 +242,9 @@ public final class SummarySearchAdapter extends ArrayAdapter<SearchResult> imple
 
         return  -1;
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IListener listener) {
+        mListener = listener;
+    }
 }
\ No newline at end of file
index dd46ee4..6f95490 100644 (file)
@@ -18,6 +18,7 @@ import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.AudioColumns;
 
 import com.cyngn.eleven.model.Song;
+import com.cyngn.eleven.sectionadapter.SectionCreator;
 import com.cyngn.eleven.utils.Lists;
 
 import java.util.ArrayList;
@@ -29,7 +30,7 @@ import java.util.List;
  * 
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class LastAddedLoader extends WrappedAsyncTaskLoader<List<Song>> {
+public class LastAddedLoader extends SectionCreator.SimpleListLoader<Song> {
 
     /**
      * The result
index ce74f26..1a7f872 100644 (file)
@@ -16,6 +16,7 @@ import com.cyngn.eleven.R;
 import com.cyngn.eleven.ui.MusicHolder;
 import com.cyngn.eleven.utils.SectionCreatorUtils.Section;
 import com.cyngn.eleven.utils.SectionCreatorUtils.SectionType;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 import java.util.TreeMap;
 
@@ -27,8 +28,8 @@ import java.util.TreeMap;
  * @param <TArrayAdapter> the arrayadapter that contains TItem and implements BasicAdapter
  */
 public class SectionAdapter<TItem,
-        TArrayAdapter extends ArrayAdapter<TItem> & SectionAdapter.BasicAdapter>
-        extends BaseAdapter {
+        TArrayAdapter extends ArrayAdapter<TItem> & SectionAdapter.BasicAdapter & IPopupMenuCallback>
+        extends BaseAdapter implements IPopupMenuCallback, IPopupMenuCallback.IListener {
     /**
      * Basic interface that the adapters implement
      */
@@ -56,6 +57,11 @@ public class SectionAdapter<TItem,
     protected boolean mFooterEnabled;
 
     /**
+     * Popup menu click listener
+     */
+    protected IListener mListener;
+
+    /**
      * {@link Context}
      */
     protected final Context mContext;
@@ -68,6 +74,7 @@ public class SectionAdapter<TItem,
     public SectionAdapter(final Activity context, final TArrayAdapter underlyingAdapter) {
         mContext = context;
         mUnderlyingAdapter = underlyingAdapter;
+        mUnderlyingAdapter.setPopupMenuClickedListener(this);
         mSections = new TreeMap<Integer, Section>();
         setupHeaderParameters(R.layout.list_header, false);
         // since we have no good default footer, just re-use the header layout
@@ -383,4 +390,16 @@ public class SectionAdapter<TItem,
 
         return -1;
     }
+
+    @Override
+    public void setPopupMenuClickedListener(IListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onPopupMenuClicked(View v, int position) {
+        if (mListener != null) {
+            mListener.onPopupMenuClicked(v, getExternalPosition(position));
+        }
+    }
 }
index 05329df..f9b4382 100644 (file)
@@ -20,6 +20,7 @@ import android.widget.TextView;
 
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.widgets.PlayPauseProgressButton;
+import com.cyngn.eleven.widgets.PopupMenuButton;
 
 import java.lang.ref.WeakReference;
 
@@ -81,6 +82,11 @@ public class MusicHolder {
     public WeakReference<View> mDivider;
 
     /**
+     * The divider for the list item
+     */
+    public WeakReference<PopupMenuButton> mPopupMenuButton;
+
+    /**
      * Constructor of <code>ViewHolder</code>
      * 
      * @param context The {@link Context} to use.
@@ -110,6 +116,10 @@ public class MusicHolder {
 
         // Get the divider for the list item
         mDivider = new WeakReference<View>(view.findViewById(R.id.divider));
+
+        // Get the pop up menu button
+        mPopupMenuButton = new WeakReference<PopupMenuButton>(
+                (PopupMenuButton)view.findViewById(R.id.popup_menu_button));
     }
 
     /**
index b020fb1..991b0d8 100644 (file)
@@ -8,9 +8,6 @@ import android.provider.MediaStore;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.Menu;
-import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
@@ -28,13 +25,13 @@ import com.cyngn.eleven.dragdrop.DragSortListView.DragScrollProfile;
 import com.cyngn.eleven.dragdrop.DragSortListView.DropListener;
 import com.cyngn.eleven.dragdrop.DragSortListView.RemoveListener;
 import com.cyngn.eleven.loaders.PlaylistSongLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
 import com.cyngn.eleven.menu.DeleteDialog;
 import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Song;
 import com.cyngn.eleven.recycler.RecycleHolder;
 import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.NavUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
 import java.util.ArrayList;
@@ -66,24 +63,14 @@ public class PlaylistDetailActivity extends DetailActivity implements
     private TextView mDurationOfPlaylist;
 
     /**
-     * Represents a song
-     */
-    private Song mSong;
-
-    /**
-     * Position of a context menu item
-     */
-    private int mSelectedPosition;
-
-    /**
-     * Id of a context menu item
+     * The Id of the playlist the songs belong to
      */
-    private long mSelectedId;
+    private long mPlaylistId;
 
     /**
-     * The Id of the playlist the songs belong to
+     * Pop up menu helper
      */
-    private long mPlaylistId;
+    private PopupMenuHelper mPopupMenuHelper;
 
     @Override
     protected int getLayoutToInflate() {
@@ -94,6 +81,76 @@ public class PlaylistDetailActivity extends DetailActivity implements
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mPopupMenuHelper = new PopupMenuHelper(this, getSupportFragmentManager()) {
+            /**
+             * Represents a song
+             */
+            private Song mSong;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                // the header has been long-pressed - for now just return, but later if we want
+                // to long-press support for the header, do that logic here
+                if (position == 0) {
+                    return null;
+                }
+
+                // Create a new song
+                mSong = mAdapter.getItem(position - 1);
+
+                return PopupMenuType.Song;
+            }
+
+            @Override
+            protected void getAdditionalIds(PopupMenuType type, ArrayList<Integer> list) {
+                super.getAdditionalIds(type, list);
+
+                list.add(FragmentMenuItems.REMOVE_FROM_PLAYLIST);
+            }
+
+            @Override
+            protected long[] getIdList() {
+                return new long[] { mSong.mSongId };
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected long getId() {
+                return mSong.mSongId;
+            }
+
+            @Override
+            protected String getArtistName() {
+                return mSong.mArtistName;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                DeleteDialog.newInstance(mSong.mSongName, getIdList(), null)
+                        .show(getSupportFragmentManager(), "DeleteDialog");
+                SystemClock.sleep(10);
+                mAdapter.notifyDataSetChanged();
+                getSupportLoaderManager().restartLoader(LOADER, null, PlaylistDetailActivity.this);
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                // do nothing here since we restart the loader ourselves
+            }
+
+            @Override
+            protected void removeFromPlaylist() {
+                mAdapter.remove(mSong);
+                mAdapter.notifyDataSetChanged();
+                MusicUtils.removeFromPlaylist(PlaylistDetailActivity.this, mSong.mSongId, mPlaylistId);
+                getSupportLoaderManager().restartLoader(LOADER, null, PlaylistDetailActivity.this);
+            }
+        };
+
         Bundle arguments = getIntent().getExtras();
         String playlistName = arguments.getString(Config.NAME);
         mPlaylistId = arguments.getLong(Config.ID);
@@ -129,11 +186,15 @@ public class PlaylistDetailActivity extends DetailActivity implements
                 R.layout.faux_playlist_header,
                 ProfileSongAdapter.DISPLAY_PLAYLIST_SETTING
         );
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
         mListView.setAdapter(mAdapter);
         // Release any references to the recycled Views
         mListView.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        mListView.setOnCreateContextMenuListener(this);
         // Play the selected song
         mListView.setOnItemClickListener(this);
         // Set the drop listener
@@ -158,118 +219,6 @@ public class PlaylistDetailActivity extends DetailActivity implements
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-                                    final ContextMenu.ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-        // Get the position of the selected item
-        final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-        mSelectedPosition = info.position - 1;
-
-        // the header has been long-pressed - for now just return, but later if we want
-        // to long-press support for the header, do that logic here
-        if (mSelectedPosition == -1) {
-            return;
-        }
-
-        // Creat a new song
-        mSong = mAdapter.getItem(mSelectedPosition);
-        mSelectedId = mSong.mSongId;
-
-        // Play the song
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                getString(R.string.context_menu_play_selection));
-
-        // Play next
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_NEXT, Menu.NONE,
-                getString(R.string.context_menu_play_next));
-
-        // Add the song to the queue
-        menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
-                getString(R.string.add_to_queue));
-
-        // Add the song to a playlist
-        final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(this, GROUP_ID, subMenu);
-
-        // View more content by the song artist
-        menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
-                getString(R.string.context_menu_more_by_artist));
-
-        // Make the song a ringtone
-        menu.add(GROUP_ID, FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE,
-                getString(R.string.context_menu_use_as_ringtone));
-
-        // Remove the song from playlist
-        menu.add(GROUP_ID, FragmentMenuItems.REMOVE_FROM_PLAYLIST, Menu.NONE,
-                getString(R.string.context_menu_remove_from_playlist));
-
-        // Delete the song
-        menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    MusicUtils.playAll(this, new long[]{
-                            mSelectedId
-                    }, 0, false);
-                    return true;
-                case FragmentMenuItems.PLAY_NEXT:
-                    MusicUtils.playNext(new long[]{
-                            mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    MusicUtils.addToQueue(this, new long[]{
-                            mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(new long[]{
-                            mSelectedId
-                    }).show(getSupportFragmentManager(), "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long playlistId = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(this, new long[]{
-                            mSelectedId
-                    }, playlistId);
-                    return true;
-                case FragmentMenuItems.MORE_BY_ARTIST:
-                    NavUtils.openArtistProfile(this, mSong.mArtistName);
-                    return true;
-                case FragmentMenuItems.USE_AS_RINGTONE:
-                    MusicUtils.setRingtone(this, mSelectedId);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    DeleteDialog.newInstance(mSong.mSongName, new long[]{
-                            mSelectedId
-                    }, null).show(getSupportFragmentManager(), "DeleteDialog");
-                    SystemClock.sleep(10);
-                    mAdapter.notifyDataSetChanged();
-                    getSupportLoaderManager().restartLoader(LOADER, null, this);
-                    return true;
-                case FragmentMenuItems.REMOVE_FROM_PLAYLIST:
-                    mAdapter.remove(mSong);
-                    mAdapter.notifyDataSetChanged();
-                    MusicUtils.removeFromPlaylist(this, mSong.mSongId, mPlaylistId);
-                    getSupportLoaderManager().restartLoader(LOADER, null, this);
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public float getSpeed(final float w, final long t) {
         if (w > 0.8f) {
             return mAdapter.getCount() / 0.001f;
@@ -283,12 +232,12 @@ public class PlaylistDetailActivity extends DetailActivity implements
      */
     @Override
     public void remove(final int which) {
-        mSong = mAdapter.getItem(which - 1);
-        mAdapter.remove(mSong);
+        Song song = mAdapter.getItem(which - 1);
+        mAdapter.remove(song);
         mAdapter.notifyDataSetChanged();
         final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", mPlaylistId);
         getContentResolver().delete(uri,
-                MediaStore.Audio.Playlists.Members.AUDIO_ID + "=" + mSong.mSongId,
+                MediaStore.Audio.Playlists.Members.AUDIO_ID + "=" + song.mSongId,
                 null);
     }
 
@@ -303,9 +252,9 @@ public class PlaylistDetailActivity extends DetailActivity implements
         }
         final int realFrom = from - 1;
         final int realTo = to - 1;
-        mSong = mAdapter.getItem(realFrom);
-        mAdapter.remove(mSong);
-        mAdapter.insert(mSong, realTo);
+        Song song = mAdapter.getItem(realFrom);
+        mAdapter.remove(song);
+        mAdapter.insert(song, realTo);
         mAdapter.notifyDataSetChanged();
         MediaStore.Audio.Playlists.Members.moveItem(getContentResolver(),
                 mPlaylistId, realFrom, realTo);
index 48422cb..64fb620 100644 (file)
@@ -49,6 +49,7 @@ import com.cyngn.eleven.IElevenService;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.SummarySearchAdapter;
 import com.cyngn.eleven.loaders.WrappedAsyncTaskLoader;
+import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.AlbumArtistDetails;
 import com.cyngn.eleven.model.SearchResult;
 import com.cyngn.eleven.model.SearchResult.ResultType;
@@ -62,8 +63,10 @@ import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.utils.MusicUtils.ServiceToken;
 import com.cyngn.eleven.utils.NavUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
 import com.cyngn.eleven.utils.SectionCreatorUtils;
 import com.cyngn.eleven.utils.SectionCreatorUtils.IItemCompare;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
 import java.util.ArrayList;
@@ -88,6 +91,11 @@ public class SearchActivity extends FragmentActivity implements
     private static int LOADING_DELAY = 500;
 
     /**
+     * Used to keep context menu items from bleeding into other fragments
+     */
+    private static final int GROUP_ID = 5;
+
+    /**
      * Identifier for the search loader
      */
     private static int SEARCH_LOADER = 0;
@@ -181,12 +189,76 @@ public class SearchActivity extends FragmentActivity implements
     private boolean mQuitting = false;
 
     /**
+     * Pop up menu helper
+     */
+    private PopupMenuHelper mPopupMenuHelper;
+
+    /**
      * {@inheritDoc}
      */
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mPopupMenuHelper = new PopupMenuHelper(this, getSupportFragmentManager()) {
+            private SearchResult mSelectedItem;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                mSelectedItem = mAdapter.getTItem(position);
+
+                return PopupMenuType.SearchResult;
+            }
+
+            @Override
+            protected long[] getIdList() {
+                switch (mSelectedItem.mType) {
+                    case Artist:
+                        return MusicUtils.getSongListForArtist(SearchActivity.this,
+                                mSelectedItem.mId);
+                    case Album:
+                        return MusicUtils.getSongListForAlbum(SearchActivity.this,
+                                mSelectedItem.mId);
+                    case Song:
+                        return new long[] { mSelectedItem.mId };
+                    case Playlist:
+                        return MusicUtils.getSongListForPlaylist(SearchActivity.this,
+                                mSelectedItem.mId);
+                    default:
+                        return null;
+                }
+            }
+
+            @Override
+            protected void getAdditionalIds(PopupMenuType type, ArrayList<Integer> list) {
+                super.getAdditionalIds(type, list);
+
+                if (mSelectedItem.mType == ResultType.Album) {
+                    list.add(FragmentMenuItems.MORE_BY_ARTIST);
+                }
+            }
+
+            @Override
+            protected String getArtistName() {
+                return mSelectedItem.mArtist;
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                // do nothing
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                // do nothing
+            }
+        };
+
         // Fade it in
         overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
 
@@ -209,6 +281,12 @@ public class SearchActivity extends FragmentActivity implements
         mAdapter.getUnderlyingAdapter().setPrefix(mFilterString);
         mAdapter.setupHeaderParameters(R.layout.list_search_header, false);
         mAdapter.setupFooterParameters(R.layout.list_search_footer, true);
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
 
         // setup the no results container
         mNoResultsContainer = (NoResultsContainer)findViewById(R.id.no_results_container);
index b70b215..c4d4b29 100644 (file)
@@ -15,33 +15,24 @@ import android.app.Activity;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.GridView;
-import android.widget.ListView;
-import android.widget.TextView;
 
 import com.cyngn.eleven.MusicStateListener;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.AlbumAdapter;
 import com.cyngn.eleven.cache.ImageFetcher;
 import com.cyngn.eleven.loaders.AlbumLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
 import com.cyngn.eleven.menu.DeleteDialog;
-import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Album;
 import com.cyngn.eleven.recycler.RecycleHolder;
 import com.cyngn.eleven.sectionadapter.SectionAdapter;
@@ -51,8 +42,8 @@ import com.cyngn.eleven.ui.activities.BaseActivity;
 import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.utils.NavUtils;
-import com.cyngn.eleven.utils.PreferenceUtils;
-import com.cyngn.eleven.utils.SectionCreatorUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 import com.viewpagerindicator.TitlePageIndicator;
 
@@ -95,24 +86,14 @@ public class AlbumFragment extends Fragment implements LoaderCallbacks<SectionLi
     private GridView mGridView;
 
     /**
-     * The list view
-     */
-    private ListView mListView;
-
-    /**
-     * Album song list
-     */
-    private long[] mAlbumList;
-
-    /**
-     * Represents an album
+     * True if the list should execute {@code #restartLoader()}.
      */
-    private Album mAlbum;
+    private boolean mShouldRefresh = false;
 
     /**
-     * True if the list should execute {@code #restartLoader()}.
+     * Pop up menu helper
      */
-    private boolean mShouldRefresh = false;
+    private PopupMenuHelper mPopupMenuHelper;
 
     /**
      * {@inheritDoc}
@@ -130,10 +111,61 @@ public class AlbumFragment extends Fragment implements LoaderCallbacks<SectionLi
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
+            /**
+             * Represents an album
+             */
+            private Album mAlbum;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                // Create a new album
+                mAlbum = mAdapter.getTItem(position);
+
+                return PopupMenuType.Album;
+            }
+
+            @Override
+            protected long[] getIdList() {
+                return MusicUtils.getSongListForAlbum(getActivity(), mAlbum.mAlbumId);
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                mShouldRefresh = true;
+                final String album = mAlbum.mAlbumName;
+                DeleteDialog.newInstance(album, getIdList(),
+                        ImageFetcher.generateAlbumCacheKey(album,mAlbum.mArtistName))
+                        .show(getFragmentManager(), "DeleteDialog");
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                mShouldRefresh = true;
+            }
+
+            @Override
+            protected String getArtistName() {
+                return mAlbum.mArtistName;
+            }
+        };
+
         int layout = R.layout.grid_items_normal;
 
         AlbumAdapter adapter = new AlbumAdapter(getActivity(), layout);
         mAdapter = new SectionAdapter<Album, AlbumAdapter>(getActivity(), adapter);
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
     }
 
     /**
@@ -172,82 +204,6 @@ public class AlbumFragment extends Fragment implements LoaderCallbacks<SectionLi
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-            final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
-        // Create a new album
-        mAlbum = mAdapter.getTItem(info.position);
-        // Create a list of the album's songs
-        mAlbumList = MusicUtils.getSongListForAlbum(getActivity(), mAlbum.mAlbumId);
-
-        // Play the album
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                getString(R.string.context_menu_play_selection));
-
-        // Add the album to the queue
-        menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
-                getString(R.string.add_to_queue));
-
-        // Add the album to a playlist
-        final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
-
-        // View more content by the album artist
-        menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
-                getString(R.string.context_menu_more_by_artist));
-
-        // Remove the album from the list
-        menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onContextItemSelected(final MenuItem item) {
-        // Avoid leaking context menu selections
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    MusicUtils.playAll(getActivity(), mAlbumList, 0, false);
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    MusicUtils.addToQueue(getActivity(), mAlbumList);
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(mAlbumList).show(getFragmentManager(),
-                            "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.MORE_BY_ARTIST:
-                    NavUtils.openArtistProfile(getActivity(), mAlbum.mArtistName);
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long id = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(getActivity(), mAlbumList, id);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    mShouldRefresh = true;
-                    final String album = mAlbum.mAlbumName;
-                    DeleteDialog.newInstance(album, mAlbumList,
-                            ImageFetcher.generateAlbumCacheKey(album,mAlbum.mArtistName))
-                            .show(getFragmentManager(), "DeleteDialog");
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void onScrollStateChanged(final AbsListView view, final int scrollState) {
         // Pause disk cache access to ensure smoother scrolling
         if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
@@ -265,8 +221,8 @@ public class AlbumFragment extends Fragment implements LoaderCallbacks<SectionLi
     @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
             final long id) {
-        mAlbum = mAdapter.getTItem(position);
-        NavUtils.openAlbumProfile(getActivity(), mAlbum.mAlbumName, mAlbum.mArtistName, mAlbum.mAlbumId);
+        Album album = mAdapter.getTItem(position);
+        NavUtils.openAlbumProfile(getActivity(), album.mAlbumName, album.mArtistName, album.mAlbumId);
     }
 
     /**
@@ -384,8 +340,6 @@ public class AlbumFragment extends Fragment implements LoaderCallbacks<SectionLi
     private void initAbsListView(final AbsListView list) {
         // Release any references to the recycled Views
         list.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        list.setOnCreateContextMenuListener(this);
         // Show the albums and songs from the selected artist
         list.setOnItemClickListener(this);
         // To help make scrolling smooth
index 3c16529..954f6ad 100644 (file)
@@ -16,49 +16,38 @@ import android.content.Context;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.text.TextUtils;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
-import android.widget.GridView;
 import android.widget.ListView;
-import android.widget.TextView;
 
 import com.cyngn.eleven.MusicStateListener;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.ArtistAdapter;
 import com.cyngn.eleven.loaders.ArtistLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
 import com.cyngn.eleven.menu.DeleteDialog;
-import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Artist;
 import com.cyngn.eleven.recycler.RecycleHolder;
 import com.cyngn.eleven.sectionadapter.SectionAdapter;
 import com.cyngn.eleven.sectionadapter.SectionCreator;
 import com.cyngn.eleven.sectionadapter.SectionListContainer;
 import com.cyngn.eleven.ui.activities.BaseActivity;
-import com.cyngn.eleven.utils.ApolloUtils;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.utils.NavUtils;
-import com.cyngn.eleven.utils.PreferenceUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
 import com.cyngn.eleven.utils.SectionCreatorUtils;
 import com.cyngn.eleven.utils.SectionCreatorUtils.IItemCompare;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 import com.viewpagerindicator.TitlePageIndicator;
 
-import java.util.List;
-
 /**
  * This class is used to display all of the artists on a user's device.
  * 
@@ -73,11 +62,6 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
     private static final int GROUP_ID = 2;
 
     /**
-     * Grid view column count. ONE - list, TWO - normal grid, FOUR - landscape
-     */
-    private static final int ONE = 1, TWO = 2, FOUR = 4;
-
-    /**
      * LoaderCallbacks identifier
      */
     private static final int LOADER = 0;
@@ -93,29 +77,19 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
     private SectionAdapter<Artist, ArtistAdapter> mAdapter;
 
     /**
-     * The grid view
-     */
-    private GridView mGridView;
-
-    /**
      * The list view
      */
     private ListView mListView;
 
     /**
-     * Artist song list
-     */
-    private long[] mArtistList;
-
-    /**
-     * Represents an artist
+     * True if the list should execute {@code #restartLoader()}.
      */
-    private Artist mArtist;
+    private boolean mShouldRefresh = false;
 
     /**
-     * True if the list should execute {@code #restartLoader()}.
+     * Pop up menu helper
      */
-    private boolean mShouldRefresh = false;
+    private PopupMenuHelper mPopupMenuHelper;
 
     /**
      * Empty constructor as per the {@link Fragment} documentation
@@ -139,10 +113,55 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        // Create the adpater
+
+        mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
+            /**
+             * Represents an artist
+             */
+            private Artist mArtist;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                // Create a new model
+                mArtist = mAdapter.getTItem(position);
+
+                return PopupMenuType.Artist;
+            }
+
+            @Override
+            protected long[] getIdList() {
+                return MusicUtils.getSongListForArtist(getActivity(), mArtist.mArtistId);
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                mShouldRefresh = true;
+                final String artist = mArtist.mArtistName;
+                DeleteDialog.newInstance(artist, getIdList(), artist).show(
+                        getFragmentManager(), "DeleteDialog");
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                mShouldRefresh = true;
+            }
+        };
+
+        // Create the adapter
         final int layout = R.layout.list_item_normal;
         ArtistAdapter adapter = new ArtistAdapter(getActivity(), layout);
         mAdapter = new SectionAdapter<Artist, ArtistAdapter>(getActivity(), adapter);
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
     }
 
     /**
@@ -182,74 +201,6 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-            final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
-        // Creat a new model
-        mArtist = mAdapter.getTItem(info.position);
-        // Create a list of the artist's songs
-        mArtistList = MusicUtils.getSongListForArtist(getActivity(), mArtist.mArtistId);
-
-        // Play the artist
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                getString(R.string.context_menu_play_selection));
-
-        // Add the artist to the queue
-        menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
-                getString(R.string.add_to_queue));
-
-        // Add the artist to a playlist
-        final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
-
-        // Delete the artist
-        menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        // Avoid leaking context menu selections
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    MusicUtils.playAll(getActivity(), mArtistList, 0, true);
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    MusicUtils.addToQueue(getActivity(), mArtistList);
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(mArtistList).show(getFragmentManager(),
-                            "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long id = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(getActivity(), mArtistList, id);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    mShouldRefresh = true;
-                    final String artist = mArtist.mArtistName;
-                    DeleteDialog.newInstance(artist, mArtistList, artist).show(
-                            getFragmentManager(), "DeleteDialog");
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void onScrollStateChanged(final AbsListView view, final int scrollState) {
         // Pause disk cache access to ensure smoother scrolling
         if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
@@ -267,8 +218,8 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
     @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
             final long id) {
-        mArtist = mAdapter.getTItem(position);
-        NavUtils.openArtistProfile(getActivity(), mArtist.mArtistName);
+        Artist artist = mAdapter.getTItem(position);
+        NavUtils.openArtistProfile(getActivity(), artist.mArtistName);
     }
 
     /**
@@ -386,8 +337,6 @@ public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionL
     private void initAbsListView(final AbsListView list) {
         // Release any references to the recycled Views
         list.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        list.setOnCreateContextMenuListener(this);
         // Show the albums and songs from the selected artist
         list.setOnItemClickListener(this);
         // To help make scrolling smooth
index c2c04bc..0da0862 100644 (file)
@@ -283,6 +283,7 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection {
             @Override
             public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                 MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, menu);
+                menu.setHeaderTitle(R.string.add_to_playlist);
             }
         });
 
index 5f1ae5a..183f80b 100644 (file)
@@ -14,23 +14,19 @@ package com.cyngn.eleven.ui.fragments;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ContentUris;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.MediaStore;
 import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
 
@@ -39,13 +35,13 @@ import com.cyngn.eleven.MusicStateListener;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.PlaylistAdapter;
 import com.cyngn.eleven.loaders.PlaylistLoader;
-import com.cyngn.eleven.menu.FragmentMenuItems;
-import com.cyngn.eleven.menu.RenamePlaylist;
 import com.cyngn.eleven.model.Playlist;
 import com.cyngn.eleven.recycler.RecycleHolder;
 import com.cyngn.eleven.ui.activities.BaseActivity;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.utils.NavUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 
 import java.util.List;
 
@@ -78,9 +74,14 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
     private ListView mListView;
 
     /**
-     * Represents a playlist
+     * True if the list should execute {@code #restartLoader()}.
      */
-    private Playlist mPlaylist;
+    private boolean mShouldRefresh = false;
+
+    /**
+     * Pop up menu helper
+     */
+    private PopupMenuHelper mPopupMenuHelper;
 
     /**
      * Empty constructor as per the {@link Fragment} documentation
@@ -104,8 +105,62 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
+            /**
+             * Represents a playlist
+             */
+            private Playlist mPlaylist;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                // Create a new playlist
+                mPlaylist = mAdapter.getItem(position);
+
+                return mPlaylist.isSmartPlaylist() ?
+                        PopupMenuType.SmartPlaylist : PopupMenuType.Playlist;
+            }
+
+            @Override
+            protected long[] getIdList() {
+                if (mPlaylist.isSmartPlaylist()) {
+                    return MusicUtils.getSongListForSmartPlaylist(getActivity(),
+                            SmartPlaylistType.getTypeById(mPlaylist.mPlaylistId));
+                } else {
+                    return MusicUtils.getSongListForPlaylist(getActivity(),
+                            mPlaylist.mPlaylistId);
+                }
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                mShouldRefresh = true;
+                buildDeleteDialog(getId(), mPlaylist.mPlaylistName).show();
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                mShouldRefresh = true;
+            }
+
+            @Override
+            protected long getId() {
+                return mPlaylist.mPlaylistId;
+            }
+        };
+
         // Create the adpater
         mAdapter = new PlaylistAdapter(getActivity());
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
     }
 
     /**
@@ -122,8 +177,6 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
         mListView.setAdapter(mAdapter);
         // Release any references to the recycled Views
         mListView.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        mListView.setOnCreateContextMenuListener(this);
         // Play the selected song
         mListView.setOnItemClickListener(this);
         return rootView;
@@ -145,85 +198,15 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-            final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
-        final int mPosition = info.position;
-        // Create a new playlist
-        mPlaylist = mAdapter.getItem(mPosition);
-
-        // Play the playlist
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                R.string.context_menu_play_selection);
-
-        // Add the playlist to the queue
-        menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE, R.string.add_to_queue);
-
-        // Delete and rename (user made playlists)
-        if (info.position > 1) {
-            menu.add(GROUP_ID, FragmentMenuItems.RENAME_PLAYLIST, Menu.NONE,
-                    R.string.context_menu_rename_playlist);
-
-            menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE, R.string.context_menu_delete);
-
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        if (item.getGroupId() == GROUP_ID) {
-            final AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    if (info.position == 0) {
-                        MusicUtils.playLastAdded(getActivity());
-                    } else {
-                        MusicUtils.playPlaylist(getActivity(), mPlaylist.mPlaylistId);
-                    }
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    long[] list = null;
-                    if (info.position == 0) {
-                        list = MusicUtils.getSongListForLastAdded(getActivity());
-                    } else {
-                        list = MusicUtils.getSongListForPlaylist(getActivity(),
-                                mPlaylist.mPlaylistId);
-                    }
-                    MusicUtils.addToQueue(getActivity(), list);
-                    return true;
-                case FragmentMenuItems.RENAME_PLAYLIST:
-                    RenamePlaylist.getInstance(mPlaylist.mPlaylistId).show(
-                            getFragmentManager(), "RenameDialog");
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    buildDeleteDialog().show();
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
             final long id) {
-        mPlaylist = mAdapter.getItem(position);
+        Playlist playlist = mAdapter.getItem(position);
 
-        SmartPlaylistType playlistType = SmartPlaylistType.getTypeById(mPlaylist.mPlaylistId);
+        SmartPlaylistType playlistType = SmartPlaylistType.getTypeById(playlist.mPlaylistId);
         if (playlistType != null) {
             NavUtils.openSmartPlaylist(getActivity(), playlistType);
         } else {
-            NavUtils.openPlaylist(getActivity(), mPlaylist.mPlaylistId, mPlaylist.mPlaylistName);
+            NavUtils.openPlaylist(getActivity(), playlist.mPlaylistId, playlist.mPlaylistName);
         }
     }
 
@@ -269,8 +252,10 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
      */
     @Override
     public void restartLoader() {
-        // Refresh the list when a playlist is deleted or renamed
-        getLoaderManager().restartLoader(LOADER, null, this);
+        if (mShouldRefresh) {
+            getLoaderManager().restartLoader(LOADER, null, this);
+        }
+        mShouldRefresh = false;
     }
 
     /**
@@ -284,21 +269,20 @@ public class PlaylistFragment extends Fragment implements LoaderCallbacks<List<P
     /**
      * Create a new {@link AlertDialog} for easy playlist deletion
      * 
-     * @param context The {@link Context} to use
-     * @param title The title of the playlist being deleted
-     * @param id The ID of the playlist being deleted
+     * @param playlistName The title of the playlist being deleted
+     * @param playlistId The ID of the playlist being deleted
      * @return A new {@link AlertDialog} used to delete playlists
      */
-    private final AlertDialog buildDeleteDialog() {
+    private final AlertDialog buildDeleteDialog(final long playlistId, final String playlistName) {
         return new AlertDialog.Builder(getActivity())
-                .setTitle(getString(R.string.delete_dialog_title, mPlaylist.mPlaylistName))
+                .setTitle(getString(R.string.delete_dialog_title, playlistName))
                 .setPositiveButton(R.string.context_menu_delete, new OnClickListener() {
 
                     @Override
                     public void onClick(final DialogInterface dialog, final int which) {
                         final Uri mUri = ContentUris.withAppendedId(
                                 MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
-                                mPlaylist.mPlaylistId);
+                                playlistId);
                         getActivity().getContentResolver().delete(mUri, null, null);
                         MusicUtils.refresh();
                     }
index 1aafce8..fad49e1 100644 (file)
@@ -20,19 +20,14 @@ import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
-import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
 
 import com.cyngn.eleven.MusicPlaybackService;
@@ -44,15 +39,14 @@ import com.cyngn.eleven.dragdrop.DragSortListView.DropListener;
 import com.cyngn.eleven.dragdrop.DragSortListView.RemoveListener;
 import com.cyngn.eleven.loaders.NowPlayingCursor;
 import com.cyngn.eleven.loaders.QueueLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
 import com.cyngn.eleven.menu.DeleteDialog;
 import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Song;
 import com.cyngn.eleven.recycler.RecycleHolder;
 import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.NavUtils;
+import com.cyngn.eleven.utils.PopupMenuHelper;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.PlayPauseProgressButton;
-import com.viewpagerindicator.TitlePageIndicator;
 
 import java.lang.ref.WeakReference;
 import java.util.List;
@@ -98,24 +92,9 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
     private DragSortListView mListView;
 
     /**
-     * Represents a song
+     * Pop up menu helper
      */
-    private Song mSong;
-
-    /**
-     * Position of a context menu item
-     */
-    private int mSelectedPosition;
-
-    /**
-     * Id of a context menu item
-     */
-    private long mSelectedId;
-
-    /**
-     * Song, album, and artist name used in the context menu
-     */
-    private String mSongName, mAlbumName, mArtistName;
+    private PopupMenuHelper mPopupMenuHelper;
 
     /**
      * Empty constructor as per the {@link Fragment} documentation
@@ -129,8 +108,75 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
+            private Song mSong;
+            private int mSelectedPosition;
+
+            @Override
+            protected PopupMenuType onPreparePopupMenu(int position) {
+                mSelectedPosition = position;
+                mSong = mAdapter.getItem(mSelectedPosition);
+
+                return PopupMenuType.Queue;
+            }
+
+            @Override
+            protected long[] getIdList() {
+                return new long[] { mSong.mSongId };
+            }
+
+            @Override
+            protected int getGroupId() {
+                return GROUP_ID;
+            }
+
+            @Override
+            protected long getId() {
+                return mSong.mSongId;
+            }
+
+            @Override
+            protected String getArtistName() {
+                return mSong.mArtistName;
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                DeleteDialog.newInstance(mSong.mSongName,
+                        new long[] { getId() }, null).show(getFragmentManager(), "DeleteDialog");
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                // do nothing
+            }
+
+            @Override
+            protected void playNext() {
+                NowPlayingCursor queue = (NowPlayingCursor)QueueLoader
+                        .makeQueueCursor(getActivity());
+                queue.removeItem(mSelectedPosition);
+                queue.close();
+                queue = null;
+                MusicUtils.playNext(getIdList());
+                refreshQueue();
+            }
+
+            @Override
+            protected void removeFromQueue() {
+                MusicUtils.removeTrack(getId());
+                refreshQueue();
+            }
+        };
+
         // Create the adpater
         mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item);
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
     }
 
     /**
@@ -147,8 +193,6 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
         mListView.setAdapter(mAdapter);
         // Release any references to the recycled Views
         mListView.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        mListView.setOnCreateContextMenuListener(this);
         // Play the selected song
         mListView.setOnItemClickListener(this);
         // Set the drop listener
@@ -260,100 +304,6 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-            final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
-        mSelectedPosition = info.position;
-        // Creat a new song
-        mSong = mAdapter.getItem(mSelectedPosition);
-        mSelectedId = mSong.mSongId;
-        mSongName = mSong.mSongName;
-        mAlbumName = mSong.mAlbumName;
-        mArtistName = mSong.mArtistName;
-
-        // Play the song next
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_NEXT, Menu.NONE,
-                getString(R.string.context_menu_play_next));
-
-        // Add the song to a playlist
-        final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
-
-        // Remove the song from the queue
-        menu.add(GROUP_ID, FragmentMenuItems.REMOVE_FROM_QUEUE, Menu.NONE,
-                getString(R.string.remove_from_queue));
-
-        // View more content by the song artist
-        menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
-                getString(R.string.context_menu_more_by_artist));
-
-        // Make the song a ringtone
-        menu.add(GROUP_ID, FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE,
-                getString(R.string.context_menu_use_as_ringtone));
-
-        // Delete the song
-        menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_NEXT:
-                    NowPlayingCursor queue = (NowPlayingCursor)QueueLoader
-                            .makeQueueCursor(getActivity());
-                    queue.removeItem(mSelectedPosition);
-                    queue.close();
-                    queue = null;
-                    MusicUtils.playNext(new long[] {
-                        mSelectedId
-                    });
-                    refreshQueue();
-                    return true;
-                case FragmentMenuItems.REMOVE_FROM_QUEUE:
-                    MusicUtils.removeTrack(mSelectedId);
-                    refreshQueue();
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(new long[] {
-                        mSelectedId
-                    }).show(getFragmentManager(), "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(getActivity(), new long[] {
-                        mSelectedId
-                    }, mPlaylistId);
-                    return true;
-                case FragmentMenuItems.MORE_BY_ARTIST:
-                    NavUtils.openArtistProfile(getActivity(), mArtistName);
-                    return true;
-                case FragmentMenuItems.USE_AS_RINGTONE:
-                    MusicUtils.setRingtone(getActivity(), mSelectedId);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    DeleteDialog.newInstance(mSong.mSongName, new long[] {
-                        mSelectedId
-                    }, null).show(getFragmentManager(), "DeleteDialog");
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
             final long id) {
         // When selecting a track from the queue, just jump there instead of
@@ -419,10 +369,10 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
      */
     @Override
     public void remove(final int which) {
-        mSong = mAdapter.getItem(which);
-        mAdapter.remove(mSong);
+        Song song = mAdapter.getItem(which);
+        mAdapter.remove(song);
         mAdapter.notifyDataSetChanged();
-        MusicUtils.removeTrack(mSong.mSongId);
+        MusicUtils.removeTrack(song.mSongId);
         // Build the cache
         mAdapter.buildCache();
     }
@@ -432,9 +382,9 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
      */
     @Override
     public void drop(final int from, final int to) {
-        mSong = mAdapter.getItem(from);
-        mAdapter.remove(mSong);
-        mAdapter.insert(mSong, to);
+        Song song = mAdapter.getItem(from);
+        mAdapter.remove(song);
+        mAdapter.insert(song, to);
         mAdapter.notifyDataSetChanged();
         MusicUtils.moveQueueItem(from, to);
         // Build the cache
@@ -442,35 +392,6 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
     }
 
     /**
-     * Scrolls the list to the currently playing song when the user touches the
-     * header in the {@link TitlePageIndicator}.
-     */
-    public void scrollToCurrentSong() {
-        final int currentSongPosition = getItemPositionBySong();
-
-        if (currentSongPosition != 0) {
-            mListView.setSelection(currentSongPosition);
-        }
-    }
-
-    /**
-     * @return The position of an item in the list based on the name of the
-     *         currently playing song.
-     */
-    private int getItemPositionBySong() {
-        final long trackId = MusicUtils.getCurrentAudioId();
-        if (mAdapter == null) {
-            return 0;
-        }
-        for (int i = 0; i < mAdapter.getCount(); i++) {
-            if (mAdapter.getItem(i).mSongId == trackId) {
-                return i;
-            }
-        }
-        return 0;
-    }
-
-    /**
      * Called to restart the loader callbacks
      */
     public void refreshQueue() {
index c200d4b..4caab6f 100644 (file)
 
 package com.cyngn.eleven.ui.fragments;
 
-import android.app.Activity;
-import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.content.Loader;
 import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 
+import com.cyngn.eleven.Config;
 import com.cyngn.eleven.MusicStateListener;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.loaders.TopTracksLoader;
 import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Song;
-import com.cyngn.eleven.provider.RecentStore;
-import com.cyngn.eleven.ui.activities.BaseActivity;
+import com.cyngn.eleven.sectionadapter.SectionCreator;
+import com.cyngn.eleven.sectionadapter.SectionListContainer;
 import com.cyngn.eleven.ui.fragments.profile.BasicSongFragment;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
-import java.util.List;
+import java.util.ArrayList;
 
 /**
  * This class is used to display all of the recently listened to songs by the
@@ -52,72 +47,19 @@ public class RecentFragment extends BasicSongFragment implements MusicStateListe
      */
     private static final int LOADER = 0;
 
-    /**
-     * True if the list should execute {@code #restartLoader()}.
-     */
-    private boolean mShouldRefresh = false;
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAttach(final Activity activity) {
-        super.onAttach(activity);
-        // Register the music status listener
-        ((BaseActivity)activity).setMusicStateListenerListener(this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-                                    final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-
-        // Remove the album from the list
-        menu.add(GROUP_ID, FragmentMenuItems.REMOVE_FROM_RECENT, Menu.NONE,
-                getString(R.string.context_menu_remove_from_recent));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onContextItemSelected(final MenuItem item) {
-        // Avoid leaking context menu selections
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.REMOVE_FROM_RECENT:
-                    mShouldRefresh = true;
-                    RecentStore.getInstance(getActivity()).removeItem(mSelectedId);
-                    MusicUtils.refresh();
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
-        return new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.RecentSongs);
+    protected void getAdditionaIdsForType(ArrayList<Integer> list) {
+        list.add(FragmentMenuItems.REMOVE_FROM_RECENT);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void restartLoader() {
-        // Update the list when the user deletes any items
-        if (mShouldRefresh) {
-            getLoaderManager().restartLoader(LOADER, null, this);
-        }
-        mShouldRefresh = false;
+    public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) {
+        TopTracksLoader loader = new TopTracksLoader(getActivity(),
+                TopTracksLoader.QueryType.RecentSongs);
+        return new SectionCreator<Song>(getActivity(), loader, null);
     }
 
     /**
@@ -125,6 +67,7 @@ public class RecentFragment extends BasicSongFragment implements MusicStateListe
      */
     @Override
     public void onMetaChanged() {
+        // refresh the list since a track playing means it should be recently played
         getLoaderManager().restartLoader(LOADER, null, this);
     }
 
@@ -140,11 +83,8 @@ public class RecentFragment extends BasicSongFragment implements MusicStateListe
 
     @Override
     public void playAll(int position) {
-        Cursor cursor = TopTracksLoader.makeRecentTracksCursor(getActivity());
-        final long[] list = MusicUtils.getSongListForCursor(cursor);
-        MusicUtils.playAll(getActivity(), list, position, false);
-        cursor.close();
-        cursor = null;
+        MusicUtils.playSmartPlaylist(getActivity(), position,
+                Config.SmartPlaylistType.RecentlyPlayed);
     }
 
     @Override
index fa60f10..660f561 100644 (file)
 
 package com.cyngn.eleven.ui.fragments;
 
-import android.app.Activity;
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.os.SystemClock;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.text.TextUtils;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.SubMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListView;
-import android.widget.TextView;
 
-import com.cyngn.eleven.MusicStateListener;
-import com.cyngn.eleven.R;
-import com.cyngn.eleven.adapters.SongAdapter;
 import com.cyngn.eleven.loaders.SongLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
-import com.cyngn.eleven.menu.DeleteDialog;
-import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Song;
-import com.cyngn.eleven.recycler.RecycleHolder;
-import com.cyngn.eleven.sectionadapter.SectionAdapter;
 import com.cyngn.eleven.sectionadapter.SectionCreator;
 import com.cyngn.eleven.sectionadapter.SectionListContainer;
-import com.cyngn.eleven.ui.activities.BaseActivity;
+import com.cyngn.eleven.ui.fragments.profile.BasicSongFragment;
 import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.NavUtils;
 import com.cyngn.eleven.utils.SectionCreatorUtils;
-import com.cyngn.eleven.widgets.NoResultsContainer;
 import com.viewpagerindicator.TitlePageIndicator;
 
-import java.util.List;
-
 /**
  * This class is used to display all of the songs on a user's device.
- * 
+ *
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public class SongFragment extends Fragment implements LoaderCallbacks<SectionListContainer<Song>>,
-        OnScrollListener, OnItemClickListener, MusicStateListener {
+public class SongFragment extends BasicSongFragment {
 
     /**
      * Used to keep context menu items from bleeding into other fragments
@@ -75,232 +43,9 @@ public class SongFragment extends Fragment implements LoaderCallbacks<SectionLis
     private static final int LOADER = 0;
 
     /**
-     * Fragment UI
-     */
-    private ViewGroup mRootView;
-
-    /**
-     * The adapter for the list
-     */
-    private SectionAdapter<Song, SongAdapter> mAdapter;
-
-    /**
-     * The list view
-     */
-    private ListView mListView;
-
-    /**
-     * Represents a song
-     */
-    private Song mSong;
-
-    /**
-     * Position of a context menu item
-     */
-    private int mSelectedPosition;
-
-    /**
-     * Id of a context menu item
-     */
-    private long mSelectedId;
-
-    /**
-     * Song, album, and artist name used in the context menu
-     */
-    private String mSongName, mAlbumName, mArtistName;
-
-    /**
-     * True if the list should execute {@code #restartLoader()}.
-     */
-    private boolean mShouldRefresh = false;
-
-    /**
-     * Empty constructor as per the {@link Fragment} documentation
-     */
-    public SongFragment() {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAttach(final Activity activity) {
-        super.onAttach(activity);
-        // Register the music status listener
-        ((BaseActivity)activity).setMusicStateListenerListener(this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreate(final Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // Create the adpater
-        mAdapter = new SectionAdapter<Song, SongAdapter>(getActivity(), new SongAdapter(getActivity(), R.layout.list_item_normal));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
-            final Bundle savedInstanceState) {
-        // The View for the fragment's UI
-        mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null);
-        // Initialize the list
-        mListView = (ListView)mRootView.findViewById(R.id.list_base);
-        // Set the data behind the list
-        mListView.setAdapter(mAdapter);
-        // Release any references to the recycled Views
-        mListView.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        mListView.setOnCreateContextMenuListener(this);
-        // Play the selected song
-        mListView.setOnItemClickListener(this);
-        // To help make scrolling smooth
-        mListView.setOnScrollListener(this);
-        return mRootView;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActivityCreated(final Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        // Enable the options menu
-        setHasOptionsMenu(true);
-        // Start the loader
-        getLoaderManager().initLoader(LOADER, null, this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-            final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
-        mSelectedPosition = info.position;
-        // Creat a new song
-        mSong = mAdapter.getTItem(mSelectedPosition);
-        mSelectedId = mSong.mSongId;
-        mSongName = mSong.mSongName;
-        mAlbumName = mSong.mAlbumName;
-        mArtistName = mSong.mArtistName;
-
-        // Play the song
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                getString(R.string.context_menu_play_selection));
-
-        // Play next
-        menu.add(GROUP_ID, FragmentMenuItems.PLAY_NEXT, Menu.NONE,
-                getString(R.string.context_menu_play_next));
-
-        // Add the song to the queue
-        menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
-                getString(R.string.add_to_queue));
-
-        // Add the song to a playlist
-        final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
-
-        // View more content by the song artist
-        menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
-                getString(R.string.context_menu_more_by_artist));
-
-        // Make the song a ringtone
-        menu.add(GROUP_ID, FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE,
-                getString(R.string.context_menu_use_as_ringtone));
-
-        // Delete the song
-        menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        if (item.getGroupId() == GROUP_ID) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    MusicUtils.playAll(getActivity(), new long[] {
-                        mSelectedId
-                    }, 0, false);
-                    return true;
-                case FragmentMenuItems.PLAY_NEXT:
-                    MusicUtils.playNext(new long[] {
-                        mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    MusicUtils.addToQueue(getActivity(), new long[] {
-                        mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(new long[] {
-                        mSelectedId
-                    }).show(getFragmentManager(), "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(getActivity(), new long[] {
-                        mSelectedId
-                    }, mPlaylistId);
-                    return true;
-                case FragmentMenuItems.MORE_BY_ARTIST:
-                    NavUtils.openArtistProfile(getActivity(), mArtistName);
-                    return true;
-                case FragmentMenuItems.USE_AS_RINGTONE:
-                    MusicUtils.setRingtone(getActivity(), mSelectedId);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    mShouldRefresh = true;
-                    DeleteDialog.newInstance(mSong.mSongName, new long[] {
-                        mSelectedId
-                    }, null).show(getFragmentManager(), "DeleteDialog");
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onScrollStateChanged(final AbsListView view, final int scrollState) {
-        // Pause disk cache access to ensure smoother scrolling
-        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
-                || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
-            mAdapter.getUnderlyingAdapter().setPauseDiskCache(true);
-        } else {
-            mAdapter.getUnderlyingAdapter().setPauseDiskCache(false);
-            mAdapter.notifyDataSetChanged();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onScroll(final AbsListView view, final int firstVisibleItem,
-                         final int visibleItemCount, final int totalItemCount) {
-        // Nothing to do
-    }
-
-    /**
      * {@inheritDoc}
      */
-    @Override
-    public void onItemClick(final AdapterView<?> parent, final View view, final int position,
-            final long id) {
+    public void playAll(int position) {
         int internalPosition = mAdapter.getInternalPosition(position);
         Cursor cursor = SongLoader.makeSongCursor(getActivity(), null);
         final long[] list = MusicUtils.getSongListForCursor(cursor);
@@ -327,30 +72,15 @@ public class SongFragment extends Fragment implements LoaderCallbacks<SectionLis
         return new SectionCreator<Song>(context, songLoader, songComparison);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onLoadFinished(final Loader<SectionListContainer<Song>> loader, final SectionListContainer<Song> data) {
-        // Check for any errors
-        if (data.mListResults.isEmpty()) {
-            // Set the empty text
-            final NoResultsContainer empty = (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
-            mListView.setEmptyView(empty);
-            return;
-        }
 
-        // Set the data
-        mAdapter.setData(data);
+    @Override
+    public int getGroupId() {
+        return GROUP_ID;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public void onLoaderReset(final Loader<SectionListContainer<Song>> loader) {
-        // Clear the data in the adapter
-        mAdapter.unload();
+    public int getLoaderId() {
+        return LOADER;
     }
 
     /**
@@ -367,7 +97,7 @@ public class SongFragment extends Fragment implements LoaderCallbacks<SectionLis
 
     /**
      * @return The position of an item in the list based on the name of the
-     *         currently playing song.
+     * currently playing song.
      */
     private int getItemPositionBySong() {
         final long trackId = MusicUtils.getCurrentAudioId();
@@ -384,33 +114,4 @@ public class SongFragment extends Fragment implements LoaderCallbacks<SectionLis
 
         return position;
     }
-
-    /**
-     * Restarts the loader.
-     */
-    public void refresh() {
-        // Wait a moment for the preference to change.
-        SystemClock.sleep(10);
-        getLoaderManager().restartLoader(LOADER, null, this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void restartLoader() {
-        // Update the list when the user deletes any items
-        if (mShouldRefresh) {
-            getLoaderManager().restartLoader(LOADER, null, this);
-        }
-        mShouldRefresh = false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onMetaChanged() {
-        // Nothing to do
-    }
 }
index c32d99b..8f13bf6 100644 (file)
 
 package com.cyngn.eleven.ui.fragments.profile;
 
+import android.app.Activity;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
-import android.widget.TextView;
 
+import com.cyngn.eleven.MusicStateListener;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.SongAdapter;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
 import com.cyngn.eleven.menu.DeleteDialog;
-import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.Song;
 import com.cyngn.eleven.recycler.RecycleHolder;
-import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.NavUtils;
+import com.cyngn.eleven.sectionadapter.SectionAdapter;
+import com.cyngn.eleven.sectionadapter.SectionListContainer;
+import com.cyngn.eleven.ui.activities.BaseActivity;
+import com.cyngn.eleven.utils.PopupMenuHelper;
+import com.cyngn.eleven.widgets.IPopupMenuCallback;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
-import java.util.List;
+import java.util.ArrayList;
 
 /**
  * This class is used to display all of the songs
  *
  * @author Andrew Neal (andrewdneal@gmail.com)
  */
-public abstract class BasicSongFragment extends Fragment implements LoaderCallbacks<List<Song>>,
-        OnItemClickListener {
+public abstract class BasicSongFragment extends Fragment implements
+        LoaderCallbacks<SectionListContainer<Song>>, OnItemClickListener, MusicStateListener {
 
     /**
      * Fragment UI
@@ -58,7 +56,7 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
     /**
      * The adapter for the list
      */
-    protected SongAdapter mAdapter;
+    protected SectionAdapter<Song, SongAdapter> mAdapter;
 
     /**
      * The list view
@@ -66,29 +64,29 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
     protected ListView mListView;
 
     /**
-     * Represents a song
+     * True if the list should execute {@code #restartLoader()}.
      */
-    protected Song mSong;
+    protected boolean mShouldRefresh = false;
 
     /**
-     * Position of a context menu item
+     * Pop up menu helper
      */
-    protected int mSelectedPosition;
+    private PopupMenuHelper mPopupMenuHelper;
 
     /**
-     * Id of a context menu item
-     */
-    protected long mSelectedId;
-
-    /**
-     * Song, album, and artist name used in the context menu
+     * Empty constructor as per the {@link Fragment} documentation
      */
-    protected String mSongName, mAlbumName, mArtistName;
+    public BasicSongFragment() {
+    }
 
     /**
-     * Empty constructor as per the {@link Fragment} documentation
+     * {@inheritDoc}
      */
-    public BasicSongFragment() {
+    @Override
+    public void onAttach(final Activity activity) {
+        super.onAttach(activity);
+        // Register the music status listener
+        ((BaseActivity)activity).setMusicStateListenerListener(this);
     }
 
     /**
@@ -97,8 +95,71 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
     @Override
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        // Create the adpater
+        mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
+            /**
+             * Represents a song
+             */
+            protected Song mSong;
+
+            @Override
+            protected PopupMenuHelper.PopupMenuType onPreparePopupMenu(int position) {
+                // Create a new song
+                mSong = mAdapter.getTItem(position);
+
+                return PopupMenuType.Song;
+            }
+
+            @Override
+            protected void getAdditionalIds(PopupMenuType type, ArrayList<Integer> list) {
+                super.getAdditionalIds(type, list);
+                BasicSongFragment.this.getAdditionaIdsForType(list);
+            }
+
+            @Override
+            protected long[] getIdList() {
+                return new long[] { mSong.mSongId };
+            }
+
+            @Override
+            protected int getGroupId() {
+                return BasicSongFragment.this.getGroupId();
+            }
+
+            @Override
+            protected void onDeleteClicked() {
+                mShouldRefresh = true;
+                DeleteDialog.newInstance(mSong.mSongName, getIdList(), null).show(
+                        getFragmentManager(), "DeleteDialog");
+            }
+
+            @Override
+            protected void setShouldRefresh() {
+                mShouldRefresh = true;
+            }
+
+            @Override
+            protected long getId() {
+                return mSong.mSongId;
+            }
+
+            @Override
+            protected String getArtistName() {
+                return mSong.mArtistName;
+            }
+        };
+
+        // Create the adapter
         mAdapter = createAdapter();
+        mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
+            @Override
+            public void onPopupMenuClicked(View v, int position) {
+                mPopupMenuHelper.showPopupMenu(v, position);
+            }
+        });
+    }
+
+    protected void getAdditionaIdsForType(ArrayList<Integer> list) {
+        // do nothing - let subclasses override
     }
 
     /**
@@ -115,8 +176,6 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
         mListView.setAdapter(mAdapter);
         // Release any references to the recycled Views
         mListView.setRecyclerListener(new RecycleHolder());
-        // Listen for ContextMenus to be created
-        mListView.setOnCreateContextMenuListener(this);
         // Play the selected song
         mListView.setOnItemClickListener(this);
         // To help make scrolling smooth
@@ -126,9 +185,9 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
                 // Pause disk cache access to ensure smoother scrolling
                 if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
                         || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
-                    mAdapter.setPauseDiskCache(true);
+                    mAdapter.getUnderlyingAdapter().setPauseDiskCache(true);
                 } else {
-                    mAdapter.setPauseDiskCache(false);
+                    mAdapter.getUnderlyingAdapter().setPauseDiskCache(false);
                     mAdapter.notifyDataSetChanged();
                 }
             }
@@ -156,8 +215,6 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
     @Override
     public void onActivityCreated(final Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-        // Enable the options menu
-        setHasOptionsMenu(true);
         // Start the loader
         getLoaderManager().initLoader(getLoaderId(), null, this);
     }
@@ -166,102 +223,6 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu(final ContextMenu menu, final View v,
-                                    final ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-        // Get the position of the selected item
-        final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
-        mSelectedPosition = info.position;
-        // Creat a new song
-        mSong = mAdapter.getItem(mSelectedPosition);
-        mSelectedId = mSong.mSongId;
-        mSongName = mSong.mSongName;
-        mAlbumName = mSong.mAlbumName;
-        mArtistName = mSong.mArtistName;
-
-        // Play the song
-        menu.add(getGroupId(), FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
-                getString(R.string.context_menu_play_selection));
-
-        // Play next
-        menu.add(getGroupId(), FragmentMenuItems.PLAY_NEXT, Menu.NONE,
-                getString(R.string.context_menu_play_next));
-
-        // Add the song to the queue
-        menu.add(getGroupId(), FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
-                getString(R.string.add_to_queue));
-
-        // Add the song to a playlist
-        final SubMenu subMenu = menu.addSubMenu(getGroupId(), FragmentMenuItems.ADD_TO_PLAYLIST,
-                Menu.NONE, R.string.add_to_playlist);
-        MusicUtils.makePlaylistMenu(getActivity(), getGroupId(), subMenu);
-
-        // View more content by the song artist
-        menu.add(getGroupId(), FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
-                getString(R.string.context_menu_more_by_artist));
-
-        // Make the song a ringtone
-        menu.add(getGroupId(), FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE,
-                getString(R.string.context_menu_use_as_ringtone));
-
-        // Delete the song
-        menu.add(getGroupId(), FragmentMenuItems.DELETE, Menu.NONE,
-                getString(R.string.context_menu_delete));
-    }
-
-    @Override
-    public boolean onContextItemSelected(final android.view.MenuItem item) {
-        if (item.getGroupId() == getGroupId()) {
-            switch (item.getItemId()) {
-                case FragmentMenuItems.PLAY_SELECTION:
-                    MusicUtils.playAll(getActivity(), new long[]{
-                            mSelectedId
-                    }, 0, false);
-                    return true;
-                case FragmentMenuItems.PLAY_NEXT:
-                    MusicUtils.playNext(new long[]{
-                            mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.ADD_TO_QUEUE:
-                    MusicUtils.addToQueue(getActivity(), new long[]{
-                            mSelectedId
-                    });
-                    return true;
-                case FragmentMenuItems.NEW_PLAYLIST:
-                    CreateNewPlaylist.getInstance(new long[]{
-                            mSelectedId
-                    }).show(getFragmentManager(), "CreatePlaylist");
-                    return true;
-                case FragmentMenuItems.PLAYLIST_SELECTED:
-                    final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0);
-                    MusicUtils.addToPlaylist(getActivity(), new long[]{
-                            mSelectedId
-                    }, mPlaylistId);
-                    return true;
-                case FragmentMenuItems.MORE_BY_ARTIST:
-                    NavUtils.openArtistProfile(getActivity(), mArtistName);
-                    return true;
-                case FragmentMenuItems.USE_AS_RINGTONE:
-                    MusicUtils.setRingtone(getActivity(), mSelectedId);
-                    return true;
-                case FragmentMenuItems.DELETE:
-                    // TODO: Smarter refresh
-                    DeleteDialog.newInstance(mSong.mSongName, new long[]{
-                            mSelectedId
-                    }, null).show(getFragmentManager(), "DeleteDialog");
-                    return true;
-                default:
-                    break;
-            }
-        }
-        return super.onContextItemSelected(item);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
                             final long id) {
         playAll(position);
@@ -271,11 +232,13 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
      * {@inheritDoc}
      */
     @Override
-    public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) {
+    public void onLoadFinished(final Loader<SectionListContainer<Song>> loader,
+                               final SectionListContainer<Song> data) {
         // Check for any errors
-        if (data.isEmpty()) {
+        if (data.mListResults.isEmpty()) {
             // Set the empty text
-            final NoResultsContainer empty = (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
+            final NoResultsContainer empty =
+                    (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
             // Setup the container strings
             setupNoResultsContainer(empty);
             // set the empty view into the list view
@@ -284,40 +247,56 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
         }
 
         // Start fresh
-        mAdapter.unload();
-        // Add the data to the adpater
-        for (final Song song : data) {
-            mAdapter.add(song);
-        }
+        mAdapter.setData(data);
+    }
 
-        // Build the cache
-        mAdapter.buildCache();
+    /**
+     * Restarts the loader.
+     */
+    public void refresh() {
+        // Wait a moment for the preference to change.
+        SystemClock.sleep(10);
+        getLoaderManager().restartLoader(getLoaderId(), null, this);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void restartLoader() {
-        getLoaderManager().restartLoader(getLoaderId(), null, this);
+        // Update the list when the user deletes any items
+        if (mShouldRefresh) {
+            getLoaderManager().restartLoader(getLoaderId(), null, this);
+        }
+        mShouldRefresh = false;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void onLoaderReset(final Loader<List<Song>> loader) {
+    public void onLoaderReset(final Loader<SectionListContainer<Song>> loader) {
         // Clear the data in the adapter
         mAdapter.unload();
     }
 
     /**
-     * If the subclasses want to use a customized SongAdapter
+     * If the subclasses want to use a customized SongAdapter they can override this method
      * @return the Song adapter
      */
-    protected SongAdapter createAdapter() {
-        return new SongAdapter(
-                getActivity(),
-                R.layout.list_item_normal
+    protected SectionAdapter<Song, SongAdapter> createAdapter() {
+        return new SectionAdapter(getActivity(),
+                new SongAdapter(
+                    getActivity(),
+                    R.layout.list_item_normal
+                )
         );
     }
 
+    @Override
+    public void onMetaChanged() {
+        // do nothing
+    }
 
     /**
      * Used to keep context menu items from bleeding into other fragments
index bbef6cd..457a042 100644 (file)
 
 package com.cyngn.eleven.ui.fragments.profile;
 
-import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.content.Loader;
 
+import com.cyngn.eleven.Config;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.loaders.LastAddedLoader;
 import com.cyngn.eleven.model.Song;
+import com.cyngn.eleven.sectionadapter.SectionCreator;
+import com.cyngn.eleven.sectionadapter.SectionListContainer;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
-import java.util.List;
-
 /**
  * This class is used to display all of the songs the user put on their device
  * within the last four weeks.
@@ -45,8 +45,9 @@ public class LastAddedFragment extends BasicSongFragment {
      * {@inheritDoc}
      */
     @Override
-    public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
-        return new LastAddedLoader(getActivity());
+    public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) {
+        LastAddedLoader loader = new LastAddedLoader(getActivity());
+        return new SectionCreator<Song>(getActivity(), loader, null);
     }
 
     @Override
@@ -61,11 +62,8 @@ public class LastAddedFragment extends BasicSongFragment {
 
     @Override
     public void playAll(int position) {
-        Cursor cursor = LastAddedLoader.makeLastAddedCursor(getActivity());
-        final long[] list = MusicUtils.getSongListForCursor(cursor);
-        MusicUtils.playAll(getActivity(), list, position, false);
-        cursor.close();
-        cursor = null;
+        MusicUtils.playSmartPlaylist(getActivity(), position,
+                Config.SmartPlaylistType.LastAdded);
     }
 
     @Override
index 00a53bc..e28824e 100644 (file)
 package com.cyngn.eleven.ui.fragments.profile;
 
 import android.app.Activity;
-import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.content.Loader;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.cyngn.eleven.Config;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.adapters.SongAdapter;
 import com.cyngn.eleven.loaders.TopTracksLoader;
 import com.cyngn.eleven.model.Song;
+import com.cyngn.eleven.sectionadapter.SectionAdapter;
+import com.cyngn.eleven.sectionadapter.SectionCreator;
+import com.cyngn.eleven.sectionadapter.SectionListContainer;
 import com.cyngn.eleven.utils.MusicUtils;
 import com.cyngn.eleven.widgets.NoResultsContainer;
 
-import java.util.List;
-
 /**
  * This class is used to display all of the songs the user put on their device
  * within the last four weeks.
@@ -50,15 +51,19 @@ public class TopTracksFragment extends BasicSongFragment {
      * {@inheritDoc}
      */
     @Override
-    public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
-        return new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.TopTracks);
+    public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) {
+        TopTracksLoader loader = new TopTracksLoader(getActivity(),
+                TopTracksLoader.QueryType.TopTracks);
+        return new SectionCreator<Song>(getActivity(), loader, null);
     }
 
     @Override
-    protected SongAdapter createAdapter() {
-        return new TopTracksAdapter(
-                getActivity(),
-                R.layout.list_item_top_tracks
+    protected SectionAdapter<Song, SongAdapter> createAdapter() {
+        return new SectionAdapter(getActivity(),
+                new TopTracksAdapter(
+                        getActivity(),
+                        R.layout.list_item_top_tracks
+                )
         );
     }
 
@@ -74,11 +79,8 @@ public class TopTracksFragment extends BasicSongFragment {
 
     @Override
     public void playAll(int position) {
-        Cursor cursor = TopTracksLoader.makeTopTracksCursor(getActivity());
-        final long[] list = MusicUtils.getSongListForCursor(cursor);
-        MusicUtils.playAll(getActivity(), list, position, false);
-        cursor.close();
-        cursor = null;
+        MusicUtils.playSmartPlaylist(getActivity(), position,
+                Config.SmartPlaylistType.TopTracks);
     }
 
     public class TopTracksAdapter extends SongAdapter {
index 7a86890..66ff57d 100644 (file)
@@ -36,12 +36,16 @@ import android.provider.Settings;
 import android.util.Log;
 import android.view.Menu;
 
+import com.cyngn.eleven.Config;
+import com.cyngn.eleven.Config.SmartPlaylistType;
 import com.cyngn.eleven.IElevenService;
 import com.cyngn.eleven.MusicPlaybackService;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.loaders.LastAddedLoader;
 import com.cyngn.eleven.loaders.PlaylistLoader;
+import com.cyngn.eleven.loaders.PlaylistSongLoader;
 import com.cyngn.eleven.loaders.SongLoader;
+import com.cyngn.eleven.loaders.TopTracksLoader;
 import com.cyngn.eleven.menu.FragmentMenuItems;
 import com.cyngn.eleven.model.AlbumArtistDetails;
 import com.cyngn.eleven.provider.RecentStore;
@@ -723,7 +727,7 @@ public final class MusicUtils {
      */
     public static void playAll(final Context context, final long[] list, int position,
             final boolean forceShuffle) {
-        if (list.length == 0 || mService == null) {
+        if (list == null || list.length == 0 || mService == null) {
             return;
         }
         try {
@@ -1188,13 +1192,7 @@ public final class MusicUtils {
      * @return The track list for a playlist
      */
     public static final long[] getSongListForPlaylist(final Context context, final long playlistId) {
-        final String[] projection = new String[] {
-            MediaStore.Audio.Playlists.Members.AUDIO_ID
-        };
-        Cursor cursor = context.getContentResolver().query(
-                MediaStore.Audio.Playlists.Members.getContentUri("external",
-                        Long.valueOf(playlistId)), projection, null, null,
-                MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
+        Cursor cursor = PlaylistSongLoader.makePlaylistSongCursor(context, playlistId);
 
         if (cursor != null) {
             final long[] list = getSongListForCursor(cursor);
@@ -1220,29 +1218,43 @@ public final class MusicUtils {
 
     /**
      * @param context The {@link Context} to use
+     * @param type The Smart Playlist Type
      * @return The song list for the last added playlist
      */
-    public static final long[] getSongListForLastAdded(final Context context) {
-        final Cursor cursor = LastAddedLoader.makeLastAddedCursor(context);
-        if (cursor != null) {
-            final int count = cursor.getCount();
-            final long[] list = new long[count];
-            for (int i = 0; i < count; i++) {
-                cursor.moveToNext();
-                list[i] = cursor.getLong(0);
+    public static final long[] getSongListForSmartPlaylist(final Context context,
+                                                           final SmartPlaylistType type) {
+        Cursor cursor = null;
+        try {
+            switch (type) {
+                case LastAdded:
+                    cursor = LastAddedLoader.makeLastAddedCursor(context);
+                    break;
+                case RecentlyPlayed:
+                    cursor = TopTracksLoader.makeRecentTracksCursor(context);
+                    break;
+                case TopTracks:
+                    cursor = TopTracksLoader.makeTopTracksCursor(context);
+                    break;
+            }
+            return MusicUtils.getSongListForCursor(cursor);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+                cursor = null;
             }
-            return list;
         }
-        return sEmptyList;
     }
 
     /**
-     * Plays the last added songs from the past two weeks.
-     *
+     * Plays the smart playlist
      * @param context The {@link Context} to use
+     * @param position the position to start playing from
+     * @param type The Smart Playlist Type
      */
-    public static void playLastAdded(final Context context) {
-        playAll(context, getSongListForLastAdded(context), 0, false);
+    public static void playSmartPlaylist(final Context context, final int position,
+                                         final SmartPlaylistType type) {
+        final long[] list = getSongListForSmartPlaylist(context, type);
+        MusicUtils.playAll(context, list, position, false);
     }
 
     /**
diff --git a/src/com/cyngn/eleven/utils/PopupMenuHelper.java b/src/com/cyngn/eleven/utils/PopupMenuHelper.java
new file mode 100644 (file)
index 0000000..856d147
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+package com.cyngn.eleven.utils;
+
+import android.app.Activity;
+import android.support.v4.app.FragmentManager;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.PopupMenu;
+
+import com.android.internal.view.menu.ContextMenuBuilder;
+import com.android.internal.view.menu.MenuBuilder;
+import com.cyngn.eleven.R;
+import com.cyngn.eleven.menu.CreateNewPlaylist;
+import com.cyngn.eleven.menu.FragmentMenuItems;
+import com.cyngn.eleven.menu.RenamePlaylist;
+import com.cyngn.eleven.provider.RecentStore;
+
+import java.util.ArrayList;
+
+/**
+ * Simple helper class that does most of the popup menu inflating and handling
+ * It has a few hooks around so that if the class wants customization they can add it on
+ * without changing this class too much
+ */
+public abstract class PopupMenuHelper implements PopupMenu.OnMenuItemClickListener {
+    // the different types of pop up menus
+    public static enum PopupMenuType {
+        Artist,
+        Album,
+        Song,
+        Playlist,
+        SmartPlaylist,
+        SearchResult,
+        Queue,
+    }
+
+    protected Activity mActivity;
+    protected PopupMenuType mType;
+    protected FragmentManager mFragmentManager;
+
+    public PopupMenuHelper(final Activity activity, final FragmentManager fragmentManager) {
+        mActivity = activity;
+        mFragmentManager = fragmentManager;
+    }
+
+    /**
+     * Call this to inflate and show the pop up menu
+     * @param view the view to anchor the popup menu against
+     * @param position the item that was clicked in the popup menu (or -1 if not relevant)
+     */
+    public void showPopupMenu(final View view, final int position) {
+        // create the popup menu
+        PopupMenu popupMenu = new PopupMenu(mActivity, view);
+        final Menu menu = popupMenu.getMenu();
+
+        // hook up the click listener
+        popupMenu.setOnMenuItemClickListener(this);
+
+        // figure what type of pop up menu it is
+        mType = onPreparePopupMenu(position);
+        if (mType != null) {
+            // inflate the menu
+            createPopupMenu(menu);
+            // show it
+            popupMenu.show();
+        }
+    }
+
+    /**
+     * This function allows classes to setup any variables before showing the popup menu
+     * @param position the position passed in from showPopupMenu
+     * @return the pop up menu type, or null if we shouldn't show a pop up menu
+     */
+    protected abstract PopupMenuType onPreparePopupMenu(final int position);
+
+    /**
+     * @return the list of ids needed for some menu actions like playing a list of songs
+     */
+    protected abstract long[] getIdList();
+
+    /**
+     * @return the group id to be used for pop up menu inflating
+     */
+    protected abstract int getGroupId();
+
+    /**
+     * called when the delete item is pressed.  Since delete is different from class to class
+     * it is not implemented in this class and relies on the contaning classes to implement
+     */
+    protected abstract void onDeleteClicked();
+
+    /**
+     * Tells the containing class that it should probably mark their loaders for refresh
+     */
+    protected abstract void setShouldRefresh();
+
+    /**
+     * @return the artist name (when needed) for "more by this artist"
+     */
+    protected String getArtistName() {
+        throw new UnsupportedOperationException("Method Not Implemented!");
+    }
+
+    /**
+     * @return the single id that is needed for the "set as my ringtone"
+     */
+    protected long getId() {
+        throw new UnsupportedOperationException("Method Not Implemented!");
+    }
+
+    /**
+     * Called when the user clicks "remove from playlist"
+     */
+    protected void removeFromPlaylist() {
+        throw new UnsupportedOperationException("Method Not Implemented!");
+    }
+
+    /**
+     * Called when the user clicks "remove from queue"
+     */
+    protected void removeFromQueue() {
+        throw new UnsupportedOperationException("Method Not Implemented!");
+    }
+
+    /**
+     * Called when the user clicks "play next".  Has a default implementation
+     */
+    protected void playNext() {
+        MusicUtils.playNext(getIdList());
+    }
+
+    /**
+     * Creates the pop up menu by inflating the menu items
+     * @param menu Menu to use for adding to
+     */
+    protected void createPopupMenu(final Menu menu) {
+        // get the default items and add them
+        int[] menuItems = getIdsForType(mType);
+        if (menuItems != null) {
+            for (int id : menuItems) {
+                addToMenu(menu, id, getStringResourceForId(id));
+            }
+        }
+
+        // if the containing class wants to add additional items, do it here
+        ArrayList<Integer> additionalItems = new ArrayList<Integer>();
+        getAdditionalIds(mType, additionalItems);
+        for (int id : additionalItems) {
+            addToMenu(menu, id, getAdditionalStringResourceForId(id));
+        }
+    }
+
+    /**
+     * Gets the default menu items for the specified type
+     * @param type of pop up menu to create
+     * @return list of menu items to inflate
+     */
+    private static int[] getIdsForType(PopupMenuType type) {
+        switch (type) {
+            case Artist:
+                return new int[] {
+                    FragmentMenuItems.PLAY_SELECTION,
+                    FragmentMenuItems.ADD_TO_QUEUE,
+                    FragmentMenuItems.ADD_TO_PLAYLIST,
+                    FragmentMenuItems.DELETE,
+                };
+            case Album:
+                return new int[] {
+                        FragmentMenuItems.PLAY_SELECTION,
+                        FragmentMenuItems.ADD_TO_QUEUE,
+                        FragmentMenuItems.ADD_TO_PLAYLIST,
+                        FragmentMenuItems.MORE_BY_ARTIST,
+                        FragmentMenuItems.DELETE,
+                };
+            case Song:
+                return new int[] {
+                        FragmentMenuItems.PLAY_SELECTION,
+                        FragmentMenuItems.PLAY_NEXT,
+                        FragmentMenuItems.ADD_TO_QUEUE,
+                        FragmentMenuItems.ADD_TO_PLAYLIST,
+                        FragmentMenuItems.MORE_BY_ARTIST,
+                        FragmentMenuItems.USE_AS_RINGTONE,
+                        FragmentMenuItems.DELETE,
+                };
+            case Playlist:
+                return new int[] {
+                        FragmentMenuItems.PLAY_SELECTION,
+                        FragmentMenuItems.ADD_TO_QUEUE,
+                        FragmentMenuItems.RENAME_PLAYLIST,
+                        FragmentMenuItems.DELETE,
+                };
+            case SmartPlaylist:
+                return new int[] {
+                        FragmentMenuItems.PLAY_SELECTION,
+                        FragmentMenuItems.ADD_TO_QUEUE,
+                };
+            case SearchResult:
+                return new int[] {
+                        FragmentMenuItems.PLAY_SELECTION,
+                        FragmentMenuItems.ADD_TO_QUEUE,
+                        FragmentMenuItems.ADD_TO_PLAYLIST,
+                };
+            case Queue:
+                return new int[] {
+                        FragmentMenuItems.PLAY_NEXT,
+                        FragmentMenuItems.ADD_TO_PLAYLIST,
+                        FragmentMenuItems.REMOVE_FROM_QUEUE,
+                        FragmentMenuItems.MORE_BY_ARTIST,
+                        FragmentMenuItems.USE_AS_RINGTONE,
+                        FragmentMenuItems.DELETE,
+                };
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the additional menu items to inflate
+     * @param type the pop up menu type
+     * @param list the list to add menu items to
+     */
+    protected void getAdditionalIds(PopupMenuType type, ArrayList<Integer> list) {
+        // do nothing
+    }
+
+    /**
+     * Gets the string resource for an id - if the string resource doesn't exist in this class
+     * the containing class can override this method
+     * @param id the menu id
+     * @return string resource id
+     */
+    protected int getAdditionalStringResourceForId(final int id) {
+        return getStringResourceForId(id);
+    }
+
+    /**
+     * Gets the string resource for an id
+     * @param id the menu id
+     * @return string resource id
+     */
+    public static int getStringResourceForId(final int id) {
+        switch (id) {
+            case FragmentMenuItems.REMOVE_FROM_RECENT:
+                return R.string.context_menu_remove_from_recent;
+            case FragmentMenuItems.PLAY_SELECTION:
+                return R.string.context_menu_play_selection;
+            case FragmentMenuItems.ADD_TO_QUEUE:
+                return R.string.add_to_queue;
+            case FragmentMenuItems.ADD_TO_PLAYLIST:
+                return R.string.add_to_playlist;
+            case FragmentMenuItems.NEW_PLAYLIST:
+                return R.string.new_playlist;
+            case FragmentMenuItems.RENAME_PLAYLIST:
+                return R.string.context_menu_rename_playlist;
+            case FragmentMenuItems.PLAYLIST_SELECTED:
+                return 0; // no string here expected
+            case FragmentMenuItems.MORE_BY_ARTIST:
+                return R.string.context_menu_more_by_artist;
+            case FragmentMenuItems.DELETE:
+                return R.string.context_menu_delete;
+            case FragmentMenuItems.FETCH_ARTIST_IMAGE:
+                return R.string.context_menu_fetch_artist_image;
+            case FragmentMenuItems.FETCH_ALBUM_ART:
+                return R.string.context_menu_fetch_album_art;
+            case FragmentMenuItems.USE_AS_RINGTONE:
+                return R.string.context_menu_use_as_ringtone;
+            case FragmentMenuItems.REMOVE_FROM_PLAYLIST:
+                return R.string.context_menu_remove_from_playlist;
+            case FragmentMenuItems.REMOVE_FROM_QUEUE:
+                return R.string.remove_from_queue;
+            case FragmentMenuItems.PLAY_NEXT:
+                return R.string.context_menu_play_next;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Simple helper function for adding an item to the menu
+     */
+    public void addToMenu(final Menu menu, final int id, final int resourceId) {
+        menu.add(getGroupId(), id, Menu.NONE, mActivity.getString(resourceId));
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        if (item.getGroupId() == getGroupId()) {
+            switch (item.getItemId()) {
+                case FragmentMenuItems.REMOVE_FROM_RECENT:
+                    setShouldRefresh();
+                    RecentStore.getInstance(mActivity).removeItem(getId());
+                    MusicUtils.refresh();
+                    return true;
+                case FragmentMenuItems.PLAY_SELECTION:
+                    MusicUtils.playAll(mActivity, getIdList(), 0, false);
+                    return true;
+                case FragmentMenuItems.ADD_TO_QUEUE:
+                    MusicUtils.addToQueue(mActivity, getIdList());
+                    return true;
+                case FragmentMenuItems.ADD_TO_PLAYLIST:
+                    ContextMenuBuilder builder = new ContextMenuBuilder(mActivity);
+                    MusicUtils.makePlaylistMenu(mActivity, getGroupId(), builder);
+                    builder.setHeaderTitle(R.string.add_to_playlist);
+                    builder.setCallback(new MenuBuilder.Callback() {
+                        @Override
+                        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+                            return onMenuItemClick(item);
+                        }
+
+                        @Override
+                        public void onMenuModeChange(MenuBuilder menu) {
+                            // do nothing
+                        }
+                    });
+                    builder.show(null, null);
+                    return true;
+                case FragmentMenuItems.NEW_PLAYLIST:
+                    CreateNewPlaylist.getInstance(getIdList()).show(
+                            mFragmentManager, "CreatePlaylist");
+                    return true;
+                case FragmentMenuItems.RENAME_PLAYLIST:
+                    setShouldRefresh();
+                    RenamePlaylist.getInstance(getId()).show(
+                            mFragmentManager, "RenameDialog");
+                    return true;
+                case FragmentMenuItems.PLAYLIST_SELECTED:
+                    final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0);
+                    MusicUtils.addToPlaylist(mActivity, getIdList(), mPlaylistId);
+                    return true;
+                case FragmentMenuItems.MORE_BY_ARTIST:
+                    NavUtils.openArtistProfile(mActivity, getArtistName());
+                    return true;
+                case FragmentMenuItems.DELETE:
+                    setShouldRefresh();
+                    onDeleteClicked();
+                    return true;
+                case FragmentMenuItems.USE_AS_RINGTONE:
+                    MusicUtils.setRingtone(mActivity, getId());
+                    return true;
+                case FragmentMenuItems.REMOVE_FROM_PLAYLIST:
+                    setShouldRefresh();
+                    removeFromPlaylist();
+                    return true;
+                case FragmentMenuItems.REMOVE_FROM_QUEUE:
+                    setShouldRefresh();
+                    removeFromQueue();
+                    return true;
+                case FragmentMenuItems.PLAY_NEXT:
+                    playNext();
+                    return true;
+                default:
+                    break;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/src/com/cyngn/eleven/widgets/IPopupMenuCallback.java b/src/com/cyngn/eleven/widgets/IPopupMenuCallback.java
new file mode 100644 (file)
index 0000000..7b73c35
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+package com.cyngn.eleven.widgets;
+
+import android.view.View;
+
+public interface IPopupMenuCallback {
+    public static interface IListener {
+        void onPopupMenuClicked(final View v, final int position);
+    };
+
+    public void setPopupMenuClickedListener(final IListener listener);
+}
diff --git a/src/com/cyngn/eleven/widgets/PopupMenuButton.java b/src/com/cyngn/eleven/widgets/PopupMenuButton.java
new file mode 100644 (file)
index 0000000..caf8152
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+
+package com.cyngn.eleven.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+public class PopupMenuButton extends ImageView implements IPopupMenuCallback,
+        View.OnClickListener {
+    protected int mPosition = -1;
+    protected IListener mClickListener = null;
+
+    public PopupMenuButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setOnClickListener(this);
+    }
+
+    public void setPosition(final int position) {
+        mPosition = position;
+    }
+
+    @Override
+    public void setPopupMenuClickedListener(final IListener listener) {
+        mClickListener = listener;
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mClickListener != null) {
+            mClickListener.onPopupMenuClicked(v, mPosition);
+        }
+    }
+}