From 9014b47b746253ec6f24e16d655d0ec36ae12401 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sat, 2 Jul 2022 01:27:12 +0200 Subject: [PATCH 01/13] Add song rating to notification --- .../ultrasonic/adapters/TrackViewHolder.kt | 2 + .../ultrasonic/fragment/PlayerFragment.kt | 44 ++++--- .../playback/AutoMediaBrowserCallback.kt | 107 ++++++++++++++++++ .../playback/MediaNotificationProvider.kt | 44 ++++++- .../service/MediaPlayerController.kt | 48 +++++--- 5 files changed, 209 insertions(+), 36 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt index 2b9c2ec5..b0f921ea 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt @@ -153,6 +153,8 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable star.setImageDrawable(imageHelper.starHollowDrawable) song.starred = false } + + // Should this be done here ? Thread { val musicService = MusicServiceFactory.getMusicService() try { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index bb06c722..f2aa408d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -33,17 +33,22 @@ import android.widget.LinearLayout import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import android.widget.TextView +import android.widget.Toast import android.widget.ViewFlipper import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.media3.common.HeartRating import androidx.media3.common.Player import androidx.media3.common.Timeline +import androidx.media3.session.SessionResult import androidx.navigation.Navigation import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView +import com.google.common.util.concurrent.FutureCallback +import com.google.common.util.concurrent.Futures import io.reactivex.rxjava3.disposables.CompositeDisposable import java.text.DateFormat import java.text.SimpleDateFormat @@ -748,25 +753,28 @@ class PlayerFragment : val isStarred = currentSong!!.starred val id = currentSong!!.id - if (isStarred) { - starMenuItem.icon = hollowStar - currentSong!!.starred = false - } else { - starMenuItem.icon = fullStar - currentSong!!.starred = true - } - Thread { - val musicService = getMusicService() - try { - if (isStarred) { - musicService.unstar(id, null, null) - } else { - musicService.star(id, null, null) + + mediaPlayerController.controller?.setRating( + id, + HeartRating(!isStarred) + )?.let { + Futures.addCallback(it, object : FutureCallback { + override fun onSuccess(result: SessionResult?) { + if (isStarred) { + starMenuItem.icon = hollowStar + currentSong!!.starred = false + } else { + starMenuItem.icon = fullStar + currentSong!!.starred = true + } } - } catch (all: Exception) { - Timber.e(all) - } - }.start() + + override fun onFailure(t: Throwable) { + Toast.makeText(context, "SetRating failed", Toast.LENGTH_SHORT).show() + } + }, this.executorService) + } + return true } R.id.menu_item_bookmark_set -> { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 29905d24..25625115 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -9,6 +9,7 @@ package org.moire.ultrasonic.playback import android.net.Uri import android.os.Bundle +import androidx.media3.common.HeartRating import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata import androidx.media3.common.MediaMetadata.FOLDER_TYPE_ALBUMS @@ -18,9 +19,15 @@ import androidx.media3.common.MediaMetadata.FOLDER_TYPE_NONE import androidx.media3.common.MediaMetadata.FOLDER_TYPE_PLAYLISTS import androidx.media3.common.MediaMetadata.FOLDER_TYPE_TITLES import androidx.media3.common.Player +import androidx.media3.common.Rating import androidx.media3.session.LibraryResult import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession +import androidx.media3.session.SessionCommand +import androidx.media3.session.SessionResult +import androidx.media3.session.SessionResult.RESULT_ERROR_BAD_VALUE +import androidx.media3.session.SessionResult.RESULT_ERROR_UNKNOWN +import androidx.media3.session.SessionResult.RESULT_SUCCESS import com.google.common.collect.ImmutableList import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture @@ -150,6 +157,22 @@ class AutoMediaBrowserCallback(var player: Player) : ) } + override fun onConnect( + session: MediaSession, + controller: MediaSession.ControllerInfo + ): MediaSession.ConnectionResult { + val connectionResult = super.onConnect(session, controller) + val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon() + + // TODO: Make a Const value list of available custom SessionCommands + availableSessionCommands.add(SessionCommand("COMMAND_CODE_SESSION_SET_RATING", Bundle())) + + return MediaSession.ConnectionResult.accept( + availableSessionCommands.build(), + connectionResult.availablePlayerCommands + ) + } + override fun onGetItem( session: MediaLibraryService.MediaLibrarySession, browser: MediaSession.ControllerInfo, @@ -177,6 +200,87 @@ class AutoMediaBrowserCallback(var player: Player) : return onLoadChildren(parentId) } + override fun onCustomCommand( + session: MediaSession, + controller: MediaSession.ControllerInfo, + customCommand: SessionCommand, + args: Bundle + ): ListenableFuture { + + /* + * It is currently not possible to edit a MediaItem after creation so the isRated value + * is stored in the track.starred value + */ + val rating = mediaPlayerController.currentPlayingLegacy?.track?.starred?.let { + HeartRating( + it + ) + } + + if (rating is HeartRating) { + return when (customCommand.customAction) { + "COMMAND_CODE_SESSION_SET_RATING" -> { + onSetRating( + session, + controller, + HeartRating(!rating.isHeart) + ) + } + else -> { + Timber.d( + "CustomCommand not recognized %s with extra %s", + customCommand.customAction, + customCommand.customExtras.toString() + ) + super.onCustomCommand(session, controller, customCommand, args) + } + } + } + return super.onCustomCommand(session, controller, customCommand, args) + } + + override fun onSetRating( + session: MediaSession, + controller: MediaSession.ControllerInfo, + rating: Rating + ): ListenableFuture { + if (session.player.currentMediaItem != null) + return onSetRating( + session, + controller, + session.player.currentMediaItem!!.mediaId, + rating + ) + return super.onSetRating(session, controller, rating) + } + + override fun onSetRating( + session: MediaSession, + controller: MediaSession.ControllerInfo, + mediaId: String, + rating: Rating + ): ListenableFuture { + return serviceScope.future { + if (rating is HeartRating) { + val musicService = MusicServiceFactory.getMusicService() + try { + if (rating.isHeart) { + musicService.star(mediaId, null, null) + } else { + musicService.unstar(mediaId, null, null) + } + } catch (all: Exception) { + Timber.e(all) + // TODO: Better handle exception + return@future SessionResult(RESULT_ERROR_UNKNOWN) + } + mediaPlayerController.currentPlayingLegacy?.track?.starred = rating.isHeart + return@future SessionResult(RESULT_SUCCESS) + } + return@future SessionResult(RESULT_ERROR_BAD_VALUE) + } + } + /* * For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer, * and thereby customarily it is required to rebuild it.. @@ -1076,6 +1180,7 @@ class AutoMediaBrowserCallback(var player: Player) : album = track.album, artist = track.artist, genre = track.genre, + starred = track.starred ) } @@ -1090,6 +1195,7 @@ class AutoMediaBrowserCallback(var player: Player) : genre: String? = null, sourceUri: Uri? = null, imageUri: Uri? = null, + starred: Boolean = false ): MediaItem { val metadata = MediaMetadata.Builder() @@ -1097,6 +1203,7 @@ class AutoMediaBrowserCallback(var player: Player) : .setTitle(title) .setArtist(artist) .setGenre(genre) + .setUserRating(HeartRating(starred)) .setFolderType(folderType) .setIsPlayable(isPlayable) .setArtworkUri(imageUri) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index d9cd7c36..15250879 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -9,15 +9,28 @@ package org.moire.ultrasonic.playback import android.content.Context import androidx.core.app.NotificationCompat +import androidx.media3.common.HeartRating import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import androidx.media3.session.CommandButton import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaNotification import androidx.media3.session.MediaSession +import androidx.media3.session.SessionCommand +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.moire.ultrasonic.R +import org.moire.ultrasonic.service.MediaPlayerController @UnstableApi -class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context) { +class MediaNotificationProvider(context: Context) : + DefaultMediaNotificationProvider(context), KoinComponent { + + /* + * It is currently not possible to edit a MediaItem after creation so the isRated value + * is stored in the track.starred value + */ + private val mediaPlayerController by inject() override fun addNotificationActions( mediaSession: MediaSession, @@ -25,7 +38,34 @@ class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProv builder: NotificationCompat.Builder, actionFactory: MediaNotification.ActionFactory ): IntArray { - return super.addNotificationActions(mediaSession, mediaButtons, builder, actionFactory) + val tmp: MutableList = mutableListOf() + val rating = mediaPlayerController.currentPlayingLegacy?.track?.starred?.let { + HeartRating( + it + ) + } + if (rating is HeartRating) { + tmp.add( + CommandButton.Builder() + .setDisplayName("Love") + .setIconResId(if (rating.isHeart) R.drawable.ic_star_full_dark else R.drawable.ic_star_hollow_dark) + .setSessionCommand( + SessionCommand( + "COMMAND_CODE_SESSION_SET_RATING", + HeartRating(rating.isHeart).toBundle() + ) + ) + .setExtras(HeartRating(rating.isHeart).toBundle()) + .setEnabled(true) + .build() + ) + } + return super.addNotificationActions( + mediaSession, + mediaButtons + tmp, + builder, + actionFactory + ) } override fun getMediaButtons( diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 73a5ef25..0dc5939d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -9,13 +9,20 @@ package org.moire.ultrasonic.service import android.content.ComponentName import android.content.Context import android.content.Intent +import android.os.Build +import android.os.Bundle import androidx.core.net.toUri +import androidx.media3.common.HeartRating import androidx.media3.common.MediaItem +import androidx.media3.common.MediaItem.RequestMetadata import androidx.media3.common.MediaMetadata import androidx.media3.common.Player import androidx.media3.common.Timeline import androidx.media3.session.MediaController +import androidx.media3.session.SessionResult import androidx.media3.session.SessionToken +import com.google.common.util.concurrent.FutureCallback +import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.MoreExecutors import io.reactivex.rxjava3.disposables.CompositeDisposable import kotlinx.coroutines.CoroutineScope @@ -579,23 +586,31 @@ class MediaPlayerController( if (legacyPlaylistManager.currentPlaying == null) return val song = legacyPlaylistManager.currentPlaying!!.track - Thread { - val musicService = getMusicService() - try { - if (song.starred) { - musicService.unstar(song.id, null, null) - } else { - musicService.star(song.id, null, null) - } - } catch (all: Exception) { - Timber.e(all) - } - }.start() + fun updateStarred() { + // Trigger an update + // TODO Update Metadata of MediaItem... + // localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying) + song.starred = !song.starred + } - // Trigger an update - // TODO Update Metadata of MediaItem... - // localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying) - song.starred = !song.starred + controller?.setRating( + song.id, + HeartRating(!song.starred) + ).let { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) { + Futures.addCallback(it, object : FutureCallback { + override fun onSuccess(result: SessionResult?) { + updateStarred() + } + + override fun onFailure(t: Throwable) { + TODO("Not yet implemented") + } + }, context.mainExecutor) + } else { + updateStarred() + } + } } @Suppress("TooGenericExceptionCaught") // The interface throws only generic exceptions @@ -668,6 +683,7 @@ fun Track.toMediaItem(): MediaItem { .setArtist(artist) .setAlbumTitle(album) .setAlbumArtist(artist) + .setUserRating(HeartRating(starred)) .build() val mediaItem = MediaItem.Builder() From 3f408600cb79515bce45bbf0f63fca3fb73dfad2 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sat, 2 Jul 2022 21:47:32 +0200 Subject: [PATCH 02/13] Add const for custom SessionCommands Improve rating call --- .../ultrasonic/fragment/PlayerFragment.kt | 2 - .../playback/AutoMediaBrowserCallback.kt | 76 +++++++++++++------ .../playback/MediaNotificationProvider.kt | 2 +- .../service/MediaPlayerController.kt | 1 - 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index f2aa408d..038e9b46 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -752,10 +752,8 @@ class PlayerFragment : if (currentSong == null) return true val isStarred = currentSong!!.starred - val id = currentSong!!.id mediaPlayerController.controller?.setRating( - id, HeartRating(!isStarred) )?.let { Futures.addCallback(it, object : FutureCallback { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 25625115..48c7b2b7 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -8,7 +8,11 @@ package org.moire.ultrasonic.playback import android.net.Uri +import android.os.Build import android.os.Bundle +import android.os.Looper +import android.widget.Toast +import android.widget.Toast.LENGTH_SHORT import androidx.media3.common.HeartRating import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata @@ -29,13 +33,18 @@ import androidx.media3.session.SessionResult.RESULT_ERROR_BAD_VALUE import androidx.media3.session.SessionResult.RESULT_ERROR_UNKNOWN import androidx.media3.session.SessionResult.RESULT_SUCCESS import com.google.common.collect.ImmutableList +import com.google.common.util.concurrent.FutureCallback import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.Runnable import kotlinx.coroutines.guava.future +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import org.checkerframework.checker.units.qual.Length import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.moire.ultrasonic.R @@ -51,6 +60,7 @@ import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util import timber.log.Timber +import kotlin.coroutines.coroutineContext private const val MEDIA_ROOT_ID = "MEDIA_ROOT_ID" private const val MEDIA_ALBUM_ID = "MEDIA_ALBUM_ID" @@ -87,6 +97,9 @@ private const val MEDIA_SEARCH_SONG_ITEM = "MEDIA_SEARCH_SONG_ITEM" private const val DISPLAY_LIMIT = 100 private const val SEARCH_LIMIT = 10 +// List of available custom SessionCommands +const val SESSION_CUSTOM_SET_RATING = "SESSION_CUSTOM_SET_RATING" + /** * MediaBrowserService implementation for e.g. Android Auto */ @@ -164,8 +177,7 @@ class AutoMediaBrowserCallback(var player: Player) : val connectionResult = super.onConnect(session, controller) val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon() - // TODO: Make a Const value list of available custom SessionCommands - availableSessionCommands.add(SessionCommand("COMMAND_CODE_SESSION_SET_RATING", Bundle())) + availableSessionCommands.add(SessionCommand(SESSION_CUSTOM_SET_RATING, Bundle())) return MediaSession.ConnectionResult.accept( availableSessionCommands.build(), @@ -207,35 +219,52 @@ class AutoMediaBrowserCallback(var player: Player) : args: Bundle ): ListenableFuture { - /* - * It is currently not possible to edit a MediaItem after creation so the isRated value - * is stored in the track.starred value - */ - val rating = mediaPlayerController.currentPlayingLegacy?.track?.starred?.let { - HeartRating( - it - ) - } - if (rating is HeartRating) { - return when (customCommand.customAction) { - "COMMAND_CODE_SESSION_SET_RATING" -> { - onSetRating( + var customCommandFuture: ListenableFuture? = null + + when (customCommand.customAction) { + SESSION_CUSTOM_SET_RATING -> { + /* + * It is currently not possible to edit a MediaItem after creation so the isRated value + * is stored in the track.starred value + * See https://github.com/androidx/media/issues/33 + */ + val track = mediaPlayerController.currentPlayingLegacy?.track + if (track != null) { + customCommandFuture = onSetRating( session, controller, - HeartRating(!rating.isHeart) + HeartRating(!track.starred) ) - } - else -> { - Timber.d( - "CustomCommand not recognized %s with extra %s", - customCommand.customAction, - customCommand.customExtras.toString() + Futures.addCallback( + customCommandFuture, + object : FutureCallback { + override fun onSuccess(result: SessionResult) { + track.starred = !track.starred + // Handle notification reload here + } + + override fun onFailure(t: Throwable) { + Toast.makeText( + mediaPlayerController.context, + "There was an error updating the rating", + LENGTH_SHORT + ).show() + } + }, MoreExecutors.directExecutor() ) - super.onCustomCommand(session, controller, customCommand, args) } } + else -> { + Timber.d( + "CustomCommand not recognized %s with extra %s", + customCommand.customAction, + customCommand.customExtras.toString() + ) + } } + if (customCommandFuture != null) + return customCommandFuture return super.onCustomCommand(session, controller, customCommand, args) } @@ -274,7 +303,6 @@ class AutoMediaBrowserCallback(var player: Player) : // TODO: Better handle exception return@future SessionResult(RESULT_ERROR_UNKNOWN) } - mediaPlayerController.currentPlayingLegacy?.track?.starred = rating.isHeart return@future SessionResult(RESULT_SUCCESS) } return@future SessionResult(RESULT_ERROR_BAD_VALUE) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 15250879..4aab0519 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -51,7 +51,7 @@ class MediaNotificationProvider(context: Context) : .setIconResId(if (rating.isHeart) R.drawable.ic_star_full_dark else R.drawable.ic_star_hollow_dark) .setSessionCommand( SessionCommand( - "COMMAND_CODE_SESSION_SET_RATING", + SESSION_CUSTOM_SET_RATING, HeartRating(rating.isHeart).toBundle() ) ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 0dc5939d..ffb5bfb2 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -594,7 +594,6 @@ class MediaPlayerController( } controller?.setRating( - song.id, HeartRating(!song.starred) ).let { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) { From d6aebd99898928e2bbe62871cc9a23d932880ddd Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sat, 2 Jul 2022 21:51:28 +0200 Subject: [PATCH 03/13] Lint --- .../moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 6 ------ .../moire/ultrasonic/playback/MediaNotificationProvider.kt | 5 ++++- .../org/moire/ultrasonic/service/MediaPlayerController.kt | 2 -- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 48c7b2b7..58ed2f9c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -8,9 +8,7 @@ package org.moire.ultrasonic.playback import android.net.Uri -import android.os.Build import android.os.Bundle -import android.os.Looper import android.widget.Toast import android.widget.Toast.LENGTH_SHORT import androidx.media3.common.HeartRating @@ -40,11 +38,8 @@ import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.Runnable import kotlinx.coroutines.guava.future -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import org.checkerframework.checker.units.qual.Length import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.moire.ultrasonic.R @@ -60,7 +55,6 @@ import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util import timber.log.Timber -import kotlin.coroutines.coroutineContext private const val MEDIA_ROOT_ID = "MEDIA_ROOT_ID" private const val MEDIA_ALBUM_ID = "MEDIA_ALBUM_ID" diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 4aab0519..60b8b68c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -48,7 +48,10 @@ class MediaNotificationProvider(context: Context) : tmp.add( CommandButton.Builder() .setDisplayName("Love") - .setIconResId(if (rating.isHeart) R.drawable.ic_star_full_dark else R.drawable.ic_star_hollow_dark) + .setIconResId( + if (rating.isHeart) R.drawable.ic_star_full_dark + else R.drawable.ic_star_hollow_dark + ) .setSessionCommand( SessionCommand( SESSION_CUSTOM_SET_RATING, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index ffb5bfb2..a0568e7c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -10,11 +10,9 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Build -import android.os.Bundle import androidx.core.net.toUri import androidx.media3.common.HeartRating import androidx.media3.common.MediaItem -import androidx.media3.common.MediaItem.RequestMetadata import androidx.media3.common.MediaMetadata import androidx.media3.common.Player import androidx.media3.common.Timeline From 16b3fcad32739c5892eaed02dc849796a12fcfcf Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sat, 2 Jul 2022 21:58:45 +0200 Subject: [PATCH 04/13] Final lint --- .../ultrasonic/fragment/PlayerFragment.kt | 31 +++++++++++-------- .../playback/AutoMediaBrowserCallback.kt | 4 +-- .../service/MediaPlayerController.kt | 20 +++++++----- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index 038e9b46..dcb5acd0 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -756,21 +756,26 @@ class PlayerFragment : mediaPlayerController.controller?.setRating( HeartRating(!isStarred) )?.let { - Futures.addCallback(it, object : FutureCallback { - override fun onSuccess(result: SessionResult?) { - if (isStarred) { - starMenuItem.icon = hollowStar - currentSong!!.starred = false - } else { - starMenuItem.icon = fullStar - currentSong!!.starred = true + Futures.addCallback( + it, + object : FutureCallback { + override fun onSuccess(result: SessionResult?) { + if (isStarred) { + starMenuItem.icon = hollowStar + currentSong!!.starred = false + } else { + starMenuItem.icon = fullStar + currentSong!!.starred = true + } } - } - override fun onFailure(t: Throwable) { - Toast.makeText(context, "SetRating failed", Toast.LENGTH_SHORT).show() - } - }, this.executorService) + override fun onFailure(t: Throwable) { + Toast.makeText(context, "SetRating failed", Toast.LENGTH_SHORT) + .show() + } + }, + this.executorService + ) } return true diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 58ed2f9c..85863490 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -213,7 +213,6 @@ class AutoMediaBrowserCallback(var player: Player) : args: Bundle ): ListenableFuture { - var customCommandFuture: ListenableFuture? = null when (customCommand.customAction) { @@ -245,7 +244,8 @@ class AutoMediaBrowserCallback(var player: Player) : LENGTH_SHORT ).show() } - }, MoreExecutors.directExecutor() + }, + MoreExecutors.directExecutor() ) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index a0568e7c..1cbc14fb 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -595,15 +595,19 @@ class MediaPlayerController( HeartRating(!song.starred) ).let { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) { - Futures.addCallback(it, object : FutureCallback { - override fun onSuccess(result: SessionResult?) { - updateStarred() - } + Futures.addCallback( + it, + object : FutureCallback { + override fun onSuccess(result: SessionResult?) { + updateStarred() + } - override fun onFailure(t: Throwable) { - TODO("Not yet implemented") - } - }, context.mainExecutor) + override fun onFailure(t: Throwable) { + TODO("Not yet implemented") + } + }, + context.mainExecutor + ) } else { updateStarred() } From 37e3ce09c1732584a1e7a9da0715bc3609102d24 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 3 Jul 2022 18:23:22 +0200 Subject: [PATCH 05/13] Update notification on Rating change --- .../playback/AutoMediaBrowserCallback.kt | 9 ++++--- .../ultrasonic/playback/PlaybackService.kt | 2 +- .../ultrasonic/util/MainThreadExecutor.java | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 85863490..12761b93 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -34,7 +34,6 @@ import com.google.common.collect.ImmutableList import com.google.common.util.concurrent.FutureCallback import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture -import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -52,6 +51,7 @@ import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.Track import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MusicServiceFactory +import org.moire.ultrasonic.util.MainThreadExecutor import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util import timber.log.Timber @@ -98,7 +98,7 @@ const val SESSION_CUSTOM_SET_RATING = "SESSION_CUSTOM_SET_RATING" * MediaBrowserService implementation for e.g. Android Auto */ @Suppress("TooManyFunctions", "LargeClass", "UnusedPrivateMember") -class AutoMediaBrowserCallback(var player: Player) : +class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibraryService) : MediaLibraryService.MediaLibrarySession.Callback, KoinComponent { private val mediaPlayerController by inject() @@ -234,7 +234,8 @@ class AutoMediaBrowserCallback(var player: Player) : object : FutureCallback { override fun onSuccess(result: SessionResult) { track.starred = !track.starred - // Handle notification reload here + // This needs to be called on the main Thread + libraryService.onUpdateNotification(session) } override fun onFailure(t: Throwable) { @@ -245,7 +246,7 @@ class AutoMediaBrowserCallback(var player: Player) : ).show() } }, - MoreExecutors.directExecutor() + MainThreadExecutor() ) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 1cc994d2..3cb043dd 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -111,7 +111,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent { player.experimentalSetOffloadSchedulingEnabled(true) // Create browser interface - librarySessionCallback = AutoMediaBrowserCallback(player) + librarySessionCallback = AutoMediaBrowserCallback(player, this) // This will need to use the AutoCalls mediaLibrarySession = MediaLibrarySession.Builder(this, player, librarySessionCallback) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java new file mode 100644 index 00000000..12c1ae55 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java @@ -0,0 +1,26 @@ +/* + * MainThreadExecutor.java + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + +package org.moire.ultrasonic.util; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.Executor; + +/* +* Executor for running Futures on the main thread +* See https://stackoverflow.com/questions/52642246/how-to-get-executor-for-main-thread-on-api-level-28 +*/ +public class MainThreadExecutor implements Executor { + private final Handler handler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(Runnable r) { + handler.post(r); + } +} From fefee74a666aab4d68c9ebba4b0689b1670dbcc7 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 3 Jul 2022 18:38:29 +0200 Subject: [PATCH 06/13] Use MainThreadExecutor --- .../service/MediaPlayerController.kt | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 1cbc14fb..70bb945d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -10,6 +10,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Build +import android.widget.Toast import androidx.core.net.toUri import androidx.media3.common.HeartRating import androidx.media3.common.MediaItem @@ -39,6 +40,7 @@ import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4 import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.util.FileUtil +import org.moire.ultrasonic.util.MainThreadExecutor import org.moire.ultrasonic.util.Settings import timber.log.Timber @@ -584,33 +586,29 @@ class MediaPlayerController( if (legacyPlaylistManager.currentPlaying == null) return val song = legacyPlaylistManager.currentPlaying!!.track - fun updateStarred() { - // Trigger an update - // TODO Update Metadata of MediaItem... - // localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying) - song.starred = !song.starred - } - controller?.setRating( HeartRating(!song.starred) ).let { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) { - Futures.addCallback( - it, - object : FutureCallback { - override fun onSuccess(result: SessionResult?) { - updateStarred() - } + Futures.addCallback( + it, + object : FutureCallback { + override fun onSuccess(result: SessionResult?) { + // Trigger an update + // TODO Update Metadata of MediaItem... + // localMediaPlayer.setCurrentPlaying(localMediaPlayer.currentPlaying) + song.starred = !song.starred + } - override fun onFailure(t: Throwable) { - TODO("Not yet implemented") - } - }, - context.mainExecutor - ) - } else { - updateStarred() - } + override fun onFailure(t: Throwable) { + Toast.makeText( + context, + "There was an error updating the rating", + Toast.LENGTH_SHORT + ).show() + } + }, + MainThreadExecutor() + ) } } From 0929a6a1bdeaa0b7eac6add5a5a813a6dcc9d089 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 3 Jul 2022 18:40:58 +0200 Subject: [PATCH 07/13] Lint --- .../kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 70bb945d..40b2ea26 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -9,7 +9,6 @@ package org.moire.ultrasonic.service import android.content.ComponentName import android.content.Context import android.content.Intent -import android.os.Build import android.widget.Toast import androidx.core.net.toUri import androidx.media3.common.HeartRating From b8c924be2763be53b406f99252082e99221f6ce5 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Mon, 4 Jul 2022 18:42:14 +0200 Subject: [PATCH 08/13] MainThreadExecutor to Kotlin --- .../ultrasonic/util/MainThreadExecutor.java | 26 ------------------- .../ultrasonic/util/MainThreadExecutor.kt | 22 ++++++++++++++++ 2 files changed, 22 insertions(+), 26 deletions(-) delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java deleted file mode 100644 index 12c1ae55..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * MainThreadExecutor.java - * Copyright (C) 2009-2022 Ultrasonic developers - * - * Distributed under terms of the GNU GPLv3 license. - */ - -package org.moire.ultrasonic.util; - -import android.os.Handler; -import android.os.Looper; - -import java.util.concurrent.Executor; - -/* -* Executor for running Futures on the main thread -* See https://stackoverflow.com/questions/52642246/how-to-get-executor-for-main-thread-on-api-level-28 -*/ -public class MainThreadExecutor implements Executor { - private final Handler handler = new Handler(Looper.getMainLooper()); - - @Override - public void execute(Runnable r) { - handler.post(r); - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt new file mode 100644 index 00000000..afc72563 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt @@ -0,0 +1,22 @@ +/* + * MainThreadExecutor.java + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ +package org.moire.ultrasonic.util + +import android.os.Handler +import android.os.Looper +import java.util.concurrent.Executor + +/* +* Executor for running Futures on the main thread +* See https://stackoverflow.com/questions/52642246/how-to-get-executor-for-main-thread-on-api-level-28 +*/ +class MainThreadExecutor : Executor { + private val handler = Handler(Looper.getMainLooper()) + override fun execute(r: Runnable) { + handler.post(r) + } +} \ No newline at end of file From aa6c037b200c9a4766744787d912c4590521fff0 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Mon, 4 Jul 2022 18:55:07 +0200 Subject: [PATCH 09/13] Remove useless request --- .../org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 12761b93..f8744bd0 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -286,7 +286,6 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr ): ListenableFuture { return serviceScope.future { if (rating is HeartRating) { - val musicService = MusicServiceFactory.getMusicService() try { if (rating.isHeart) { musicService.star(mediaId, null, null) From 05ada9297d35ef667a5e21622b8a3456db7a6684 Mon Sep 17 00:00:00 2001 From: birdbird <67737443+tzugen@users.noreply.github.com> Date: Tue, 5 Jul 2022 18:18:02 +0200 Subject: [PATCH 10/13] Update MediaNotificationProvider.kt --- .../org/moire/ultrasonic/playback/MediaNotificationProvider.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 60b8b68c..3e0ae5f8 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -28,7 +28,8 @@ class MediaNotificationProvider(context: Context) : /* * It is currently not possible to edit a MediaItem after creation so the isRated value - * is stored in the track.starred value + * is stored in the track.starred value. See https://github.com/androidx/media/issues/33 + * TODO: Once the bug is fixed remove this circular reference! */ private val mediaPlayerController by inject() From d034fc9c718950ac74b9adf5ca799e47b54c7e3a Mon Sep 17 00:00:00 2001 From: birdbird <67737443+tzugen@users.noreply.github.com> Date: Tue, 5 Jul 2022 18:34:24 +0200 Subject: [PATCH 11/13] Update AutoMediaBrowserCallback.kt --- .../org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index f8744bd0..06a85d8b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -171,6 +171,10 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr val connectionResult = super.onConnect(session, controller) val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon() + /* + * TODO: Currently we need to create a custom session command, see https://github.com/androidx/media/issues/107 + * When this issue is fixed we should be able to remove this method again + */ availableSessionCommands.add(SessionCommand(SESSION_CUSTOM_SET_RATING, Bundle())) return MediaSession.ConnectionResult.accept( From 7de775dc2679c91f20a90a9dd21085fb405f9a7b Mon Sep 17 00:00:00 2001 From: tzugen Date: Tue, 5 Jul 2022 18:38:26 +0200 Subject: [PATCH 12/13] Formatting --- .../moire/ultrasonic/playback/MediaNotificationProvider.kt | 6 ++++++ .../kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 3e0ae5f8..fc159127 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -40,6 +40,12 @@ class MediaNotificationProvider(context: Context) : actionFactory: MediaNotification.ActionFactory ): IntArray { val tmp: MutableList = mutableListOf() + /* + * TODO: + * It is currently not possible to edit a MediaItem after creation so the isRated value + * is stored in the track.starred value + * See https://github.com/androidx/media/issues/33 + */ val rating = mediaPlayerController.currentPlayingLegacy?.track?.starred?.let { HeartRating( it diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt index afc72563..a1d162c6 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MainThreadExecutor.kt @@ -19,4 +19,4 @@ class MainThreadExecutor : Executor { override fun execute(r: Runnable) { handler.post(r) } -} \ No newline at end of file +} From 240a2fa8f6c502a1af71149583311698cc6c3a69 Mon Sep 17 00:00:00 2001 From: tzugen Date: Tue, 5 Jul 2022 19:21:07 +0200 Subject: [PATCH 13/13] Fix a bug that the musicService was incorrectly cached... --- .../org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index 06a85d8b..6797ee53 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -103,7 +103,6 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr private val mediaPlayerController by inject() private val activeServerProvider: ActiveServerProvider by inject() - private val musicService = MusicServiceFactory.getMusicService() private val serviceJob = Job() private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) @@ -113,6 +112,7 @@ class AutoMediaBrowserCallback(var player: Player, val libraryService: MediaLibr private var randomSongsCache: List? = null private var searchSongsCache: List? = null + private val musicService get() = MusicServiceFactory.getMusicService() private val isOffline get() = ActiveServerProvider.isOffline() private val useId3Tags get() = Settings.shouldUseId3Tags private val musicFolderId get() = activeServerProvider.getActiveServer().musicFolderId