diff --git a/app/src/main/assets/changelog.html b/app/src/main/assets/changelog.html
index 19218355..5a8a31ea 100644
--- a/app/src/main/assets/changelog.html
+++ b/app/src/main/assets/changelog.html
@@ -28,6 +28,8 @@
Version 0.9.43 beta5
+ - NEW: Support for Android Marshmallow's permission system..
+
- IMPROVEMENT: The sliding panel should open and close a bit smoother now..
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java
index 5400ea72..841e6919 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java
@@ -77,29 +77,36 @@ public class AlbumLoader {
return new Album(id, albumName, artist, artistId, songCount, year);
}
+ @Nullable
public static Cursor makeAlbumCursor(@NonNull final Context context, final String selection, final String[] values) {
return makeAlbumCursor(context, selection, values, PreferenceUtil.getInstance(context).getAlbumSortOrder());
}
+ @Nullable
public static Cursor makeAlbumCursor(@NonNull final Context context, final String selection, final String[] values, final String sortOrder) {
return makeAlbumCursor(context, MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, selection, values, sortOrder);
}
+ @Nullable
public static Cursor makeAlbumCursor(@NonNull final Context context, @NonNull final Uri contentUri, final String selection, final String[] values, final String sortOrder) {
- return context.getContentResolver().query(contentUri,
- new String[]{
+ try {
+ return context.getContentResolver().query(contentUri,
+ new String[]{
/* 0 */
- BaseColumns._ID,
+ BaseColumns._ID,
/* 1 */
- AlbumColumns.ALBUM,
+ AlbumColumns.ALBUM,
/* 2 */
- AlbumColumns.ARTIST,
+ AlbumColumns.ARTIST,
/* 3 */
- AudioColumns.ARTIST_ID,
+ AudioColumns.ARTIST_ID,
/* 4 */
- AlbumColumns.NUMBER_OF_SONGS,
+ AlbumColumns.NUMBER_OF_SONGS,
/* 5 */
- AlbumColumns.FIRST_YEAR,
- }, selection, values, sortOrder);
+ AlbumColumns.FIRST_YEAR,
+ }, selection, values, sortOrder);
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistAlbumLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistAlbumLoader.java
index d2382960..d5d04ac9 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistAlbumLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistAlbumLoader.java
@@ -21,11 +21,15 @@ public class ArtistAlbumLoader {
}
public static Cursor makeArtistAlbumCursor(@NonNull final Context context, final int artistId) {
- return AlbumLoader.makeAlbumCursor(context,
- MediaStore.Audio.Artists.Albums.getContentUri("external", artistId),
- null,
- null,
- PreferenceUtil.getInstance(context).getArtistAlbumSortOrder()
- );
+ try {
+ return AlbumLoader.makeAlbumCursor(context,
+ MediaStore.Audio.Artists.Albums.getContentUri("external", artistId),
+ null,
+ null,
+ PreferenceUtil.getInstance(context).getArtistAlbumSortOrder()
+ );
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java
index 67d213f6..b261e3ac 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java
@@ -74,17 +74,22 @@ public class ArtistLoader {
return new Artist(id, artistName, albumCount, songCount);
}
+ @Nullable
public static Cursor makeArtistCursor(@NonNull final Context context, final String selection, final String[] values) {
- return context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
- new String[]{
+ try {
+ return context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
+ new String[]{
/* 0 */
- BaseColumns._ID,
+ BaseColumns._ID,
/* 1 */
- ArtistColumns.ARTIST,
+ ArtistColumns.ARTIST,
/* 2 */
- ArtistColumns.NUMBER_OF_ALBUMS,
+ ArtistColumns.NUMBER_OF_ALBUMS,
/* 3 */
- ArtistColumns.NUMBER_OF_TRACKS
- }, selection, values, PreferenceUtil.getInstance(context).getArtistSortOrder());
+ ArtistColumns.NUMBER_OF_TRACKS
+ }, selection, values, PreferenceUtil.getInstance(context).getArtistSortOrder());
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistSongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistSongLoader.java
index 76a78a14..e2eba644 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistSongLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistSongLoader.java
@@ -21,13 +21,17 @@ public class ArtistSongLoader {
}
public static Cursor makeArtistSongCursor(@NonNull final Context context, final int artistId) {
- return SongLoader.makeSongCursor(
- context,
- MediaStore.Audio.AudioColumns.ARTIST_ID + "=?",
- new String[]{
- String.valueOf(artistId)
- },
- PreferenceUtil.getInstance(context).getArtistSongSortOrder()
- );
+ try {
+ return SongLoader.makeSongCursor(
+ context,
+ MediaStore.Audio.AudioColumns.ARTIST_ID + "=?",
+ new String[]{
+ String.valueOf(artistId)
+ },
+ PreferenceUtil.getInstance(context).getArtistSongSortOrder()
+ );
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java
index 69397cc2..8bc95b16 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java
@@ -75,13 +75,18 @@ public class PlaylistLoader {
return new Playlist(id, name);
}
+ @Nullable
public static Cursor makePlaylistCursor(@NonNull final Context context, final String selection, final String[] values) {
- return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
- new String[]{
+ try {
+ return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ new String[]{
/* 0 */
- BaseColumns._ID,
+ BaseColumns._ID,
/* 1 */
- PlaylistsColumns.NAME
- }, selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER);
+ PlaylistsColumns.NAME
+ }, selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER);
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java
index ef4e9fe7..6ff4dedb 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java
@@ -45,30 +45,34 @@ public class PlaylistSongLoader {
}
public static Cursor makePlaylistSongCursor(@NonNull final Context context, final int playlistId) {
- return context.getContentResolver().query(
- MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
- new String[]{
+ try {
+ return context.getContentResolver().query(
+ MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
+ new String[]{
/* 0 */
- MediaStore.Audio.Playlists.Members.AUDIO_ID,
+ MediaStore.Audio.Playlists.Members.AUDIO_ID,
/* 1 */
- AudioColumns.TITLE,
+ AudioColumns.TITLE,
/* 2 */
- AudioColumns.ARTIST,
+ AudioColumns.ARTIST,
/* 3 */
- AudioColumns.ALBUM,
+ AudioColumns.ALBUM,
/* 4 */
- AudioColumns.DURATION,
+ AudioColumns.DURATION,
/* 5 */
- AudioColumns.TRACK,
+ AudioColumns.TRACK,
/* 6 */
- AudioColumns.ALBUM_ID,
+ AudioColumns.ALBUM_ID,
/* 7 */
- AudioColumns.ARTIST_ID,
+ AudioColumns.ARTIST_ID,
/* 8 */
- AudioColumns.DATA,
+ AudioColumns.DATA,
/* 9 */
- MediaStore.Audio.Playlists.Members._ID
- }, SongLoader.BASE_SELECTION, null,
- MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
+ MediaStore.Audio.Playlists.Members._ID
+ }, SongLoader.BASE_SELECTION, null,
+ MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java
index 18124dfd..7db01603 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java
@@ -13,6 +13,8 @@ import com.kabouzeid.gramophone.util.PreferenceUtil;
import java.util.ArrayList;
+import hugo.weaving.DebugLog;
+
/**
* @author Karim Abou Zeid (kabouzeid)
*/
@@ -77,36 +79,43 @@ public class SongLoader {
return new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber, data);
}
+ @Nullable
public static Cursor makeSongCursor(@NonNull final Context context, final String selection, final String[] values) {
return makeSongCursor(context, selection, values, PreferenceUtil.getInstance(context).getSongSortOrder());
}
+ @DebugLog
+ @Nullable
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable final String selection, final String[] values, final String sortOrder) {
String baseSelection = BASE_SELECTION;
if (selection != null && !selection.trim().equals("")) {
baseSelection += " AND " + selection;
}
- return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- new String[]{
+ try {
+ return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ new String[]{
/* 0 */
- BaseColumns._ID,
+ BaseColumns._ID,
/* 1 */
- AudioColumns.TITLE,
+ AudioColumns.TITLE,
/* 2 */
- AudioColumns.ARTIST,
+ AudioColumns.ARTIST,
/* 3 */
- AudioColumns.ALBUM,
+ AudioColumns.ALBUM,
/* 4 */
- AudioColumns.DURATION,
+ AudioColumns.DURATION,
/* 5 */
- AudioColumns.TRACK,
+ AudioColumns.TRACK,
/* 6 */
- AudioColumns.ARTIST_ID,
+ AudioColumns.ARTIST_ID,
/* 7 */
- AudioColumns.ALBUM_ID,
+ AudioColumns.ALBUM_ID,
/* 8 */
- AudioColumns.DATA
- }, baseSelection, values, sortOrder);
+ AudioColumns.DATA
+ }, baseSelection, values, sortOrder);
+ } catch (SecurityException e) {
+ return null;
+ }
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
index 2867a94e..c619fe2c 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
@@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -139,7 +138,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private boolean isServiceInUse;
private static String getTrackUri(@NonNull Song song) {
- return ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, song.id).toString();
+ return MusicUtil.getSongUri(song.id).toString();
}
@Override
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java
index 92972e40..58617f4f 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java
@@ -1,14 +1,19 @@
package com.kabouzeid.gramophone.ui.activities.base;
+import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.widget.Toast;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener;
@@ -17,24 +22,53 @@ import com.kabouzeid.gramophone.service.MusicService;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import hugo.weaving.DebugLog;
+
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements ServiceConnection, MusicServiceEventListener {
public static final String TAG = AbsMusicServiceActivity.class.getSimpleName();
+ public static final int REQUEST_EXTERNAL_STORAGE_PERMISSION = 0;
+
private final ArrayList mMusicServiceEventListener = new ArrayList<>();
private MusicPlayerRemote.ServiceToken serviceToken;
private MusicStateReceiver musicStateReceiver;
private boolean receiverRegistered;
+ private boolean hasExternalStoragePermission;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ checkExternalStoragePermissions();
serviceToken = MusicPlayerRemote.bindToService(this, this);
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // the handler is necessary to avoid "java.lang.RuntimeException: Performing pause of activity that is not resumed"
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ recreateIfPermissionsChanged();
+ }
+ }, 200);
+ }
+
+ protected void recreateIfPermissionsChanged() {
+ if (didPermissionsChanged()) {
+ recreate();
+ }
+ }
+
+ private boolean didPermissionsChanged() {
+ return hasExternalStoragePermission != hasExternalStoragePermission();
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (!receiverRegistered) {
@@ -161,4 +195,30 @@ public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements
}
}
}
+
+ private void checkExternalStoragePermissions() {
+ hasExternalStoragePermission = hasExternalStoragePermission();
+ if (hasExternalStoragePermission) {
+ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE_PERMISSION);
+ }
+ }
+
+ private boolean hasExternalStoragePermission() {
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
+ }
+
+ @DebugLog
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_EXTERNAL_STORAGE_PERMISSION) {
+ for (int grantResult : grantResults) {
+ if (grantResult == PackageManager.PERMISSION_GRANTED) {
+ recreate();
+ return;
+ }
+ }
+ Toast.makeText(AbsMusicServiceActivity.this, "You must grant permission to external storage in order to explore your music", Toast.LENGTH_SHORT).show();
+ }
+ }
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
index 48d635fa..c9525ee3 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
@@ -86,22 +86,25 @@ public class MusicUtil {
return;
}
- Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.MediaColumns.TITLE},
- BaseColumns._ID + "=?",
- new String[]{String.valueOf(id)},
- null);
try {
- if (cursor != null && cursor.getCount() == 1) {
- cursor.moveToFirst();
- Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString());
- final String message = context.getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0));
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
- }
- } finally {
- if (cursor != null) {
- cursor.close();
+ Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ new String[]{MediaStore.MediaColumns.TITLE},
+ BaseColumns._ID + "=?",
+ new String[]{String.valueOf(id)},
+ null);
+ try {
+ if (cursor != null && cursor.getCount() == 1) {
+ cursor.moveToFirst();
+ Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString());
+ final String message = context.getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0));
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
+ } catch (SecurityException ignored) {
}
}
@@ -174,46 +177,50 @@ public class MusicUtil {
}
}
selection.append(")");
- final Cursor cursor = context.getContentResolver().query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(),
- null, null);
- if (cursor != null) {
- // Step 1: Remove selected tracks from the current playlist, as well
- // as from the album art cache
- cursor.moveToFirst();
- while (!cursor.isAfterLast()) {
- final int id = cursor.getInt(0);
- final Song song = SongLoader.getSong(context, id);
- MusicPlayerRemote.removeFromQueue(song);
- cursor.moveToNext();
- }
- // Step 2: Remove selected tracks from the database
- context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- selection.toString(), null);
-
- // Step 3: Remove files from card
- cursor.moveToFirst();
- while (!cursor.isAfterLast()) {
- final String name = cursor.getString(1);
- try { // File.delete can throw a security exception
- final File f = new File(name);
- if (!f.delete()) {
- // I'm not sure if we'd ever get here (deletion would
- // have to fail, but no exception thrown)
- Log.e("MusicUtils", "Failed to delete file " + name);
- }
+ try {
+ final Cursor cursor = context.getContentResolver().query(
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(),
+ null, null);
+ if (cursor != null) {
+ // Step 1: Remove selected tracks from the current playlist, as well
+ // as from the album art cache
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ final int id = cursor.getInt(0);
+ final Song song = SongLoader.getSong(context, id);
+ MusicPlayerRemote.removeFromQueue(song);
cursor.moveToNext();
- } catch (@NonNull final SecurityException ex) {
- cursor.moveToNext();
- } catch (NullPointerException e) {
- Log.e("MusicUtils", "Failed to find file " + name);
}
+
+ // Step 2: Remove selected tracks from the database
+ context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ selection.toString(), null);
+
+ // Step 3: Remove files from card
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ final String name = cursor.getString(1);
+ try { // File.delete can throw a security exception
+ final File f = new File(name);
+ if (!f.delete()) {
+ // I'm not sure if we'd ever get here (deletion would
+ // have to fail, but no exception thrown)
+ Log.e("MusicUtils", "Failed to delete file " + name);
+ }
+ cursor.moveToNext();
+ } catch (@NonNull final SecurityException ex) {
+ cursor.moveToNext();
+ } catch (NullPointerException e) {
+ Log.e("MusicUtils", "Failed to find file " + name);
+ }
+ }
+ cursor.close();
}
- cursor.close();
+ context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
+ Toast.makeText(context, context.getString(R.string.deleted_x_songs, songs.size()), Toast.LENGTH_SHORT).show();
+ } catch (SecurityException ignored) {
}
- context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
- Toast.makeText(context, context.getString(R.string.deleted_x_songs, songs.size()), Toast.LENGTH_SHORT).show();
}
public static Playlist getFavoritesPlaylist(@NonNull final Context context) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java
index 8bb5ea54..faed12ae 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java
@@ -3,7 +3,6 @@ package com.kabouzeid.gramophone.util;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
@@ -16,7 +15,6 @@ import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.PlaylistSong;
import com.kabouzeid.gramophone.model.Song;
-import com.kabouzeid.gramophone.service.MusicService;
import java.util.ArrayList;
import java.util.List;
@@ -29,28 +27,31 @@ public class PlaylistsUtil {
public static int createPlaylist(@NonNull final Context context, @Nullable final String name) {
int id = -1;
if (name != null && name.length() > 0) {
- Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.Audio.Playlists._ID}, MediaStore.Audio.PlaylistsColumns.NAME + "=?", new String[]{name}, null);
- if (cursor == null || cursor.getCount() < 1) {
- final ContentValues values = new ContentValues(1);
- values.put(MediaStore.Audio.PlaylistsColumns.NAME, name);
- final Uri uri = context.getContentResolver().insert(
- MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
- values);
- if (uri != null) {
- // necessary because somehow the MediaStoreObserver is not notified when adding a playlist
- context.sendBroadcast(new Intent(MusicService.MEDIA_STORE_CHANGED));
- Toast.makeText(context, context.getResources().getString(
- R.string.created_playlist_x, name), Toast.LENGTH_SHORT).show();
- id = Integer.parseInt(uri.getLastPathSegment());
+ try {
+ Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ new String[]{MediaStore.Audio.Playlists._ID}, MediaStore.Audio.PlaylistsColumns.NAME + "=?", new String[]{name}, null);
+ if (cursor == null || cursor.getCount() < 1) {
+ final ContentValues values = new ContentValues(1);
+ values.put(MediaStore.Audio.PlaylistsColumns.NAME, name);
+ final Uri uri = context.getContentResolver().insert(
+ MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ values);
+ if (uri != null) {
+ // necessary because somehow the MediaStoreObserver is not notified when adding a playlist
+ context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
+ Toast.makeText(context, context.getResources().getString(
+ R.string.created_playlist_x, name), Toast.LENGTH_SHORT).show();
+ id = Integer.parseInt(uri.getLastPathSegment());
+ }
+ } else {
+ if (cursor.moveToFirst()) {
+ id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID));
+ }
}
- } else {
- if (cursor.moveToFirst()) {
- id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID));
+ if (cursor != null) {
+ cursor.close();
}
- }
- if (cursor != null) {
- cursor.close();
+ } catch (SecurityException ignored) {
}
}
if (id == -1) {
@@ -70,7 +71,10 @@ public class PlaylistsUtil {
}
}
selection.append(")");
- context.getContentResolver().delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, selection.toString(), null);
+ try {
+ context.getContentResolver().delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, selection.toString(), null);
+ } catch (SecurityException ignored) {
+ }
}
public static void addToPlaylist(@NonNull final Context context, final Song song, final int playlistId, final boolean showToastOnFinish) {
@@ -90,24 +94,27 @@ public class PlaylistsUtil {
int base = 0;
try {
- cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ cursor = resolver.query(uri, projection, null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- base = cursor.getInt(0) + 1;
+ if (cursor != null && cursor.moveToFirst()) {
+ base = cursor.getInt(0) + 1;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
- } finally {
- if (cursor != null) {
- cursor.close();
+
+ int numInserted = 0;
+ for (int offSet = 0; offSet < size; offSet += 1000)
+ numInserted += resolver.bulkInsert(uri, makeInsertItems(songs, offSet, 1000, base));
+
+ if (showToastOnFinish) {
+ Toast.makeText(context, context.getResources().getString(
+ R.string.inserted_x_songs_into_playlist, numInserted), Toast.LENGTH_SHORT).show();
}
- }
-
- int numinserted = 0;
- for (int offSet = 0; offSet < size; offSet += 1000)
- numinserted += resolver.bulkInsert(uri, makeInsertItems(songs, offSet, 1000, base));
-
- if (showToastOnFinish) {
- Toast.makeText(context, context.getResources().getString(
- R.string.inserted_x_songs_into_playlist, numinserted), Toast.LENGTH_SHORT).show();
+ } catch (SecurityException ignored) {
}
}
@@ -133,7 +140,10 @@ public class PlaylistsUtil {
String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?";
String[] selectionArgs = new String[]{String.valueOf(song.id)};
- context.getContentResolver().delete(uri, selection, selectionArgs);
+ try {
+ context.getContentResolver().delete(uri, selection, selectionArgs);
+ } catch (SecurityException ignored) {
+ }
}
public static void removeFromPlaylist(@NonNull final Context context, @NonNull final List songs) {
@@ -145,23 +155,30 @@ public class PlaylistsUtil {
selectionArgs[i] = String.valueOf(songs.get(i).idInPlayList);
}
String selection = MediaStore.Audio.Playlists.Members._ID + " in (";
+ //noinspection unused
for (String selectionArg : selectionArgs) selection += "?, ";
selection = selection.substring(0, selection.length() - 2) + ")";
- context.getContentResolver().delete(uri, selection, selectionArgs);
+ try {
+ context.getContentResolver().delete(uri, selection, selectionArgs);
+ } catch (SecurityException ignored) {
+ }
}
public static boolean doPlaylistContains(@NonNull final Context context, final long playlistId, final int songId) {
if (playlistId != -1) {
- Cursor c = context.getContentResolver().query(
- MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
- new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?", new String[]{String.valueOf(songId)}, null);
- int count = 0;
- if (c != null) {
- count = c.getCount();
- c.close();
+ try {
+ Cursor c = context.getContentResolver().query(
+ MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
+ new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?", new String[]{String.valueOf(songId)}, null);
+ int count = 0;
+ if (c != null) {
+ count = c.getCount();
+ c.close();
+ }
+ return count > 0;
+ } catch (SecurityException ignored) {
}
- return count > 0;
}
return false;
}
@@ -174,27 +191,33 @@ public class PlaylistsUtil {
public static void renamePlaylist(@NonNull final Context context, final long id, final String newName) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Audio.PlaylistsColumns.NAME, newName);
- context.getContentResolver().update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
- contentValues,
- MediaStore.Audio.Playlists._ID + "=?",
- new String[]{String.valueOf(id)});
+ try {
+ context.getContentResolver().update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ contentValues,
+ MediaStore.Audio.Playlists._ID + "=?",
+ new String[]{String.valueOf(id)});
+ } catch (SecurityException ignored) {
+ }
}
public static String getNameForPlaylist(@NonNull final Context context, final long id) {
- Cursor cursor = context.getContentResolver().query(
- MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.Audio.PlaylistsColumns.NAME},
- BaseColumns._ID + "=?",
- new String[]{String.valueOf(id)},
- null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- return cursor.getString(0);
+ try {
+ Cursor cursor = context.getContentResolver().query(
+ MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ new String[]{MediaStore.Audio.PlaylistsColumns.NAME},
+ BaseColumns._ID + "=?",
+ new String[]{String.valueOf(id)},
+ null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
}
- } finally {
- cursor.close();
}
+ } catch (SecurityException ignored) {
}
return "";
}