Fix Android issues
This commit is contained in:
parent
1096cea0b4
commit
f6ff0f6565
10 changed files with 1051 additions and 952 deletions
|
@ -371,6 +371,7 @@ object MusicPlayerRemote {
|
|||
return false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removeFromQueue(song: Song): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService!!.removeSong(song)
|
||||
|
|
|
@ -74,6 +74,7 @@ object SongLoader {
|
|||
return song
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getSong(context: Context, queryId: Int): Song {
|
||||
val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString()))
|
||||
return getSong(cursor)
|
||||
|
|
|
@ -38,6 +38,6 @@ class MediaStoreObserver(
|
|||
|
||||
companion object {
|
||||
// milliseconds to delay before calling refresh to aggregate events
|
||||
private val REFRESH_DELAY: Long = 500
|
||||
private const val REFRESH_DELAY: Long = 500
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -14,17 +14,6 @@
|
|||
|
||||
package code.name.monkey.retromusic.service;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
import static code.name.monkey.retromusic.service.MusicService.DUCK;
|
||||
import static code.name.monkey.retromusic.service.MusicService.META_CHANGED;
|
||||
import static code.name.monkey.retromusic.service.MusicService.PLAY_STATE_CHANGED;
|
||||
|
@ -32,10 +21,20 @@ import static code.name.monkey.retromusic.service.MusicService.REPEAT_MODE_NONE;
|
|||
import static code.name.monkey.retromusic.service.MusicService.TRACK_ENDED;
|
||||
import static code.name.monkey.retromusic.service.MusicService.TRACK_WENT_TO_NEXT;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import androidx.annotation.NonNull;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
class PlaybackHandler extends Handler {
|
||||
|
||||
private float currentDuckVolume = 1.0f;
|
||||
|
||||
@NonNull
|
||||
private final WeakReference<MusicService> mService;
|
||||
private float currentDuckVolume = 1.0f;
|
||||
|
||||
PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
|
||||
super(looper);
|
||||
|
@ -79,9 +78,14 @@ class PlaybackHandler extends Handler {
|
|||
break;
|
||||
|
||||
case TRACK_WENT_TO_NEXT:
|
||||
if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
|
||||
if (service.pendingQuit || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
|
||||
service.pause();
|
||||
service.seek(0);
|
||||
if (service.pendingQuit) {
|
||||
service.pendingQuit = false;
|
||||
service.quit();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
service.position = service.nextPosition;
|
||||
service.prepareNextImpl();
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.graphics.Bitmap
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
|
@ -32,7 +33,7 @@ import java.io.BufferedOutputStream
|
|||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class CustomArtistImageUtil private constructor(context: Context) {
|
||||
|
@ -80,7 +81,7 @@ class CustomArtistImageUtil private constructor(context: Context) {
|
|||
if (succesful) {
|
||||
mPreferences.edit().putBoolean(getFileName(artist), true).commit()
|
||||
ArtistSignatureUtil.getInstance(App.getContext()).updateArtistSignature(artist.name)
|
||||
App.getContext().contentResolver.notifyChange(Uri.parse("content://media"), null) // trigger media store changed to force artist image reload
|
||||
App.getContext().contentResolver.notifyChange(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null) // trigger media store changed to force artist image reload
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -95,7 +96,7 @@ class CustomArtistImageUtil private constructor(context: Context) {
|
|||
override fun doInBackground(vararg params: Void): Void? {
|
||||
mPreferences.edit().putBoolean(getFileName(artist), false).commit()
|
||||
ArtistSignatureUtil.getInstance(App.getContext()).updateArtistSignature(artist.name)
|
||||
App.getContext().contentResolver.notifyChange(Uri.parse("content://media"), null) // trigger media store changed to force artist image reload
|
||||
App.getContext().contentResolver.notifyChange(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null) // trigger media store changed to force artist image reload
|
||||
|
||||
val file = getFile(artist)
|
||||
if (!file.exists()) {
|
||||
|
|
|
@ -103,6 +103,7 @@ public class MusicUtil {
|
|||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Uri localUri = Uri.parse("content://media/external/audio/albumart");
|
||||
contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null);
|
||||
contentResolver.notifyChange(localUri, null);
|
||||
}
|
||||
|
||||
public static void deleteTracks(
|
||||
|
@ -149,8 +150,8 @@ public class MusicUtil {
|
|||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast()) {
|
||||
final int id = cursor.getInt(0);
|
||||
final Song song = SongLoader.INSTANCE.getSong(activity, id);
|
||||
MusicPlayerRemote.INSTANCE.removeFromQueue(song);
|
||||
final Song song = SongLoader.getSong(activity, id);
|
||||
MusicPlayerRemote.removeFromQueue(song);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
|
||||
|
@ -325,14 +326,6 @@ public class MusicUtil {
|
|||
return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getSongInfoString(@NonNull Song song) {
|
||||
return MusicUtil.buildInfoString(
|
||||
song.getArtistName(),
|
||||
song.getAlbumName()
|
||||
);
|
||||
}
|
||||
|
||||
public static long getTotalDuration(@NonNull List<Song> songs) {
|
||||
long duration = 0;
|
||||
for (int i = 0; i < songs.size(); i++) {
|
||||
|
@ -366,6 +359,7 @@ public class MusicUtil {
|
|||
values.put("_data", path);
|
||||
|
||||
contentResolver.insert(artworkUri, values);
|
||||
contentResolver.notifyChange(artworkUri, null);
|
||||
}
|
||||
|
||||
public static boolean isArtistNameUnknown(@Nullable String artistName) {
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -23,54 +25,72 @@ 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;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PlaylistsUtil {
|
||||
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 boolean doesPlaylistExist(@NonNull final Context context, final String name) {
|
||||
return doesPlaylistExist(context,
|
||||
MediaStore.Audio.PlaylistsColumns.NAME + "=?",
|
||||
new String[]{name});
|
||||
public static void addToPlaylist(@NonNull Context context,
|
||||
@NonNull List<Song> songs,
|
||||
int playlistId,
|
||||
boolean showToastOnFinish) {
|
||||
|
||||
ArrayList<Song> noSongs = new ArrayList<Song>();
|
||||
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);
|
||||
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);
|
||||
final Uri uri = context.getContentResolver().insert(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();
|
||||
Toast.makeText(context, context.getResources().getString(R.string.created_playlist_x, name),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
id = Integer.parseInt(uri.getLastPathSegment());
|
||||
}
|
||||
} else {
|
||||
|
@ -109,55 +129,63 @@ public class PlaylistsUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static void addToPlaylist(@NonNull Context context,
|
||||
@NonNull Song song,
|
||||
int playlistId,
|
||||
boolean showToastOnFinish) {
|
||||
List<Song> helperList = new ArrayList<>();
|
||||
helperList.add(song);
|
||||
addToPlaylist(context, helperList, playlistId, showToastOnFinish);
|
||||
}
|
||||
|
||||
public static void addToPlaylist(@NonNull Context context,
|
||||
@NonNull List<Song> songs,
|
||||
int playlistId,
|
||||
boolean showToastOnFinish) {
|
||||
|
||||
ArrayList<Song> noSongs = new ArrayList<Song>();
|
||||
for (Song song : songs) {
|
||||
if (!doPlaylistContains(context, playlistId, song.getId())) {
|
||||
noSongs.add(song);
|
||||
public 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)});
|
||||
}
|
||||
|
||||
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);
|
||||
public static boolean doesPlaylistExist(@NonNull final Context context, final String name) {
|
||||
return doesPlaylistExist(context,
|
||||
MediaStore.Audio.PlaylistsColumns.NAME + "=?",
|
||||
new String[]{name});
|
||||
}
|
||||
|
||||
|
||||
int base = 0;
|
||||
try (Cursor cursor = resolver.query(uri, projection, null, null, null)) {
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
base = cursor.getInt(0) + 1;
|
||||
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) {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ContentValues[] makeInsertItems(@NonNull final List<Song> songs, final int offset, int len, final int base) {
|
||||
public static ContentValues[] makeInsertItems(@NonNull final List<Song> songs, final int offset, int len,
|
||||
final int base) {
|
||||
if (offset + len > songs.size()) {
|
||||
len = songs.size() - offset;
|
||||
}
|
||||
|
@ -172,6 +200,11 @@ public class PlaylistsUtil {
|
|||
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);
|
||||
|
@ -194,7 +227,9 @@ public class PlaylistsUtil {
|
|||
}
|
||||
String selection = MediaStore.Audio.Playlists.Members._ID + " in (";
|
||||
//noinspection unused
|
||||
for (String selectionArg : selectionArgs) selection += "?, ";
|
||||
for (String selectionArg : selectionArgs) {
|
||||
selection += "?, ";
|
||||
}
|
||||
selection = selection.substring(0, selection.length() - 2) + ")";
|
||||
|
||||
try {
|
||||
|
@ -203,29 +238,6 @@ public class PlaylistsUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public 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 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 renamePlaylist(@NonNull final Context context, final long id, final String newName) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaStore.Audio.PlaylistsColumns.NAME, newName);
|
||||
|
@ -234,39 +246,27 @@ public class PlaylistsUtil {
|
|||
contentValues,
|
||||
MediaStore.Audio.Playlists._ID + "=?",
|
||||
new String[]{String.valueOf(id)});
|
||||
context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File savePlaylist(@NonNull Context context,
|
||||
@NonNull Playlist playlist) throws IOException {
|
||||
@NonNull Playlist playlist) throws IOException {
|
||||
return M3UWriter.write(context, new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
|
||||
}
|
||||
|
||||
private static boolean doesPlaylistExist(@NonNull Context context, @NonNull final String selection, @NonNull final String[] values) {
|
||||
static void addToPlaylist(@NonNull Context context,
|
||||
@NonNull Song song,
|
||||
int playlistId,
|
||||
boolean showToastOnFinish) {
|
||||
List<Song> 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);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
android:id="@+id/songCurrentProgress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center_vertical|left|end"
|
||||
android:paddingStart="8dp"
|
||||
android:singleLine="true"
|
||||
|
|
|
@ -10,7 +10,7 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.android.tools.build:bundletool:0.9.0'
|
||||
classpath "gradle.plugin.ru.cleverpumpkin.proguard-dictionaries-generator:plugin:1.0.7"
|
||||
|
|
Loading…
Reference in a new issue