From 5837753b4aad79f1987d68d71151f0beecd55ac1 Mon Sep 17 00:00:00 2001 From: Christopher Eby Date: Sun, 18 Sep 2011 16:52:42 -0500 Subject: [PATCH] Eliminate Song.query; always query populated songs instead --- src/org/kreed/vanilla/PlaybackService.java | 30 ++--- src/org/kreed/vanilla/Song.java | 33 +----- src/org/kreed/vanilla/SongTimeline.java | 124 ++++++++++++++++----- 3 files changed, 113 insertions(+), 74 deletions(-) diff --git a/src/org/kreed/vanilla/PlaybackService.java b/src/org/kreed/vanilla/PlaybackService.java index 7ccb7292..bf349651 100644 --- a/src/org/kreed/vanilla/PlaybackService.java +++ b/src/org/kreed/vanilla/PlaybackService.java @@ -586,30 +586,24 @@ public final class PlaybackService extends Service implements Handler.Callback, Song song = mTimeline.shiftCurrentSong(delta); mCurrentSong = song; - if (song == null) { + if (song == null || song.id == -1 || song.path == null) { if (Song.isSongAvailable()) { - // We either have a stale song id or have no songs in the - // the timeline. Clear the timeline to get rid of the possible - // stale song id. - mTimeline.clear(); - - if ((mState & FLAG_RANDOM) == 0) { - // Tell the user to pick some songs. - setFlag(FLAG_EMPTY_QUEUE); - return null; - } else { - // Add a random song to replace the stale one. - return setCurrentSong(0); + int flag = (mState & FLAG_RANDOM) == 0 ? FLAG_EMPTY_QUEUE : FLAG_ERROR; + synchronized (mStateLock) { + updateState((mState | flag) & ~FLAG_NO_MEDIA); } + return null; } else { // we don't have any songs : / - setFlag(FLAG_NO_MEDIA); + synchronized (mStateLock) { + updateState((mState | FLAG_NO_MEDIA) & ~FLAG_EMPTY_QUEUE); + } return null; } - } else if ((mState & FLAG_NO_MEDIA) != 0) { - unsetFlag(FLAG_NO_MEDIA); - } else if ((mState & FLAG_EMPTY_QUEUE) != 0) { - unsetFlag(FLAG_EMPTY_QUEUE); + } else if ((mState & (FLAG_NO_MEDIA|FLAG_EMPTY_QUEUE)) != 0) { + synchronized (mStateLock) { + updateState(mState & ~(FLAG_EMPTY_QUEUE|FLAG_NO_MEDIA)); + } } mHandler.removeMessages(PROCESS_SONG); diff --git a/src/org/kreed/vanilla/Song.java b/src/org/kreed/vanilla/Song.java index 2f9ff3ce..b2cae8ba 100644 --- a/src/org/kreed/vanilla/Song.java +++ b/src/org/kreed/vanilla/Song.java @@ -43,6 +43,10 @@ public class Song { * Indicates that this song was randomly selected from all songs. */ private static final int FLAG_RANDOM = 0x1; + /** + * The number of flags. + */ + public static final int FLAG_COUNT = 1; /** * A cache of covers that have been loaded with getCover(). @@ -319,35 +323,6 @@ public class Song { duration = cursor.getLong(7); } - /** - * Query the MediaStore, if necessary, to fill this Song's fields. - * - * @param force Query even if fields have already been populated - * @return true if fields have been populated, false otherwise - */ - public boolean query(boolean force) - { - if (path != null && !force) - return true; - if (id == -1) - return false; - - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); - Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - String selection = MediaStore.Audio.Media._ID + '=' + id; - Cursor cursor = resolver.query(media, FILLED_PROJECTION, selection, null, null); - - id = -1; - - if (cursor != null) { - if (cursor.moveToNext()) - populate(cursor); - cursor.close(); - } - - return id != -1; - } - /** * Get the id of the given song. * diff --git a/src/org/kreed/vanilla/SongTimeline.java b/src/org/kreed/vanilla/SongTimeline.java index e68d3133..3c84fdec 100644 --- a/src/org/kreed/vanilla/SongTimeline.java +++ b/src/org/kreed/vanilla/SongTimeline.java @@ -28,9 +28,14 @@ import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; import android.util.Log; /** @@ -85,13 +90,13 @@ public final class SongTimeline { * Header for state file to help indicate if the file is in the right * format. */ - private static final long STATE_FILE_MAGIC = 0x8f9d3a2fca33L; + private static final long STATE_FILE_MAGIC = 0xf89daa2fac33L; /** * All the songs currently contained in the timeline. Each Song object * should be unique, even if it refers to the same media. */ - private ArrayList mSongs = new ArrayList(); + private ArrayList mSongs; /** * The position of the current song (i.e. the playing song). */ @@ -137,6 +142,32 @@ public final class SongTimeline { */ private Callback mCallback; + /** + * Compares the ids of songs. + */ + public static class IdComparator implements Comparator { + @Override + public int compare(Song a, Song b) + { + if (a.id == b.id) + return 0; + if (a.id > b.id) + return 1; + return -1; + } + } + + /** + * Compares the flags of songs. + */ + public static class FlagComparator implements Comparator { + @Override + public int compare(Song a, Song b) + { + return a.flags - b.flags; + } + } + /** * Initializes the timeline with the state stored in the state file created * by a call to save state. @@ -149,30 +180,81 @@ public final class SongTimeline { int extra = -1; try { - DataInputStream in = new DataInputStream(context.openFileInput(STATE_FILE)); - if (in.readLong() == STATE_FILE_MAGIC) { - int n = in.readInt(); - if (n > 0) { - ArrayList songs = new ArrayList(n); + synchronized (this) { + DataInputStream in = new DataInputStream(context.openFileInput(STATE_FILE)); + if (in.readLong() == STATE_FILE_MAGIC) { + int n = in.readInt(); + if (n > 0) { + ArrayList songs = new ArrayList(n); - for (int i = 0; i != n; ++i) - songs.add(new Song(in.readLong(), in.readInt())); + // Fill the selection with the ids of all the saved songs + // and initialize the timeline with unpopulated songs. + StringBuilder selection = new StringBuilder("_ID IN ("); + for (int i = 0; i != n; ++i) { + long id = in.readLong(); + int flags = in.readInt(); + // Add the index so we can sort + flags |= i << Song.FLAG_COUNT; + songs.add(new Song(id, flags)); - mSongs = songs; - mCurrentPos = in.readInt(); + if (i != 0) + selection.append(','); + selection.append(id); + } + selection.append(')'); + + // Sort songs by id---this is the order the query will + // return its results in. + Collections.sort(songs, new IdComparator()); + + ContentResolver resolver = context.getContentResolver(); + Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + + Cursor cursor = resolver.query(media, Song.FILLED_PROJECTION, selection.toString(), null, "_id"); + if (cursor != null) { + cursor.moveToNext(); + + // Loop through timeline entries, looking for a row + // that matches the id. One row may match multiple + // entries. + Iterator it = songs.iterator(); + while (it.hasNext()) { + Song e = it.next(); + while (cursor.getLong(0) < e.id) + cursor.moveToNext(); + if (cursor.getLong(0) == e.id) + e.populate(cursor); + else + // We weren't able to query this song. + it.remove(); + } + + cursor.close(); + + // Revert to the order the songs were saved in. + Collections.sort(songs, new FlagComparator()); + + mSongs = songs; + } + } + + mCurrentPos = Math.min(mSongs == null ? 0 : mSongs.size(), in.readInt()); mFinishAction = in.readInt(); mShuffle = in.readBoolean(); extra = in.readInt(); + + in.close(); } } - - in.close(); } catch (EOFException e) { Log.w("VanillaMusic", "Failed to load state", e); } catch (IOException e) { Log.w("VanillaMusic", "Failed to load state", e); } + if (mSongs == null) + mSongs = new ArrayList(); + return extra; } @@ -200,7 +282,7 @@ public final class SongTimeline { Song song = songs.get(i); if (song == null) { out.writeLong(-1); - out.writeInt(-1); + out.writeInt(0); } else { out.writeLong(song.id); out.writeInt(song.flags); @@ -285,8 +367,7 @@ public final class SongTimeline { /** * Returns the song delta places away from the current - * position. Returns null if there is a problem retrieving the song - * (caused by either an empty library or stale song id). + * position. Returns null if there is a problem retrieving the song. * * @param delta The offset from the current position. Must be -1, 0, or 1. */ @@ -335,10 +416,6 @@ public final class SongTimeline { // we have no songs in the library return null; - if (!song.query(false)) - // we have a stale song id - return null; - return song; } @@ -568,11 +645,4 @@ public final class SongTimeline { return mFinishAction == FINISH_STOP && mCurrentPos == mSongs.size() - 1; } } - - public void clear() - { - synchronized (this) { - mSongs.clear(); - } - } }