2021-07-18 13:17:29 +02:00
|
|
|
/*
|
2022-04-04 21:18:07 +02:00
|
|
|
* CustomMediaLibrarySessionCallback.kt
|
|
|
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
2021-07-18 13:17:29 +02:00
|
|
|
*
|
|
|
|
* Distributed under terms of the GNU GPLv3 license.
|
|
|
|
*/
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
package org.moire.ultrasonic.playback
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
import android.net.Uri
|
2021-07-12 16:13:34 +02:00
|
|
|
import android.os.Bundle
|
2022-04-04 21:18:07 +02:00
|
|
|
import androidx.media3.common.MediaItem
|
|
|
|
import androidx.media3.common.MediaMetadata
|
|
|
|
import androidx.media3.common.MediaMetadata.FOLDER_TYPE_ALBUMS
|
|
|
|
import androidx.media3.common.MediaMetadata.FOLDER_TYPE_ARTISTS
|
|
|
|
import androidx.media3.common.MediaMetadata.FOLDER_TYPE_MIXED
|
|
|
|
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.session.LibraryResult
|
|
|
|
import androidx.media3.session.MediaLibraryService
|
|
|
|
import androidx.media3.session.MediaSession
|
|
|
|
import androidx.media3.session.SessionResult
|
|
|
|
import com.google.common.collect.ImmutableList
|
|
|
|
import com.google.common.util.concurrent.Futures
|
|
|
|
import com.google.common.util.concurrent.ListenableFuture
|
2021-07-16 17:29:21 +02:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.Job
|
2022-04-04 21:18:07 +02:00
|
|
|
import kotlinx.coroutines.guava.future
|
2021-07-16 17:29:21 +02:00
|
|
|
import kotlinx.coroutines.launch
|
2022-04-04 21:18:07 +02:00
|
|
|
import org.koin.core.component.KoinComponent
|
|
|
|
import org.koin.core.component.inject
|
2021-07-16 17:29:21 +02:00
|
|
|
import org.moire.ultrasonic.R
|
2021-07-18 11:33:39 +02:00
|
|
|
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
2022-04-04 21:18:07 +02:00
|
|
|
import org.moire.ultrasonic.app.UApp
|
2021-07-16 17:29:21 +02:00
|
|
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
|
|
|
import org.moire.ultrasonic.domain.MusicDirectory
|
2021-07-18 11:33:39 +02:00
|
|
|
import org.moire.ultrasonic.domain.SearchCriteria
|
|
|
|
import org.moire.ultrasonic.domain.SearchResult
|
2022-03-27 14:57:07 +02:00
|
|
|
import org.moire.ultrasonic.domain.Track
|
2022-04-04 21:18:07 +02:00
|
|
|
import org.moire.ultrasonic.service.MediaPlayerController
|
|
|
|
import org.moire.ultrasonic.service.MusicServiceFactory
|
2021-09-24 18:20:53 +02:00
|
|
|
import org.moire.ultrasonic.util.Settings
|
2021-07-16 17:29:21 +02:00
|
|
|
import org.moire.ultrasonic.util.Util
|
2021-07-12 16:13:34 +02:00
|
|
|
import timber.log.Timber
|
|
|
|
|
2021-07-18 13:17:29 +02:00
|
|
|
private const val MEDIA_ROOT_ID = "MEDIA_ROOT_ID"
|
|
|
|
private const val MEDIA_ALBUM_ID = "MEDIA_ALBUM_ID"
|
|
|
|
private const val MEDIA_ALBUM_PAGE_ID = "MEDIA_ALBUM_PAGE_ID"
|
|
|
|
private const val MEDIA_ALBUM_NEWEST_ID = "MEDIA_ALBUM_NEWEST_ID"
|
|
|
|
private const val MEDIA_ALBUM_RECENT_ID = "MEDIA_ALBUM_RECENT_ID"
|
|
|
|
private const val MEDIA_ALBUM_FREQUENT_ID = "MEDIA_ALBUM_FREQUENT_ID"
|
|
|
|
private const val MEDIA_ALBUM_RANDOM_ID = "MEDIA_ALBUM_RANDOM_ID"
|
|
|
|
private const val MEDIA_ALBUM_STARRED_ID = "MEDIA_ALBUM_STARRED_ID"
|
|
|
|
private const val MEDIA_SONG_RANDOM_ID = "MEDIA_SONG_RANDOM_ID"
|
|
|
|
private const val MEDIA_SONG_STARRED_ID = "MEDIA_SONG_STARRED_ID"
|
|
|
|
private const val MEDIA_ARTIST_ID = "MEDIA_ARTIST_ID"
|
|
|
|
private const val MEDIA_LIBRARY_ID = "MEDIA_LIBRARY_ID"
|
|
|
|
private const val MEDIA_PLAYLIST_ID = "MEDIA_PLAYLIST_ID"
|
|
|
|
private const val MEDIA_SHARE_ID = "MEDIA_SHARE_ID"
|
|
|
|
private const val MEDIA_BOOKMARK_ID = "MEDIA_BOOKMARK_ID"
|
|
|
|
private const val MEDIA_PODCAST_ID = "MEDIA_PODCAST_ID"
|
|
|
|
private const val MEDIA_ALBUM_ITEM = "MEDIA_ALBUM_ITEM"
|
|
|
|
private const val MEDIA_PLAYLIST_SONG_ITEM = "MEDIA_PLAYLIST_SONG_ITEM"
|
|
|
|
private const val MEDIA_PLAYLIST_ITEM = "MEDIA_PLAYLIST_ITEM"
|
|
|
|
private const val MEDIA_ARTIST_ITEM = "MEDIA_ARTIST_ITEM"
|
|
|
|
private const val MEDIA_ARTIST_SECTION = "MEDIA_ARTIST_SECTION"
|
|
|
|
private const val MEDIA_ALBUM_SONG_ITEM = "MEDIA_ALBUM_SONG_ITEM"
|
|
|
|
private const val MEDIA_SONG_STARRED_ITEM = "MEDIA_SONG_STARRED_ITEM"
|
|
|
|
private const val MEDIA_SONG_RANDOM_ITEM = "MEDIA_SONG_RANDOM_ITEM"
|
|
|
|
private const val MEDIA_SHARE_ITEM = "MEDIA_SHARE_ITEM"
|
|
|
|
private const val MEDIA_SHARE_SONG_ITEM = "MEDIA_SHARE_SONG_ITEM"
|
|
|
|
private const val MEDIA_BOOKMARK_ITEM = "MEDIA_BOOKMARK_ITEM"
|
|
|
|
private const val MEDIA_PODCAST_ITEM = "MEDIA_PODCAST_ITEM"
|
|
|
|
private const val MEDIA_PODCAST_EPISODE_ITEM = "MEDIA_PODCAST_EPISODE_ITEM"
|
|
|
|
private const val MEDIA_SEARCH_SONG_ITEM = "MEDIA_SEARCH_SONG_ITEM"
|
2021-07-16 17:29:21 +02:00
|
|
|
|
|
|
|
// Currently the display limit for long lists is 100 items
|
2021-07-18 13:17:29 +02:00
|
|
|
private const val DISPLAY_LIMIT = 100
|
|
|
|
private const val SEARCH_LIMIT = 10
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private const val SEARCH_QUERY_PREFIX_COMPAT = "androidx://media3-session/playFromSearch"
|
|
|
|
private const val SEARCH_QUERY_PREFIX = "androidx://media3-session/setMediaUri"
|
|
|
|
|
2021-07-16 17:29:21 +02:00
|
|
|
/**
|
|
|
|
* MediaBrowserService implementation for e.g. Android Auto
|
|
|
|
*/
|
2022-04-05 21:56:13 +02:00
|
|
|
@Suppress("TooManyFunctions", "LargeClass", "UnusedPrivateMember")
|
2022-04-04 21:18:07 +02:00
|
|
|
class AutoMediaBrowserCallback(var player: Player) :
|
|
|
|
MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback, KoinComponent {
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2021-07-16 17:29:21 +02:00
|
|
|
private val mediaPlayerController by inject<MediaPlayerController>()
|
|
|
|
private val activeServerProvider: ActiveServerProvider by inject()
|
2021-07-18 11:33:39 +02:00
|
|
|
private val musicService = MusicServiceFactory.getMusicService()
|
2021-07-16 17:29:21 +02:00
|
|
|
|
|
|
|
private val serviceJob = Job()
|
|
|
|
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
|
|
|
|
2022-03-27 14:57:07 +02:00
|
|
|
private var playlistCache: List<Track>? = null
|
|
|
|
private var starredSongsCache: List<Track>? = null
|
|
|
|
private var randomSongsCache: List<Track>? = null
|
|
|
|
private var searchSongsCache: List<Track>? = null
|
2021-07-16 17:29:21 +02:00
|
|
|
|
|
|
|
private val isOffline get() = ActiveServerProvider.isOffline()
|
2021-09-24 18:20:53 +02:00
|
|
|
private val useId3Tags get() = Settings.shouldUseId3Tags
|
2021-07-16 17:29:21 +02:00
|
|
|
private val musicFolderId get() = activeServerProvider.getActiveServer().musicFolderId
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
/**
|
|
|
|
* Called when a {@link MediaBrowser} requests the root {@link MediaItem} by {@link
|
|
|
|
* MediaBrowser#getLibraryRoot(LibraryParams)}.
|
|
|
|
*
|
|
|
|
* <p>Return a {@link ListenableFuture} to send a {@link LibraryResult} back to the browser
|
|
|
|
* asynchronously. You can also return a {@link LibraryResult} directly by using Guava's
|
|
|
|
* {@link Futures#immediateFuture(Object)}.
|
|
|
|
*
|
|
|
|
* <p>The {@link LibraryResult#params} may differ from the given {@link LibraryParams params}
|
|
|
|
* if the session can't provide a root that matches with the {@code params}.
|
|
|
|
*
|
|
|
|
* <p>To allow browsing the media library, return a {@link LibraryResult} with {@link
|
|
|
|
* LibraryResult#RESULT_SUCCESS} and a root {@link MediaItem} with a valid {@link
|
|
|
|
* MediaItem#mediaId}. The media id is required for the browser to get the children under the
|
|
|
|
* root.
|
|
|
|
*
|
|
|
|
* <p>Interoperability: If this callback is called because a legacy {@link
|
|
|
|
* android.support.v4.media.MediaBrowserCompat} has requested a {@link
|
|
|
|
* androidx.media.MediaBrowserServiceCompat.BrowserRoot}, then the main thread may be blocked
|
|
|
|
* until the returned future is done. If your service may be queried by a legacy {@link
|
|
|
|
* android.support.v4.media.MediaBrowserCompat}, you should ensure that the future completes
|
|
|
|
* quickly to avoid blocking the main thread for a long period of time.
|
|
|
|
*
|
|
|
|
* @param session The session for this event.
|
|
|
|
* @param browser The browser information.
|
|
|
|
* @param params The optional parameters passed by the browser.
|
|
|
|
* @return A pending result that will be resolved with a root media item.
|
|
|
|
* @see SessionCommand#COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT
|
|
|
|
*/
|
|
|
|
override fun onGetLibraryRoot(
|
|
|
|
session: MediaLibraryService.MediaLibrarySession,
|
|
|
|
browser: MediaSession.ControllerInfo,
|
|
|
|
params: MediaLibraryService.LibraryParams?
|
|
|
|
): ListenableFuture<LibraryResult<MediaItem>> {
|
|
|
|
return Futures.immediateFuture(
|
|
|
|
LibraryResult.ofItem(
|
|
|
|
buildMediaItem(
|
|
|
|
"Root Folder",
|
|
|
|
MEDIA_ROOT_ID,
|
|
|
|
isPlayable = false,
|
|
|
|
folderType = FOLDER_TYPE_MIXED
|
|
|
|
),
|
|
|
|
params
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
override fun onGetItem(
|
|
|
|
session: MediaLibraryService.MediaLibrarySession,
|
|
|
|
browser: MediaSession.ControllerInfo,
|
|
|
|
mediaId: String
|
|
|
|
): ListenableFuture<LibraryResult<MediaItem>> {
|
|
|
|
playFromMediaId(mediaId)
|
|
|
|
|
2022-04-05 21:56:13 +02:00
|
|
|
// TODO:
|
2022-04-05 10:10:24 +02:00
|
|
|
// Create LRU Cache of MediaItems, fill it in the other calls
|
|
|
|
// and retrieve it here.
|
2022-04-04 21:18:07 +02:00
|
|
|
return Futures.immediateFuture(
|
|
|
|
LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onGetChildren(
|
|
|
|
session: MediaLibraryService.MediaLibrarySession,
|
|
|
|
browser: MediaSession.ControllerInfo,
|
|
|
|
parentId: String,
|
|
|
|
page: Int,
|
|
|
|
pageSize: Int,
|
|
|
|
params: MediaLibraryService.LibraryParams?
|
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
// TODO: params???
|
|
|
|
return onLoadChildren(parentId)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun setMediaItemFromSearchQuery(query: String) {
|
|
|
|
// Only accept query with pattern "play [Title]" or "[Title]"
|
|
|
|
// Where [Title]: must be exactly matched
|
|
|
|
// If no media with exact name found, play a random media instead
|
|
|
|
val mediaTitle =
|
|
|
|
if (query.startsWith("play ", ignoreCase = true)) {
|
|
|
|
query.drop(5)
|
|
|
|
} else {
|
|
|
|
query
|
|
|
|
}
|
|
|
|
|
|
|
|
playFromMediaId(mediaTitle)
|
|
|
|
}
|
2021-10-31 15:22:15 +01:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
override fun onSetMediaUri(
|
|
|
|
session: MediaSession,
|
|
|
|
controller: MediaSession.ControllerInfo,
|
|
|
|
uri: Uri,
|
|
|
|
extras: Bundle
|
|
|
|
): Int {
|
|
|
|
|
|
|
|
if (uri.toString().startsWith(SEARCH_QUERY_PREFIX) ||
|
|
|
|
uri.toString().startsWith(SEARCH_QUERY_PREFIX_COMPAT)
|
|
|
|
) {
|
|
|
|
val searchQuery =
|
|
|
|
uri.getQueryParameter("query")
|
|
|
|
?: return SessionResult.RESULT_ERROR_NOT_SUPPORTED
|
|
|
|
setMediaItemFromSearchQuery(searchQuery)
|
|
|
|
|
|
|
|
return SessionResult.RESULT_SUCCESS
|
|
|
|
} else {
|
|
|
|
return SessionResult.RESULT_ERROR_NOT_SUPPORTED
|
2021-11-02 17:45:01 +01:00
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Suppress("ReturnCount", "ComplexMethod")
|
|
|
|
fun onLoadChildren(
|
|
|
|
parentId: String,
|
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
Timber.d("AutoMediaBrowserService onLoadChildren called. ParentId: %s", parentId)
|
|
|
|
|
|
|
|
val parentIdParts = parentId.split('|')
|
|
|
|
|
|
|
|
when (parentIdParts.first()) {
|
|
|
|
MEDIA_ROOT_ID -> return getRootItems()
|
|
|
|
MEDIA_LIBRARY_ID -> return getLibrary()
|
|
|
|
MEDIA_ARTIST_ID -> return getArtists()
|
|
|
|
MEDIA_ARTIST_SECTION -> return getArtists(parentIdParts[1])
|
|
|
|
MEDIA_ALBUM_ID -> return getAlbums(AlbumListType.SORTED_BY_NAME)
|
|
|
|
MEDIA_ALBUM_PAGE_ID -> return getAlbums(
|
|
|
|
AlbumListType.fromName(parentIdParts[1]), parentIdParts[2].toInt()
|
|
|
|
)
|
|
|
|
MEDIA_PLAYLIST_ID -> return getPlaylists()
|
|
|
|
MEDIA_ALBUM_FREQUENT_ID -> return getAlbums(AlbumListType.FREQUENT)
|
|
|
|
MEDIA_ALBUM_NEWEST_ID -> return getAlbums(AlbumListType.NEWEST)
|
|
|
|
MEDIA_ALBUM_RECENT_ID -> return getAlbums(AlbumListType.RECENT)
|
|
|
|
MEDIA_ALBUM_RANDOM_ID -> return getAlbums(AlbumListType.RANDOM)
|
|
|
|
MEDIA_ALBUM_STARRED_ID -> return getAlbums(AlbumListType.STARRED)
|
|
|
|
MEDIA_SONG_RANDOM_ID -> return getRandomSongs()
|
|
|
|
MEDIA_SONG_STARRED_ID -> return getStarredSongs()
|
|
|
|
MEDIA_SHARE_ID -> return getShares()
|
|
|
|
MEDIA_BOOKMARK_ID -> return getBookmarks()
|
|
|
|
MEDIA_PODCAST_ID -> return getPodcasts()
|
|
|
|
MEDIA_PLAYLIST_ITEM -> return getPlaylist(parentIdParts[1], parentIdParts[2])
|
|
|
|
MEDIA_ARTIST_ITEM -> return getAlbumsForArtist(
|
|
|
|
parentIdParts[1], parentIdParts[2]
|
|
|
|
)
|
|
|
|
MEDIA_ALBUM_ITEM -> return getSongsForAlbum(parentIdParts[1], parentIdParts[2])
|
|
|
|
MEDIA_SHARE_ITEM -> return getSongsForShare(parentIdParts[1])
|
|
|
|
MEDIA_PODCAST_ITEM -> return getPodcastEpisodes(parentIdParts[1])
|
|
|
|
else -> return Futures.immediateFuture(LibraryResult.ofItemList(listOf(), null))
|
2021-07-12 16:13:34 +02:00
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
}
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
fun onSearch(
|
|
|
|
query: String,
|
|
|
|
extras: Bundle?,
|
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
Timber.d("AutoMediaBrowserService onSearch query: %s", query)
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
|
|
|
|
return serviceScope.future {
|
|
|
|
val criteria = SearchCriteria(query, SEARCH_LIMIT, SEARCH_LIMIT, SEARCH_LIMIT)
|
|
|
|
val searchResult = callWithErrorHandling { musicService.search(criteria) }
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
// TODO Add More... button to categories
|
|
|
|
if (searchResult != null) {
|
|
|
|
searchResult.artists.map { artist ->
|
|
|
|
mediaItems.add(
|
|
|
|
artist.name ?: "",
|
|
|
|
listOf(MEDIA_ARTIST_ITEM, artist.id, artist.name).joinToString("|"),
|
|
|
|
FOLDER_TYPE_ARTISTS
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
searchResult.albums.map { album ->
|
|
|
|
mediaItems.add(
|
|
|
|
album.title ?: "",
|
|
|
|
listOf(MEDIA_ALBUM_ITEM, album.id, album.name)
|
|
|
|
.joinToString("|"),
|
|
|
|
FOLDER_TYPE_ALBUMS
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
searchSongsCache = searchResult.songs
|
|
|
|
searchResult.songs.map { song ->
|
|
|
|
mediaItems.add(
|
|
|
|
buildMediaItemFromTrack(
|
|
|
|
song,
|
|
|
|
listOf(MEDIA_SEARCH_SONG_ITEM, song.id).joinToString("|"),
|
|
|
|
isPlayable = true
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
|
|
|
}
|
2021-07-12 16:13:34 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 17:45:01 +01:00
|
|
|
@Suppress("MagicNumber", "ComplexMethod")
|
|
|
|
private fun playFromMediaId(mediaId: String?) {
|
|
|
|
Timber.d(
|
|
|
|
"AutoMediaBrowserService onPlayFromMediaIdRequested called. mediaId: %s",
|
|
|
|
mediaId
|
|
|
|
)
|
|
|
|
|
|
|
|
if (mediaId == null) return
|
|
|
|
val mediaIdParts = mediaId.split('|')
|
|
|
|
|
|
|
|
when (mediaIdParts.first()) {
|
|
|
|
MEDIA_PLAYLIST_ITEM -> playPlaylist(mediaIdParts[1], mediaIdParts[2])
|
|
|
|
MEDIA_PLAYLIST_SONG_ITEM -> playPlaylistSong(
|
|
|
|
mediaIdParts[1], mediaIdParts[2], mediaIdParts[3]
|
|
|
|
)
|
|
|
|
MEDIA_ALBUM_ITEM -> playAlbum(mediaIdParts[1], mediaIdParts[2])
|
|
|
|
MEDIA_ALBUM_SONG_ITEM -> playAlbumSong(
|
|
|
|
mediaIdParts[1], mediaIdParts[2], mediaIdParts[3]
|
|
|
|
)
|
|
|
|
MEDIA_SONG_STARRED_ID -> playStarredSongs()
|
|
|
|
MEDIA_SONG_STARRED_ITEM -> playStarredSong(mediaIdParts[1])
|
|
|
|
MEDIA_SONG_RANDOM_ID -> playRandomSongs()
|
|
|
|
MEDIA_SONG_RANDOM_ITEM -> playRandomSong(mediaIdParts[1])
|
|
|
|
MEDIA_SHARE_ITEM -> playShare(mediaIdParts[1])
|
|
|
|
MEDIA_SHARE_SONG_ITEM -> playShareSong(mediaIdParts[1], mediaIdParts[2])
|
|
|
|
MEDIA_BOOKMARK_ITEM -> playBookmark(mediaIdParts[1])
|
|
|
|
MEDIA_PODCAST_ITEM -> playPodcast(mediaIdParts[1])
|
|
|
|
MEDIA_PODCAST_EPISODE_ITEM -> playPodcastEpisode(
|
|
|
|
mediaIdParts[1], mediaIdParts[2]
|
|
|
|
)
|
|
|
|
MEDIA_SEARCH_SONG_ITEM -> playSearch(mediaIdParts[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playFromSearchCommand(query: String?) {
|
|
|
|
Timber.d("AutoMediaBrowserService onPlayFromSearchRequested query: %s", query)
|
|
|
|
if (query.isNullOrBlank()) playRandomSongs()
|
|
|
|
|
|
|
|
serviceScope.launch {
|
|
|
|
val criteria = SearchCriteria(query!!, 0, 0, DISPLAY_LIMIT)
|
|
|
|
val searchResult = callWithErrorHandling { musicService.search(criteria) }
|
|
|
|
|
|
|
|
// Try to find the best match
|
|
|
|
if (searchResult != null) {
|
|
|
|
val song = searchResult.songs
|
|
|
|
.asSequence()
|
|
|
|
.sortedByDescending { song -> song.starred }
|
|
|
|
.sortedByDescending { song -> song.averageRating }
|
|
|
|
.sortedByDescending { song -> song.userRating }
|
|
|
|
.sortedByDescending { song -> song.closeness }
|
|
|
|
.firstOrNull()
|
|
|
|
|
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-18 13:17:29 +02:00
|
|
|
private fun playSearch(id: String) {
|
2021-07-18 11:33:39 +02:00
|
|
|
serviceScope.launch {
|
|
|
|
// If there is no cache, we can't play the selected song.
|
|
|
|
if (searchSongsCache != null) {
|
|
|
|
val song = searchSongsCache!!.firstOrNull { x -> x.id == id }
|
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
2021-07-12 16:13:34 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getRootItems(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
if (!isOffline)
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.music_library_label,
|
|
|
|
MEDIA_LIBRARY_ID,
|
|
|
|
null
|
|
|
|
)
|
2021-07-12 16:13:34 +02:00
|
|
|
|
|
|
|
mediaItems.add(
|
2021-07-16 17:29:21 +02:00
|
|
|
R.string.main_artists_title,
|
|
|
|
MEDIA_ARTIST_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
null,
|
|
|
|
folderType = FOLDER_TYPE_ARTISTS
|
2021-07-12 16:13:34 +02:00
|
|
|
)
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
if (!isOffline)
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_title,
|
|
|
|
MEDIA_ALBUM_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
null,
|
|
|
|
folderType = FOLDER_TYPE_ALBUMS
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
2021-07-12 16:13:34 +02:00
|
|
|
|
|
|
|
mediaItems.add(
|
2021-07-16 17:29:21 +02:00
|
|
|
R.string.playlist_label,
|
|
|
|
MEDIA_PLAYLIST_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
null,
|
|
|
|
folderType = FOLDER_TYPE_PLAYLISTS
|
2021-07-12 16:13:34 +02:00
|
|
|
)
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return Futures.immediateFuture(LibraryResult.ofItemList(mediaItems, null))
|
2021-07-12 16:13:34 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getLibrary(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-12 16:13:34 +02:00
|
|
|
|
2021-07-16 17:29:21 +02:00
|
|
|
// Songs
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_songs_random,
|
|
|
|
MEDIA_SONG_RANDOM_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_songs_title,
|
|
|
|
folderType = FOLDER_TYPE_TITLES
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
2021-07-12 16:13:34 +02:00
|
|
|
|
|
|
|
mediaItems.add(
|
2021-07-16 17:29:21 +02:00
|
|
|
R.string.main_songs_starred,
|
|
|
|
MEDIA_SONG_STARRED_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_songs_title,
|
|
|
|
folderType = FOLDER_TYPE_TITLES
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Albums
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_newest,
|
|
|
|
MEDIA_ALBUM_NEWEST_ID,
|
|
|
|
R.string.main_albums_title
|
|
|
|
)
|
|
|
|
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_recent,
|
|
|
|
MEDIA_ALBUM_RECENT_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_albums_title,
|
|
|
|
folderType = FOLDER_TYPE_ALBUMS
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_frequent,
|
|
|
|
MEDIA_ALBUM_FREQUENT_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_albums_title,
|
|
|
|
folderType = FOLDER_TYPE_ALBUMS
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_random,
|
|
|
|
MEDIA_ALBUM_RANDOM_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_albums_title,
|
|
|
|
folderType = FOLDER_TYPE_ALBUMS
|
2021-07-12 16:13:34 +02:00
|
|
|
)
|
|
|
|
|
2021-07-16 17:29:21 +02:00
|
|
|
mediaItems.add(
|
|
|
|
R.string.main_albums_starred,
|
|
|
|
MEDIA_ALBUM_STARRED_ID,
|
2022-04-04 21:18:07 +02:00
|
|
|
R.string.main_albums_title,
|
|
|
|
folderType = FOLDER_TYPE_ALBUMS
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Other
|
2022-04-04 21:18:07 +02:00
|
|
|
mediaItems.add(R.string.button_bar_shares, MEDIA_SHARE_ID, null)
|
|
|
|
mediaItems.add(R.string.button_bar_bookmarks, MEDIA_BOOKMARK_ID, null)
|
|
|
|
mediaItems.add(R.string.button_bar_podcasts, MEDIA_PODCAST_ID, null)
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return Futures.immediateFuture(LibraryResult.ofItemList(mediaItems, null))
|
2021-07-12 16:13:34 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2021-07-18 13:17:29 +02:00
|
|
|
private fun getArtists(
|
|
|
|
section: String? = null
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val childMediaId: String
|
2021-07-16 17:29:21 +02:00
|
|
|
var artists = if (!isOffline && useId3Tags) {
|
2021-07-18 11:33:39 +02:00
|
|
|
childMediaId = MEDIA_ARTIST_ITEM
|
2021-07-16 17:29:21 +02:00
|
|
|
// TODO this list can be big so we're not refreshing.
|
|
|
|
// Maybe a refresh menu item can be added
|
2021-07-18 11:33:39 +02:00
|
|
|
callWithErrorHandling { musicService.getArtists(false) }
|
2021-07-16 17:29:21 +02:00
|
|
|
} else {
|
2021-07-18 11:33:39 +02:00
|
|
|
// This will be handled at getSongsForAlbum, which supports navigation
|
|
|
|
childMediaId = MEDIA_ALBUM_ITEM
|
|
|
|
callWithErrorHandling { musicService.getIndexes(musicFolderId, false) }
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
if (artists != null) {
|
|
|
|
if (section != null)
|
|
|
|
artists = artists.filter { artist ->
|
|
|
|
getSectionFromName(artist.name ?: "") == section
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
// If there are too many artists, create alphabetic index of them
|
2021-07-18 13:17:29 +02:00
|
|
|
if (section == null && artists.count() > DISPLAY_LIMIT) {
|
2021-07-18 11:33:39 +02:00
|
|
|
val index = mutableListOf<String>()
|
|
|
|
// TODO This sort should use ignoredArticles somehow...
|
|
|
|
artists = artists.sortedBy { artist -> artist.name }
|
|
|
|
artists.map { artist ->
|
|
|
|
val currentSection = getSectionFromName(artist.name ?: "")
|
|
|
|
if (!index.contains(currentSection)) {
|
|
|
|
index.add(currentSection)
|
|
|
|
mediaItems.add(
|
|
|
|
currentSection,
|
|
|
|
listOf(MEDIA_ARTIST_SECTION, currentSection).joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_ARTISTS
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
artists.map { artist ->
|
2021-07-16 17:29:21 +02:00
|
|
|
mediaItems.add(
|
2021-07-18 11:33:39 +02:00
|
|
|
artist.name ?: "",
|
|
|
|
listOf(childMediaId, artist.id, artist.name).joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_ARTISTS
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getAlbumsForArtist(
|
|
|
|
id: String,
|
|
|
|
name: String
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
|
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val albums = if (!isOffline && useId3Tags) {
|
2021-07-18 13:17:29 +02:00
|
|
|
callWithErrorHandling { musicService.getArtist(id, name, false) }
|
2021-07-16 17:29:21 +02:00
|
|
|
} else {
|
2021-11-26 17:03:33 +01:00
|
|
|
callWithErrorHandling {
|
|
|
|
musicService.getMusicDirectory(id, name, false).getAlbums()
|
|
|
|
}
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
|
2021-11-26 17:03:33 +01:00
|
|
|
albums?.map { album ->
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.add(
|
|
|
|
album.title ?: "",
|
|
|
|
listOf(MEDIA_ALBUM_ITEM, album.id, album.name)
|
|
|
|
.joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_ALBUMS
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getSongsForAlbum(
|
|
|
|
id: String,
|
|
|
|
name: String
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val songs = listSongsInMusicService(id, name)
|
|
|
|
|
|
|
|
if (songs != null) {
|
|
|
|
if (songs.getChildren(includeDirs = true, includeFiles = false).count() == 0 &&
|
|
|
|
songs.getChildren(includeDirs = false, includeFiles = true).count() > 0
|
|
|
|
)
|
|
|
|
mediaItems.addPlayAllItem(listOf(MEDIA_ALBUM_ITEM, id, name).joinToString("|"))
|
|
|
|
|
|
|
|
// TODO: Paging is not implemented for songs, is it necessary at all?
|
2021-11-26 17:03:33 +01:00
|
|
|
val items = songs.getTracks().take(DISPLAY_LIMIT)
|
2021-07-18 11:33:39 +02:00
|
|
|
items.map { item ->
|
|
|
|
if (item.isDirectory)
|
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
item.title ?: "",
|
|
|
|
listOf(MEDIA_ALBUM_ITEM, item.id, item.name).joinToString("|"),
|
|
|
|
FOLDER_TYPE_TITLES
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
else
|
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
item,
|
|
|
|
listOf(
|
|
|
|
MEDIA_ALBUM_SONG_ITEM,
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
item.id
|
|
|
|
).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getAlbums(
|
2021-07-18 11:33:39 +02:00
|
|
|
type: AlbumListType,
|
|
|
|
page: Int? = null
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
|
|
|
|
return serviceScope.future {
|
2021-07-18 13:17:29 +02:00
|
|
|
val offset = (page ?: 0) * DISPLAY_LIMIT
|
2021-07-18 11:33:39 +02:00
|
|
|
val albums = if (useId3Tags) {
|
2021-07-18 13:17:29 +02:00
|
|
|
callWithErrorHandling {
|
|
|
|
musicService.getAlbumList2(
|
|
|
|
type.typeName, DISPLAY_LIMIT, offset, null
|
|
|
|
)
|
|
|
|
}
|
2021-07-18 11:33:39 +02:00
|
|
|
} else {
|
2021-07-18 13:17:29 +02:00
|
|
|
callWithErrorHandling {
|
|
|
|
musicService.getAlbumList(
|
|
|
|
type.typeName, DISPLAY_LIMIT, offset, null
|
|
|
|
)
|
|
|
|
}
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
|
2021-11-30 00:46:48 +01:00
|
|
|
albums?.map { album ->
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.add(
|
|
|
|
album.title ?: "",
|
|
|
|
listOf(MEDIA_ALBUM_ITEM, album.id, album.name)
|
|
|
|
.joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_ALBUMS
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-11-27 00:51:41 +01:00
|
|
|
if (albums?.size ?: 0 >= DISPLAY_LIMIT)
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.add(
|
|
|
|
R.string.search_more,
|
|
|
|
listOf(MEDIA_ALBUM_PAGE_ID, type.typeName, (page ?: 0) + 1).joinToString("|"),
|
|
|
|
null
|
|
|
|
)
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getPlaylists(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val playlists = callWithErrorHandling { musicService.getPlaylists(true) }
|
|
|
|
playlists?.map { playlist ->
|
2021-07-16 17:29:21 +02:00
|
|
|
mediaItems.add(
|
|
|
|
playlist.name,
|
|
|
|
listOf(MEDIA_PLAYLIST_ITEM, playlist.id, playlist.name)
|
|
|
|
.joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_PLAYLISTS
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-18 13:17:29 +02:00
|
|
|
private fun getPlaylist(
|
|
|
|
id: String,
|
|
|
|
name: String,
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val content = callWithErrorHandling { musicService.getPlaylist(id, name) }
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
if (content != null) {
|
2021-11-27 00:51:41 +01:00
|
|
|
if (content.size > 1)
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.addPlayAllItem(
|
|
|
|
listOf(MEDIA_PLAYLIST_ITEM, id, name).joinToString("|")
|
|
|
|
)
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
// Playlist should be cached as it may contain random elements
|
2021-11-26 17:03:33 +01:00
|
|
|
playlistCache = content.getTracks()
|
2021-07-18 13:17:29 +02:00
|
|
|
playlistCache!!.take(DISPLAY_LIMIT).map { item ->
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
item,
|
|
|
|
listOf(
|
|
|
|
MEDIA_PLAYLIST_SONG_ITEM,
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
item.id
|
|
|
|
).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playPlaylist(id: String, name: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
if (playlistCache == null) {
|
|
|
|
// This can only happen if Android Auto cached items, but Ultrasonic has forgot them
|
2021-07-18 11:33:39 +02:00
|
|
|
val content = callWithErrorHandling { musicService.getPlaylist(id, name) }
|
2021-11-26 17:03:33 +01:00
|
|
|
playlistCache = content?.getTracks()
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
2022-04-03 23:57:50 +02:00
|
|
|
if (playlistCache != null) playSongs(playlistCache!!)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playPlaylistSong(id: String, name: String, songId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
if (playlistCache == null) {
|
|
|
|
// This can only happen if Android Auto cached items, but Ultrasonic has forgot them
|
2021-07-18 11:33:39 +02:00
|
|
|
val content = callWithErrorHandling { musicService.getPlaylist(id, name) }
|
2021-11-26 17:03:33 +01:00
|
|
|
playlistCache = content?.getTracks()
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
2021-07-18 13:17:29 +02:00
|
|
|
val song = playlistCache?.firstOrNull { x -> x.id == songId }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playAlbum(id: String, name: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val songs = listSongsInMusicService(id, name)
|
2021-11-26 17:03:33 +01:00
|
|
|
if (songs != null) playSongs(songs.getTracks())
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playAlbumSong(id: String, name: String, songId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val songs = listSongsInMusicService(id, name)
|
2021-11-26 17:03:33 +01:00
|
|
|
val song = songs?.getTracks()?.firstOrNull { x -> x.id == songId }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getPodcasts(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
|
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val podcasts = callWithErrorHandling { musicService.getPodcastsChannels(false) }
|
|
|
|
|
|
|
|
podcasts?.map { podcast ->
|
|
|
|
mediaItems.add(
|
|
|
|
podcast.title ?: "",
|
|
|
|
listOf(MEDIA_PODCAST_ITEM, podcast.id).joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_MIXED
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun getPodcastEpisodes(
|
|
|
|
id: String
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val episodes = callWithErrorHandling { musicService.getPodcastEpisodes(id) }
|
|
|
|
|
|
|
|
if (episodes != null) {
|
2021-11-26 17:03:33 +01:00
|
|
|
if (episodes.getTracks().count() > 1)
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.addPlayAllItem(listOf(MEDIA_PODCAST_ITEM, id).joinToString("|"))
|
|
|
|
|
2021-11-26 17:03:33 +01:00
|
|
|
episodes.getTracks().map { episode ->
|
2021-07-18 13:17:29 +02:00
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
episode,
|
|
|
|
listOf(MEDIA_PODCAST_EPISODE_ITEM, id, episode.id)
|
|
|
|
.joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 13:17:29 +02:00
|
|
|
)
|
|
|
|
)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playPodcast(id: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val episodes = callWithErrorHandling { musicService.getPodcastEpisodes(id) }
|
|
|
|
if (episodes != null) {
|
2021-11-26 17:03:33 +01:00
|
|
|
playSongs(episodes.getTracks())
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playPodcastEpisode(id: String, episodeId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val episodes = callWithErrorHandling { musicService.getPodcastEpisodes(id) }
|
|
|
|
if (episodes != null) {
|
|
|
|
val selectedEpisode = episodes
|
2021-11-26 17:03:33 +01:00
|
|
|
.getTracks()
|
2021-07-18 11:33:39 +02:00
|
|
|
.firstOrNull { episode -> episode.id == episodeId }
|
|
|
|
if (selectedEpisode != null) playSong(selectedEpisode)
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getBookmarks(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val bookmarks = callWithErrorHandling { musicService.getBookmarks() }
|
|
|
|
if (bookmarks != null) {
|
|
|
|
val songs = Util.getSongsFromBookmarks(bookmarks)
|
|
|
|
|
2021-11-26 17:03:33 +01:00
|
|
|
songs.getTracks().map { song ->
|
2021-07-18 13:17:29 +02:00
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
song,
|
|
|
|
listOf(MEDIA_BOOKMARK_ITEM, song.id).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 13:17:29 +02:00
|
|
|
)
|
|
|
|
)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playBookmark(id: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val bookmarks = callWithErrorHandling { musicService.getBookmarks() }
|
|
|
|
if (bookmarks != null) {
|
|
|
|
val songs = Util.getSongsFromBookmarks(bookmarks)
|
2021-11-26 17:03:33 +01:00
|
|
|
val song = songs.getTracks().firstOrNull { song -> song.id == id }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getShares(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val shares = callWithErrorHandling { musicService.getShares(false) }
|
|
|
|
|
|
|
|
shares?.map { share ->
|
|
|
|
mediaItems.add(
|
|
|
|
share.name ?: "",
|
|
|
|
listOf(MEDIA_SHARE_ITEM, share.id)
|
|
|
|
.joinToString("|"),
|
2022-04-04 21:18:07 +02:00
|
|
|
FOLDER_TYPE_MIXED
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun getSongsForShare(
|
|
|
|
id: String
|
2022-04-04 21:18:07 +02:00
|
|
|
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val shares = callWithErrorHandling { musicService.getShares(false) }
|
|
|
|
|
2021-07-18 13:17:29 +02:00
|
|
|
val selectedShare = shares?.firstOrNull { share -> share.id == id }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (selectedShare != null) {
|
|
|
|
|
|
|
|
if (selectedShare.getEntries().count() > 1)
|
|
|
|
mediaItems.addPlayAllItem(listOf(MEDIA_SHARE_ITEM, id).joinToString("|"))
|
|
|
|
|
|
|
|
selectedShare.getEntries().map { song ->
|
2021-07-18 13:17:29 +02:00
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
song,
|
|
|
|
listOf(MEDIA_SHARE_SONG_ITEM, id, song.id).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 13:17:29 +02:00
|
|
|
)
|
|
|
|
)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun playShare(id: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val shares = callWithErrorHandling { musicService.getShares(false) }
|
2021-07-18 13:17:29 +02:00
|
|
|
val selectedShare = shares?.firstOrNull { share -> share.id == id }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (selectedShare != null) {
|
|
|
|
playSongs(selectedShare.getEntries())
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun playShareSong(id: String, songId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
val shares = callWithErrorHandling { musicService.getShares(false) }
|
2021-07-18 13:17:29 +02:00
|
|
|
val selectedShare = shares?.firstOrNull { share -> share.id == id }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (selectedShare != null) {
|
2021-07-18 13:17:29 +02:00
|
|
|
val song = selectedShare.getEntries().firstOrNull { x -> x.id == songId }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getStarredSongs(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 11:33:39 +02:00
|
|
|
val songs = listStarredSongsInMusicService()
|
|
|
|
|
|
|
|
if (songs != null) {
|
|
|
|
if (songs.songs.count() > 1)
|
|
|
|
mediaItems.addPlayAllItem(listOf(MEDIA_SONG_STARRED_ID).joinToString("|"))
|
|
|
|
|
|
|
|
// TODO: Paging is not implemented for songs, is it necessary at all?
|
2021-07-18 13:17:29 +02:00
|
|
|
val items = songs.songs.take(DISPLAY_LIMIT)
|
2021-07-18 11:33:39 +02:00
|
|
|
starredSongsCache = items
|
|
|
|
items.map { song ->
|
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
song,
|
|
|
|
listOf(MEDIA_SONG_STARRED_ITEM, song.id).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun playStarredSongs() {
|
|
|
|
serviceScope.launch {
|
|
|
|
if (starredSongsCache == null) {
|
|
|
|
// This can only happen if Android Auto cached items, but Ultrasonic has forgot them
|
|
|
|
val content = listStarredSongsInMusicService()
|
|
|
|
starredSongsCache = content?.songs
|
|
|
|
}
|
2022-04-03 23:57:50 +02:00
|
|
|
if (starredSongsCache != null) playSongs(starredSongsCache!!)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2021-07-18 11:33:39 +02:00
|
|
|
private fun playStarredSong(songId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
if (starredSongsCache == null) {
|
|
|
|
// This can only happen if Android Auto cached items, but Ultrasonic has forgot them
|
|
|
|
val content = listStarredSongsInMusicService()
|
|
|
|
starredSongsCache = content?.songs
|
|
|
|
}
|
2021-07-18 13:17:29 +02:00
|
|
|
val song = starredSongsCache?.firstOrNull { x -> x.id == songId }
|
2021-07-18 11:33:39 +02:00
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun getRandomSongs(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
|
|
val mediaItems: MutableList<MediaItem> = ArrayList()
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
return serviceScope.future {
|
2021-07-18 13:17:29 +02:00
|
|
|
val songs = callWithErrorHandling { musicService.getRandomSongs(DISPLAY_LIMIT) }
|
2021-07-18 11:33:39 +02:00
|
|
|
|
|
|
|
if (songs != null) {
|
2021-11-27 00:51:41 +01:00
|
|
|
if (songs.size > 1)
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaItems.addPlayAllItem(listOf(MEDIA_SONG_RANDOM_ID).joinToString("|"))
|
|
|
|
|
|
|
|
// TODO: Paging is not implemented for songs, is it necessary at all?
|
2021-11-26 17:03:33 +01:00
|
|
|
val items = songs.getTracks()
|
2021-07-18 11:33:39 +02:00
|
|
|
randomSongsCache = items
|
|
|
|
items.map { song ->
|
|
|
|
mediaItems.add(
|
2022-04-04 21:18:07 +02:00
|
|
|
buildMediaItemFromTrack(
|
|
|
|
song,
|
|
|
|
listOf(MEDIA_SONG_RANDOM_ITEM, song.id).joinToString("|"),
|
|
|
|
isPlayable = true
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
return@future LibraryResult.ofItemList(mediaItems, null)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playRandomSongs() {
|
|
|
|
serviceScope.launch {
|
|
|
|
if (randomSongsCache == null) {
|
|
|
|
// This can only happen if Android Auto cached items, but Ultrasonic has forgot them
|
|
|
|
// In this case we request a new set of random songs
|
2021-07-18 13:17:29 +02:00
|
|
|
val content = callWithErrorHandling { musicService.getRandomSongs(DISPLAY_LIMIT) }
|
2021-11-26 17:03:33 +01:00
|
|
|
randomSongsCache = content?.getTracks()
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
2022-04-03 23:57:50 +02:00
|
|
|
if (randomSongsCache != null) playSongs(randomSongsCache!!)
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun playRandomSong(songId: String) {
|
|
|
|
serviceScope.launch {
|
|
|
|
// If there is no cache, we can't play the selected song.
|
|
|
|
if (randomSongsCache != null) {
|
|
|
|
val song = randomSongsCache!!.firstOrNull { x -> x.id == songId }
|
|
|
|
if (song != null) playSong(song)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun listSongsInMusicService(id: String, name: String): MusicDirectory? {
|
2021-09-24 18:20:53 +02:00
|
|
|
return if (!ActiveServerProvider.isOffline() && Settings.shouldUseId3Tags) {
|
2021-07-18 11:33:39 +02:00
|
|
|
callWithErrorHandling { musicService.getAlbum(id, name, false) }
|
|
|
|
} else {
|
|
|
|
callWithErrorHandling { musicService.getMusicDirectory(id, name, false) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun listStarredSongsInMusicService(): SearchResult? {
|
2021-09-24 18:20:53 +02:00
|
|
|
return if (Settings.shouldUseId3Tags) {
|
2021-07-18 11:33:39 +02:00
|
|
|
callWithErrorHandling { musicService.getStarred2() }
|
|
|
|
} else {
|
|
|
|
callWithErrorHandling { musicService.getStarred() }
|
|
|
|
}
|
2021-07-16 17:29:21 +02:00
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun MutableList<MediaItem>.add(
|
2021-07-16 17:29:21 +02:00
|
|
|
title: String,
|
|
|
|
mediaId: String,
|
2022-04-04 21:18:07 +02:00
|
|
|
folderType: Int
|
2021-07-16 17:29:21 +02:00
|
|
|
) {
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
val mediaItem = buildMediaItem(
|
|
|
|
title,
|
|
|
|
mediaId,
|
|
|
|
isPlayable = false,
|
|
|
|
folderType = folderType
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
this.add(mediaItem)
|
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun MutableList<MediaItem>.add(
|
2021-07-16 17:29:21 +02:00
|
|
|
resId: Int,
|
|
|
|
mediaId: String,
|
|
|
|
groupNameId: Int?,
|
2022-04-04 21:18:07 +02:00
|
|
|
browsable: Boolean = true,
|
|
|
|
folderType: Int = FOLDER_TYPE_MIXED
|
2021-07-16 17:29:21 +02:00
|
|
|
) {
|
2022-04-04 21:18:07 +02:00
|
|
|
val applicationContext = UApp.applicationContext()
|
2021-07-16 17:29:21 +02:00
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
val mediaItem = buildMediaItem(
|
|
|
|
applicationContext.getString(resId),
|
|
|
|
mediaId,
|
|
|
|
isPlayable = false,
|
|
|
|
folderType = folderType
|
2021-07-16 17:29:21 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
this.add(mediaItem)
|
|
|
|
}
|
|
|
|
|
2022-04-04 21:18:07 +02:00
|
|
|
private fun MutableList<MediaItem>.addPlayAllItem(
|
2021-07-18 11:33:39 +02:00
|
|
|
mediaId: String,
|
|
|
|
) {
|
|
|
|
this.add(
|
|
|
|
R.string.select_album_play_all,
|
|
|
|
mediaId,
|
|
|
|
null,
|
|
|
|
false
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-07-16 17:29:21 +02:00
|
|
|
private fun getSectionFromName(name: String): String {
|
|
|
|
var section = name.first().uppercaseChar()
|
|
|
|
if (!section.isLetter()) section = '#'
|
|
|
|
return section.toString()
|
|
|
|
}
|
2021-07-18 11:33:39 +02:00
|
|
|
|
2022-04-03 23:57:50 +02:00
|
|
|
private fun playSongs(songs: List<Track>) {
|
2021-08-27 23:53:31 +02:00
|
|
|
mediaPlayerController.addToPlaylist(
|
2021-07-18 11:33:39 +02:00
|
|
|
songs,
|
2022-04-03 23:57:50 +02:00
|
|
|
cachePermanently = false,
|
2021-07-18 11:33:39 +02:00
|
|
|
autoPlay = true,
|
|
|
|
shuffle = false,
|
2022-04-03 23:57:50 +02:00
|
|
|
insertionMode = MediaPlayerController.InsertionMode.CLEAR
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-03-27 14:57:07 +02:00
|
|
|
private fun playSong(song: Track) {
|
2021-08-27 23:53:31 +02:00
|
|
|
mediaPlayerController.addToPlaylist(
|
2021-07-18 11:33:39 +02:00
|
|
|
listOf(song),
|
2022-04-03 23:57:50 +02:00
|
|
|
cachePermanently = false,
|
2021-07-18 11:33:39 +02:00
|
|
|
autoPlay = false,
|
|
|
|
shuffle = false,
|
2022-04-03 23:57:50 +02:00
|
|
|
insertionMode = MediaPlayerController.InsertionMode.AFTER_CURRENT
|
2021-07-18 11:33:39 +02:00
|
|
|
)
|
2022-04-03 23:57:50 +02:00
|
|
|
if (mediaPlayerController.mediaItemCount > 1) mediaPlayerController.next()
|
2021-07-19 15:31:52 +02:00
|
|
|
else mediaPlayerController.play()
|
2021-07-18 11:33:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun <T> callWithErrorHandling(function: () -> T): T? {
|
|
|
|
// TODO Implement better error handling
|
|
|
|
return try {
|
|
|
|
function()
|
|
|
|
} catch (all: Exception) {
|
|
|
|
Timber.i(all)
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
2022-04-04 21:18:07 +02:00
|
|
|
|
|
|
|
private fun buildMediaItemFromTrack(
|
|
|
|
track: Track,
|
|
|
|
mediaId: String,
|
|
|
|
isPlayable: Boolean
|
|
|
|
): MediaItem {
|
|
|
|
|
|
|
|
return buildMediaItem(
|
|
|
|
title = track.title ?: "",
|
|
|
|
mediaId = mediaId,
|
|
|
|
isPlayable = isPlayable,
|
|
|
|
folderType = FOLDER_TYPE_NONE,
|
|
|
|
album = track.album,
|
|
|
|
artist = track.artist,
|
|
|
|
genre = track.genre,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Suppress("LongParameterList")
|
|
|
|
private fun buildMediaItem(
|
|
|
|
title: String,
|
|
|
|
mediaId: String,
|
|
|
|
isPlayable: Boolean,
|
|
|
|
@MediaMetadata.FolderType folderType: Int,
|
|
|
|
album: String? = null,
|
|
|
|
artist: String? = null,
|
|
|
|
genre: String? = null,
|
|
|
|
sourceUri: Uri? = null,
|
|
|
|
imageUri: Uri? = null,
|
|
|
|
): MediaItem {
|
|
|
|
val metadata =
|
|
|
|
MediaMetadata.Builder()
|
|
|
|
.setAlbumTitle(album)
|
|
|
|
.setTitle(title)
|
|
|
|
.setArtist(artist)
|
|
|
|
.setGenre(genre)
|
|
|
|
.setFolderType(folderType)
|
|
|
|
.setIsPlayable(isPlayable)
|
|
|
|
.setArtworkUri(imageUri)
|
|
|
|
.build()
|
|
|
|
return MediaItem.Builder()
|
|
|
|
.setMediaId(mediaId)
|
|
|
|
.setMediaMetadata(metadata)
|
|
|
|
.setUri(sourceUri)
|
|
|
|
.build()
|
|
|
|
}
|
2022-04-05 10:10:24 +02:00
|
|
|
}
|