feat: cleaned up MediaService class, added support for Android Auto repository

This commit is contained in:
antonio 2023-12-21 09:52:00 +01:00
parent 1a407341ec
commit ed60677608

View file

@ -0,0 +1,241 @@
package com.cappielloantonio.tempo.service
import android.content.Context
import android.os.Bundle
import androidx.annotation.OptIn
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.CommandButton
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 com.cappielloantonio.tempo.R
import com.cappielloantonio.tempo.repository.AutomotiveRepository
import com.google.common.collect.ImmutableList
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
open class MediaLibrarySessionCallback(
context: Context,
automotiveRepository: AutomotiveRepository
) :
MediaLibraryService.MediaLibrarySession.Callback {
private val TAG = "MediaLibraryServiceCall"
init {
MediaBrowserTree.initialize(context, automotiveRepository)
}
private val customLayoutCommandButtons: List<CommandButton> = listOf(
CommandButton.Builder()
.setDisplayName(context.getString(R.string.exo_controls_shuffle_on_description))
.setSessionCommand(
SessionCommand(
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY
)
).setIconResId(R.drawable.exo_icon_shuffle_off).build(),
CommandButton.Builder()
.setDisplayName(context.getString(R.string.exo_controls_shuffle_off_description))
.setSessionCommand(
SessionCommand(
CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY
)
).setIconResId(R.drawable.exo_icon_shuffle_on).build()
)
@OptIn(UnstableApi::class)
val mediaNotificationSessionCommands =
MediaSession.ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon()
.also { builder ->
customLayoutCommandButtons.forEach { commandButton ->
commandButton.sessionCommand?.let { builder.add(it) }
}
}.build()
@OptIn(UnstableApi::class)
override fun onConnect(
session: MediaSession, controller: MediaSession.ControllerInfo
): MediaSession.ConnectionResult {
if (session.isMediaNotificationController(controller) || session.isAutomotiveController(
controller
) || session.isAutoCompanionController(controller)
) {
val customLayout =
customLayoutCommandButtons[if (session.player.shuffleModeEnabled) 1 else 0]
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
.setAvailableSessionCommands(mediaNotificationSessionCommands)
.setCustomLayout(ImmutableList.of(customLayout)).build()
}
return MediaSession.ConnectionResult.AcceptedResultBuilder(session).build()
}
@OptIn(UnstableApi::class)
override fun onCustomCommand(
session: MediaSession,
controller: MediaSession.ControllerInfo,
customCommand: SessionCommand,
args: Bundle
): ListenableFuture<SessionResult> {
if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON == customCommand.customAction) {
session.player.shuffleModeEnabled = true
session.setCustomLayout(
session.mediaNotificationControllerInfo!!,
ImmutableList.of(customLayoutCommandButtons[1])
)
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
} else if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF == customCommand.customAction) {
session.player.shuffleModeEnabled = false
session.setCustomLayout(
session.mediaNotificationControllerInfo!!,
ImmutableList.of(customLayoutCommandButtons[0])
)
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
}
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_ERROR_NOT_SUPPORTED))
}
override fun onGetLibraryRoot(
session: MediaLibraryService.MediaLibrarySession,
browser: MediaSession.ControllerInfo,
params: MediaLibraryService.LibraryParams?
): ListenableFuture<LibraryResult<MediaItem>> {
return Futures.immediateFuture(LibraryResult.ofItem(MediaBrowserTree.getRootItem(), params))
}
override fun onGetChildren(
session: MediaLibraryService.MediaLibrarySession,
browser: MediaSession.ControllerInfo,
parentId: String,
page: Int,
pageSize: Int,
params: MediaLibraryService.LibraryParams?
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
return MediaBrowserTree.getChildren(parentId, params)
}
/* override fun onGetItem(
session: MediaLibraryService.MediaLibrarySession,
browser: MediaSession.ControllerInfo,
mediaId: String
): ListenableFuture<LibraryResult<MediaItem>> {
Log.d(TAG, "onGetItem()")
return MediaBrowserTree.getItem(mediaId)
} */
/* override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: List<MediaItem>
): ListenableFuture<List<MediaItem>> {
Log.d(TAG, "onAddMediaItems()")
return Futures.immediateFuture(mediaItems)
}
@OptIn(UnstableApi::class)
override fun onSetMediaItems(
mediaSession: MediaSession,
browser: MediaSession.ControllerInfo,
mediaItems: List<MediaItem>,
startIndex: Int,
startPositionMs: Long
): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
Log.d(TAG, "onSetMediaItems()")
val mediaItemss: MutableList<MediaItem> = ArrayList()
val mediaMetadata = MediaMetadata.Builder()
.setTitle("Titolo")
.setAlbumTitle("Titolo album")
.setArtist("Artista")
.setIsBrowsable(false)
.setIsPlayable(true)
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
.build()
val mediaItem = MediaItem.Builder()
.setMediaId(mediaItems.get(0).mediaId)
.setMediaMetadata(mediaMetadata)
.setUri(MusicUtil.getStreamUri(mediaItems.get(0).mediaId))
.build()
mediaItemss.add(mediaItem)
return Futures.immediateFuture(
MediaSession.MediaItemsWithStartPosition(
mediaItemss, 0, 0
)
)
} */
/* @OptIn(UnstableApi::class) // MediaSession.MediaItemsWithStartPosition
private fun maybeExpandSingleItemToPlaylist(
mediaItem: MediaItem, startIndex: Int, startPositionMs: Long
): MediaSession.MediaItemsWithStartPosition? {
var playlist = listOf<MediaItem>()
var indexInPlaylist = startIndex
MediaBrowserTree.getItem(mediaItem.mediaId)?.apply {
if (mediaMetadata.isBrowsable == true) {
playlist = MediaBrowserTree.getChildren(mediaId)
} else if (requestMetadata.searchQuery == null) {
MediaBrowserTree.getParentId(mediaId)?.let {
playlist = MediaBrowserTree.getChildren(it).map { mediaItem ->
if (mediaItem.mediaId == mediaId) MediaBrowserTree.expandItem(mediaItem)!! else mediaItem
}
indexInPlaylist = MediaBrowserTree.getIndexInMediaItems(mediaId, playlist)
}
}
}
if (playlist.isNotEmpty()) {
return MediaSession.MediaItemsWithStartPosition(
playlist, indexInPlaylist, startPositionMs
)
}
return null
} */
/* override fun onSearch(
session: MediaLibraryService.MediaLibrarySession,
browser: MediaSession.ControllerInfo,
query: String,
params: MediaLibraryService.LibraryParams?
): ListenableFuture<LibraryResult<Void>> {
session.notifySearchResultChanged(browser, query, MediaBrowserTree.search(query).size, params)
return Futures.immediateFuture(LibraryResult.ofVoid())
} */
/* override fun onGetSearchResult(
session: MediaLibraryService.MediaLibrarySession,
browser: MediaSession.ControllerInfo,
query: String,
page: Int,
pageSize: Int,
params: MediaLibraryService.LibraryParams?
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
return Futures.immediateFuture(
LibraryResult.ofItemList(
MediaBrowserTree.search(query), params
)
)
} */
companion object {
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
"android.media3.session.demo.SHUFFLE_ON"
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
"android.media3.session.demo.SHUFFLE_OFF"
}
}