summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlinus_lee <llee@cyngn.com>2014-10-03 11:06:43 -0700
committerlinus_lee <llee@cyngn.com>2014-11-20 12:51:32 -0800
commita03f5408a9f104ed814a4ad481d313ef85078e95 (patch)
tree805659a272f4843b2f3778d778a030d846d5de04
parent182b5cd29b970c7bc560ac5541752b937c09935e (diff)
downloadandroid_packages_apps_Eleven-a03f5408a9f104ed814a4ad481d313ef85078e95.tar.gz
android_packages_apps_Eleven-a03f5408a9f104ed814a4ad481d313ef85078e95.tar.bz2
android_packages_apps_Eleven-a03f5408a9f104ed814a4ad481d313ef85078e95.zip
Eleven: Add song indicators, change ProfileSongAdapter to use SongAdapter
Much larger change than anticipated. A look of hook ins into the MusicPlaybackService Also a lot of changes here and there Still, a portion of the music playback service needs to be rewritten in bug https://cyanogen.atlassian.net/browse/MUSIC-44 https://cyanogen.atlassian.net/browse/MUSIC-56 Change-Id: I644b4347e1655ebb529cd0dd0a7a34ce2bd399c1
-rw-r--r--res/drawable-xxhdpi/now_playing_icon.pngbin0 -> 1005 bytes
-rw-r--r--res/layout/album_detail_song.xml33
-rw-r--r--res/layout/artist_detail_song.xml32
-rw-r--r--res/layout/list_item_common.xml23
-rw-r--r--res/layout/list_item_top_tracks.xml24
-rw-r--r--src/com/cyngn/eleven/Config.java27
-rw-r--r--src/com/cyngn/eleven/IElevenService.aidl8
-rw-r--r--src/com/cyngn/eleven/MusicPlaybackService.java370
-rw-r--r--src/com/cyngn/eleven/adapters/AlbumAdapter.java3
-rw-r--r--src/com/cyngn/eleven/adapters/AlbumDetailSongAdapter.java7
-rw-r--r--src/com/cyngn/eleven/adapters/ArtistDetailSongAdapter.java8
-rw-r--r--src/com/cyngn/eleven/adapters/DetailSongAdapter.java29
-rw-r--r--src/com/cyngn/eleven/adapters/ProfileSongAdapter.java242
-rw-r--r--src/com/cyngn/eleven/adapters/SongAdapter.java68
-rw-r--r--src/com/cyngn/eleven/loaders/LastAddedLoader.java1
-rw-r--r--src/com/cyngn/eleven/model/SearchResult.java15
-rw-r--r--src/com/cyngn/eleven/provider/MusicDB.java27
-rw-r--r--src/com/cyngn/eleven/provider/MusicPlaybackState.java230
-rw-r--r--src/com/cyngn/eleven/provider/PlaylistArtworkStore.java6
-rw-r--r--src/com/cyngn/eleven/provider/RecentStore.java7
-rw-r--r--src/com/cyngn/eleven/provider/SearchHistory.java5
-rw-r--r--src/com/cyngn/eleven/provider/SongPlayCount.java6
-rw-r--r--src/com/cyngn/eleven/service/MusicPlaybackTrack.aidl3
-rw-r--r--src/com/cyngn/eleven/service/MusicPlaybackTrack.java99
-rw-r--r--src/com/cyngn/eleven/ui/MusicHolder.java7
-rw-r--r--src/com/cyngn/eleven/ui/activities/SearchActivity.java12
-rw-r--r--src/com/cyngn/eleven/ui/activities/ShortcutActivity.java2
-rw-r--r--src/com/cyngn/eleven/ui/fragments/AlbumDetailFragment.java18
-rw-r--r--src/com/cyngn/eleven/ui/fragments/ArtistDetailFragment.java22
-rw-r--r--src/com/cyngn/eleven/ui/fragments/ArtistFragment.java11
-rw-r--r--src/com/cyngn/eleven/ui/fragments/PlaylistDetailFragment.java67
-rw-r--r--src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java11
-rw-r--r--src/com/cyngn/eleven/ui/fragments/QueueFragment.java28
-rw-r--r--src/com/cyngn/eleven/ui/fragments/RecentFragment.java35
-rw-r--r--src/com/cyngn/eleven/ui/fragments/SongFragment.java3
-rw-r--r--src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java41
-rw-r--r--src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java10
-rw-r--r--src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java22
-rw-r--r--src/com/cyngn/eleven/utils/AlbumPopupMenuHelper.java11
-rw-r--r--src/com/cyngn/eleven/utils/MusicUtils.java53
-rw-r--r--src/com/cyngn/eleven/utils/NavUtils.java2
-rw-r--r--src/com/cyngn/eleven/utils/PopupMenuHelper.java12
42 files changed, 1123 insertions, 517 deletions
diff --git a/res/drawable-xxhdpi/now_playing_icon.png b/res/drawable-xxhdpi/now_playing_icon.png
new file mode 100644
index 0000000..2792c21
--- /dev/null
+++ b/res/drawable-xxhdpi/now_playing_icon.png
Binary files differ
diff --git a/res/layout/album_detail_song.xml b/res/layout/album_detail_song.xml
index 17fbdaa..e40d842 100644
--- a/res/layout/album_detail_song.xml
+++ b/res/layout/album_detail_song.xml
@@ -2,15 +2,28 @@
android:layout_width="match_parent"
android:layout_height="70dp" >
- <com.cyngn.eleven.widgets.PopupMenuButton
- android:id="@+id/overflow"
- android:layout_width="@dimen/overflow_width"
- android:layout_height="68dp"
- android:layout_alignParentTop="true"
+ <LinearLayout
+ android:id="@+id/right_container"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
android:layout_alignParentRight="true"
- android:layout_marginBottom="2dp"
- android:gravity="center"
- android:src="@drawable/menu_button" />
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/now_playing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_playing_icon"
+ android:visibility="gone" />
+
+ <com.cyngn.eleven.widgets.PopupMenuButton
+ android:id="@+id/overflow"
+ android:layout_width="@dimen/overflow_width"
+ android:layout_height="@dimen/overflow_height"
+ android:gravity="center"
+ android:src="@drawable/menu_button" />
+ </LinearLayout>
<TextView
android:id="@+id/title"
@@ -19,7 +32,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
- android:layout_toLeftOf="@id/overflow"
+ android:layout_toLeftOf="@id/right_container"
android:paddingTop="6dp"
android:layout_marginTop="@dimen/list_preferred_item_padding"
android:layout_marginLeft="@dimen/list_preferred_item_padding" />
@@ -31,7 +44,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignParentLeft="true"
- android:layout_toLeftOf="@id/overflow"
+ android:layout_toLeftOf="@id/right_container"
android:layout_marginTop="-2dp"
android:layout_marginLeft="@dimen/standard_padding" />
diff --git a/res/layout/artist_detail_song.xml b/res/layout/artist_detail_song.xml
index e5cfa8d..ec99655 100644
--- a/res/layout/artist_detail_song.xml
+++ b/res/layout/artist_detail_song.xml
@@ -10,15 +10,27 @@
android:layout_alignParentLeft="true"
android:layout_margin="@dimen/list_preferred_item_padding" />
- <com.cyngn.eleven.widgets.PopupMenuButton
- android:id="@+id/overflow"
- android:layout_width="@dimen/overflow_width"
- android:layout_height="68dp"
- android:layout_alignParentTop="true"
+ <LinearLayout
+ android:id="@+id/right_container"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
android:layout_alignParentRight="true"
- android:layout_marginBottom="2dp"
- android:gravity="center"
- android:src="@drawable/menu_button" />
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/now_playing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_playing_icon"
+ android:visibility="gone" />
+
+ <com.cyngn.eleven.widgets.PopupMenuButton
+ android:id="@+id/overflow"
+ android:layout_width="@dimen/overflow_width"
+ android:layout_height="@dimen/overflow_height"
+ android:src="@drawable/menu_button" />
+ </LinearLayout>
<TextView
android:id="@+id/title"
@@ -26,7 +38,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/overflow"
+ android:layout_toLeftOf="@id/right_container"
android:layout_toRightOf="@id/album_art"
android:layout_marginTop="16dp" />
@@ -37,7 +49,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_toRightOf="@id/album_art"
- android:layout_toLeftOf="@id/overflow"
+ android:layout_toLeftOf="@id/right_container"
android:layout_marginTop="-2dp" />
<ImageView
diff --git a/res/layout/list_item_common.xml b/res/layout/list_item_common.xml
index f9f09ee..b4ef0c7 100644
--- a/res/layout/list_item_common.xml
+++ b/res/layout/list_item_common.xml
@@ -34,15 +34,28 @@
style="@style/ListItemSecondaryText.Single"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_toLeftOf="@+id/popup_menu_button"
+ android:layout_toLeftOf="@+id/right_container"
android:layout_centerVertical="true" />
- <com.cyngn.eleven.widgets.PopupMenuButton
- android:id="@id/popup_menu_button"
+ <LinearLayout
+ android:id="@id/right_container"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:src="@drawable/menu_button" />
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/now_playing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_playing_icon"
+ android:visibility="gone" />
+ <com.cyngn.eleven.widgets.PopupMenuButton
+ android:id="@+id/popup_menu_button"
+ android:layout_width="@dimen/overflow_width"
+ android:layout_height="@dimen/overflow_height"
+ android:src="@drawable/menu_button" />
+ </LinearLayout>
</merge> \ No newline at end of file
diff --git a/res/layout/list_item_top_tracks.xml b/res/layout/list_item_top_tracks.xml
index 9a8bfb6..aae5043 100644
--- a/res/layout/list_item_top_tracks.xml
+++ b/res/layout/list_item_top_tracks.xml
@@ -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/popup_menu_button"
+ android:layout_toLeftOf="@+id/right_container"
android:gravity="center_vertical"
android:minHeight="@dimen/item_normal_height"
android:paddingLeft="@dimen/list_preferred_item_padding" >
@@ -75,13 +75,27 @@
android:layout_below="@+id/line_one" />
</RelativeLayout>
- <com.cyngn.eleven.widgets.PopupMenuButton
- android:id="@id/popup_menu_button"
+ <LinearLayout
+ android:id="@id/right_container"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:src="@drawable/menu_button" />
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/now_playing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/now_playing_icon"
+ android:visibility="gone" />
+
+ <com.cyngn.eleven.widgets.PopupMenuButton
+ android:id="@+id/popup_menu_button"
+ android:layout_width="@dimen/overflow_width"
+ android:layout_height="@dimen/overflow_height"
+ android:src="@drawable/menu_button" />
+ </LinearLayout>
</RelativeLayout>
<ImageView
diff --git a/src/com/cyngn/eleven/Config.java b/src/com/cyngn/eleven/Config.java
index af8f1ee..c1b84f5 100644
--- a/src/com/cyngn/eleven/Config.java
+++ b/src/com/cyngn/eleven/Config.java
@@ -98,4 +98,31 @@ public final class Config {
return null;
}
}
+
+ /**
+ * This helps identify where an id has come from. Mainly used to determine when a user
+ * clicks a song where that song came from (artist/album/playlist)
+ */
+ public static enum IdType {
+ NA(0),
+ Artist(1),
+ Album(2),
+ Playlist(3);
+
+ public final int mId;
+
+ IdType(final int id) {
+ mId = id;
+ }
+
+ public static IdType getTypeById(int id) {
+ for (IdType type : values()) {
+ if (type.mId == id) {
+ return type;
+ }
+ }
+
+ throw new IllegalArgumentException("Unrecognized id: " + id);
+ }
+ }
}
diff --git a/src/com/cyngn/eleven/IElevenService.aidl b/src/com/cyngn/eleven/IElevenService.aidl
index c15ccf1..9f8813f 100644
--- a/src/com/cyngn/eleven/IElevenService.aidl
+++ b/src/com/cyngn/eleven/IElevenService.aidl
@@ -1,17 +1,17 @@
package com.cyngn.eleven;
-import android.graphics.Bitmap;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
interface IElevenService
{
void openFile(String path);
- void open(in long [] list, int position);
+ void open(in long [] list, int position, long sourceId, int sourceType);
void stop();
void pause();
void play();
void prev(boolean forcePrevious);
void next();
- void enqueue(in long [] list, int action);
+ void enqueue(in long [] list, int action, long sourceId, int sourceType);
void setQueuePosition(int index);
void setShuffleMode(int shufflemode);
void setRepeatMode(int repeatmode);
@@ -27,6 +27,8 @@ interface IElevenService
long position();
long seek(long pos);
long getAudioId();
+ MusicPlaybackTrack getCurrentTrack();
+ MusicPlaybackTrack getTrack(int index);
long getNextAudioId();
long getPreviousAudioId();
long getArtistId();
diff --git a/src/com/cyngn/eleven/MusicPlaybackService.java b/src/com/cyngn/eleven/MusicPlaybackService.java
index 81d0067..443088c 100644
--- a/src/com/cyngn/eleven/MusicPlaybackService.java
+++ b/src/com/cyngn/eleven/MusicPlaybackService.java
@@ -29,7 +29,6 @@ import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
import android.media.RemoteControlClient;
import android.media.audiofx.AudioEffect;
import android.net.Uri;
@@ -47,21 +46,24 @@ import android.provider.MediaStore.Audio.AlbumColumns;
import android.provider.MediaStore.Audio.AudioColumns;
import android.util.Log;
+import com.cyngn.eleven.Config.IdType;
import com.cyngn.eleven.appwidgets.AppWidgetLarge;
import com.cyngn.eleven.appwidgets.AppWidgetLargeAlternate;
import com.cyngn.eleven.appwidgets.AppWidgetSmall;
import com.cyngn.eleven.cache.ImageCache;
import com.cyngn.eleven.cache.ImageFetcher;
+import com.cyngn.eleven.provider.MusicPlaybackState;
import com.cyngn.eleven.provider.RecentStore;
import com.cyngn.eleven.provider.SongPlayCount;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.cyngn.eleven.utils.ApolloUtils;
import com.cyngn.eleven.utils.Lists;
-import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.PreferenceUtils;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.LinkedList;
+import java.util.ListIterator;
import java.util.Random;
import java.util.TreeSet;
@@ -325,7 +327,7 @@ public class MusicPlaybackService extends Service {
/**
* Keeps a mapping of the track history
*/
- private static final LinkedList<Integer> mHistory = Lists.newLinkedList();
+ private static LinkedList<Integer> mHistory = Lists.newLinkedList();
/**
* Used to shuffle the tracks
@@ -444,8 +446,6 @@ public class MusicPlaybackService extends Service {
// playlists
private int mCardId;
- private int mPlayListLen = 0;
-
private int mPlayPos = -1;
private int mNextPlayPos = -1;
@@ -460,7 +460,7 @@ public class MusicPlaybackService extends Service {
private int mServiceStartId = -1;
- private long[] mPlayList = null;
+ private ArrayList<MusicPlaybackTrack> mPlaylist = new ArrayList<MusicPlaybackTrack>(100);
private long[] mAutoShuffleList = null;
@@ -489,6 +489,11 @@ public class MusicPlaybackService extends Service {
private SongPlayCount mSongPlayCountCache;
/**
+ * Stores the playback state
+ */
+ private MusicPlaybackState mPlaybackStateStore;
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -518,7 +523,7 @@ public class MusicPlaybackService extends Service {
// before stopping the service, so that pause/resume isn't slow.
// Also delay stopping the service if we're transitioning between
// tracks.
- } else if (mPlayListLen > 0 || mPlayerHandler.hasMessages(TRACK_ENDED)) {
+ } else if (mPlaylist.size() > 0 || mPlayerHandler.hasMessages(TRACK_ENDED)) {
scheduleDelayedShutdown();
return true;
}
@@ -549,6 +554,9 @@ public class MusicPlaybackService extends Service {
// gets the song play count cache
mSongPlayCountCache = SongPlayCount.getInstance(this);
+ // gets a pointer to the playback state store
+ mPlaybackStateStore = MusicPlaybackState.getInstance(this);
+
// Initialize the notification helper
mNotificationHelper = new NotificationHelper(this);
@@ -921,8 +929,8 @@ public class MusicPlaybackService extends Service {
return 0;
} else if (first < 0) {
first = 0;
- } else if (last >= mPlayListLen) {
- last = mPlayListLen - 1;
+ } else if (last >= mPlaylist.size()) {
+ last = mPlaylist.size() - 1;
}
boolean gotonext = false;
@@ -932,21 +940,41 @@ public class MusicPlaybackService extends Service {
} else if (mPlayPos > last) {
mPlayPos -= last - first + 1;
}
- final int num = mPlayListLen - last - 1;
- for (int i = 0; i < num; i++) {
- mPlayList[first + i] = mPlayList[last + 1 + i];
- }
- mPlayListLen -= last - first + 1;
+ final int numToRemove = last - first + 1;
+ if (first == 0 && last == mPlaylist.size() - 1) {
+ mPlaylist.clear();
+ mHistory.clear();
+ } else {
+ for (int i = 0; i < numToRemove; i++) {
+ mPlaylist.remove(first);
+ }
+
+ // remove the items from the history
+ // this is not ideal as the history shouldn't be impacted by this
+ // but since we are removing items from the array, it will throw
+ // an exception if we keep it around. Idealistically with the queue
+ // rewrite this should be all be fixed
+ // https://cyanogen.atlassian.net/browse/MUSIC-44
+ ListIterator<Integer> positionIterator = mHistory.listIterator();
+ while (positionIterator.hasNext()) {
+ int pos = positionIterator.next();
+ if (pos >= first && pos <= last) {
+ positionIterator.remove();
+ } else if (pos > last) {
+ positionIterator.set(pos - numToRemove);
+ }
+ }
+ }
if (gotonext) {
- if (mPlayListLen == 0) {
+ if (mPlaylist.size() == 0) {
stop(true);
mPlayPos = -1;
closeCursor();
} else {
if (mShuffleMode != SHUFFLE_NONE) {
mPlayPos = getNextPosition(true);
- } else if (mPlayPos >= mPlayListLen) {
+ } else if (mPlayPos >= mPlaylist.size()) {
mPlayPos = 0;
}
final boolean wasPlaying = isPlaying();
@@ -968,27 +996,23 @@ public class MusicPlaybackService extends Service {
* @param list The list to add
* @param position The position to place the tracks
*/
- private void addToPlayList(final long[] list, int position) {
+ private void addToPlayList(final long[] list, int position, long sourceId, IdType sourceType) {
final int addlen = list.length;
if (position < 0) {
- mPlayListLen = 0;
+ mPlaylist.clear();
position = 0;
}
- ensurePlayListCapacity(mPlayListLen + addlen);
- if (position > mPlayListLen) {
- position = mPlayListLen;
- }
- final int tailsize = mPlayListLen - position;
- for (int i = tailsize; i > 0; i--) {
- mPlayList[position + i] = mPlayList[position + i - addlen];
+ mPlaylist.ensureCapacity(mPlaylist.size() + addlen);
+ if (position > mPlaylist.size()) {
+ position = mPlaylist.size();
}
- for (int i = 0; i < addlen; i++) {
- mPlayList[position + i] = list[i];
+ for (int i = 0; i < list.length; i++) {
+ mPlaylist.add(new MusicPlaybackTrack(list[i], sourceId, sourceType, i));
}
- mPlayListLen += addlen;
- if (mPlayListLen == 0) {
+
+ if (mPlaylist.size() == 0) {
closeCursor();
notifyChange(META_CHANGED);
}
@@ -1072,12 +1096,12 @@ public class MusicPlaybackService extends Service {
synchronized (this) {
closeCursor();
- if (mPlayListLen == 0) {
+ if (mPlaylist.size() == 0) {
return;
}
stop(false);
- updateCursor(mPlayList[mPlayPos]);
+ updateCursor(mPlaylist.get(mPlayPos).mId);
while (true) {
if (mCursor != null
&& openFile(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/"
@@ -1088,7 +1112,7 @@ public class MusicPlaybackService extends Service {
// cursor now, because
// we're either going to create a new one next, or stop trying
closeCursor();
- if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) {
+ if (mOpenFailedCounter++ < 10 && mPlaylist.size() > 1) {
final int pos = getNextPosition(false);
if (pos < 0) {
scheduleDelayedShutdown();
@@ -1101,7 +1125,7 @@ public class MusicPlaybackService extends Service {
mPlayPos = pos;
stop(false);
mPlayPos = pos;
- updateCursor(mPlayList[mPlayPos]);
+ updateCursor(mPlaylist.get(mPlayPos).mId);
} else {
mOpenFailedCounter = 0;
Log.w(TAG, "Failed to open file for playback");
@@ -1133,7 +1157,7 @@ public class MusicPlaybackService extends Service {
}
return mPlayPos;
} else if (mShuffleMode == SHUFFLE_NORMAL) {
- final int numTracks = mPlayListLen;
+ final int numTracks = mPlaylist.size();
// count the number of times a track has been played
final int[] trackNumPlays = new int[numTracks];
@@ -1200,7 +1224,7 @@ public class MusicPlaybackService extends Service {
doAutoShuffleUpdate();
return mPlayPos + 1;
} else {
- if (mPlayPos >= mPlayListLen - 1) {
+ if (mPlayPos >= mPlaylist.size() - 1) {
if (mRepeatMode == REPEAT_NONE && !force) {
return -1;
} else if (mRepeatMode == REPEAT_ALL || force) {
@@ -1227,8 +1251,8 @@ public class MusicPlaybackService extends Service {
private void setNextTrack(int position) {
mNextPlayPos = position;
if (D) Log.d(TAG, "setNextTrack: next play position = " + mNextPlayPos);
- if (mNextPlayPos >= 0 && mPlayList != null) {
- final long id = mPlayList[mNextPlayPos];
+ if (mNextPlayPos >= 0 && mPlaylist != null) {
+ final long id = mPlaylist.get(mNextPlayPos).mId;
mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
} else {
mPlayer.setNextDataSource(null);
@@ -1275,7 +1299,7 @@ public class MusicPlaybackService extends Service {
removeTracks(0, mPlayPos - 9);
notify = true;
}
- final int toAdd = 7 - (mPlayListLen - (mPlayPos < 0 ? -1 : mPlayPos));
+ final int toAdd = 7 - (mPlaylist.size() - (mPlayPos < 0 ? -1 : mPlayPos));
for (int i = 0; i < toAdd; i++) {
int lookback = mHistory.size();
int idx = -1;
@@ -1290,8 +1314,7 @@ public class MusicPlaybackService extends Service {
if (mHistory.size() > MAX_HISTORY_SIZE) {
mHistory.remove(0);
}
- ensurePlayListCapacity(mPlayListLen + 1);
- mPlayList[mPlayListLen++] = mAutoShuffleList[idx];
+ mPlaylist.add(new MusicPlaybackTrack(mAutoShuffleList[idx], -1, IdType.NA, -1));
notify = true;
}
if (notify) {
@@ -1319,27 +1342,6 @@ public class MusicPlaybackService extends Service {
}
/**
- * Makes sure the playlist has enough space to hold all of the songs
- *
- * @param size The size of the playlist
- */
- private void ensurePlayListCapacity(final int size) {
- if (mPlayList == null || size > mPlayList.length) {
- // reallocate at 2x requested size so we don't
- // need to grow and copy the array for every
- // insert
- final long[] newlist = new long[size * 2];
- final int len = mPlayList != null ? mPlayList.length : mPlayListLen;
- for (int i = 0; i < len; i++) {
- newlist[i] = mPlayList[i];
- }
- mPlayList = newlist;
- }
- // FIXME: shrink the array when the needed size is much smaller
- // than the allocated size
- }
-
- /**
* Notify the change-receivers that something has changed.
*/
private void notifyChange(final String what) {
@@ -1443,43 +1445,9 @@ public class MusicPlaybackService extends Service {
final SharedPreferences.Editor editor = mPreferences.edit();
if (full) {
- final StringBuilder q = new StringBuilder();
- int len = mPlayListLen;
- for (int i = 0; i < len; i++) {
- long n = mPlayList[i];
- if (n < 0) {
- continue;
- } else if (n == 0) {
- q.append("0;");
- } else {
- while (n != 0) {
- final int digit = (int)(n & 0xf);
- n >>>= 4;
- q.append(HEX_DIGITS[digit]);
- }
- q.append(";");
- }
- }
- editor.putString("queue", q.toString());
+ mPlaybackStateStore.saveState(mPlaylist,
+ mShuffleMode != SHUFFLE_NONE ? mHistory : null);
editor.putInt("cardid", mCardId);
- if (mShuffleMode != SHUFFLE_NONE) {
- len = mHistory.size();
- q.setLength(0);
- for (int i = 0; i < len; i++) {
- int n = mHistory.get(i);
- if (n == 0) {
- q.append("0;");
- } else {
- while (n != 0) {
- final int digit = n & 0xf;
- n >>>= 4;
- q.append(HEX_DIGITS[digit]);
- }
- q.append(";");
- }
- }
- editor.putString("history", q.toString());
- }
}
editor.putInt("curpos", mPlayPos);
if (mPlayer.isInitialized()) {
@@ -1495,50 +1463,24 @@ public class MusicPlaybackService extends Service {
* Apollo
*/
private void reloadQueue() {
- String q = null;
int id = mCardId;
if (mPreferences.contains("cardid")) {
id = mPreferences.getInt("cardid", ~mCardId);
}
if (id == mCardId) {
- q = mPreferences.getString("queue", "");
- }
- int qlen = q != null ? q.length() : 0;
- if (qlen > 1) {
- int plen = 0;
- int n = 0;
- int shift = 0;
- for (int i = 0; i < qlen; i++) {
- final char c = q.charAt(i);
- if (c == ';') {
- ensurePlayListCapacity(plen + 1);
- mPlayList[plen] = n;
- plen++;
- n = 0;
- shift = 0;
- } else {
- if (c >= '0' && c <= '9') {
- n += c - '0' << shift;
- } else if (c >= 'a' && c <= 'f') {
- n += 10 + c - 'a' << shift;
- } else {
- plen = 0;
- break;
- }
- shift += 4;
- }
- }
- mPlayListLen = plen;
+ mPlaylist = mPlaybackStateStore.getQueue();
+ }
+ if (mPlaylist.size() > 0) {
final int pos = mPreferences.getInt("curpos", 0);
- if (pos < 0 || pos >= mPlayListLen) {
- mPlayListLen = 0;
+ if (pos < 0 || pos >= mPlaylist.size()) {
+ mPlaylist.clear();
return;
}
mPlayPos = pos;
- updateCursor(mPlayList[mPlayPos]);
+ updateCursor(mPlaylist.get(mPlayPos).mId);
if (mCursor == null) {
SystemClock.sleep(3000);
- updateCursor(mPlayList[mPlayPos]);
+ updateCursor(mPlaylist.get(mPlayPos).mId);
}
synchronized (this) {
closeCursor();
@@ -1546,7 +1488,7 @@ public class MusicPlaybackService extends Service {
openCurrentAndNext();
}
if (!mPlayer.isInitialized()) {
- mPlayListLen = 0;
+ mPlaylist.clear();
return;
}
@@ -1570,36 +1512,7 @@ public class MusicPlaybackService extends Service {
shufmode = SHUFFLE_NONE;
}
if (shufmode != SHUFFLE_NONE) {
- q = mPreferences.getString("history", "");
- qlen = q != null ? q.length() : 0;
- if (qlen > 1) {
- plen = 0;
- n = 0;
- shift = 0;
- mHistory.clear();
- for (int i = 0; i < qlen; i++) {
- final char c = q.charAt(i);
- if (c == ';') {
- if (n >= mPlayListLen) {
- mHistory.clear();
- break;
- }
- mHistory.add(n);
- n = 0;
- shift = 0;
- } else {
- if (c >= '0' && c <= '9') {
- n += c - '0' << shift;
- } else if (c >= 'a' && c <= 'f') {
- n += 10 + c - 'a' << shift;
- } else {
- mHistory.clear();
- break;
- }
- shift += 4;
- }
- }
- }
+ mHistory = mPlaybackStateStore.getHistory(mPlaylist.size());
}
if (shufmode == SHUFFLE_AUTO) {
if (!makeAutoShuffleList()) {
@@ -1647,9 +1560,8 @@ public class MusicPlaybackService extends Service {
}
try {
if (mCursor != null) {
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayList[0] = mCursor.getLong(IDCOLIDX);
+ mPlaylist.clear();
+ mPlaylist.add(new MusicPlaybackTrack(mCursor.getLong(IDCOLIDX), -1, IdType.NA, -1));
mPlayPos = 0;
mHistory.clear();
}
@@ -1714,8 +1626,8 @@ public class MusicPlaybackService extends Service {
public int removeTrack(final long id) {
int numremoved = 0;
synchronized (this) {
- for (int i = 0; i < mPlayListLen; i++) {
- if (mPlayList[i] == id) {
+ for (int i = 0; i < mPlaylist.size(); i++) {
+ if (mPlaylist.get(i).mId == id) {
numremoved += removeTracksInternal(i, i);
i--;
}
@@ -1877,28 +1789,46 @@ public class MusicPlaybackService extends Service {
}
/**
- * Returns the current audio ID
- *
- * @return The current track ID
+ * @return The audio id of the track
*/
public long getAudioId() {
- synchronized (this) {
- if (mPlayPos >= 0 && mPlayer.isInitialized()) {
- return mPlayList[mPlayPos];
- }
+ MusicPlaybackTrack track = getCurrentTrack();
+ if (track != null) {
+ return track.mId;
}
+
return -1;
}
/**
+ * Gets the currently playing music track
+ */
+ public MusicPlaybackTrack getCurrentTrack() {
+ return getTrack(mPlayPos);
+ }
+
+ /**
+ * Gets the music track from the queue at the specified index
+ * @param index position
+ * @return music track or null
+ */
+ public synchronized MusicPlaybackTrack getTrack(int index) {
+ if (index >= 0 && index < mPlaylist.size() && mPlayer.isInitialized()) {
+ return mPlaylist.get(index);
+ }
+
+ return null;
+ }
+
+ /**
* Returns the next audio ID
*
* @return The next track ID
*/
public long getNextAudioId() {
synchronized (this) {
- if (mNextPlayPos >= 0 && mPlayer.isInitialized()) {
- return mPlayList[mNextPlayPos];
+ if (mNextPlayPos >= 0 && mNextPlayPos < mPlaylist.size() && mPlayer.isInitialized()) {
+ return mPlaylist.get(mNextPlayPos).mId;
}
}
return -1;
@@ -1913,8 +1843,8 @@ public class MusicPlaybackService extends Service {
synchronized (this) {
if (mPlayer.isInitialized()) {
int pos = getPreviousPlayPosition(false);
- if (pos >= 0) {
- return mPlayList[pos];
+ if (pos >= 0 && pos < mPlaylist.size()) {
+ return mPlaylist.get(pos).mId;
}
}
}
@@ -1972,10 +1902,10 @@ public class MusicPlaybackService extends Service {
*/
public long[] getQueue() {
synchronized (this) {
- final int len = mPlayListLen;
+ final int len = mPlaylist.size();
final long[] list = new long[len];
for (int i = 0; i < len; i++) {
- list[i] = mPlayList[i];
+ list[i] = mPlaylist.get(i).mId;
}
return list;
}
@@ -1994,7 +1924,7 @@ public class MusicPlaybackService extends Service {
* @param list The list of tracks to open
* @param position The position to start playback at
*/
- public void open(final long[] list, final int position) {
+ public void open(final long[] list, final int position, long sourceId, IdType sourceType) {
synchronized (this) {
if (mShuffleMode == SHUFFLE_AUTO) {
mShuffleMode = SHUFFLE_NORMAL;
@@ -2002,23 +1932,23 @@ public class MusicPlaybackService extends Service {
final long oldId = getAudioId();
final int listlength = list.length;
boolean newlist = true;
- if (mPlayListLen == listlength) {
+ if (mPlaylist.size() == listlength) {
newlist = false;
for (int i = 0; i < listlength; i++) {
- if (list[i] != mPlayList[i]) {
+ if (list[i] != mPlaylist.get(i).mId) {
newlist = true;
break;
}
}
}
if (newlist) {
- addToPlayList(list, -1);
+ addToPlayList(list, -1, sourceId, sourceType);
notifyChange(QUEUE_CHANGED);
}
if (position >= 0) {
mPlayPos = position;
} else {
- mPlayPos = mShuffler.nextInt(mPlayListLen);
+ mPlayPos = mShuffler.nextInt(mPlaylist.size());
}
mHistory.clear();
openCurrentAndNext();
@@ -2084,7 +2014,7 @@ public class MusicPlaybackService extends Service {
cancelShutdown();
updateNotification();
- } else if (mPlayListLen <= 0) {
+ } else if (mPlaylist.size() <= 0) {
setShuffleMode(SHUFFLE_AUTO);
}
}
@@ -2111,7 +2041,7 @@ public class MusicPlaybackService extends Service {
public void gotoNext(final boolean force) {
if (D) Log.d(TAG, "Going to next track");
synchronized (this) {
- if (mPlayListLen <= 0) {
+ if (mPlaylist.size() <= 0) {
if (D) Log.d(TAG, "No play queue");
scheduleDelayedShutdown();
return;
@@ -2200,7 +2130,7 @@ public class MusicPlaybackService extends Service {
if (mPlayPos > 0) {
return mPlayPos - 1;
} else {
- return mPlayListLen - 1;
+ return mPlaylist.size() - 1;
}
}
}
@@ -2223,29 +2153,23 @@ public class MusicPlaybackService extends Service {
*/
public void moveQueueItem(int index1, int index2) {
synchronized (this) {
- if (index1 >= mPlayListLen) {
- index1 = mPlayListLen - 1;
+ if (index1 >= mPlaylist.size()) {
+ index1 = mPlaylist.size() - 1;
}
- if (index2 >= mPlayListLen) {
- index2 = mPlayListLen - 1;
+ if (index2 >= mPlaylist.size()) {
+ index2 = mPlaylist.size() - 1;
}
+
+ final MusicPlaybackTrack track = mPlaylist.remove(index1);
if (index1 < index2) {
- final long tmp = mPlayList[index1];
- for (int i = index1; i < index2; i++) {
- mPlayList[i] = mPlayList[i + 1];
- }
- mPlayList[index2] = tmp;
+ mPlaylist.add(index2, track);
if (mPlayPos == index1) {
mPlayPos = index2;
} else if (mPlayPos >= index1 && mPlayPos <= index2) {
mPlayPos--;
}
} else if (index2 < index1) {
- final long tmp = mPlayList[index1];
- for (int i = index1; i > index2; i--) {
- mPlayList[i] = mPlayList[i - 1];
- }
- mPlayList[index2] = tmp;
+ mPlaylist.add(index2, track);
if (mPlayPos == index1) {
mPlayPos = index2;
} else if (mPlayPos >= index2 && mPlayPos <= index1) {
@@ -2277,14 +2201,14 @@ public class MusicPlaybackService extends Service {
*/
public void setShuffleMode(final int shufflemode) {
synchronized (this) {
- if (mShuffleMode == shufflemode && mPlayListLen > 0) {
+ if (mShuffleMode == shufflemode && mPlaylist.size() > 0) {
return;
}
mShuffleMode = shufflemode;
if (mShuffleMode == SHUFFLE_AUTO) {
if (makeAutoShuffleList()) {
- mPlayListLen = 0;
+ mPlaylist.clear();
doAutoShuffleUpdate();
mPlayPos = 0;
openCurrentAndNext();
@@ -2326,16 +2250,16 @@ public class MusicPlaybackService extends Service {
* @param list The list to queue
* @param action The action to take
*/
- public void enqueue(final long[] list, final int action) {
+ public void enqueue(final long[] list, final int action, long sourceId, IdType sourceType) {
synchronized (this) {
- if (action == NEXT && mPlayPos + 1 < mPlayListLen) {
- addToPlayList(list, mPlayPos + 1);
+ if (action == NEXT && mPlayPos + 1 < mPlaylist.size()) {
+ addToPlayList(list, mPlayPos + 1, sourceId, sourceType);
notifyChange(QUEUE_CHANGED);
} else {
- addToPlayList(list, Integer.MAX_VALUE);
+ addToPlayList(list, Integer.MAX_VALUE, sourceId, sourceType);
notifyChange(QUEUE_CHANGED);
if (action == NOW) {
- mPlayPos = mPlayListLen - list.length;
+ mPlayPos = mPlaylist.size() - list.length;
openCurrentAndNext();
play();
notifyChange(META_CHANGED);
@@ -2496,7 +2420,7 @@ public class MusicPlaybackService extends Service {
if (service.mCursor != null) {
service.mCursor.close();
}
- service.updateCursor(service.mPlayList[service.mPlayPos]);
+ service.updateCursor(service.mPlaylist.get(service.mPlayPos).mId);
service.notifyChange(META_CHANGED);
service.updateNotification();
break;
@@ -2854,8 +2778,9 @@ public class MusicPlaybackService extends Service {
* {@inheritDoc}
*/
@Override
- public void open(final long[] list, final int position) throws RemoteException {
- mService.get().open(list, position);
+ public void open(final long[] list, final int position, long sourceId, int sourceType)
+ throws RemoteException {
+ mService.get().open(list, position, sourceId, IdType.getTypeById(sourceType));
}
/**
@@ -2902,8 +2827,9 @@ public class MusicPlaybackService extends Service {
* {@inheritDoc}
*/
@Override
- public void enqueue(final long[] list, final int action) throws RemoteException {
- mService.get().enqueue(list, action);
+ public void enqueue(final long[] list, final int action, long sourceId, int sourceType)
+ throws RemoteException {
+ mService.get().enqueue(list, action, sourceId, IdType.getTypeById(sourceType));
}
/**
@@ -3022,6 +2948,22 @@ public class MusicPlaybackService extends Service {
* {@inheritDoc}
*/
@Override
+ public MusicPlaybackTrack getCurrentTrack() throws RemoteException {
+ return mService.get().getCurrentTrack();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MusicPlaybackTrack getTrack(int index) throws RemoteException {
+ return mService.get().getTrack(index);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public long getNextAudioId() throws RemoteException {
return mService.get().getNextAudioId();
}
diff --git a/src/com/cyngn/eleven/adapters/AlbumAdapter.java b/src/com/cyngn/eleven/adapters/AlbumAdapter.java
index d970d99..9ab3aad 100644
--- a/src/com/cyngn/eleven/adapters/AlbumAdapter.java
+++ b/src/com/cyngn/eleven/adapters/AlbumAdapter.java
@@ -21,6 +21,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.R;
import com.cyngn.eleven.cache.ImageFetcher;
import com.cyngn.eleven.model.Album;
@@ -182,7 +183,7 @@ public class AlbumAdapter extends ArrayAdapter<Album>
public void onClick(final View v) {
final long id = getItem(position).mAlbumId;
final long[] list = MusicUtils.getSongListForAlbum(getContext(), id);
- MusicUtils.playAll(getContext(), list, 0, false);
+ MusicUtils.playAll(getContext(), list, 0, id, Config.IdType.Album, false);
}
});
}
diff --git a/src/com/cyngn/eleven/adapters/AlbumDetailSongAdapter.java b/src/com/cyngn/eleven/adapters/AlbumDetailSongAdapter.java
index c7dd899..431bffe 100644
--- a/src/com/cyngn/eleven/adapters/AlbumDetailSongAdapter.java
+++ b/src/com/cyngn/eleven/adapters/AlbumDetailSongAdapter.java
@@ -27,10 +27,15 @@ public abstract class AlbumDetailSongAdapter extends DetailSongAdapter {
protected int rowLayoutId() { return R.layout.album_detail_song; }
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Album;
+ }
+
@Override // LoaderCallbacks
public Loader<List<Song>> onCreateLoader(int id, Bundle args) {
onLoading();
- return new AlbumSongLoader(mActivity, args.getLong(Config.ID));
+ setSourceId(args.getLong(Config.ID));
+ return new AlbumSongLoader(mActivity, getSourceId());
}
@Override // LoaderCallbacks
diff --git a/src/com/cyngn/eleven/adapters/ArtistDetailSongAdapter.java b/src/com/cyngn/eleven/adapters/ArtistDetailSongAdapter.java
index 384aa9a..f5d0f4f 100644
--- a/src/com/cyngn/eleven/adapters/ArtistDetailSongAdapter.java
+++ b/src/com/cyngn/eleven/adapters/ArtistDetailSongAdapter.java
@@ -16,17 +16,21 @@ import com.cyngn.eleven.model.Song;
import java.util.List;
public abstract class ArtistDetailSongAdapter extends DetailSongAdapter {
-
public ArtistDetailSongAdapter(Activity activity) {
super(activity);
}
protected int rowLayoutId() { return R.layout.artist_detail_song; }
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Artist;
+ }
+
@Override // LoaderCallbacks
public Loader<List<Song>> onCreateLoader(int id, Bundle args) {
onLoading();
- return new ArtistSongLoader(mActivity, args.getLong(Config.ID));
+ setSourceId(args.getLong(Config.ID));
+ return new ArtistSongLoader(mActivity, getSourceId());
}
protected Holder newHolder(View root, ImageFetcher fetcher) {
diff --git a/src/com/cyngn/eleven/adapters/DetailSongAdapter.java b/src/com/cyngn/eleven/adapters/DetailSongAdapter.java
index bed1956..38e0384 100644
--- a/src/com/cyngn/eleven/adapters/DetailSongAdapter.java
+++ b/src/com/cyngn/eleven/adapters/DetailSongAdapter.java
@@ -9,11 +9,14 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
+import android.widget.ImageView;
import android.widget.TextView;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.R;
import com.cyngn.eleven.cache.ImageFetcher;
import com.cyngn.eleven.model.Song;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.cyngn.eleven.utils.ApolloUtils;
import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.widgets.IPopupMenuCallback;
@@ -29,6 +32,8 @@ public abstract class DetailSongAdapter extends BaseAdapter
private final LayoutInflater mInflater;
private List<Song> mSongs = Collections.emptyList();
private IListener mListener;
+ private long mSourceId = -1;
+ private MusicPlaybackTrack mCurrentlyPlayingTrack;
public DetailSongAdapter(final Activity activity) {
mActivity = activity;
@@ -45,6 +50,16 @@ public abstract class DetailSongAdapter extends BaseAdapter
@Override
public long getItemId(int pos) { return pos; }
+ protected long getSourceId() { return mSourceId; }
+ protected void setSourceId(long id) { mSourceId = id; }
+
+ public void setCurrentlyPlayingTrack(MusicPlaybackTrack currentTrack) {
+ if (mCurrentlyPlayingTrack == null || !mCurrentlyPlayingTrack.equals(currentTrack)) {
+ mCurrentlyPlayingTrack = currentTrack;
+ notifyDataSetChanged();
+ }
+ }
+
@Override
public View getView(int pos, View convertView, ViewGroup parent) {
if(convertView == null) {
@@ -59,12 +74,22 @@ public abstract class DetailSongAdapter extends BaseAdapter
holder.popupMenuButton.setPopupMenuClickedListener(mListener);
holder.popupMenuButton.setPosition(pos);
+ if (mCurrentlyPlayingTrack != null
+ && mCurrentlyPlayingTrack.mSourceId == getSourceId()
+ && mCurrentlyPlayingTrack.mSourceType == getSourceType()
+ && mCurrentlyPlayingTrack.mId == song.mSongId) {
+ holder.playIcon.setVisibility(View.VISIBLE);
+ } else {
+ holder.playIcon.setVisibility(View.GONE);
+ }
+
return convertView;
}
protected abstract int rowLayoutId();
protected abstract void onLoading();
protected abstract void onNoResults();
+ protected abstract Config.IdType getSourceType();
@Override // OnItemClickListener
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
@@ -76,7 +101,7 @@ public abstract class DetailSongAdapter extends BaseAdapter
for(int i = 0; i < toPlay.length; i++) {
toPlay[i] = getItem(position + i).mSongId;
}
- MusicUtils.playAll(mActivity, toPlay, -1, false);
+ MusicUtils.playAll(mActivity, toPlay, -1, getSourceId(), getSourceType(), false);
}
@Override // LoaderCallbacks
@@ -107,11 +132,13 @@ public abstract class DetailSongAdapter extends BaseAdapter
protected ImageFetcher fetcher;
protected TextView title;
protected PopupMenuButton popupMenuButton;
+ protected ImageView playIcon;
protected Holder(View root, ImageFetcher fetcher) {
this.fetcher = fetcher;
title = (TextView)root.findViewById(R.id.title);
popupMenuButton = (PopupMenuButton)root.findViewById(R.id.overflow);
+ playIcon = (ImageView)root.findViewById(R.id.now_playing);
}
protected abstract void update(Song song);
diff --git a/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java b/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java
index 88ecb53..c4318e8 100644
--- a/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java
+++ b/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java
@@ -17,111 +17,40 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import com.cyngn.eleven.cache.ImageFetcher;
-import com.cyngn.eleven.model.Artist;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.model.Song;
-import com.cyngn.eleven.ui.MusicHolder;
-import com.cyngn.eleven.ui.fragments.profile.LastAddedFragment;
-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;
+import java.util.Collection;
/**
- * This {@link ArrayAdapter} is used to display the songs for a particular
- * artist, album, playlist, or genre for
- * {@link com.cyngn.eleven.ui.fragments.PlaylistDetailFragment},{@link LastAddedFragment}.
+ * This {@link ArrayAdapter} is used to display the songs for a particular playlist
+ * {@link com.cyngn.eleven.ui.fragments.PlaylistDetailFragment}
*
* @author Andrew Neal (andrewdneal@gmail.com)
*/
-public class ProfileSongAdapter extends ArrayAdapter<Song> implements IPopupMenuCallback {
-
- /**
- * Default display setting: title/album
- */
- public static final int DISPLAY_DEFAULT_SETTING = 0;
-
- /**
- * Playlist display setting: title/artist-album
- */
- public static final int DISPLAY_PLAYLIST_SETTING = 1;
-
- /**
- * Album display setting: title/duration
- */
- public static final int DISPLAY_ALBUM_SETTING = 2;
-
- /**
- * The header view
- */
- private static final int ITEM_VIEW_TYPE_HEADER = 0;
-
- /**
- * * The data in the list.
- */
- private static final int ITEM_VIEW_TYPE_MUSIC = 1;
-
- /**
- * Number of views (List Items, header)
- */
- private static final int VIEW_TYPE_COUNT = 2;
-
+public class ProfileSongAdapter extends SongAdapter {
/**
- * LayoutInflater
+ * Instead of having random +1 and -1 sprinkled around, this variable will show what is really
+ * related to the header
*/
- private final LayoutInflater mInflater;
+ public static final int NUM_HEADERS = 1;
/**
- * Fake header Id
+ * Fake header layout Id
*/
private final int mHeaderId;
/**
- * The resource Id of the layout to inflate
- */
- private final int mLayoutId;
-
- /**
- * Image cache and image fetcher
- */
- private final ImageFetcher mImageFetcher;
-
- /**
- * Display setting for the second line in a song fragment
- */
- private final int mDisplaySetting;
-
- /**
- * Used to set the size of the data in the adapter
- */
- 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
* @param layoutId The resource Id of the view to inflate.
- * @param setting defines the content of the second line
*/
- public ProfileSongAdapter(final Activity activity, final int layoutId, final int headerId, final int setting) {
- super(activity, 0);
- // Used to create the custom layout
- mInflater = LayoutInflater.from(activity);
+ public ProfileSongAdapter(final long playlistId, final Activity activity, final int layoutId,
+ final int headerId) {
+ super(activity, layoutId, playlistId, Config.IdType.Playlist);
// Cache the header
mHeaderId = headerId;
- // Get the layout Id
- mLayoutId = layoutId;
- // Know what to put in line two
- mDisplaySetting = setting;
- // Initialize the cache & image fetcher
- mImageFetcher = ApolloUtils.getImageFetcher(activity);
}
/**
@@ -139,92 +68,24 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> implements IPopupMenu
return convertView;
}
- // Recycle MusicHolder's items
- MusicHolder holder;
- if (convertView == null) {
- 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();
- }
-
- // 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
- switch (mDisplaySetting) {
- // show duration if on album fragment
- case DISPLAY_ALBUM_SETTING:
- holder.mLineOneRight.get().setVisibility(View.GONE);
-
- holder.mLineTwo.get().setText(
- MusicUtils.makeShortTimeString(getContext(), song.mDuration));
- break;
- case DISPLAY_PLAYLIST_SETTING:
- if (song.mDuration == -1) {
- holder.mLineOneRight.get().setVisibility(View.GONE);
- } else {
- holder.mLineOneRight.get().setVisibility(View.VISIBLE);
- holder.mLineOneRight.get().setText(
- MusicUtils.makeShortTimeString(getContext(), song.mDuration));
- }
-
- holder.mLineTwo.get().setText(MusicUtils.makeCombinedString(getContext(),
- song.mArtistName, song.mAlbumName));
-
- // Asynchronously load the album image
- if (song.mAlbumId >= 0) {
- mImageFetcher.loadAlbumImage(song.mArtistName, song.mAlbumName, song.mAlbumId,
- holder.mImage.get());
- }
- break;
- case DISPLAY_DEFAULT_SETTING:
- default:
- holder.mLineOneRight.get().setVisibility(View.VISIBLE);
-
- holder.mLineOneRight.get().setText(
- MusicUtils.makeShortTimeString(getContext(), song.mDuration));
- holder.mLineTwo.get().setText(song.mAlbumName);
- break;
- }
- return convertView;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean hasStableIds() {
- return true;
+ return super.getView(position, convertView, parent);
}
/**
* {@inheritDoc}
*/
- @Override
- public int getCount() {
- final int size = mCount.size();
- return size == 0 ? 0 : size + 1;
+ protected boolean showNowPlayingIndicator(final Song song, final int position) {
+ return super.showNowPlayingIndicator(song, position)
+ && mCurrentlyPlayingTrack.mSourcePosition == position - NUM_HEADERS;
}
- /**
- * {@inheritDoc}
- */
@Override
- public long getItemId(final int position) {
+ public boolean isEnabled(int position) {
if (position == 0) {
- return -1;
+ return false;
}
- return position - 1;
+
+ return super.isEnabled(position);
}
/**
@@ -232,7 +93,7 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> implements IPopupMenu
*/
@Override
public int getViewTypeCount() {
- return VIEW_TYPE_COUNT;
+ return super.getViewTypeCount() + NUM_HEADERS;
}
/**
@@ -241,55 +102,34 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> implements IPopupMenu
@Override
public int getItemViewType(final int position) {
if (position == 0) {
- return ITEM_VIEW_TYPE_HEADER;
- }
- return ITEM_VIEW_TYPE_MUSIC;
- }
-
- /**
- * @param pause True to temporarily pause the disk cache, false otherwise.
- */
- public void setPauseDiskCache(final boolean pause) {
- if (mImageFetcher != null) {
- mImageFetcher.setPauseDiskCache(pause);
- }
- }
-
- /**
- * @param artist The key used to find the cached artist to remove
- */
- public void removeFromCache(final Artist artist) {
- if (mImageFetcher != null) {
- mImageFetcher.removeFromCache(artist.mArtistName);
+ // since our view type count adds 1 to the super class, we can return viewtypecount - 1
+ return getViewTypeCount() - 1;
}
+ return super.getItemViewType(position);
}
- /**
- * Method that unloads and clears the items in the adapter
- */
- public void unload() {
- clear();
+ @Override
+ public void addAll(Collection<? extends Song> collection) {
+ // insert a header if one is needed
+ insertHeader();
+ super.addAll(collection);
}
- /**
- * @param data The {@link List} used to return the count for the adapter.
- */
- public void setCount(final List<Song> data) {
- mCount = data;
+ @Override
+ public void addAll(Song... items) {
+ // insert a header if one is needed
+ insertHeader();
+ super.addAll(items);
}
/**
- * Since we inject headers with this class, to actually determine if it is empty
- * we need to look at the underlying data
- * @return true if underlying data is empty
+ * Make sure we insert our header when we add items
*/
- @Override
- public boolean isEmpty() {
- return (mCount == null || mCount.size() == 0);
- }
-
- @Override
- public void setPopupMenuClickedListener(IPopupMenuCallback.IListener listener) {
- mListener = listener;
+ private void insertHeader() {
+ if (getCount() == 0) {
+ // add a dummy entry to the underlying adapter. This is needed otherwise the
+ // underlying adapter could crash because getCount() doesn't match up
+ add(new Song(-1, null, null, null, -1, -1, -1));
+ }
}
}
diff --git a/src/com/cyngn/eleven/adapters/SongAdapter.java b/src/com/cyngn/eleven/adapters/SongAdapter.java
index ebc34e3..0293656 100644
--- a/src/com/cyngn/eleven/adapters/SongAdapter.java
+++ b/src/com/cyngn/eleven/adapters/SongAdapter.java
@@ -18,10 +18,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.cache.ImageFetcher;
import com.cyngn.eleven.model.Artist;
import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.sectionadapter.SectionAdapter;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.cyngn.eleven.ui.MusicHolder;
import com.cyngn.eleven.ui.MusicHolder.DataHolder;
import com.cyngn.eleven.ui.fragments.QueueFragment;
@@ -74,17 +76,34 @@ public class SongAdapter extends ArrayAdapter<Song>
private IPopupMenuCallback.IListener mListener;
/**
+ * Current music track
+ */
+ protected MusicPlaybackTrack mCurrentlyPlayingTrack;
+
+ /**
+ * Source id and type
+ */
+ protected long mSourceId;
+ protected Config.IdType mSourceType;
+
+ /**
* Constructor of <code>SongAdapter</code>
*
* @param context The {@link Context} to use.
* @param layoutId The resource Id of the view to inflate.
+ * @param sourceId The source id that the adapter is created from
+ * @param sourceType The source type that the adapter is created from
*/
- public SongAdapter(final Activity context, final int layoutId) {
+ public SongAdapter(final Activity context, final int layoutId, final long sourceId,
+ final Config.IdType sourceType) {
super(context, 0);
// Get the layout Id
mLayoutId = layoutId;
// Initialize the cache & image fetcher
mImageFetcher = ApolloUtils.getImageFetcher(context);
+ // set the source id and type
+ mSourceId = sourceId;
+ mSourceType = sourceType;
}
/**
@@ -138,10 +157,36 @@ public class SongAdapter extends ArrayAdapter<Song>
}
}
+ View nowPlayingIndicator = holder.mNowPlayingIndicator.get();
+ if (nowPlayingIndicator != null) {
+ if (showNowPlayingIndicator(item, position)) {
+ nowPlayingIndicator.setVisibility(View.VISIBLE);
+ } else {
+ nowPlayingIndicator.setVisibility(View.GONE);
+ }
+ }
+
return convertView;
}
/**
+ * Determines whether the song at the position should show the currently playing indicator
+ * @param song the song in question
+ * @param position the position of the song
+ * @return true if we want to show the indicator
+ */
+ protected boolean showNowPlayingIndicator(final Song song, final int position) {
+ if (mCurrentlyPlayingTrack != null
+ && mCurrentlyPlayingTrack.mSourceId == mSourceId
+ && mCurrentlyPlayingTrack.mSourceType == mSourceType
+ && mCurrentlyPlayingTrack.mId == song.mSongId) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -168,6 +213,11 @@ public class SongAdapter extends ArrayAdapter<Song>
// Build the song
final Song song = getItem(i);
+ // skip special placeholders
+ if (song.mSongId == -1) {
+ continue;
+ }
+
// Build the data holder
mData[i] = new DataHolder();
// Song Id
@@ -243,4 +293,20 @@ public class SongAdapter extends ArrayAdapter<Song>
public void setPopupMenuClickedListener(IListener listener) {
mListener = listener;
}
+
+ /**
+ * Sets the currently playing track for the adapter to know when to show indicators
+ * @param currentTrack the currently playing track
+ * @return true if the current track is different
+ */
+ public boolean setCurrentlyPlayingTrack(MusicPlaybackTrack currentTrack) {
+ if (mCurrentlyPlayingTrack == null || !mCurrentlyPlayingTrack.equals(currentTrack)) {
+ mCurrentlyPlayingTrack = currentTrack;
+
+ notifyDataSetChanged();
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/src/com/cyngn/eleven/loaders/LastAddedLoader.java b/src/com/cyngn/eleven/loaders/LastAddedLoader.java
index 6f95490..ae62c0a 100644
--- a/src/com/cyngn/eleven/loaders/LastAddedLoader.java
+++ b/src/com/cyngn/eleven/loaders/LastAddedLoader.java
@@ -31,7 +31,6 @@ import java.util.List;
* @author Andrew Neal (andrewdneal@gmail.com)
*/
public class LastAddedLoader extends SectionCreator.SimpleListLoader<Song> {
-
/**
* The result
*/
diff --git a/src/com/cyngn/eleven/model/SearchResult.java b/src/com/cyngn/eleven/model/SearchResult.java
index b86ff07..f5736b1 100644
--- a/src/com/cyngn/eleven/model/SearchResult.java
+++ b/src/com/cyngn/eleven/model/SearchResult.java
@@ -7,6 +7,7 @@ import android.database.Cursor;
import android.provider.MediaStore;
import android.util.Log;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.utils.MusicUtils;
import java.util.Comparator;
@@ -60,6 +61,20 @@ public class SearchResult {
return Unknown;
}
}
+
+ public Config.IdType getSourceType() {
+ switch (this) {
+ case Artist:
+ return Config.IdType.Artist;
+ case Album:
+ return Config.IdType.Album;
+ case Playlist:
+ return Config.IdType.Playlist;
+ case Song:
+ default:
+ return Config.IdType.NA;
+ }
+ }
};
public ResultType mType;
diff --git a/src/com/cyngn/eleven/provider/MusicDB.java b/src/com/cyngn/eleven/provider/MusicDB.java
index 794b661..c8b7c23 100644
--- a/src/com/cyngn/eleven/provider/MusicDB.java
+++ b/src/com/cyngn/eleven/provider/MusicDB.java
@@ -6,10 +6,22 @@ package com.cyngn.eleven.provider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
public class MusicDB extends SQLiteOpenHelper {
+ /**
+ * Version History
+ * v1 Sept 22 2014 Initial Merge of tables
+ * Has PlaylistArtworkstore, RecentStore, SearchHistory, SongPlayCount
+ * v2 Oct 7 2014 Added a new class MusicPlaybackState - need to bump version so the new
+ * tables are created, but need to remove all drops from other classes to
+ * maintain data
+ *
+ */
+
+
/* Version constant to increment when the database should be rebuilt */
- private static final int VERSION = 1;
+ private static final int VERSION = 2;
/* Name of database file */
public static final String DATABASENAME = "musicdb.db";
@@ -42,6 +54,7 @@ public class MusicDB extends SQLiteOpenHelper {
RecentStore.getInstance(mContext).onCreate(db);
SongPlayCount.getInstance(mContext).onCreate(db);
SearchHistory.getInstance(mContext).onCreate(db);
+ MusicPlaybackState.getInstance(mContext).onCreate(db);
}
@Override
@@ -50,5 +63,17 @@ public class MusicDB extends SQLiteOpenHelper {
RecentStore.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
SongPlayCount.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
SearchHistory.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
+ MusicPlaybackState.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(MusicDB.class.getSimpleName(),
+ "Downgrading from: " + oldVersion + " to " + newVersion + ". Dropping tables");
+ PlaylistArtworkStore.getInstance(mContext).onDowngrade(db, oldVersion, newVersion);
+ RecentStore.getInstance(mContext).onDowngrade(db, oldVersion, newVersion);
+ SongPlayCount.getInstance(mContext).onDowngrade(db, oldVersion, newVersion);
+ SearchHistory.getInstance(mContext).onDowngrade(db, oldVersion, newVersion);
+ MusicPlaybackState.getInstance(mContext).onDowngrade(db, oldVersion, newVersion);
}
}
diff --git a/src/com/cyngn/eleven/provider/MusicPlaybackState.java b/src/com/cyngn/eleven/provider/MusicPlaybackState.java
new file mode 100644
index 0000000..dfead29
--- /dev/null
+++ b/src/com/cyngn/eleven/provider/MusicPlaybackState.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+package com.cyngn.eleven.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.cyngn.eleven.Config;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
+import com.cyngn.eleven.utils.Lists;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * This keeps track of the music playback and history state of the playback service
+ */
+public class MusicPlaybackState {
+ private static MusicPlaybackState sInstance = null;
+
+ private MusicDB mMusicDatabase = null;
+
+ /**
+ * Constructor of <code>MusicPlaybackState</code>
+ *
+ * @param context The {@link android.content.Context} to use
+ */
+ public MusicPlaybackState(final Context context) {
+ mMusicDatabase = MusicDB.getInstance(context);
+ }
+
+ public void onCreate(final SQLiteDatabase db) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CREATE TABLE IF NOT EXISTS ");
+ builder.append(PlaybackQueueColumns.NAME);
+ builder.append("(");
+
+ builder.append(PlaybackQueueColumns.TRACK_ID);
+ builder.append(" LONG NOT NULL,");
+
+ builder.append(PlaybackQueueColumns.SOURCE_ID);
+ builder.append(" LONG NOT NULL,");
+
+ builder.append(PlaybackQueueColumns.SOURCE_TYPE);
+ builder.append(" INT NOT NULL,");
+
+ builder.append(PlaybackQueueColumns.SOURCE_POSITION);
+ builder.append(" INT NOT NULL);");
+
+ db.execSQL(builder.toString());
+
+ builder = new StringBuilder();
+ builder.append("CREATE TABLE IF NOT EXISTS ");
+ builder.append(PlaybackHistoryColumns.NAME);
+ builder.append("(");
+
+ builder.append(PlaybackHistoryColumns.POSITION);
+ builder.append(" INT NOT NULL);");
+
+ db.execSQL(builder.toString());
+ }
+
+ public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
+ // this table was created in version 2 so call the onCreate method if we hit that scenario
+ if (oldVersion == 1 && newVersion > 1) {
+ onCreate(db);
+ }
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // If we ever have downgrade, drop the table to be safe
+ db.execSQL("DROP TABLE IF EXISTS " + PlaybackQueueColumns.NAME);
+ db.execSQL("DROP TABLE IF EXISTS " + PlaybackHistoryColumns.NAME);
+ onCreate(db);
+ }
+
+ /**
+ * @param context The {@link android.content.Context} to use
+ * @return A new instance of this class.
+ */
+ public static final synchronized MusicPlaybackState getInstance(final Context context) {
+ if (sInstance == null) {
+ sInstance = new MusicPlaybackState(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ /**
+ * Clears the existing database and saves the queue and history into the db so that when the
+ * app is restarted, the tracks you were listening to is restored
+ * @param queue the queue to save
+ * @param history the history to save
+ */
+ public synchronized void saveState(final ArrayList<MusicPlaybackTrack> queue,
+ LinkedList<Integer> history) {
+ final SQLiteDatabase database = mMusicDatabase.getWritableDatabase();
+ database.beginTransaction();
+
+ try {
+ database.delete(PlaybackQueueColumns.NAME, null, null);
+ database.delete(PlaybackHistoryColumns.NAME, null, null);
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ }
+
+ final int NUM_PROCESS = 20;
+ int position = 0;
+ while (position < queue.size()) {
+ database.beginTransaction();
+ try {
+ for (int i = position; i < queue.size() && i < position + NUM_PROCESS; i++) {
+ MusicPlaybackTrack track = queue.get(i);
+ ContentValues values = new ContentValues(4);
+
+ values.put(PlaybackQueueColumns.TRACK_ID, track.mId);
+ values.put(PlaybackQueueColumns.SOURCE_ID, track.mSourceId);
+ values.put(PlaybackQueueColumns.SOURCE_TYPE, track.mSourceType.mId);
+ values.put(PlaybackQueueColumns.SOURCE_POSITION, track.mSourcePosition);
+
+ database.insert(PlaybackQueueColumns.NAME, null, values);
+ }
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ position += NUM_PROCESS;
+ }
+ }
+
+ if (history != null) {
+ Iterator<Integer> iter = history.iterator();
+ while (iter.hasNext()) {
+ database.beginTransaction();
+ try {
+ for (int i = 0; iter.hasNext() && i < NUM_PROCESS; i++) {
+ ContentValues values = new ContentValues(1);
+ values.put(PlaybackHistoryColumns.POSITION, iter.next());
+
+ database.insert(PlaybackHistoryColumns.NAME, null, values);
+ }
+
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ }
+ }
+ }
+ }
+
+ public ArrayList<MusicPlaybackTrack> getQueue() {
+ ArrayList<MusicPlaybackTrack> results = Lists.newArrayList();
+
+ Cursor cursor = null;
+ try {
+ cursor = mMusicDatabase.getReadableDatabase().query(PlaybackQueueColumns.NAME, null,
+ null, null, null, null, null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ results.ensureCapacity(cursor.getCount());
+
+ do {
+ results.add(new MusicPlaybackTrack(cursor.getLong(0), cursor.getLong(1),
+ Config.IdType.getTypeById(cursor.getInt(2)), cursor.getInt(3)));
+ } while (cursor.moveToNext());
+ }
+
+ return results;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ cursor = null;
+ }
+ }
+ }
+
+ public LinkedList<Integer> getHistory(final int playlistSize) {
+ LinkedList<Integer> results = Lists.newLinkedList();
+
+ Cursor cursor = null;
+ try {
+ cursor = mMusicDatabase.getReadableDatabase().query(PlaybackHistoryColumns.NAME, null,
+ null, null, null, null, null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ int pos = cursor.getInt(0);
+ if (pos >= 0 && pos < playlistSize) {
+ results.add(pos);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ return results;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ cursor = null;
+ }
+ }
+ }
+
+ public class PlaybackQueueColumns {
+ /* Table name */
+ public static final String NAME = "playbackqueue";
+
+ /* track id */
+ public static final String TRACK_ID = "trackid";
+
+ /* the id of the source where this track is being played from (artist/album/playlist) */
+ public static final String SOURCE_ID = "sourceid";
+
+ /* the type of the source where this track is being played from (artist/album/playlist) */
+ public static final String SOURCE_TYPE = "sourcetype";
+
+ /* the position - this is used in playlists where the song appears multiple times */
+ public static final String SOURCE_POSITION = "sourceposition";
+ }
+
+ public class PlaybackHistoryColumns {
+ /* Table name */
+ public static final String NAME = "playbackhistory";
+
+ /* the position of the history item within the queue */
+ public static final String POSITION = "position";
+ }
+}
diff --git a/src/com/cyngn/eleven/provider/PlaylistArtworkStore.java b/src/com/cyngn/eleven/provider/PlaylistArtworkStore.java
index 632c967..ee8faf2 100644
--- a/src/com/cyngn/eleven/provider/PlaylistArtworkStore.java
+++ b/src/com/cyngn/eleven/provider/PlaylistArtworkStore.java
@@ -86,7 +86,11 @@ public class PlaylistArtworkStore {
}
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
- // If we ever have upgrade, this code should be changed to handle this more gracefully
+ // No upgrade path needed yet
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + PlaylistArtworkStoreColumns.NAME);
onCreate(db);
}
diff --git a/src/com/cyngn/eleven/provider/RecentStore.java b/src/com/cyngn/eleven/provider/RecentStore.java
index 860c6e3..cffbe08 100644
--- a/src/com/cyngn/eleven/provider/RecentStore.java
+++ b/src/com/cyngn/eleven/provider/RecentStore.java
@@ -18,7 +18,7 @@ import android.database.sqlite.SQLiteDatabase;
public class RecentStore {
/* Maximum # of items in the db */
- private static final int MAX_ITEMS_IN_DB = 500;
+ private static final int MAX_ITEMS_IN_DB = 100;
private static RecentStore sInstance = null;
@@ -40,6 +40,11 @@ public class RecentStore {
}
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
+ // No upgrade path needed yet
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + RecentStoreColumns.NAME);
onCreate(db);
}
diff --git a/src/com/cyngn/eleven/provider/SearchHistory.java b/src/com/cyngn/eleven/provider/SearchHistory.java
index 3f02c65..8f86f8d 100644
--- a/src/com/cyngn/eleven/provider/SearchHistory.java
+++ b/src/com/cyngn/eleven/provider/SearchHistory.java
@@ -29,6 +29,11 @@ public class SearchHistory {
}
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
+ // No upgrade path needed yet
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + SearchHistoryColumns.NAME);
onCreate(db);
}
diff --git a/src/com/cyngn/eleven/provider/SongPlayCount.java b/src/com/cyngn/eleven/provider/SongPlayCount.java
index 77ab5de..3e6e7d5 100644
--- a/src/com/cyngn/eleven/provider/SongPlayCount.java
+++ b/src/com/cyngn/eleven/provider/SongPlayCount.java
@@ -85,7 +85,11 @@ public class SongPlayCount {
}
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
- // If we ever have upgrade, this code should be changed to handle this more gracefully
+ // No upgrade path needed yet
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + SongPlayCountColumns.NAME);
onCreate(db);
}
diff --git a/src/com/cyngn/eleven/service/MusicPlaybackTrack.aidl b/src/com/cyngn/eleven/service/MusicPlaybackTrack.aidl
new file mode 100644
index 0000000..682a0df
--- /dev/null
+++ b/src/com/cyngn/eleven/service/MusicPlaybackTrack.aidl
@@ -0,0 +1,3 @@
+package com.cyngn.eleven.service;
+
+parcelable MusicPlaybackTrack; \ No newline at end of file
diff --git a/src/com/cyngn/eleven/service/MusicPlaybackTrack.java b/src/com/cyngn/eleven/service/MusicPlaybackTrack.java
new file mode 100644
index 0000000..71e5795
--- /dev/null
+++ b/src/com/cyngn/eleven/service/MusicPlaybackTrack.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+package com.cyngn.eleven.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.cyngn.eleven.Config;
+
+/**
+ * This is used by the music playback service to track the music tracks it is playing
+ * It has extra meta data to determine where the track came from so that we can show the appropriate
+ * song playing indicator
+ */
+public class MusicPlaybackTrack implements Parcelable {
+ /**
+ * The track id
+ */
+ public long mId;
+
+ /**
+ * Where was this track added from? Artist id/Album id/Playlist id
+ */
+ public long mSourceId;
+
+ /**
+ * Where was this track added from? Artist/Album/Playlist
+ */
+ public Config.IdType mSourceType;
+
+ /**
+ * This is only used for playlists since it is possible that a playlist can contain the same
+ * song multiple times. So to prevent the song indicator showing up multiple times, we need
+ * to keep track of the position
+ */
+ public int mSourcePosition;
+
+ /**
+ * Parcelable creator
+ */
+ public static final Creator<MusicPlaybackTrack> CREATOR = new Creator<MusicPlaybackTrack>() {
+ @Override
+ public MusicPlaybackTrack createFromParcel(Parcel source) {
+ return new MusicPlaybackTrack(source);
+ }
+
+ @Override
+ public MusicPlaybackTrack[] newArray(int size) {
+ return new MusicPlaybackTrack[size];
+ }
+ };
+
+ public MusicPlaybackTrack(long id, long sourceId, Config.IdType type, int sourcePosition) {
+ mId = id;
+ mSourceId = sourceId;
+ mSourceType = type;
+ mSourcePosition = sourcePosition;
+ }
+
+ public MusicPlaybackTrack(Parcel in) {
+ mId = in.readLong();
+ mSourceId = in.readLong();
+ mSourceType = Config.IdType.getTypeById(in.readInt());
+ mSourcePosition = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mId);
+ dest.writeLong(mSourceId);
+ dest.writeInt(mSourceType.mId);
+ dest.writeInt(mSourcePosition);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MusicPlaybackTrack) {
+ MusicPlaybackTrack other = (MusicPlaybackTrack)o;
+ if (other != null) {
+ if (mId == other.mId
+ && mSourceId == other.mSourceId
+ && mSourceType == other.mSourceType
+ && mSourcePosition == other.mSourcePosition) {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ return super.equals(o);
+ }
+}
diff --git a/src/com/cyngn/eleven/ui/MusicHolder.java b/src/com/cyngn/eleven/ui/MusicHolder.java
index f9b4382..9c4ce78 100644
--- a/src/com/cyngn/eleven/ui/MusicHolder.java
+++ b/src/com/cyngn/eleven/ui/MusicHolder.java
@@ -77,6 +77,11 @@ public class MusicHolder {
public WeakReference<View> mPlayPauseProgressContainer;
/**
+ * The song indicator for the currently playing track
+ */
+ public WeakReference<View> mNowPlayingIndicator;
+
+ /**
* The divider for the list item
*/
public WeakReference<View> mDivider;
@@ -114,6 +119,8 @@ public class MusicHolder {
mPlayPauseProgressContainer = new WeakReference<View>(
view.findViewById(R.id.play_pause_container));
+ mNowPlayingIndicator = new WeakReference<View>(view.findViewById(R.id.now_playing));
+
// Get the divider for the list item
mDivider = new WeakReference<View>(view.findViewById(R.id.divider));
diff --git a/src/com/cyngn/eleven/ui/activities/SearchActivity.java b/src/com/cyngn/eleven/ui/activities/SearchActivity.java
index 20d558e..712d997 100644
--- a/src/com/cyngn/eleven/ui/activities/SearchActivity.java
+++ b/src/com/cyngn/eleven/ui/activities/SearchActivity.java
@@ -227,6 +227,16 @@ public class SearchActivity extends FragmentActivity implements
}
@Override
+ protected long getSourceId() {
+ return mSelectedItem.mId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return mSelectedItem.mType.getSourceType();
+ }
+
+ @Override
protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) {
super.updateMenuIds(type, set);
@@ -684,7 +694,7 @@ public class SearchActivity extends FragmentActivity implements
final long[] list = new long[]{
item.mId
};
- MusicUtils.playAll(this, list, 0, false);
+ MusicUtils.playAll(this, list, 0, -1, Config.IdType.NA, false);
break;
}
}
diff --git a/src/com/cyngn/eleven/ui/activities/ShortcutActivity.java b/src/com/cyngn/eleven/ui/activities/ShortcutActivity.java
index f6d3a9e..79d3b57 100644
--- a/src/com/cyngn/eleven/ui/activities/ShortcutActivity.java
+++ b/src/com/cyngn/eleven/ui/activities/ShortcutActivity.java
@@ -296,7 +296,7 @@ public class ShortcutActivity extends FragmentActivity implements ServiceConnect
final boolean shouldOpenAudioPlayer = mIntent.getBooleanExtra(OPEN_AUDIO_PLAYER, true);
// Play the list
if (mList != null && mList.length > 0) {
- MusicUtils.playAll(this, mList, 0, mShouldShuffle);
+ MusicUtils.playAll(this, mList, 0, -1, Config.IdType.NA, mShouldShuffle);
}
// Open the now playing screen
diff --git a/src/com/cyngn/eleven/ui/fragments/AlbumDetailFragment.java b/src/com/cyngn/eleven/ui/fragments/AlbumDetailFragment.java
index 40de324..916aea5 100644
--- a/src/com/cyngn/eleven/ui/fragments/AlbumDetailFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/AlbumDetailFragment.java
@@ -16,6 +16,7 @@ import com.cyngn.eleven.model.Album;
import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.utils.AlbumPopupMenuHelper;
import com.cyngn.eleven.utils.GenreFetcher;
+import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.utils.PopupMenuHelper;
import com.cyngn.eleven.utils.SongPopupMenuHelper;
import com.cyngn.eleven.widgets.IPopupMenuCallback;
@@ -114,6 +115,16 @@ public class AlbumDetailFragment extends BaseFragment {
public Song getSong(int position) {
return mSongAdapter.getItem(position);
}
+
+ @Override
+ protected long getSourceId() {
+ return mAlbumId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Album;
+ }
};
mHeaderPopupMenuHelper = new AlbumPopupMenuHelper(getActivity(), getChildFragmentManager()) {
@@ -178,4 +189,11 @@ public class AlbumDetailFragment extends BaseFragment {
public void restartLoader() {
getLoaderManager().restartLoader(LOADER_ID, getArguments(), mSongAdapter);
}
+
+ @Override
+ public void onMetaChanged() {
+ super.onMetaChanged();
+
+ mSongAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack());
+ }
} \ No newline at end of file
diff --git a/src/com/cyngn/eleven/ui/fragments/ArtistDetailFragment.java b/src/com/cyngn/eleven/ui/fragments/ArtistDetailFragment.java
index d1012e6..6388307 100644
--- a/src/com/cyngn/eleven/ui/fragments/ArtistDetailFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/ArtistDetailFragment.java
@@ -19,6 +19,7 @@ import com.cyngn.eleven.menu.FragmentMenuItems;
import com.cyngn.eleven.model.Album;
import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.utils.AlbumPopupMenuHelper;
+import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.utils.SongPopupMenuHelper;
import com.cyngn.eleven.widgets.IPopupMenuCallback;
import com.cyngn.eleven.widgets.LoadingEmptyContainer;
@@ -50,6 +51,10 @@ public class ArtistDetailFragment extends DetailFragment {
return getArguments().getString(Config.ARTIST_NAME);
}
+ protected long getArtistId() {
+ return getArguments().getLong(Config.ID);
+ }
+
@Override
protected void onViewCreated() {
super.onViewCreated();
@@ -129,6 +134,16 @@ public class ArtistDetailFragment extends DetailFragment {
}
@Override
+ protected long getSourceId() {
+ return getArtistId();
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Artist;
+ }
+
+ @Override
protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) {
super.updateMenuIds(type, set);
@@ -165,4 +180,11 @@ public class ArtistDetailFragment extends DetailFragment {
lm.restartLoader(ALBUM_LOADER_ID, arguments, mAlbumAdapter);
lm.restartLoader(SONG_LOADER_ID, arguments, mSongAdapter);
}
+
+ @Override
+ public void onMetaChanged() {
+ super.onMetaChanged();
+
+ mSongAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack());
+ }
} \ No newline at end of file
diff --git a/src/com/cyngn/eleven/ui/fragments/ArtistFragment.java b/src/com/cyngn/eleven/ui/fragments/ArtistFragment.java
index e1507a2..465d5e1 100644
--- a/src/com/cyngn/eleven/ui/fragments/ArtistFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/ArtistFragment.java
@@ -26,6 +26,7 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.MusicStateListener;
import com.cyngn.eleven.R;
import com.cyngn.eleven.adapters.ArtistAdapter;
@@ -121,6 +122,16 @@ public class ArtistFragment extends MusicBrowserFragment implements
}
@Override
+ protected long getSourceId() {
+ return mArtist.mArtistId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Artist;
+ }
+
+ @Override
protected void onDeleteClicked() {
final String artist = mArtist.mArtistName;
DeleteDialog.newInstance(artist, getIdList(), artist).show(
diff --git a/src/com/cyngn/eleven/ui/fragments/PlaylistDetailFragment.java b/src/com/cyngn/eleven/ui/fragments/PlaylistDetailFragment.java
index 0afec03..9306563 100644
--- a/src/com/cyngn/eleven/ui/fragments/PlaylistDetailFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/PlaylistDetailFragment.java
@@ -33,7 +33,6 @@ import com.cyngn.eleven.widgets.IPopupMenuCallback;
import com.cyngn.eleven.widgets.LoadingEmptyContainer;
import com.cyngn.eleven.widgets.NoResultsContainer;
-import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@@ -104,7 +103,7 @@ public class PlaylistDetailFragment extends DetailFragment implements
return null;
}
- return mAdapter.getItem(position - 1);
+ return mAdapter.getItem(position);
}
@Override
@@ -115,6 +114,16 @@ public class PlaylistDetailFragment extends DetailFragment implements
}
@Override
+ protected long getSourceId() {
+ return mPlaylistId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Playlist;
+ }
+
+ @Override
protected void removeFromPlaylist() {
mAdapter.remove(mSong);
mAdapter.notifyDataSetChanged();
@@ -141,10 +150,10 @@ public class PlaylistDetailFragment extends DetailFragment implements
mListView.setOnScrollListener(PlaylistDetailFragment.this);
mAdapter = new ProfileSongAdapter(
+ mPlaylistId,
getActivity(),
R.layout.edit_track_list_item,
- R.layout.faux_playlist_header,
- ProfileSongAdapter.DISPLAY_PLAYLIST_SETTING
+ R.layout.faux_playlist_header
);
mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
@Override
@@ -201,8 +210,13 @@ public class PlaylistDetailFragment extends DetailFragment implements
*/
@Override
public void remove(final int which) {
- Song song = mAdapter.getItem(which - 1);
+ if (which == 0) {
+ return;
+ }
+
+ Song song = mAdapter.getItem(which);
mAdapter.remove(song);
+ mAdapter.buildCache();
mAdapter.notifyDataSetChanged();
final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", mPlaylistId);
getActivity().getContentResolver().delete(uri,
@@ -214,17 +228,18 @@ public class PlaylistDetailFragment extends DetailFragment implements
* {@inheritDoc}
*/
@Override
- public void drop(final int from, final int to) {
- if (from == 0 || to == 0) {
- mAdapter.notifyDataSetChanged();
- return;
- }
- final int realFrom = from - 1;
- final int realTo = to - 1;
- Song song = mAdapter.getItem(realFrom);
+ public void drop(int from, int to) {
+ from = Math.max(ProfileSongAdapter.NUM_HEADERS, from);
+ to = Math.max(ProfileSongAdapter.NUM_HEADERS, to);
+
+ Song song = mAdapter.getItem(from);
mAdapter.remove(song);
- mAdapter.insert(song, realTo);
+ mAdapter.insert(song, to);
+ mAdapter.buildCache();
mAdapter.notifyDataSetChanged();
+
+ final int realFrom = from - ProfileSongAdapter.NUM_HEADERS;
+ final int realTo = to - ProfileSongAdapter.NUM_HEADERS;
MediaStore.Audio.Playlists.Members.moveItem(getActivity().getContentResolver(),
mPlaylistId, realFrom, realTo);
}
@@ -241,7 +256,8 @@ public class PlaylistDetailFragment extends DetailFragment implements
Cursor cursor = PlaylistSongLoader.makePlaylistSongCursor(getActivity(),
mPlaylistId);
final long[] list = MusicUtils.getSongListForCursor(cursor);
- MusicUtils.playAll(getActivity(), list, position - 1, false);
+ MusicUtils.playAll(getActivity(), list, position - ProfileSongAdapter.NUM_HEADERS,
+ mPlaylistId, Config.IdType.Playlist, false);
cursor.close();
cursor = null;
}
@@ -278,8 +294,6 @@ public class PlaylistDetailFragment extends DetailFragment implements
// hide the header container
mHeaderContainer.setVisibility(View.INVISIBLE);
- // Return the correct count
- mAdapter.setCount(new ArrayList<Song>());
// Start fresh
mAdapter.unload();
} else {
@@ -289,7 +303,9 @@ public class PlaylistDetailFragment extends DetailFragment implements
// Start fresh
mAdapter.unload();
// Return the correct count
- mAdapter.setCount(data);
+ mAdapter.addAll(data);
+ // build the cache
+ mAdapter.buildCache();
// set the number of songs
String numberOfSongs = MusicUtils.makeLabel(getActivity(), R.plurals.Nsongs,
data.size());
@@ -299,7 +315,6 @@ public class PlaylistDetailFragment extends DetailFragment implements
// Add the data to the adapter
for (final Song song : data) {
- mAdapter.add(song);
duration += song.mDuration;
}
@@ -322,4 +337,18 @@ public class PlaylistDetailFragment extends DetailFragment implements
getLoaderManager().restartLoader(0, getArguments(), this);
}
+
+ @Override
+ public void onMetaChanged() {
+ super.onMetaChanged();
+
+ mAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack());
+ }
+
+ @Override
+ public void onPlaylistChanged() {
+ super.onPlaylistChanged();
+
+ restartLoader();
+ }
} \ No newline at end of file
diff --git a/src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java b/src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java
index 81bf19a..0041d6d 100644
--- a/src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/PlaylistFragment.java
@@ -28,6 +28,7 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.Config.SmartPlaylistType;
import com.cyngn.eleven.MusicStateListener;
import com.cyngn.eleven.R;
@@ -119,6 +120,16 @@ public class PlaylistFragment extends MusicBrowserFragment implements
}
@Override
+ protected long getSourceId() {
+ return mPlaylist.mPlaylistId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Playlist;
+ }
+
+ @Override
protected void onDeleteClicked() {
buildDeleteDialog(getId(), mPlaylist.mPlaylistName).show();
}
diff --git a/src/com/cyngn/eleven/ui/fragments/QueueFragment.java b/src/com/cyngn/eleven/ui/fragments/QueueFragment.java
index a08ff6e..2acc275 100644
--- a/src/com/cyngn/eleven/ui/fragments/QueueFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/QueueFragment.java
@@ -29,8 +29,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ProgressBar;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.MusicPlaybackService;
import com.cyngn.eleven.R;
import com.cyngn.eleven.adapters.SongAdapter;
@@ -43,6 +43,7 @@ import com.cyngn.eleven.loaders.QueueLoader;
import com.cyngn.eleven.menu.DeleteDialog;
import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.recycler.RecycleHolder;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.cyngn.eleven.ui.activities.SlidingPanelActivity;
import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.utils.PopupMenuHelper;
@@ -117,11 +118,13 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
private Song mSong;
private int mSelectedPosition;
+ private MusicPlaybackTrack mSelectedTrack;
@Override
protected PopupMenuType onPreparePopupMenu(int position) {
mSelectedPosition = position;
mSong = mAdapter.getItem(mSelectedPosition);
+ mSelectedTrack = MusicUtils.getTrack(mSelectedPosition);
return PopupMenuType.Queue;
}
@@ -132,6 +135,24 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
}
@Override
+ protected long getSourceId() {
+ if (mSelectedTrack == null) {
+ return -1;
+ }
+
+ return mSelectedTrack.mSourceId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ if (mSelectedTrack == null) {
+ return Config.IdType.NA;
+ }
+
+ return mSelectedTrack.mSourceType;
+ }
+
+ @Override
protected String getArtistName() {
return mSong.mArtistName;
}
@@ -149,7 +170,7 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
queue.removeItem(mSelectedPosition);
queue.close();
queue = null;
- MusicUtils.playNext(getIdList());
+ MusicUtils.playNext(getIdList(), getSourceId(), getSourceType());
refreshQueue();
}
@@ -161,7 +182,8 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song
};
// Create the adapter
- mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item);
+ mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item,
+ -1, Config.IdType.NA);
mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
@Override
public void onPopupMenuClicked(View v, int position) {
diff --git a/src/com/cyngn/eleven/ui/fragments/RecentFragment.java b/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
index a8b01f8..fc7f9b1 100644
--- a/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
@@ -11,11 +11,13 @@
package com.cyngn.eleven.ui.fragments;
+import android.app.Activity;
import android.os.Bundle;
import android.support.v4.content.Loader;
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.menu.FragmentMenuItems;
import com.cyngn.eleven.model.Song;
@@ -64,6 +66,8 @@ public class RecentFragment extends BasicSongFragment implements ISetupActionBar
*/
@Override
public void onMetaChanged() {
+ super.onMetaChanged();
+
// refresh the list since a track playing means it should be recently played
restartLoader();
}
@@ -91,5 +95,36 @@ public class RecentFragment extends BasicSongFragment implements ISetupActionBar
public void setupActionBar() {
((BaseActivity)getActivity()).setupActionBar(R.string.playlist_recently_played);
}
+
+ @Override
+ protected long getFragmentSourceId() {
+ return Config.SmartPlaylistType.RecentlyPlayed.mId;
+ }
+
+ @Override
+ protected Config.IdType getFragmentSourceType() {
+ return Config.IdType.Playlist;
+ }
+
+ @Override
+ protected SongAdapter createAdapter() {
+ return new RecentAdapter(
+ getActivity(),
+ R.layout.list_item_normal,
+ getFragmentSourceId(),
+ getFragmentSourceType()
+ );
+ }
+
+ private class RecentAdapter extends SongAdapter {
+ public RecentAdapter(Activity context, int layoutId, long sourceId, Config.IdType sourceType) {
+ super(context, layoutId, sourceId, sourceType);
+ }
+
+ @Override
+ protected boolean showNowPlayingIndicator(Song song, int position) {
+ return position == 0 && super.showNowPlayingIndicator(song, position);
+ }
+ }
}
diff --git a/src/com/cyngn/eleven/ui/fragments/SongFragment.java b/src/com/cyngn/eleven/ui/fragments/SongFragment.java
index 0024314..4149716 100644
--- a/src/com/cyngn/eleven/ui/fragments/SongFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/SongFragment.java
@@ -17,6 +17,7 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.adapters.PagerAdapter;
import com.cyngn.eleven.loaders.SongLoader;
import com.cyngn.eleven.model.Song;
@@ -41,7 +42,7 @@ public class SongFragment extends BasicSongFragment {
int internalPosition = mAdapter.getInternalPosition(position);
Cursor cursor = SongLoader.makeSongCursor(getActivity(), null);
final long[] list = MusicUtils.getSongListForCursor(cursor);
- MusicUtils.playAll(getActivity(), list, internalPosition, false);
+ MusicUtils.playAll(getActivity(), list, internalPosition, -1, Config.IdType.NA, false);
cursor.close();
cursor = null;
}
diff --git a/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
index 1b8b781..0541127 100644
--- a/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
@@ -11,7 +11,6 @@
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;
@@ -26,6 +25,7 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.MusicStateListener;
import com.cyngn.eleven.R;
import com.cyngn.eleven.adapters.SongAdapter;
@@ -33,7 +33,9 @@ import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.recycler.RecycleHolder;
import com.cyngn.eleven.sectionadapter.SectionAdapter;
import com.cyngn.eleven.sectionadapter.SectionListContainer;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.cyngn.eleven.ui.activities.BaseActivity;
+import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.utils.PopupMenuHelper;
import com.cyngn.eleven.utils.SongPopupMenuHelper;
import com.cyngn.eleven.widgets.IPopupMenuCallback;
@@ -94,6 +96,16 @@ public abstract class BasicSongFragment extends Fragment implements
}
@Override
+ protected long getSourceId() {
+ return getFragmentSourceId();
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return getFragmentSourceType();
+ }
+
+ @Override
protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) {
super.updateMenuIds(type, set);
BasicSongFragment.this.updateMenuIds(set);
@@ -101,7 +113,7 @@ public abstract class BasicSongFragment extends Fragment implements
};
// Create the adapter
- mAdapter = createAdapter();
+ mAdapter = new SectionAdapter(getActivity(), createAdapter());
mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
@Override
public void onPopupMenuClicked(View v, int position) {
@@ -110,6 +122,14 @@ public abstract class BasicSongFragment extends Fragment implements
});
}
+ protected long getFragmentSourceId() {
+ return -1;
+ }
+
+ protected Config.IdType getFragmentSourceType() {
+ return Config.IdType.NA;
+ }
+
protected void updateMenuIds(TreeSet<Integer> set) {
// do nothing - let subclasses override
}
@@ -245,12 +265,12 @@ public abstract class BasicSongFragment extends Fragment implements
* If the subclasses want to use a customized SongAdapter they can override this method
* @return the Song adapter
*/
- protected SectionAdapter<Song, SongAdapter> createAdapter() {
- return new SectionAdapter(getActivity(),
- new SongAdapter(
- getActivity(),
- R.layout.list_item_normal
- )
+ protected SongAdapter createAdapter() {
+ return new SongAdapter(
+ getActivity(),
+ R.layout.list_item_normal,
+ getFragmentSourceId(),
+ getFragmentSourceType()
);
}
@@ -264,7 +284,10 @@ public abstract class BasicSongFragment extends Fragment implements
@Override
public void onMetaChanged() {
- // do nothing
+ MusicPlaybackTrack currentTrack = MusicUtils.getCurrentTrack();
+ if (mAdapter.getUnderlyingAdapter().setCurrentlyPlayingTrack(currentTrack)) {
+ mAdapter.notifyDataSetChanged();
+ }
}
@Override
diff --git a/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java
index 92493ad..336c3c8 100644
--- a/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java
@@ -74,4 +74,14 @@ public class LastAddedFragment extends BasicSongFragment implements ISetupAction
public void setupActionBar() {
((BaseActivity)getActivity()).setupActionBar(R.string.playlist_last_added);
}
+
+ @Override
+ protected long getFragmentSourceId() {
+ return Config.SmartPlaylistType.LastAdded.mId;
+ }
+
+ @Override
+ protected Config.IdType getFragmentSourceType() {
+ return Config.IdType.Playlist;
+ }
}
diff --git a/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
index 7fbf413..b728596 100644
--- a/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
@@ -58,12 +58,10 @@ public class TopTracksFragment extends BasicSongFragment implements ISetupAction
}
@Override
- protected SectionAdapter<Song, SongAdapter> createAdapter() {
- return new SectionAdapter(getActivity(),
- new TopTracksAdapter(
- getActivity(),
- R.layout.list_item_top_tracks
- )
+ protected SongAdapter createAdapter() {
+ return new TopTracksAdapter(
+ getActivity(),
+ R.layout.list_item_top_tracks
);
}
@@ -85,7 +83,7 @@ public class TopTracksFragment extends BasicSongFragment implements ISetupAction
public class TopTracksAdapter extends SongAdapter {
public TopTracksAdapter (final Activity context, final int layoutId) {
- super(context, layoutId);
+ super(context, layoutId, getFragmentSourceId(), getFragmentSourceType());
}
@Override
@@ -104,4 +102,14 @@ public class TopTracksFragment extends BasicSongFragment implements ISetupAction
empty.setMainText(R.string.empty_top_tracks_main);
empty.setSecondaryText(R.string.empty_top_tracks_secondary);
}
+
+ @Override
+ protected long getFragmentSourceId() {
+ return Config.SmartPlaylistType.TopTracks.mId;
+ }
+
+ @Override
+ protected Config.IdType getFragmentSourceType() {
+ return Config.IdType.Playlist;
+ }
}
diff --git a/src/com/cyngn/eleven/utils/AlbumPopupMenuHelper.java b/src/com/cyngn/eleven/utils/AlbumPopupMenuHelper.java
index a4ea428..f03a0c8 100644
--- a/src/com/cyngn/eleven/utils/AlbumPopupMenuHelper.java
+++ b/src/com/cyngn/eleven/utils/AlbumPopupMenuHelper.java
@@ -7,6 +7,7 @@ package com.cyngn.eleven.utils;
import android.app.Activity;
import android.support.v4.app.FragmentManager;
+import com.cyngn.eleven.Config;
import com.cyngn.eleven.cache.ImageFetcher;
import com.cyngn.eleven.menu.DeleteDialog;
import com.cyngn.eleven.model.Album;
@@ -37,6 +38,16 @@ public abstract class AlbumPopupMenuHelper extends PopupMenuHelper {
}
@Override
+ protected long getSourceId() {
+ return mAlbum.mAlbumId;
+ }
+
+ @Override
+ protected Config.IdType getSourceType() {
+ return Config.IdType.Album;
+ }
+
+ @Override
protected void onDeleteClicked() {
final String album = mAlbum.mAlbumName;
DeleteDialog.newInstance(album, getIdList(),
diff --git a/src/com/cyngn/eleven/utils/MusicUtils.java b/src/com/cyngn/eleven/utils/MusicUtils.java
index f2cc778..9fb390e 100644
--- a/src/com/cyngn/eleven/utils/MusicUtils.java
+++ b/src/com/cyngn/eleven/utils/MusicUtils.java
@@ -36,7 +36,7 @@ import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
-import com.cyngn.eleven.Config;
+import com.cyngn.eleven.Config.IdType;
import com.cyngn.eleven.Config.SmartPlaylistType;
import com.cyngn.eleven.IElevenService;
import com.cyngn.eleven.MusicPlaybackService;
@@ -50,6 +50,7 @@ import com.cyngn.eleven.menu.FragmentMenuItems;
import com.cyngn.eleven.model.AlbumArtistDetails;
import com.cyngn.eleven.provider.RecentStore;
import com.cyngn.eleven.provider.SongPlayCount;
+import com.cyngn.eleven.service.MusicPlaybackTrack;
import com.devspark.appmsg.AppMsg;
import java.io.File;
@@ -464,6 +465,32 @@ public final class MusicUtils {
}
/**
+ * @return The current Music Playback Track
+ */
+ public static final MusicPlaybackTrack getCurrentTrack() {
+ if (mService != null) {
+ try {
+ return mService.getCurrentTrack();
+ } catch (final RemoteException ignored) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return The Music Playback Track at the specified index
+ */
+ public static final MusicPlaybackTrack getTrack(int index) {
+ if (mService != null) {
+ try {
+ return mService.getTrack(index);
+ } catch (final RemoteException ignored) {
+ }
+ }
+ return null;
+ }
+
+ /**
* @return The next song Id.
*/
public static final long getNextAudioId() {
@@ -664,7 +691,7 @@ public final class MusicUtils {
public static void playArtist(final Context context, final long artistId, int position) {
final long[] artistList = getSongListForArtist(context, artistId);
if (artistList != null) {
- playAll(context, artistList, position, false);
+ playAll(context, artistList, position, artistId, IdType.Artist, false);
}
}
@@ -726,7 +753,8 @@ public final class MusicUtils {
* @param forceShuffle True to force a shuffle, false otherwise.
*/
public static void playAll(final Context context, final long[] list, int position,
- final boolean forceShuffle) {
+ final long sourceId, final IdType sourceType,
+ final boolean forceShuffle) {
if (list == null || list.length == 0 || mService == null) {
return;
}
@@ -748,7 +776,7 @@ public final class MusicUtils {
if (position < 0) {
position = 0;
}
- mService.open(list, forceShuffle ? -1 : position);
+ mService.open(list, forceShuffle ? -1 : position, sourceId, sourceType.mId);
mService.play();
} catch (final RemoteException ignored) {
}
@@ -757,12 +785,12 @@ public final class MusicUtils {
/**
* @param list The list to enqueue.
*/
- public static void playNext(final long[] list) {
+ public static void playNext(final long[] list, final long sourceId, final IdType sourceType) {
if (mService == null) {
return;
}
try {
- mService.enqueue(list, MusicPlaybackService.NEXT);
+ mService.enqueue(list, MusicPlaybackService.NEXT, sourceId, sourceType.mId);
} catch (final RemoteException ignored) {
}
}
@@ -789,7 +817,7 @@ public final class MusicUtils {
return;
}
}
- mService.open(mTrackList, -1);
+ mService.open(mTrackList, -1, -1, IdType.NA.mId);
mService.play();
cursor.close();
cursor = null;
@@ -887,7 +915,7 @@ public final class MusicUtils {
public static void playAlbum(final Context context, final long albumId, int position) {
final long[] albumList = getSongListForAlbum(context, albumId);
if (albumList != null) {
- playAll(context, albumList, position, false);
+ playAll(context, albumList, position, albumId, IdType.Album, false);
}
}
@@ -1000,12 +1028,13 @@ public final class MusicUtils {
* @param context The {@link Context} to use.
* @param list The list to enqueue.
*/
- public static void addToQueue(final Context context, final long[] list) {
+ public static void addToQueue(final Context context, final long[] list, long sourceId,
+ IdType sourceType) {
if (mService == null) {
return;
}
try {
- mService.enqueue(list, MusicPlaybackService.LAST);
+ mService.enqueue(list, MusicPlaybackService.LAST, sourceId, sourceType.mId);
final String message = makeLabel(context, R.plurals.NNNtrackstoqueue, list.length);
AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show();
} catch (final RemoteException ignored) {
@@ -1214,7 +1243,7 @@ public final class MusicUtils {
public static void playPlaylist(final Context context, final long playlistId) {
final long[] playlistList = getSongListForPlaylist(context, playlistId);
if (playlistList != null) {
- playAll(context, playlistList, -1, false);
+ playAll(context, playlistList, -1, playlistId, IdType.Playlist, false);
}
}
@@ -1256,7 +1285,7 @@ public final class MusicUtils {
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);
+ MusicUtils.playAll(context, list, position, type.mId, IdType.Playlist, false);
}
/**
diff --git a/src/com/cyngn/eleven/utils/NavUtils.java b/src/com/cyngn/eleven/utils/NavUtils.java
index f7fd50d..1f8fdfd 100644
--- a/src/com/cyngn/eleven/utils/NavUtils.java
+++ b/src/com/cyngn/eleven/utils/NavUtils.java
@@ -84,7 +84,7 @@ public final class NavUtils {
// Create the intent to launch the profile activity
final Intent intent = new Intent(context, HomeActivity.class);
intent.setAction(HomeActivity.ACTION_VIEW_SMART_PLAYLIST);
- intent.putExtra(Config.SMART_PLAYLIST_TYPE, type.ordinal());
+ intent.putExtra(Config.SMART_PLAYLIST_TYPE, type.mId);
context.startActivity(intent);
}
diff --git a/src/com/cyngn/eleven/utils/PopupMenuHelper.java b/src/com/cyngn/eleven/utils/PopupMenuHelper.java
index e771578..5db7bdf 100644
--- a/src/com/cyngn/eleven/utils/PopupMenuHelper.java
+++ b/src/com/cyngn/eleven/utils/PopupMenuHelper.java
@@ -12,13 +12,13 @@ import android.widget.PopupMenu;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
+import com.cyngn.eleven.Config;
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;
import java.util.TreeSet;
/**
@@ -82,6 +82,9 @@ public abstract class PopupMenuHelper implements PopupMenu.OnMenuItemClickListen
*/
protected abstract long[] getIdList();
+ protected abstract long getSourceId();
+ protected abstract Config.IdType getSourceType();
+
/**
* @return the group id to be used for pop up menu inflating
*/
@@ -133,7 +136,7 @@ public abstract class PopupMenuHelper implements PopupMenu.OnMenuItemClickListen
* Called when the user clicks "play next". Has a default implementation
*/
protected void playNext() {
- MusicUtils.playNext(getIdList());
+ MusicUtils.playNext(getIdList(), getSourceId(), getSourceType());
}
/**
@@ -299,10 +302,11 @@ public abstract class PopupMenuHelper implements PopupMenu.OnMenuItemClickListen
MusicUtils.refresh();
return true;
case FragmentMenuItems.PLAY_SELECTION:
- MusicUtils.playAll(mActivity, getIdList(), 0, false);
+ MusicUtils.playAll(mActivity, getIdList(), 0, getSourceId(), getSourceType(),
+ false);
return true;
case FragmentMenuItems.ADD_TO_QUEUE:
- MusicUtils.addToQueue(mActivity, getIdList());
+ MusicUtils.addToQueue(mActivity, getIdList(), getSourceId(), getSourceType());
return true;
case FragmentMenuItems.ADD_TO_PLAYLIST:
ContextMenuBuilder builder = new ContextMenuBuilder(mActivity);