mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
feat: test: Implemented initial functional version with Android Auto support
This commit is contained in:
parent
e8c7c065e2
commit
d6cc4fc028
8 changed files with 758 additions and 160 deletions
|
|
@ -14,17 +14,19 @@ import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||||
import com.cappielloantonio.tempo.model.Chronology;
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.model.Favorite;
|
import com.cappielloantonio.tempo.model.Favorite;
|
||||||
import com.cappielloantonio.tempo.model.Queue;
|
import com.cappielloantonio.tempo.model.Queue;
|
||||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||||
import com.cappielloantonio.tempo.model.Server;
|
import com.cappielloantonio.tempo.model.Server;
|
||||||
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 3,
|
version = 8,
|
||||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class},
|
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class},
|
||||||
autoMigrations = {@AutoMigration(from = 2, to = 3)}
|
autoMigrations = {@AutoMigration(from = 7, to = 8)}
|
||||||
)
|
)
|
||||||
@TypeConverters({DateConverters.class})
|
@TypeConverters({DateConverters.class})
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
|
@ -52,4 +54,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
public abstract ChronologyDao chronologyDao();
|
public abstract ChronologyDao chronologyDao();
|
||||||
|
|
||||||
public abstract FavoriteDao favoriteDao();
|
public abstract FavoriteDao favoriteDao();
|
||||||
|
|
||||||
|
public abstract SessionMediaItemDao sessionMediaItemDao();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.cappielloantonio.tempo.database.dao;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.model.Queue;
|
||||||
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface SessionMediaItemDao {
|
||||||
|
@Query("SELECT * FROM session_media_item WHERE id = :id")
|
||||||
|
SessionMediaItem get(String id);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM session_media_item WHERE timestamp = :timestamp")
|
||||||
|
List<SessionMediaItem> get(long timestamp);
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
void insert(SessionMediaItem sessionMediaItem);
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
void insertAll(List<SessionMediaItem> sessionMediaItems);
|
||||||
|
|
||||||
|
@Query("DELETE FROM session_media_item")
|
||||||
|
void deleteAll();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,279 @@
|
||||||
|
package com.cappielloantonio.tempo.model
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.MediaItem.RequestMetadata
|
||||||
|
import androidx.media3.common.MediaMetadata
|
||||||
|
import androidx.media3.common.MimeTypes
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode
|
||||||
|
import com.cappielloantonio.tempo.util.Constants
|
||||||
|
import com.cappielloantonio.tempo.util.MusicUtil
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences.getImageSize
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Entity(tableName = "session_media_item")
|
||||||
|
class SessionMediaItem() {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ColumnInfo(name = "index")
|
||||||
|
var index: Int = 0
|
||||||
|
|
||||||
|
@ColumnInfo(name = "id")
|
||||||
|
var id: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "parent_id")
|
||||||
|
var parentId: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_dir")
|
||||||
|
var isDir: Boolean = false
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var title: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var album: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var artist: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var track: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var year: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var genre: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "cover_art_id")
|
||||||
|
var coverArtId: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var size: Long? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "content_type")
|
||||||
|
var contentType: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var suffix: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo("transcoding_content_type")
|
||||||
|
var transcodedContentType: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "transcoded_suffix")
|
||||||
|
var transcodedSuffix: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var duration: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo("bitrate")
|
||||||
|
var bitrate: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var path: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_video")
|
||||||
|
var isVideo: Boolean = false
|
||||||
|
|
||||||
|
@ColumnInfo(name = "user_rating")
|
||||||
|
var userRating: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "average_rating")
|
||||||
|
var averageRating: Double? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "play_count")
|
||||||
|
var playCount: Long? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "disc_number")
|
||||||
|
var discNumber: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var created: Date? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var starred: Date? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "album_id")
|
||||||
|
var albumId: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "artist_id")
|
||||||
|
var artistId: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo
|
||||||
|
var type: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "bookmark_position")
|
||||||
|
var bookmarkPosition: Long? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "original_width")
|
||||||
|
var originalWidth: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "original_height")
|
||||||
|
var originalHeight: Int? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "stream_id")
|
||||||
|
var streamId: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "stream_url")
|
||||||
|
var streamUrl: String? = null
|
||||||
|
|
||||||
|
@ColumnInfo(name = "timestamp")
|
||||||
|
var timestamp: Long? = null
|
||||||
|
|
||||||
|
constructor(child: Child) : this() {
|
||||||
|
id = child.id
|
||||||
|
parentId = child.parentId
|
||||||
|
isDir = child.isDir
|
||||||
|
title = child.title
|
||||||
|
album = child.album
|
||||||
|
artist = child.artist
|
||||||
|
track = child.track
|
||||||
|
year = child.year
|
||||||
|
genre = child.genre
|
||||||
|
coverArtId = child.coverArtId
|
||||||
|
size = child.size
|
||||||
|
contentType = child.contentType
|
||||||
|
suffix = child.suffix
|
||||||
|
transcodedContentType = child.transcodedContentType
|
||||||
|
transcodedSuffix = child.transcodedSuffix
|
||||||
|
duration = child.duration
|
||||||
|
bitrate = child.bitrate
|
||||||
|
path = child.path
|
||||||
|
isVideo = child.isVideo
|
||||||
|
userRating = child.userRating
|
||||||
|
averageRating = child.averageRating
|
||||||
|
playCount = child.playCount
|
||||||
|
discNumber = child.discNumber
|
||||||
|
created = child.created
|
||||||
|
starred = child.starred
|
||||||
|
albumId = child.albumId
|
||||||
|
artistId = child.artistId
|
||||||
|
type = Constants.MEDIA_TYPE_MUSIC
|
||||||
|
bookmarkPosition = child.bookmarkPosition
|
||||||
|
originalWidth = child.originalWidth
|
||||||
|
originalHeight = child.originalHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(podcastEpisode: PodcastEpisode) : this() {
|
||||||
|
id = podcastEpisode.id
|
||||||
|
parentId = podcastEpisode.parentId
|
||||||
|
isDir = podcastEpisode.isDir
|
||||||
|
title = podcastEpisode.title
|
||||||
|
album = podcastEpisode.album
|
||||||
|
artist = podcastEpisode.artist
|
||||||
|
year = podcastEpisode.year
|
||||||
|
genre = podcastEpisode.genre
|
||||||
|
coverArtId = podcastEpisode.coverArtId
|
||||||
|
size = podcastEpisode.size
|
||||||
|
contentType = podcastEpisode.contentType
|
||||||
|
suffix = podcastEpisode.suffix
|
||||||
|
duration = podcastEpisode.duration
|
||||||
|
bitrate = podcastEpisode.bitrate
|
||||||
|
path = podcastEpisode.path
|
||||||
|
isVideo = podcastEpisode.isVideo
|
||||||
|
created = podcastEpisode.created
|
||||||
|
artistId = podcastEpisode.artistId
|
||||||
|
streamId = podcastEpisode.streamId
|
||||||
|
type = Constants.MEDIA_TYPE_PODCAST
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(internetRadioStation: InternetRadioStation) : this() {
|
||||||
|
id = internetRadioStation.id
|
||||||
|
title = internetRadioStation.name
|
||||||
|
streamUrl = internetRadioStation.streamUrl
|
||||||
|
type = Constants.MEDIA_TYPE_RADIO
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMediaItem(): MediaItem {
|
||||||
|
val uri: Uri = getStreamUri()
|
||||||
|
val artworkUri = Uri.parse(CustomGlideRequest.createUrl(coverArtId, getImageSize()))
|
||||||
|
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putString("id", id)
|
||||||
|
bundle.putString("parentId", parentId)
|
||||||
|
bundle.putBoolean("isDir", isDir)
|
||||||
|
bundle.putString("title", title)
|
||||||
|
bundle.putString("album", album)
|
||||||
|
bundle.putString("artist", artist)
|
||||||
|
bundle.putInt("track", track ?: 0)
|
||||||
|
bundle.putInt("year", year ?: 0)
|
||||||
|
bundle.putString("genre", genre)
|
||||||
|
bundle.putString("coverArtId", coverArtId)
|
||||||
|
bundle.putLong("size", size ?: 0)
|
||||||
|
bundle.putString("contentType", contentType)
|
||||||
|
bundle.putString("suffix", suffix)
|
||||||
|
bundle.putString("transcodedContentType", transcodedContentType)
|
||||||
|
bundle.putString("transcodedSuffix", transcodedSuffix)
|
||||||
|
bundle.putInt("duration", duration ?: 0)
|
||||||
|
bundle.putInt("bitrate", bitrate ?: 0)
|
||||||
|
bundle.putString("path", path)
|
||||||
|
bundle.putBoolean("isVideo", isVideo)
|
||||||
|
bundle.putInt("userRating", userRating ?: 0)
|
||||||
|
bundle.putDouble("averageRating", averageRating ?: .0)
|
||||||
|
bundle.putLong("playCount", playCount ?: 0)
|
||||||
|
bundle.putInt("discNumber", discNumber ?: 0)
|
||||||
|
bundle.putLong("created", created?.time ?: 0)
|
||||||
|
bundle.putLong("starred", starred?.time ?: 0)
|
||||||
|
bundle.putString("albumId", albumId)
|
||||||
|
bundle.putString("artistId", artistId)
|
||||||
|
bundle.putString("type", Constants.MEDIA_TYPE_MUSIC)
|
||||||
|
bundle.putLong("bookmarkPosition", bookmarkPosition ?: 0)
|
||||||
|
bundle.putInt("originalWidth", originalWidth ?: 0)
|
||||||
|
bundle.putInt("originalHeight", originalHeight ?: 0)
|
||||||
|
bundle.putString("uri", uri.toString())
|
||||||
|
|
||||||
|
return MediaItem.Builder()
|
||||||
|
.setMediaId(id!!)
|
||||||
|
.setMediaMetadata(
|
||||||
|
MediaMetadata.Builder()
|
||||||
|
.setTitle(MusicUtil.getReadableString(title))
|
||||||
|
.setTrackNumber(track ?: 0)
|
||||||
|
.setDiscNumber(discNumber ?: 0)
|
||||||
|
.setReleaseYear(year ?: 0)
|
||||||
|
.setAlbumTitle(MusicUtil.getReadableString(album))
|
||||||
|
.setArtist(MusicUtil.getReadableString(artist))
|
||||||
|
.setArtworkUri(artworkUri)
|
||||||
|
.setExtras(bundle)
|
||||||
|
.setIsBrowsable(false)
|
||||||
|
.setIsPlayable(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.setRequestMetadata(
|
||||||
|
RequestMetadata.Builder()
|
||||||
|
.setMediaUri(uri)
|
||||||
|
.setExtras(bundle)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||||
|
.setUri(uri)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStreamUri(): Uri {
|
||||||
|
return when (type) {
|
||||||
|
Constants.MEDIA_TYPE_MUSIC -> {
|
||||||
|
MusicUtil.getStreamUri(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constants.MEDIA_TYPE_PODCAST -> {
|
||||||
|
MusicUtil.getStreamUri(streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constants.MEDIA_TYPE_RADIO -> {
|
||||||
|
Uri.parse(streamUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
MusicUtil.getStreamUri(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,15 +9,22 @@ import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.session.LibraryResult;
|
import androidx.media3.session.LibraryResult;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Index;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||||
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
@ -25,13 +32,17 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class AutomotiveRepository {
|
public class AutomotiveRepository {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
|
@ -86,7 +97,7 @@ public class AutomotiveRepository {
|
||||||
return listenableFuture;
|
return listenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredSongs(String prefix) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredSongs() {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
|
|
@ -98,29 +109,9 @@ public class AutomotiveRepository {
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) {
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) {
|
||||||
List<Child> songs = response.body().getSubsonicResponse().getStarred2().getSongs();
|
List<Child> songs = response.body().getSubsonicResponse().getStarred2().getSongs();
|
||||||
|
|
||||||
List<MediaItem> mediaItems = new ArrayList<>();
|
setChildrenMetadata(songs);
|
||||||
|
|
||||||
for (Child song : songs) {
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(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<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
|
@ -252,7 +243,7 @@ public class AutomotiveRepository {
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null) {
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null && response.body().getSubsonicResponse().getMusicFolders().getMusicFolders() != null) {
|
||||||
List<MusicFolder> musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders();
|
List<MusicFolder> musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders();
|
||||||
|
|
||||||
List<MediaItem> mediaItems = new ArrayList<>();
|
List<MediaItem> mediaItems = new ArrayList<>();
|
||||||
|
|
@ -291,6 +282,137 @@ public class AutomotiveRepository {
|
||||||
return listenableFuture;
|
return listenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getIndexes(String prefix, String id) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getIndexes(id, null)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getIndexes() != null) {
|
||||||
|
List<MediaItem> mediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
if (response.body().getSubsonicResponse().getIndexes().getIndices() != null) {
|
||||||
|
List<Index> indices = response.body().getSubsonicResponse().getIndexes().getIndices();
|
||||||
|
|
||||||
|
for (Index index : indices) {
|
||||||
|
if (index.getArtists() != null) {
|
||||||
|
for (Artist artist : index.getArtists()) {
|
||||||
|
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||||
|
.setTitle(artist.getName())
|
||||||
|
.setIsBrowsable(true)
|
||||||
|
.setIsPlayable(false)
|
||||||
|
.setMediaType(MediaMetadata.MEDIA_TYPE_ARTIST)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MediaItem mediaItem = new MediaItem.Builder()
|
||||||
|
.setMediaId(prefix + artist.getId())
|
||||||
|
.setMediaMetadata(mediaMetadata)
|
||||||
|
.setUri("")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mediaItems.add(mediaItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.body().getSubsonicResponse().getIndexes().getChildren() != null) {
|
||||||
|
List<Child> children = response.body().getSubsonicResponse().getIndexes().getChildren();
|
||||||
|
|
||||||
|
for (Child song : children) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenMetadata(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getDirectories(String prefix, String id) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getMusicDirectory(id)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getDirectory() != null && response.body().getSubsonicResponse().getDirectory().getChildren() != null) {
|
||||||
|
Directory directory = response.body().getSubsonicResponse().getDirectory();
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Child child : directory.getChildren()) {
|
||||||
|
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(child.getCoverArtId(), Preferences.getImageSize()));
|
||||||
|
|
||||||
|
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||||
|
.setTitle(child.getTitle())
|
||||||
|
.setIsBrowsable(child.isDir())
|
||||||
|
.setIsPlayable(!child.isDir())
|
||||||
|
.setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
|
||||||
|
.setArtworkUri(artworkUri)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MediaItem mediaItem = new MediaItem.Builder()
|
||||||
|
.setMediaId(child.isDir() ? prefix + child.getId() : child.getId())
|
||||||
|
.setMediaMetadata(mediaMetadata)
|
||||||
|
.setUri(!child.isDir() ? MusicUtil.getStreamUri(child.getId()) : Uri.parse(""))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mediaItems.add(mediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenMetadata(directory.getChildren().stream().filter(child -> !child.isDir()).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylists(String prefix) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylists(String prefix) {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
|
@ -373,6 +495,8 @@ public class AutomotiveRepository {
|
||||||
mediaItems.add(mediaItem);
|
mediaItems.add(mediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPodcastEpisodesMetadata(episodes);
|
||||||
|
|
||||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
listenableFuture.set(libraryResult);
|
listenableFuture.set(libraryResult);
|
||||||
|
|
@ -422,6 +546,8 @@ public class AutomotiveRepository {
|
||||||
mediaItems.add(mediaItem);
|
mediaItems.add(mediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInternetRadioStationsMetadata(radioStations);
|
||||||
|
|
||||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
listenableFuture.set(libraryResult);
|
listenableFuture.set(libraryResult);
|
||||||
|
|
@ -452,29 +578,9 @@ public class AutomotiveRepository {
|
||||||
|
|
||||||
List<Child> tracks = response.body().getSubsonicResponse().getAlbum().getSongs();
|
List<Child> tracks = response.body().getSubsonicResponse().getAlbum().getSongs();
|
||||||
|
|
||||||
List<MediaItem> mediaItems = new ArrayList<>();
|
setChildrenMetadata(tracks);
|
||||||
|
|
||||||
for (Child track : tracks) {
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(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<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
|
@ -486,10 +592,234 @@ public class AutomotiveRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return listenableFuture;
|
return listenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylistSongs(String id) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getPlaylistClient()
|
||||||
|
.getPlaylist(id)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylist() != null && response.body().getSubsonicResponse().getPlaylist().getEntries() != null) {
|
||||||
|
List<Child> tracks = response.body().getSubsonicResponse().getPlaylist().getEntries();
|
||||||
|
|
||||||
|
setChildrenMetadata(tracks);
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildrenMetadata(List<Child> children) {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Child child : children) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(child);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItems.add(sessionMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||||
|
Thread thread = new Thread(insertAll);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPodcastEpisodesMetadata(List<PodcastEpisode> podcastEpisodes) {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
for (PodcastEpisode podcastEpisode : podcastEpisodes) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(podcastEpisode);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItems.add(sessionMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||||
|
Thread thread = new Thread(insertAll);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInternetRadioStationsMetadata(List<InternetRadioStation> internetRadioStations) {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
for (InternetRadioStation internetRadioStation : internetRadioStations) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(internetRadioStation);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItems.add(sessionMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||||
|
Thread thread = new Thread(insertAll);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionMediaItem getSessionMediaItem(String id) {
|
||||||
|
SessionMediaItem sessionMediaItem = null;
|
||||||
|
|
||||||
|
GetMediaItemThreadSafe getMediaItemThreadSafe = new GetMediaItemThreadSafe(sessionMediaItemDao, id);
|
||||||
|
Thread thread = new Thread(getMediaItemThreadSafe);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
sessionMediaItem = getMediaItemThreadSafe.getSessionMediaItem();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionMediaItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MediaItem> getMetadatas(long timestamp) {
|
||||||
|
List<MediaItem> mediaItems = Collections.emptyList();
|
||||||
|
|
||||||
|
GetMediaItemsThreadSafe getMediaItemsThreadSafe = new GetMediaItemsThreadSafe(sessionMediaItemDao, timestamp);
|
||||||
|
Thread thread = new Thread(getMediaItemsThreadSafe);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
thread.join();
|
||||||
|
mediaItems = getMediaItemsThreadSafe.getMediaItems();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteMetadata() {
|
||||||
|
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(sessionMediaItemDao);
|
||||||
|
Thread thread = new Thread(delete);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetMediaItemThreadSafe implements Runnable {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private SessionMediaItem sessionMediaItem;
|
||||||
|
|
||||||
|
public GetMediaItemThreadSafe(SessionMediaItemDao sessionMediaItemDao, String id) {
|
||||||
|
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sessionMediaItem = sessionMediaItemDao.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionMediaItem getSessionMediaItem() {
|
||||||
|
return sessionMediaItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetMediaItemsThreadSafe implements Runnable {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao;
|
||||||
|
private final Long timestamp;
|
||||||
|
|
||||||
|
private List<MediaItem> mediaItems = new ArrayList<>();
|
||||||
|
|
||||||
|
public GetMediaItemsThreadSafe(SessionMediaItemDao sessionMediaItemDao, Long timestamp) {
|
||||||
|
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<SessionMediaItem> sessionMediaItems = sessionMediaItemDao.get(timestamp);
|
||||||
|
sessionMediaItems.forEach(sessionMediaItem -> mediaItems.add(sessionMediaItem.getMediaItem()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MediaItem> getMediaItems() {
|
||||||
|
return mediaItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* private static class InsertThreadSafe implements Runnable {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao;
|
||||||
|
private final List<Child> children;
|
||||||
|
private final List<PodcastEpisode> podcastEpisodes;
|
||||||
|
private final List<InternetRadioStation> internetRadioStations;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
public InsertThreadSafe(SessionMediaItemDao sessionMediaItemDao, List<Child> children, List<PodcastEpisode> podcastEpisodes, List<InternetRadioStation> internetRadioStations, long timestamp) {
|
||||||
|
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||||
|
this.children = children;
|
||||||
|
this.podcastEpisodes = podcastEpisodes;
|
||||||
|
this.internetRadioStations = internetRadioStations;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (children != null) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(children);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItemDao.insert(sessionMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (podcastEpisodes != null) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(podcastEpisodes);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItemDao.insert(sessionMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internetRadioStations != null) {
|
||||||
|
SessionMediaItem sessionMediaItem = new SessionMediaItem(internetRadioStations);
|
||||||
|
sessionMediaItem.setTimestamp(timestamp);
|
||||||
|
sessionMediaItemDao.insert(sessionMediaItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
private static class InsertAllThreadSafe implements Runnable {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao;
|
||||||
|
private final List<SessionMediaItem> sessionMediaItems;
|
||||||
|
|
||||||
|
public InsertAllThreadSafe(SessionMediaItemDao sessionMediaItemDao, List<SessionMediaItem> sessionMediaItems) {
|
||||||
|
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||||
|
this.sessionMediaItems = sessionMediaItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sessionMediaItemDao.insertAll(sessionMediaItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeleteAllThreadSafe implements Runnable {
|
||||||
|
private final SessionMediaItemDao sessionMediaItemDao;
|
||||||
|
|
||||||
|
public DeleteAllThreadSafe(SessionMediaItemDao sessionMediaItemDao) {
|
||||||
|
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sessionMediaItemDao.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ public class MappingUtil {
|
||||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||||
.setArtworkUri(artworkUri)
|
.setArtworkUri(artworkUri)
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
|
.setIsBrowsable(false)
|
||||||
|
.setIsPlayable(true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
|
|
@ -116,6 +118,8 @@ public class MappingUtil {
|
||||||
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||||
|
.setIsBrowsable(false)
|
||||||
|
.setIsPlayable(true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
|
|
@ -145,6 +149,8 @@ public class MappingUtil {
|
||||||
.setTitle(internetRadioStation.getName())
|
.setTitle(internetRadioStation.getName())
|
||||||
.setArtist(internetRadioStation.getStreamUrl())
|
.setArtist(internetRadioStation.getStreamUrl())
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
|
.setIsBrowsable(false)
|
||||||
|
.setIsPlayable(true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
|
|
@ -193,6 +199,8 @@ public class MappingUtil {
|
||||||
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
||||||
.setArtworkUri(artworkUri)
|
.setArtworkUri(artworkUri)
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
|
.setIsBrowsable(false)
|
||||||
|
.setIsPlayable(true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRequestMetadata(
|
.setRequestMetadata(
|
||||||
|
|
@ -201,12 +209,6 @@ public class MappingUtil {
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
/* .setClippingConfiguration(
|
|
||||||
new MediaItem.ClippingConfiguration.Builder()
|
|
||||||
.setStartPositionMs(0)
|
|
||||||
.setEndPositionMs(podcastEpisode.getDuration() * 1000)
|
|
||||||
.build()
|
|
||||||
) */
|
|
||||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||||
.setUri(uri)
|
.setUri(uri)
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,19 @@ package com.cappielloantonio.tempo.service
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MediaItem.SubtitleConfiguration
|
import androidx.media3.common.MediaItem.SubtitleConfiguration
|
||||||
import androidx.media3.common.MediaMetadata
|
import androidx.media3.common.MediaMetadata
|
||||||
import androidx.media3.session.LibraryResult
|
import androidx.media3.session.LibraryResult
|
||||||
import androidx.media3.session.MediaLibraryService
|
import androidx.media3.session.MediaLibraryService
|
||||||
|
import com.cappielloantonio.tempo.model.SessionMediaItem
|
||||||
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
object MediaBrowserTree {
|
object MediaBrowserTree {
|
||||||
|
|
||||||
|
|
@ -42,6 +44,8 @@ object MediaBrowserTree {
|
||||||
|
|
||||||
// Second level LIBRARY_ID
|
// Second level LIBRARY_ID
|
||||||
private const val FOLDER_ID = "[folderID]"
|
private const val FOLDER_ID = "[folderID]"
|
||||||
|
private const val INDEX_ID = "[indexID]"
|
||||||
|
private const val DIRECTORY_ID = "[directoryID]"
|
||||||
private const val PLAYLIST_ID = "[playlistID]"
|
private const val PLAYLIST_ID = "[playlistID]"
|
||||||
|
|
||||||
// Second level OTHER_ID
|
// Second level OTHER_ID
|
||||||
|
|
@ -343,7 +347,7 @@ object MediaBrowserTree {
|
||||||
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
|
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
|
||||||
BEST_OF_ID -> automotiveRepository.getStarredArtists(id, true)
|
BEST_OF_ID -> automotiveRepository.getStarredArtists(id, true)
|
||||||
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id, true)
|
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id, true)
|
||||||
STARRED_TRACKS_ID -> automotiveRepository.getStarredSongs(id)
|
STARRED_TRACKS_ID -> automotiveRepository.starredSongs
|
||||||
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
|
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
|
||||||
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id, false)
|
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id, false)
|
||||||
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
|
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
|
||||||
|
|
@ -359,7 +363,6 @@ object MediaBrowserTree {
|
||||||
id.removePrefix(
|
id.removePrefix(
|
||||||
MOST_PLAYED_ID
|
MOST_PLAYED_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,7 +371,6 @@ object MediaBrowserTree {
|
||||||
id.removePrefix(
|
id.removePrefix(
|
||||||
LAST_PLAYED_ID
|
LAST_PLAYED_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,7 +379,6 @@ object MediaBrowserTree {
|
||||||
id.removePrefix(
|
id.removePrefix(
|
||||||
RECENTLY_ADDED_ID
|
RECENTLY_ADDED_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,11 +391,38 @@ object MediaBrowserTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.startsWith(FOLDER_ID)) {
|
if (id.startsWith(FOLDER_ID)) {
|
||||||
|
return automotiveRepository.getIndexes(
|
||||||
|
INDEX_ID,
|
||||||
|
id.removePrefix(
|
||||||
|
FOLDER_ID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.startsWith(INDEX_ID)) {
|
||||||
|
return automotiveRepository.getDirectories(
|
||||||
|
DIRECTORY_ID,
|
||||||
|
id.removePrefix(
|
||||||
|
INDEX_ID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.startsWith(DIRECTORY_ID)) {
|
||||||
|
return automotiveRepository.getDirectories(
|
||||||
|
DIRECTORY_ID,
|
||||||
|
id.removePrefix(
|
||||||
|
DIRECTORY_ID
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.startsWith(PLAYLIST_ID)) {
|
if (id.startsWith(PLAYLIST_ID)) {
|
||||||
|
return automotiveRepository.getPlaylistSongs(
|
||||||
|
id.removePrefix(
|
||||||
|
PLAYLIST_ID
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.startsWith(PODCAST_ID)) {
|
if (id.startsWith(PODCAST_ID)) {
|
||||||
|
|
@ -410,28 +438,27 @@ object MediaBrowserTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItem(id: String): ListenableFuture<LibraryResult<MediaItem>> {
|
// https://github.com/androidx/media/issues/156
|
||||||
val mediaMetadata = MediaMetadata.Builder()
|
fun getItems(mediaItems: List<MediaItem>): List<MediaItem> {
|
||||||
.setTitle("Titolo")
|
val updatedMediaItems = ArrayList<MediaItem>()
|
||||||
.setAlbumTitle("Titolo album")
|
|
||||||
.setArtist("Artista")
|
|
||||||
.setIsBrowsable(false)
|
|
||||||
.setIsPlayable(true)
|
|
||||||
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val mediaItem = MediaItem.Builder()
|
mediaItems.forEach {
|
||||||
.setMediaId(id)
|
if (it.localConfiguration?.uri != null) {
|
||||||
.setMediaMetadata(mediaMetadata)
|
updatedMediaItems.add(it)
|
||||||
.setUri(MusicUtil.getStreamUri(id))
|
} else {
|
||||||
.build()
|
val sessionMediaItem = automotiveRepository.getSessionMediaItem(it.mediaId)
|
||||||
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofItem(mediaItem, null))
|
if (sessionMediaItem != null) {
|
||||||
|
var toAdd = automotiveRepository.getMetadatas(sessionMediaItem.timestamp!!)
|
||||||
|
val index = toAdd.indexOfFirst { mediaItem -> mediaItem.mediaId == it.mediaId }
|
||||||
|
|
||||||
/* if (treeNodes[id]?.item != null) {
|
toAdd = toAdd.subList(index, toAdd.size)
|
||||||
return Futures.immediateFuture(LibraryResult.ofItem(treeNodes[id]!!.item, null))
|
|
||||||
|
updatedMediaItems.addAll(toAdd)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)) */
|
return updatedMediaItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,91 +121,17 @@ open class MediaLibrarySessionCallback(
|
||||||
return MediaBrowserTree.getChildren(parentId, params)
|
return MediaBrowserTree.getChildren(parentId, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* override fun onGetItem(
|
override fun onAddMediaItems(
|
||||||
session: MediaLibraryService.MediaLibrarySession,
|
|
||||||
browser: MediaSession.ControllerInfo,
|
|
||||||
mediaId: String
|
|
||||||
): ListenableFuture<LibraryResult<MediaItem>> {
|
|
||||||
Log.d(TAG, "onGetItem()")
|
|
||||||
|
|
||||||
return MediaBrowserTree.getItem(mediaId)
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* override fun onAddMediaItems(
|
|
||||||
mediaSession: MediaSession,
|
mediaSession: MediaSession,
|
||||||
controller: MediaSession.ControllerInfo,
|
controller: MediaSession.ControllerInfo,
|
||||||
mediaItems: List<MediaItem>
|
mediaItems: List<MediaItem>
|
||||||
): ListenableFuture<List<MediaItem>> {
|
): ListenableFuture<List<MediaItem>> {
|
||||||
Log.d(TAG, "onAddMediaItems()")
|
return super.onAddMediaItems(
|
||||||
|
mediaSession,
|
||||||
return Futures.immediateFuture(mediaItems)
|
controller,
|
||||||
}
|
MediaBrowserTree.getItems(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(
|
/* override fun onSearch(
|
||||||
session: MediaLibraryService.MediaLibrarySession,
|
session: MediaLibraryService.MediaLibrarySession,
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||||
if (this::castPlayer.isInitialized) castPlayer.release()
|
if (this::castPlayer.isInitialized) castPlayer.release()
|
||||||
player.release()
|
player.release()
|
||||||
mediaLibrarySession.release()
|
mediaLibrarySession.release()
|
||||||
|
automotiveRepository.deleteMetadata()
|
||||||
clearListener()
|
clearListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue