mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2026-10-22 23:42:14 +03:00
Merge branch 'feature/auto-genres' into 'develop'
feat(auto): add Genres browsing to Android Auto See merge request ultrasonic/ultrasonic!1382
This commit is contained in:
commit
c4febfc371
1 file changed
+93
+93
@@ -91,6 +91,9 @@ 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"
|
||||
private const val MEDIA_GENRE_ID = "MEDIA_GENRE_ID"
|
||||
private const val MEDIA_GENRE_ITEM = "MEDIA_GENRE_ITEM"
|
||||
private const val MEDIA_GENRE_SONG_ITEM = "MEDIA_GENRE_SONG_ITEM"
|
||||
|
||||
// Currently the display limit for long lists is 100 items
|
||||
private const val DISPLAY_LIMIT = 100
|
||||
@@ -124,6 +127,7 @@ class MediaLibrarySessionCallback(
|
||||
private var starredSongsCache: List<Track>? = null
|
||||
private var randomSongsCache: List<Track>? = null
|
||||
private var searchSongsCache: List<Track>? = null
|
||||
private var genreSongsCache: List<Track>? = null
|
||||
|
||||
private val musicService get() = musicServiceFactory.getMusicService()
|
||||
private val isOffline get() = activeServerProvider.isOffline()
|
||||
@@ -659,6 +663,10 @@ class MediaLibrarySessionCallback(
|
||||
|
||||
MEDIA_ARTIST_PLAY_ID -> playAllForArtist(mediaIdParts[1], mediaIdParts[2])
|
||||
|
||||
MEDIA_GENRE_ITEM -> playGenre(mediaIdParts[1])
|
||||
|
||||
MEDIA_GENRE_SONG_ITEM -> playGenreSong(mediaIdParts[1])
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -731,6 +739,10 @@ class MediaLibrarySessionCallback(
|
||||
|
||||
MEDIA_PODCAST_ITEM -> getPodcastEpisodes(parentIdParts[1])
|
||||
|
||||
MEDIA_GENRE_ID -> getGenres()
|
||||
|
||||
MEDIA_GENRE_ITEM -> getSongsForGenre(parentIdParts[1])
|
||||
|
||||
else -> Futures.immediateFuture(LibraryResult.ofItemList(listOf(), null))
|
||||
}
|
||||
}
|
||||
@@ -846,6 +858,10 @@ class MediaLibrarySessionCallback(
|
||||
|
||||
MEDIA_ARTIST_PLAY_ID -> playAllForArtist(mediaIdParts[1], mediaIdParts[2])
|
||||
|
||||
MEDIA_GENRE_ITEM -> playGenre(mediaIdParts[1])
|
||||
|
||||
MEDIA_GENRE_SONG_ITEM -> playGenreSong(mediaIdParts[1])
|
||||
|
||||
else -> {
|
||||
listOf()
|
||||
}
|
||||
@@ -879,6 +895,72 @@ class MediaLibrarySessionCallback(
|
||||
}.ifEmpty { null }
|
||||
}
|
||||
|
||||
private fun getGenres(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
val mediaItems: MutableList<MediaItem> = ArrayList()
|
||||
|
||||
return mainScope.future {
|
||||
val genres = serviceScope.future {
|
||||
callWithErrorHandling { musicService.getGenres(false) }
|
||||
}.await()
|
||||
|
||||
genres?.sortedBy { it.name }?.map { genre ->
|
||||
mediaItems.add(
|
||||
genre.name,
|
||||
listOf(MEDIA_GENRE_ITEM, genre.name).joinToString("|"),
|
||||
mediaType = MEDIA_TYPE_FOLDER_MIXED,
|
||||
isBrowsable = true
|
||||
)
|
||||
}
|
||||
return@future LibraryResult.ofItemList(mediaItems, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSongsForGenre(
|
||||
genre: String
|
||||
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
val mediaItems: MutableList<MediaItem> = ArrayList()
|
||||
|
||||
return mainScope.future {
|
||||
val songs = serviceScope.future {
|
||||
callWithErrorHandling { musicService.getSongsByGenre(genre, DISPLAY_LIMIT, 0) }
|
||||
}.await()
|
||||
|
||||
if (songs != null) {
|
||||
val tracks = songs.getTracks()
|
||||
genreSongsCache = tracks
|
||||
|
||||
if (tracks.size > 1) {
|
||||
mediaItems.addPlayAllItem(listOf(MEDIA_GENRE_ITEM, genre).joinToString("|"))
|
||||
}
|
||||
|
||||
tracks.map { song ->
|
||||
mediaItems.add(
|
||||
song.toMediaItem(
|
||||
listOf(MEDIA_GENRE_SONG_ITEM, song.id).joinToString("|")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
return@future LibraryResult.ofItemList(mediaItems, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playGenre(genre: String): List<Track>? {
|
||||
if (genreSongsCache == null) {
|
||||
val content = serviceScope.future {
|
||||
callWithErrorHandling { musicService.getSongsByGenre(genre, DISPLAY_LIMIT, 0) }
|
||||
}.get()
|
||||
genreSongsCache = content?.getTracks()
|
||||
}
|
||||
return genreSongsCache
|
||||
}
|
||||
|
||||
private fun playGenreSong(songId: String): List<Track>? {
|
||||
val song = genreSongsCache?.firstOrNull { it.id == songId }
|
||||
if (song != null) return listOf(song)
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getRootItems(): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
||||
val mediaItems: MutableList<MediaItem> = ArrayList()
|
||||
|
||||
@@ -922,6 +1004,17 @@ class MediaLibrarySessionCallback(
|
||||
icon = R.drawable.ic_menu_playlists
|
||||
)
|
||||
|
||||
if (!isOffline) {
|
||||
mediaItems.add(
|
||||
R.string.main_genres_title,
|
||||
MEDIA_GENRE_ID,
|
||||
null,
|
||||
isBrowsable = true,
|
||||
mediaType = MEDIA_TYPE_FOLDER_MIXED,
|
||||
icon = R.drawable.ic_menu_browse
|
||||
)
|
||||
}
|
||||
|
||||
return Futures.immediateFuture(LibraryResult.ofItemList(mediaItems, null))
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user