Add const for custom SessionCommands

Improve rating call
This commit is contained in:
Maxence G 2022-07-02 21:47:32 +02:00
parent 9014b47b74
commit 3f408600cb
No known key found for this signature in database
GPG Key ID: DC1FD9409E3FE284
4 changed files with 53 additions and 28 deletions

View File

@ -752,10 +752,8 @@ class PlayerFragment :
if (currentSong == null) return true if (currentSong == null) return true
val isStarred = currentSong!!.starred val isStarred = currentSong!!.starred
val id = currentSong!!.id
mediaPlayerController.controller?.setRating( mediaPlayerController.controller?.setRating(
id,
HeartRating(!isStarred) HeartRating(!isStarred)
)?.let { )?.let {
Futures.addCallback(it, object : FutureCallback<SessionResult> { Futures.addCallback(it, object : FutureCallback<SessionResult> {

View File

@ -8,7 +8,11 @@
package org.moire.ultrasonic.playback package org.moire.ultrasonic.playback
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle 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.HeartRating
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata 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_ERROR_UNKNOWN
import androidx.media3.session.SessionResult.RESULT_SUCCESS import androidx.media3.session.SessionResult.RESULT_SUCCESS
import com.google.common.collect.ImmutableList 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.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.guava.future import kotlinx.coroutines.guava.future
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.checkerframework.checker.units.qual.Length
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.R 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.Settings
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.coroutineContext
private const val MEDIA_ROOT_ID = "MEDIA_ROOT_ID" private const val MEDIA_ROOT_ID = "MEDIA_ROOT_ID"
private const val MEDIA_ALBUM_ID = "MEDIA_ALBUM_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 DISPLAY_LIMIT = 100
private const val SEARCH_LIMIT = 10 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 * MediaBrowserService implementation for e.g. Android Auto
*/ */
@ -164,8 +177,7 @@ class AutoMediaBrowserCallback(var player: Player) :
val connectionResult = super.onConnect(session, controller) val connectionResult = super.onConnect(session, controller)
val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon() val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
// TODO: Make a Const value list of available custom SessionCommands availableSessionCommands.add(SessionCommand(SESSION_CUSTOM_SET_RATING, Bundle()))
availableSessionCommands.add(SessionCommand("COMMAND_CODE_SESSION_SET_RATING", Bundle()))
return MediaSession.ConnectionResult.accept( return MediaSession.ConnectionResult.accept(
availableSessionCommands.build(), availableSessionCommands.build(),
@ -207,35 +219,52 @@ class AutoMediaBrowserCallback(var player: Player) :
args: Bundle args: Bundle
): ListenableFuture<SessionResult> { ): ListenableFuture<SessionResult> {
/*
* 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) { var customCommandFuture: ListenableFuture<SessionResult>? = null
return when (customCommand.customAction) {
"COMMAND_CODE_SESSION_SET_RATING" -> { when (customCommand.customAction) {
onSetRating( 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, session,
controller, controller,
HeartRating(!rating.isHeart) HeartRating(!track.starred)
) )
} Futures.addCallback(
else -> { customCommandFuture,
Timber.d( object : FutureCallback<SessionResult> {
"CustomCommand not recognized %s with extra %s", override fun onSuccess(result: SessionResult) {
customCommand.customAction, track.starred = !track.starred
customCommand.customExtras.toString() // 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) return super.onCustomCommand(session, controller, customCommand, args)
} }
@ -274,7 +303,6 @@ class AutoMediaBrowserCallback(var player: Player) :
// TODO: Better handle exception // TODO: Better handle exception
return@future SessionResult(RESULT_ERROR_UNKNOWN) return@future SessionResult(RESULT_ERROR_UNKNOWN)
} }
mediaPlayerController.currentPlayingLegacy?.track?.starred = rating.isHeart
return@future SessionResult(RESULT_SUCCESS) return@future SessionResult(RESULT_SUCCESS)
} }
return@future SessionResult(RESULT_ERROR_BAD_VALUE) return@future SessionResult(RESULT_ERROR_BAD_VALUE)

View File

@ -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) .setIconResId(if (rating.isHeart) R.drawable.ic_star_full_dark else R.drawable.ic_star_hollow_dark)
.setSessionCommand( .setSessionCommand(
SessionCommand( SessionCommand(
"COMMAND_CODE_SESSION_SET_RATING", SESSION_CUSTOM_SET_RATING,
HeartRating(rating.isHeart).toBundle() HeartRating(rating.isHeart).toBundle()
) )
) )

View File

@ -594,7 +594,6 @@ class MediaPlayerController(
} }
controller?.setRating( controller?.setRating(
song.id,
HeartRating(!song.starred) HeartRating(!song.starred)
).let { ).let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && it != null) {