diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java new file mode 100644 index 00000000..5420df32 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java @@ -0,0 +1,495 @@ +package com.cappielloantonio.tempo.repository; + + +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; +import androidx.media3.session.LibraryResult; + +import com.cappielloantonio.tempo.App; +import com.cappielloantonio.tempo.glide.CustomGlideRequest; +import com.cappielloantonio.tempo.subsonic.base.ApiResponse; +import com.cappielloantonio.tempo.subsonic.models.AlbumID3; +import com.cappielloantonio.tempo.subsonic.models.ArtistID3; +import com.cappielloantonio.tempo.subsonic.models.Child; +import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation; +import com.cappielloantonio.tempo.subsonic.models.MusicFolder; +import com.cappielloantonio.tempo.subsonic.models.Playlist; +import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode; +import com.cappielloantonio.tempo.util.MusicUtil; +import com.cappielloantonio.tempo.util.Preferences; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class AutomotiveRepository { + public ListenableFuture>> getAlbums(String prefix, String type, int size) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getAlbumSongListClient() + .getAlbumList2(type, size, 0, null, null) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) { + List albums = response.body().getSubsonicResponse().getAlbumList2().getAlbums(); + + List mediaItems = new ArrayList<>(); + + for (AlbumID3 album : albums) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(album.getName()) + .setAlbumTitle(album.getName()) + .setArtist(album.getArtist()) + .setGenre(album.getGenre()) + .setIsBrowsable(true) + .setIsPlayable(false) + .setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + album.getId()) + .setMediaMetadata(mediaMetadata) + .setUri("") + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getStarredSongs(String prefix) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getAlbumSongListClient() + .getStarred2() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) { + List songs = response.body().getSubsonicResponse().getStarred2().getSongs(); + + List mediaItems = new ArrayList<>(); + + for (Child song : songs) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(song.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(song.getTitle()) + .setAlbumTitle(song.getAlbum()) + .setArtist(song.getArtist()) + .setIsBrowsable(false) + .setIsPlayable(true) + .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + song.getId()) + .setMediaMetadata(mediaMetadata) + .setUri(MusicUtil.getStreamUri(song.getId())) + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getStarredAlbums(String prefix) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getAlbumSongListClient() + .getStarred2() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getAlbums() != null) { + List albums = response.body().getSubsonicResponse().getStarred2().getAlbums(); + + List mediaItems = new ArrayList<>(); + + for (AlbumID3 album : albums) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(album.getName()) + .setArtist(album.getArtist()) + .setGenre(album.getGenre()) + .setIsBrowsable(true) + .setIsPlayable(false) + .setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + album.getId()) + .setMediaMetadata(mediaMetadata) + .setUri("") + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getStarredArtists(String prefix, boolean playFromThere) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getAlbumSongListClient() + .getStarred2() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getArtists() != null) { + List artists = response.body().getSubsonicResponse().getStarred2().getArtists(); + + List mediaItems = new ArrayList<>(); + + for (ArtistID3 artist : artists) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(artist.getName()) + .setIsBrowsable(!playFromThere) + .setIsPlayable(playFromThere) + .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + artist.getId()) + .setMediaMetadata(mediaMetadata) + .setUri("") + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getMusicFolders(String prefix) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getBrowsingClient() + .getMusicFolders() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null) { + List musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders(); + + List mediaItems = new ArrayList<>(); + + for (MusicFolder musicFolder : musicFolders) { + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(musicFolder.getName()) + .setIsBrowsable(true) + .setIsPlayable(false) + .setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + musicFolder.getId()) + .setMediaMetadata(mediaMetadata) + .setUri("") + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getPlaylists(String prefix) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getPlaylistClient() + .getPlaylists() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylists() != null && response.body().getSubsonicResponse().getPlaylists().getPlaylists() != null) { + List playlists = response.body().getSubsonicResponse().getPlaylists().getPlaylists(); + + List mediaItems = new ArrayList<>(); + + for (Playlist playlist : playlists) { + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(playlist.getName()) + .setIsBrowsable(true) + .setIsPlayable(false) + .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + playlist.getId()) + .setMediaMetadata(mediaMetadata) + .setUri("") + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getNewestPodcastEpisodes(String prefix, int count) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getPodcastClient() + .getNewestPodcasts(count) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getNewestPodcasts() != null && response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes() != null) { + List episodes = response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes(); + + List mediaItems = new ArrayList<>(); + + for (PodcastEpisode episode : episodes) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(episode.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(episode.getTitle()) + .setIsBrowsable(false) + .setIsPlayable(true) + .setMediaType(MediaMetadata.MEDIA_TYPE_PODCAST_EPISODE) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + episode.getId()) + .setMediaMetadata(mediaMetadata) + .setUri(MusicUtil.getStreamUri(episode.getStreamId())) + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getInternetRadioStations(String prefix) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getInternetRadioClient() + .getInternetRadioStations() + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getInternetRadioStations() != null && response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations() != null) { + + List radioStations = response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations(); + + List mediaItems = new ArrayList<>(); + + for (InternetRadioStation radioStation : radioStations) { + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(radioStation.getName()) + .setIsBrowsable(false) + .setIsPlayable(true) + .setMediaType(MediaMetadata.MEDIA_TYPE_RADIO_STATION) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(prefix + radioStation.getId()) + .setMediaMetadata(mediaMetadata) + .setUri(radioStation.getStreamUrl()) + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + listenableFuture.setException(t); + } + }); + + return listenableFuture; + } + + public ListenableFuture>> getAlbumTracks(String id) { + final SettableFuture>> listenableFuture = SettableFuture.create(); + + App.getSubsonicClientInstance(false) + .getBrowsingClient() + .getAlbum(id) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null && response.body().getSubsonicResponse().getAlbum().getSongs() != null) { + + List tracks = response.body().getSubsonicResponse().getAlbum().getSongs(); + + List mediaItems = new ArrayList<>(); + + for (Child track : tracks) { + Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(track.getCoverArtId(), Preferences.getImageSize())); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder() + .setTitle(track.getTitle()) + .setAlbumTitle(track.getAlbum()) + .setArtist(track.getArtist()) + .setIsBrowsable(false) + .setIsPlayable(true) + .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC) + .setArtworkUri(artworkUri) + .build(); + + MediaItem mediaItem = new MediaItem.Builder() + .setMediaId(track.getId()) + .setMediaMetadata(mediaMetadata) + .setUri(MusicUtil.getStreamUri(track.getId())) + .build(); + + mediaItems.add(mediaItem); + } + + LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null); + + listenableFuture.set(libraryResult); + } else { + listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + } + }); + + return listenableFuture; + } +} diff --git a/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt b/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt new file mode 100644 index 00000000..d7353c90 --- /dev/null +++ b/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt @@ -0,0 +1,437 @@ +package com.cappielloantonio.tempo.service + +import android.content.Context +import android.net.Uri +import androidx.media3.common.MediaItem +import androidx.media3.common.MediaItem.SubtitleConfiguration +import androidx.media3.common.MediaMetadata +import androidx.media3.session.LibraryResult +import androidx.media3.session.MediaLibraryService +import com.cappielloantonio.tempo.repository.AutomotiveRepository +import com.cappielloantonio.tempo.util.MusicUtil +import com.google.common.collect.ImmutableList +import com.google.common.util.concurrent.Futures +import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.SettableFuture + +object MediaBrowserTree { + + private lateinit var automotiveRepository: AutomotiveRepository + + private var treeNodes: MutableMap = mutableMapOf() + + private var isInitialized = false + + // Root + private const val ROOT_ID = "[rootID]" + + // First level + private const val HOME_ID = "[homeID]" + private const val LIBRARY_ID = "[libraryID]" + private const val OTHER_ID = "[otherID]" + + // Second level HOME_ID + private const val MOST_PLAYED_ID = "[mostPlayedID]" + private const val LAST_PLAYED_ID = "[lastPlayedID]" + private const val RECENTLY_ADDED_ID = "[recentlyAddedID]" + private const val BEST_OF_ID = "[bestOfID]" + private const val MADE_FOR_YOU_ID = "[madeForYouID]" + private const val STARRED_TRACKS_ID = "[starredTracksID]" + private const val STARRED_ALBUMS_ID = "[starredAlbumsID]" + private const val STARRED_ARTISTS_ID = "[starredArtistsID]" + + // Second level LIBRARY_ID + private const val FOLDER_ID = "[folderID]" + private const val PLAYLIST_ID = "[playlistID]" + + // Second level OTHER_ID + private const val PODCAST_ID = "[podcastID]" + private const val RADIO_ID = "[radioID]" + private const val DOWNLOAD_ID = "[downloadID]" + + private class MediaItemNode(val item: MediaItem) { + private val children: MutableList = ArrayList() + + fun addChild(childID: String) { + this.children.add(treeNodes[childID]!!.item) + } + + fun getChildren(): ListenableFuture>> { + val listenableFuture = SettableFuture.create>>() + val libraryResult = LibraryResult.ofItemList(children, null) + + listenableFuture.set(libraryResult) + + return listenableFuture + } + } + + private fun buildMediaItem( + title: String, + mediaId: String, + isPlayable: Boolean, + isBrowsable: Boolean, + mediaType: @MediaMetadata.MediaType Int, + subtitleConfigurations: List = mutableListOf(), + 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) + .setIsBrowsable(isBrowsable) + .setIsPlayable(isPlayable) + .setArtworkUri(imageUri) + .setMediaType(mediaType) + .build() + + return MediaItem.Builder() + .setMediaId(mediaId) + .setSubtitleConfigurations(subtitleConfigurations) + .setMediaMetadata(metadata) + .setUri(sourceUri) + .build() + } + + fun initialize(context: Context, automotiveRepository: AutomotiveRepository) { + this.automotiveRepository = automotiveRepository + + if (isInitialized) return + + isInitialized = true + + // Root level + + treeNodes[ROOT_ID] = + MediaItemNode( + buildMediaItem( + title = "Root Folder", + mediaId = ROOT_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + // First level + + treeNodes[HOME_ID] = + MediaItemNode( + buildMediaItem( + title = "Home", + mediaId = HOME_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + treeNodes[LIBRARY_ID] = + MediaItemNode( + buildMediaItem( + title = "Library", + mediaId = LIBRARY_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + treeNodes[OTHER_ID] = + MediaItemNode( + buildMediaItem( + title = "Other", + mediaId = OTHER_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + treeNodes[ROOT_ID]!!.addChild(HOME_ID) + treeNodes[ROOT_ID]!!.addChild(LIBRARY_ID) + treeNodes[ROOT_ID]!!.addChild(OTHER_ID) + + // Second level HOME_ID + + treeNodes[MOST_PLAYED_ID] = + MediaItemNode( + buildMediaItem( + title = "Most played", + mediaId = MOST_PLAYED_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS + ) + ) + + treeNodes[LAST_PLAYED_ID] = + MediaItemNode( + buildMediaItem( + title = "Last played", + mediaId = LAST_PLAYED_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS + ) + ) + + treeNodes[RECENTLY_ADDED_ID] = + MediaItemNode( + buildMediaItem( + title = "Recently added", + mediaId = RECENTLY_ADDED_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS + ) + ) + + treeNodes[BEST_OF_ID] = + MediaItemNode( + buildMediaItem( + title = "Best of", + mediaId = BEST_OF_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS + ) + ) + + treeNodes[MADE_FOR_YOU_ID] = + MediaItemNode( + buildMediaItem( + title = "Made for you", + mediaId = MADE_FOR_YOU_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS + ) + ) + + treeNodes[STARRED_TRACKS_ID] = + MediaItemNode( + buildMediaItem( + title = "Starred tracks", + mediaId = STARRED_TRACKS_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + treeNodes[STARRED_ALBUMS_ID] = + MediaItemNode( + buildMediaItem( + title = "Starred albums", + mediaId = STARRED_ALBUMS_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS + ) + ) + + treeNodes[STARRED_ARTISTS_ID] = + MediaItemNode( + buildMediaItem( + title = "Starred artists", + mediaId = STARRED_ARTISTS_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ARTISTS + ) + ) + + treeNodes[HOME_ID]!!.addChild(MOST_PLAYED_ID) + treeNodes[HOME_ID]!!.addChild(LAST_PLAYED_ID) + treeNodes[HOME_ID]!!.addChild(RECENTLY_ADDED_ID) + treeNodes[HOME_ID]!!.addChild(BEST_OF_ID) + treeNodes[HOME_ID]!!.addChild(MADE_FOR_YOU_ID) + // treeNodes[HOME_ID]!!.addChild(STARRED_TRACKS_ID) + // treeNodes[HOME_ID]!!.addChild(STARRED_ALBUMS_ID) + // treeNodes[HOME_ID]!!.addChild(STARRED_ARTISTS_ID) + + // Second level LIBRARY_ID + + treeNodes[FOLDER_ID] = + MediaItemNode( + buildMediaItem( + title = "Folders", + mediaId = FOLDER_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED + ) + ) + + treeNodes[PLAYLIST_ID] = + MediaItemNode( + buildMediaItem( + title = "Playlists", + mediaId = PLAYLIST_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS + ) + ) + + treeNodes[LIBRARY_ID]!!.addChild(FOLDER_ID) + treeNodes[LIBRARY_ID]!!.addChild(PLAYLIST_ID) + + // Second level OTHER_ID + + treeNodes[PODCAST_ID] = + MediaItemNode( + buildMediaItem( + title = "Podcasts", + mediaId = PODCAST_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PODCASTS + ) + ) + + treeNodes[RADIO_ID] = + MediaItemNode( + buildMediaItem( + title = "Radio stations", + mediaId = RADIO_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS + ) + ) + + treeNodes[DOWNLOAD_ID] = + MediaItemNode( + buildMediaItem( + title = "Downloads", + mediaId = DOWNLOAD_ID, + isPlayable = false, + isBrowsable = true, + mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS + ) + ) + + treeNodes[OTHER_ID]!!.addChild(PODCAST_ID) + treeNodes[OTHER_ID]!!.addChild(RADIO_ID) + // treeNodes[OTHER_ID]!!.addChild(DOWNLOAD_ID) + } + + fun getRootItem(): MediaItem { + return treeNodes[ROOT_ID]!!.item + } + + fun getChildren( + id: String, + params: MediaLibraryService.LibraryParams? + ): ListenableFuture>> { + return when (id) { + ROOT_ID -> treeNodes[ROOT_ID]?.getChildren()!! + HOME_ID -> treeNodes[HOME_ID]?.getChildren()!! + LIBRARY_ID -> treeNodes[LIBRARY_ID]?.getChildren()!! + OTHER_ID -> treeNodes[OTHER_ID]?.getChildren()!! + + MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 100) + LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 100) + RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100) + BEST_OF_ID -> automotiveRepository.getStarredArtists(id, true) + MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id, true) + STARRED_TRACKS_ID -> automotiveRepository.getStarredSongs(id) + STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id) + STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id, false) + FOLDER_ID -> automotiveRepository.getMusicFolders(id) + PLAYLIST_ID -> automotiveRepository.getPlaylists(id) + PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(id, 100) + RADIO_ID -> automotiveRepository.getInternetRadioStations(id) + + DOWNLOAD_ID -> Futures.immediateFuture(null) + + else -> { + if (id.startsWith(MOST_PLAYED_ID)) { + return automotiveRepository.getAlbumTracks( + id.removePrefix( + MOST_PLAYED_ID + ) + + ) + } + + if (id.startsWith(LAST_PLAYED_ID)) { + return automotiveRepository.getAlbumTracks( + id.removePrefix( + LAST_PLAYED_ID + ) + + ) + } + + if (id.startsWith(RECENTLY_ADDED_ID)) { + return automotiveRepository.getAlbumTracks( + id.removePrefix( + RECENTLY_ADDED_ID + ) + + ) + } + + if (id.startsWith(BEST_OF_ID)) { + + } + + if (id.startsWith(MADE_FOR_YOU_ID)) { + + } + + if (id.startsWith(FOLDER_ID)) { + + } + + if (id.startsWith(PLAYLIST_ID)) { + + } + + if (id.startsWith(PODCAST_ID)) { + + } + + if (id.startsWith(RADIO_ID)) { + + } + + return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)) + } + } + } + + fun getItem(id: String): ListenableFuture> { + 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(id) + .setMediaMetadata(mediaMetadata) + .setUri(MusicUtil.getStreamUri(id)) + .build() + + return Futures.immediateFuture(LibraryResult.ofItem(mediaItem, null)) + + /* if (treeNodes[id]?.item != null) { + return Futures.immediateFuture(LibraryResult.ofItem(treeNodes[id]!!.item, null)) + } + + return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)) */ + } +}