diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java new file mode 100644 index 000000000..b6df807e2 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package code.name.monkey.retromusic.util; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Environment; +import android.provider.BaseColumns; +import android.provider.MediaStore; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.helper.M3UWriter; +import code.name.monkey.retromusic.model.Playlist; +import code.name.monkey.retromusic.model.PlaylistSong; +import code.name.monkey.retromusic.model.Song; + +import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; + +public class PlaylistsUtil { + + public static void addToPlaylist(@NonNull Context context, + @NonNull List songs, + int playlistId, + boolean showToastOnFinish) { + + ArrayList noSongs = new ArrayList(); + for (Song song : songs) { + if (!doPlaylistContains(context, playlistId, song.getId())) { + noSongs.add(song); + } + } + + final int size = noSongs.size(); + final ContentResolver resolver = context.getContentResolver(); + final String[] projection = new String[]{"max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")",}; + final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); + + int base = 0; + try (Cursor cursor = resolver.query(uri, projection, null, null, null)) { + + if (cursor != null && cursor.moveToFirst()) { + base = cursor.getInt(0) + 1; + } + } catch (SecurityException ignored) { + } + + int numInserted = 0; + for (int offSet = 0; offSet < size; offSet += 1000) { + numInserted += resolver.bulkInsert(uri, makeInsertItems(noSongs, offSet, 1000, base)); + } + + if (showToastOnFinish) { + Toast.makeText(context, context.getResources().getString( + R.string.inserted_x_songs_into_playlist_x, numInserted, getNameForPlaylist(context, playlistId)), + Toast.LENGTH_SHORT).show(); + } + } + + public static int createPlaylist(@NonNull final Context context, @Nullable final String name) { + int id = -1; + if (name != null && name.length() > 0) { + try { + Cursor cursor = context.getContentResolver() + .query(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(EXTERNAL_CONTENT_URI, values); + if (uri != null) { + Toast.makeText(context, context.getResources().getString(R.string.created_playlist_x, name), + Toast.LENGTH_SHORT).show(); + id = Integer.parseInt(uri.getLastPathSegment()); + } + } else { + // Playlist exists + if (cursor.moveToFirst()) { + id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID)); + } + } + if (cursor != null) { + cursor.close(); + } + } catch (SecurityException ignored) { + } + } + if (id == -1) { + Toast.makeText(context, context.getResources().getString( + R.string.could_not_create_playlist), Toast.LENGTH_SHORT).show(); + } + return id; + } + + public static void deletePlaylists(@NonNull final Context context, @NonNull final ArrayList playlists) { + final StringBuilder selection = new StringBuilder(); + selection.append(MediaStore.Audio.Playlists._ID + " IN ("); + for (int i = 0; i < playlists.size(); i++) { + selection.append(playlists.get(i).id); + if (i < playlists.size() - 1) { + selection.append(","); + } + } + selection.append(")"); + try { + context.getContentResolver().delete(EXTERNAL_CONTENT_URI, selection.toString(), null); + context.getContentResolver().notifyChange(Uri.parse("content://media"), null); + } catch (SecurityException ignored) { + } + } + + static boolean doPlaylistContains(@NonNull final Context context, final long playlistId, + final int songId) { + if (playlistId != -1) { + 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 false; + } + + public static boolean doesPlaylistExist(@NonNull final Context context, final int playlistId) { + return playlistId != -1 && doesPlaylistExist(context, + MediaStore.Audio.Playlists._ID + "=?", + new String[]{String.valueOf(playlistId)}); + } + + public static String getNameForPlaylist(@NonNull final Context context, final long id) { + try { + Cursor cursor = context.getContentResolver().query(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(); + } + } + } catch (SecurityException ignored) { + } + return ""; + } + + @NonNull + public static ContentValues[] makeInsertItems(@NonNull final List songs, final int offset, int len, + final int base) { + if (offset + len > songs.size()) { + len = songs.size() - offset; + } + + ContentValues[] contentValues = new ContentValues[len]; + + for (int i = 0; i < len; i++) { + contentValues[i] = new ContentValues(); + contentValues[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + offset + i); + contentValues[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(offset + i).getId()); + } + return contentValues; + } + + public static boolean moveItem(@NonNull final Context context, int playlistId, int from, int to) { + return MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(), + playlistId, from, to); + } + + public static void removeFromPlaylist(@NonNull final Context context, @NonNull final Song song, int playlistId) { + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( + "external", playlistId); + String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?"; + String[] selectionArgs = new String[]{String.valueOf(song.getId())}; + + try { + context.getContentResolver().delete(uri, selection, selectionArgs); + } catch (SecurityException ignored) { + } + } + + public static void removeFromPlaylist(@NonNull final Context context, @NonNull final List songs) { + final int playlistId = songs.get(0).getPlaylistId(); + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( + "external", playlistId); + String selectionArgs[] = new String[songs.size()]; + for (int i = 0; i < selectionArgs.length; i++) { + selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList()); + } + String selection = MediaStore.Audio.Playlists.Members._ID + " in ("; + //noinspection unused + for (String selectionArg : selectionArgs) { + selection += "?, "; + } + selection = selection.substring(0, selection.length() - 2) + ")"; + + try { + context.getContentResolver().delete(uri, selection, selectionArgs); + } catch (SecurityException ignored) { + } + } + + 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); + try { + context.getContentResolver().update(EXTERNAL_CONTENT_URI, + contentValues, + MediaStore.Audio.Playlists._ID + "=?", + new String[]{String.valueOf(id)}); + } catch (SecurityException ignored) { + } + } + + @Nullable + public static File savePlaylist(@NonNull Context context, + @NonNull Playlist playlist) throws IOException { + return M3UWriter.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist); + } + + static void addToPlaylist(@NonNull Context context, + @NonNull Song song, + int playlistId, + boolean showToastOnFinish) { + List helperList = new ArrayList<>(); + helperList.add(song); + addToPlaylist(context, helperList, playlistId, showToastOnFinish); + } + + private static boolean doesPlaylistExist(@NonNull Context context, @NonNull final String selection, + @NonNull final String[] values) { + Cursor cursor = context.getContentResolver().query(EXTERNAL_CONTENT_URI, + new String[]{}, selection, values, null); + + boolean exists = false; + if (cursor != null) { + exists = cursor.getCount() != 0; + cursor.close(); + } + return exists; + } +} \ No newline at end of file