Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit cc5aa78b authored by Sebastiano Barezzi's avatar Sebastiano Barezzi
Browse files

Twelve: PlaybackService: Implement favorite toggling

Media3 test app has support for this

Change-Id: I83986f095217dff3ca3930822f6e26ce04e8416c
parent 2e6eb908
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import org.lineageos.twelve.models.Provider
import org.lineageos.twelve.models.ProviderIdentifier
import org.lineageos.twelve.models.ProviderType
import org.lineageos.twelve.models.Result
import org.lineageos.twelve.models.Result.Companion.getOrNull
import org.lineageos.twelve.repositories.MediaRepository
import org.lineageos.twelve.utils.PermissionsUtils

@@ -274,6 +275,23 @@ class MediaRepositoryTree(
        it.toMedia3MediaItem(context.resources)
    }

    /**
     * Set the favorite status of a media item. Note that it will only work for audio items.
     *
     * @param mediaId The media ID of the item
     * @param isFavorite The new favorite status
     * @return Whether the operation was successful
     */
    suspend fun setFavorite(
        mediaId: String,
        isFavorite: Boolean,
    ) = mediaIdToMediaItemUri(mediaId)?.let {
        when (mediaItemUriToMediaType(it)) {
            MediaType.AUDIO -> repository.setFavorite(it, isFavorite).getOrNull()
            else -> null
        }
    }?.let { true } ?: false

    /**
     * Convert this media ID to a [Uri] if valid.
     */
+49 −0
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import androidx.lifecycle.ServiceLifecycleDispatcher
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.HeartRating
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Rating
import androidx.media3.common.listen
import androidx.media3.common.util.UnstableApi
import androidx.media3.common.util.Util
@@ -37,6 +39,7 @@ import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionError
import androidx.media3.session.SessionResult
import androidx.preference.PreferenceManager
import com.google.common.util.concurrent.Futures
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.guava.future
@@ -48,6 +51,7 @@ import org.lineageos.twelve.TwelveApplication
import org.lineageos.twelve.ext.enableFloatOutput
import org.lineageos.twelve.ext.enableOffload
import org.lineageos.twelve.ext.mapAsync
import org.lineageos.twelve.ext.mediaItems
import org.lineageos.twelve.ext.next
import org.lineageos.twelve.ext.setOffloadEnabled
import org.lineageos.twelve.ext.skipSilence
@@ -208,6 +212,51 @@ class PlaybackService : MediaLibraryService(), LifecycleOwner {
                .build()
        }

        override fun onSetRating(
            session: MediaSession,
            controller: MediaSession.ControllerInfo,
            mediaId: String,
            rating: Rating
        ) = lifecycleScope.future {
            val heartRating = rating as? HeartRating ?: return@future SessionResult(
                SessionError.ERROR_NOT_SUPPORTED
            )

            SessionResult(
                when (mediaRepositoryTree.setFavorite(mediaId, heartRating.isHeart)) {
                    true -> {
                        // Horrible.
                        player.mediaItems.forEachIndexed { index, mediaItem ->
                            if (mediaItem.mediaId == mediaId) {
                                player.replaceMediaItem(
                                    index,
                                    mediaItem.buildUpon()
                                        .setMediaMetadata(
                                            mediaItem.mediaMetadata.buildUpon()
                                                .setUserRating(heartRating)
                                                .build()
                                        )
                                        .build(),
                                )
                            }
                        }

                        SessionResult.RESULT_SUCCESS
                    }

                    false -> SessionError.ERROR_UNKNOWN
                }
            )
        }

        override fun onSetRating(
            session: MediaSession,
            controller: MediaSession.ControllerInfo,
            rating: Rating
        ) = player.currentMediaItem?.let {
            onSetRating(session, controller, it.mediaId, rating)
        } ?: Futures.immediateFuture(SessionResult(SessionError.ERROR_INVALID_STATE))

        override fun onPlaybackResumption(
            mediaSession: MediaSession,
            controller: MediaSession.ControllerInfo