Fix Android issues

This commit is contained in:
h4h13 2020-02-01 19:52:57 +05:30
parent 1096cea0b4
commit f6ff0f6565
10 changed files with 1051 additions and 952 deletions

View file

@ -371,6 +371,7 @@ object MusicPlayerRemote {
return false
}
@JvmStatic
fun removeFromQueue(song: Song): Boolean {
if (musicService != null) {
musicService!!.removeSong(song)

View file

@ -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)

View file

@ -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
}
}

View file

@ -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();

View file

@ -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()) {

View file

@ -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) {

View file

@ -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 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 doesPlaylistExist(@NonNull final Context context, final String name) {
return doesPlaylistExist(context,
MediaStore.Audio.PlaylistsColumns.NAME + "=?",
new String[]{name});
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 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();
}
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);
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 {
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);

View file

@ -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"

View file

@ -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"