summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlinus_lee <llee@cyngn.com>2014-11-03 17:18:35 -0800
committerlinus_lee <llee@cyngn.com>2014-12-08 15:19:48 -0800
commit1f86c5725d8ca216040199be2a1c664d1861eed8 (patch)
treed69b0b4aca44fce217ccd214b43c5ea6c60da4c4
parent8274981ad06f75f5c2ab15879083d9ee5f031554 (diff)
downloadandroid_packages_apps_Eleven-1f86c5725d8ca216040199be2a1c664d1861eed8.tar.gz
android_packages_apps_Eleven-1f86c5725d8ca216040199be2a1c664d1861eed8.tar.bz2
android_packages_apps_Eleven-1f86c5725d8ca216040199be2a1c664d1861eed8.zip
Eleven: Fix playlist reordering not working after awhile
Still trying to find the root cause (if it is Eleven or external) but this will find the errors and fix it after the fact https://cyanogen.atlassian.net/browse/MUSIC-179 Change-Id: Iaa3e27522aefc656f37ff7e727e9129c1a07f12e
-rw-r--r--src/com/cyngn/eleven/loaders/PlaylistSongLoader.java148
-rw-r--r--src/com/cyngn/eleven/utils/MusicUtils.java25
2 files changed, 160 insertions, 13 deletions
diff --git a/src/com/cyngn/eleven/loaders/PlaylistSongLoader.java b/src/com/cyngn/eleven/loaders/PlaylistSongLoader.java
index eba3c8f..13dd9be 100644
--- a/src/com/cyngn/eleven/loaders/PlaylistSongLoader.java
+++ b/src/com/cyngn/eleven/loaders/PlaylistSongLoader.java
@@ -11,10 +11,16 @@
package com.cyngn.eleven.loaders;
+import android.content.ContentProviderOperation;
import android.content.Context;
+import android.content.OperationApplicationException;
import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
+import android.provider.MediaStore.Audio.Playlists;
+import android.util.Log;
import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.utils.Lists;
@@ -25,10 +31,11 @@ import java.util.List;
/**
* Used to query {@link MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI} and
* return the songs for a particular playlist.
- *
+ *
* @author Andrew Neal (andrewdneal@gmail.com)
*/
public class PlaylistSongLoader extends WrappedAsyncTaskLoader<List<Song>> {
+ private static final String TAG = PlaylistSongLoader.class.getSimpleName();
/**
* The result
@@ -43,15 +50,15 @@ public class PlaylistSongLoader extends WrappedAsyncTaskLoader<List<Song>> {
/**
* The Id of the playlist the songs belong to.
*/
- private final Long mPlaylistID;
+ private final long mPlaylistID;
/**
* Constructor of <code>SongLoader</code>
- *
- * @param context The {@link Context} to use
- * @param playlistID The Id of the playlist the songs belong to.
+ *
+ * @param context The {@link Context} to use
+ * @param playlistId The Id of the playlist the songs belong to.
*/
- public PlaylistSongLoader(final Context context, final Long playlistId) {
+ public PlaylistSongLoader(final Context context, final long playlistId) {
super(context);
mPlaylistID = playlistId;
}
@@ -61,8 +68,55 @@ public class PlaylistSongLoader extends WrappedAsyncTaskLoader<List<Song>> {
*/
@Override
public List<Song> loadInBackground() {
+ final int playlistCount = countPlaylist(getContext(), mPlaylistID);
+
// Create the Cursor
mCursor = makePlaylistSongCursor(getContext(), mPlaylistID);
+
+ if (mCursor != null) {
+ boolean runCleanup = false;
+
+ // if the raw playlist count differs from the mapped playlist count (ie the raw mapping
+ // table vs the mapping table join the audio table) that means the playlist mapping table
+ // is messed up
+ if (mCursor.getCount() != playlistCount) {
+ Log.w(TAG, "Count Differs - raw is: " + playlistCount + " while cursor is " +
+ mCursor.getCount());
+
+ runCleanup = true;
+ }
+
+ // check if the play order is already messed up by duplicates
+ if (!runCleanup && mCursor.moveToFirst()) {
+ final int playOrderCol = mCursor.getColumnIndexOrThrow(Playlists.Members.PLAY_ORDER);
+
+ int lastPlayOrder = -1;
+ do {
+ int playOrder = mCursor.getInt(playOrderCol);
+ // if we have duplicate play orders, we need to recreate the playlist
+ if (playOrder == lastPlayOrder) {
+ runCleanup = true;
+ break;
+ }
+ lastPlayOrder = playOrder;
+ } while (mCursor.moveToNext());
+ }
+
+ if (runCleanup) {
+ Log.w(TAG, "Playlist order has flaws - recreating playlist");
+
+ // cleanup the playlist
+ cleanupPlaylist(getContext(), mPlaylistID, mCursor);
+
+ // create a new cursor
+ mCursor.close();
+ mCursor = makePlaylistSongCursor(getContext(), mPlaylistID);
+ if (mCursor != null) {
+ Log.d(TAG, "New Count is: " + mCursor.getCount());
+ }
+ }
+ }
+
// Gather the data
if (mCursor != null && mCursor.moveToFirst()) {
do {
@@ -113,6 +167,86 @@ public class PlaylistSongLoader extends WrappedAsyncTaskLoader<List<Song>> {
}
/**
+ * Cleans up the playlist based on the passed in cursor's data
+ * @param context The {@link Context} to use
+ * @param playlistId playlistId to clean up
+ * @param cursor data to repopulate the playlist with
+ */
+ private static void cleanupPlaylist(final Context context, final long playlistId,
+ final Cursor cursor) {
+ Log.w(TAG, "Cleaning up playlist: " + playlistId);
+
+ final int idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID);
+ final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
+
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+
+ // Delete all results in the playlist
+ ops.add(ContentProviderOperation.newDelete(uri).build());
+
+ // yield the db every 100 records to prevent ANRs
+ final int YIELD_FREQUENCY = 100;
+
+ // for each item, reset the play order position
+ if (cursor.moveToFirst() && cursor.getCount() > 0) {
+ do {
+ final ContentProviderOperation.Builder builder =
+ ContentProviderOperation.newInsert(uri)
+ .withValue(Playlists.Members.PLAY_ORDER, cursor.getPosition())
+ .withValue(Playlists.Members.AUDIO_ID, cursor.getLong(idCol));
+
+ // yield at the end and not at 0 by incrementing by 1
+ if ((cursor.getPosition() + 1) % YIELD_FREQUENCY == 0) {
+ builder.withYieldAllowed(true);
+ }
+ ops.add(builder.build());
+ } while (cursor.moveToNext());
+ }
+
+ try {
+ // run the batch operation
+ context.getContentResolver().applyBatch(MediaStore.AUTHORITY, ops);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException " + e + " while cleaning up playlist " + playlistId);
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "OperationApplicationException " + e + " while cleaning up playlist "
+ + playlistId);
+ }
+ }
+
+ /**
+ * Returns the playlist count for the raw playlist mapping table
+ * @param context The {@link Context} to use
+ * @param playlistId playlistId to count
+ * @return the number of tracks in the raw playlist mapping table
+ */
+ private static int countPlaylist(final Context context, final long playlistId) {
+ Cursor c = null;
+ try {
+ // when we query using only the audio_id column we will get the raw mapping table
+ // results - which will tell us if the table has rows that don't exist in the normal
+ // table
+ c = context.getContentResolver().query(
+ MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
+ new String[]{
+ MediaStore.Audio.Playlists.Members.AUDIO_ID,
+ }, null, null,
+ MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
+
+ if (c != null) {
+ return c.getCount();
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ c = null;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
* Creates the {@link Cursor} used to run the query.
*
* @param context The {@link Context} to use.
@@ -142,6 +276,8 @@ public class PlaylistSongLoader extends WrappedAsyncTaskLoader<List<Song>> {
AudioColumns.DURATION,
/* 7 */
AudioColumns.YEAR,
+ /* 8 */
+ Playlists.Members.PLAY_ORDER,
}, mSelection.toString(), null,
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
}
diff --git a/src/com/cyngn/eleven/utils/MusicUtils.java b/src/com/cyngn/eleven/utils/MusicUtils.java
index e100e87..d3c76bf 100644
--- a/src/com/cyngn/eleven/utils/MusicUtils.java
+++ b/src/com/cyngn/eleven/utils/MusicUtils.java
@@ -1086,14 +1086,25 @@ public final class MusicUtils {
final int size = ids.length;
final ContentResolver resolver = context.getContentResolver();
final String[] projection = new String[] {
- "count(*)"
+ "max(" + Playlists.Members.PLAY_ORDER + ")",
};
final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistid);
- Cursor cursor = resolver.query(uri, projection, null, null, null);
- cursor.moveToFirst();
- final int base = cursor.getInt(0);
- cursor.close();
- cursor = null;
+ Cursor cursor = null;
+ int base = 0;
+
+ try {
+ cursor = resolver.query(uri, projection, null, null, null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ base = cursor.getInt(0) + 1;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ cursor = null;
+ }
+ }
+
int numinserted = 0;
for (int offSet = 0; offSet < size; offSet += 1000) {
makeInsertItems(ids, offSet, 1000, base);
@@ -1136,7 +1147,7 @@ public final class MusicUtils {
try {
mService.enqueue(list, MusicPlaybackService.LAST, sourceId, sourceType.mId);
final String message = makeLabel(context, R.plurals.NNNtrackstoqueue, list.length);
- CustomToast.makeText((Activity)context, message,CustomToast.LENGTH_SHORT).show();
+ CustomToast.makeText((Activity) context, message, CustomToast.LENGTH_SHORT).show();
} catch (final RemoteException ignored) {
}
}