mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
feat: added "Recent songs" and "Random" menu items in Android Auto
This commit is contained in:
parent
14d6128df0
commit
54e988b70e
3 changed files with 103 additions and 0 deletions
|
|
@ -12,6 +12,9 @@ import java.util.List;
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public interface ChronologyDao {
|
public interface ChronologyDao {
|
||||||
|
@Query("SELECT * FROM chronology WHERE server == :server GROUP BY id ORDER BY timestamp DESC LIMIT :count")
|
||||||
|
LiveData<List<Chronology>> getLastPlayed(String server, int count);
|
||||||
|
|
||||||
@Query("SELECT * FROM chronology WHERE timestamp >= :startDate AND timestamp < :endDate AND server == :server GROUP BY id ORDER BY COUNT(id) DESC LIMIT 9")
|
@Query("SELECT * FROM chronology WHERE timestamp >= :startDate AND timestamp < :endDate AND server == :server GROUP BY id ORDER BY COUNT(id) DESC LIMIT 9")
|
||||||
LiveData<List<Chronology>> getAllFrom(long startDate, long endDate, String server);
|
LiveData<List<Chronology>> getAllFrom(long startDate, long endDate, String server);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
@ -12,9 +16,13 @@ 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.AppDatabase;
|
||||||
|
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
|
||||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.tempo.model.Chronology;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||||
|
import com.cappielloantonio.tempo.service.DownloaderManager;
|
||||||
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.Artist;
|
||||||
|
|
@ -26,6 +34,7 @@ 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.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
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;
|
||||||
|
|
@ -44,6 +53,7 @@ import retrofit2.Response;
|
||||||
|
|
||||||
public class AutomotiveRepository {
|
public class AutomotiveRepository {
|
||||||
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
||||||
|
private final ChronologyDao chronologyDao = AppDatabase.getInstance().chronologyDao();
|
||||||
|
|
||||||
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();
|
||||||
|
|
@ -132,6 +142,66 @@ public class AutomotiveRepository {
|
||||||
return listenableFuture;
|
return listenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getRandomSongs(int count) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getAlbumSongListClient()
|
||||||
|
.getRandomSongs(100, null, 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().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) {
|
||||||
|
List<Child> songs = response.body().getSubsonicResponse().getRandomSongs().getSongs();
|
||||||
|
|
||||||
|
setChildrenMetadata(songs);
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> 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<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
listenableFuture.setException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getRecentlyPlayedSongs(String server, int count) {
|
||||||
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
chronologyDao.getLastPlayed(server, count).observeForever(new Observer<List<Chronology>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Chronology> chronology) {
|
||||||
|
if (chronology != null && !chronology.isEmpty()) {
|
||||||
|
List<Child> songs = new ArrayList<>(chronology);
|
||||||
|
|
||||||
|
setChildrenMetadata(songs);
|
||||||
|
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||||
|
|
||||||
|
listenableFuture.set(libraryResult);
|
||||||
|
} else {
|
||||||
|
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
chronologyDao.getLastPlayed(server, count).removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return listenableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
||||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package com.cappielloantonio.tempo.service
|
package com.cappielloantonio.tempo.service
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
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 com.cappielloantonio.tempo.repository.AutomotiveRepository
|
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences.getServerId
|
||||||
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
|
||||||
|
|
@ -31,10 +33,12 @@ object MediaBrowserTree {
|
||||||
private const val MOST_PLAYED_ID = "[mostPlayedID]"
|
private const val MOST_PLAYED_ID = "[mostPlayedID]"
|
||||||
private const val LAST_PLAYED_ID = "[lastPlayedID]"
|
private const val LAST_PLAYED_ID = "[lastPlayedID]"
|
||||||
private const val RECENTLY_ADDED_ID = "[recentlyAddedID]"
|
private const val RECENTLY_ADDED_ID = "[recentlyAddedID]"
|
||||||
|
private const val RECENT_SONGS_ID = "[recentSongsID]"
|
||||||
private const val MADE_FOR_YOU_ID = "[madeForYouID]"
|
private const val MADE_FOR_YOU_ID = "[madeForYouID]"
|
||||||
private const val STARRED_TRACKS_ID = "[starredTracksID]"
|
private const val STARRED_TRACKS_ID = "[starredTracksID]"
|
||||||
private const val STARRED_ALBUMS_ID = "[starredAlbumsID]"
|
private const val STARRED_ALBUMS_ID = "[starredAlbumsID]"
|
||||||
private const val STARRED_ARTISTS_ID = "[starredArtistsID]"
|
private const val STARRED_ARTISTS_ID = "[starredArtistsID]"
|
||||||
|
private const val RANDOM_ID = "[randomID]"
|
||||||
|
|
||||||
// Second level LIBRARY_ID
|
// Second level LIBRARY_ID
|
||||||
private const val FOLDER_ID = "[folderID]"
|
private const val FOLDER_ID = "[folderID]"
|
||||||
|
|
@ -193,6 +197,17 @@ object MediaBrowserTree {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
treeNodes[RECENT_SONGS_ID] =
|
||||||
|
MediaItemNode(
|
||||||
|
buildMediaItem(
|
||||||
|
title = "Recent songs",
|
||||||
|
mediaId = RECENT_SONGS_ID,
|
||||||
|
isPlayable = false,
|
||||||
|
isBrowsable = true,
|
||||||
|
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
treeNodes[MADE_FOR_YOU_ID] =
|
treeNodes[MADE_FOR_YOU_ID] =
|
||||||
MediaItemNode(
|
MediaItemNode(
|
||||||
buildMediaItem(
|
buildMediaItem(
|
||||||
|
|
@ -237,13 +252,26 @@ object MediaBrowserTree {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
treeNodes[RANDOM_ID] =
|
||||||
|
MediaItemNode(
|
||||||
|
buildMediaItem(
|
||||||
|
title = "Random",
|
||||||
|
mediaId = RANDOM_ID,
|
||||||
|
isPlayable = false,
|
||||||
|
isBrowsable = true,
|
||||||
|
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
treeNodes[HOME_ID]!!.addChild(MOST_PLAYED_ID)
|
treeNodes[HOME_ID]!!.addChild(MOST_PLAYED_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(LAST_PLAYED_ID)
|
treeNodes[HOME_ID]!!.addChild(LAST_PLAYED_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(RECENTLY_ADDED_ID)
|
treeNodes[HOME_ID]!!.addChild(RECENTLY_ADDED_ID)
|
||||||
|
treeNodes[HOME_ID]!!.addChild(RECENT_SONGS_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(MADE_FOR_YOU_ID)
|
treeNodes[HOME_ID]!!.addChild(MADE_FOR_YOU_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(STARRED_TRACKS_ID)
|
treeNodes[HOME_ID]!!.addChild(STARRED_TRACKS_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(STARRED_ALBUMS_ID)
|
treeNodes[HOME_ID]!!.addChild(STARRED_ALBUMS_ID)
|
||||||
treeNodes[HOME_ID]!!.addChild(STARRED_ARTISTS_ID)
|
treeNodes[HOME_ID]!!.addChild(STARRED_ARTISTS_ID)
|
||||||
|
treeNodes[HOME_ID]!!.addChild(RANDOM_ID)
|
||||||
|
|
||||||
// Second level LIBRARY_ID
|
// Second level LIBRARY_ID
|
||||||
|
|
||||||
|
|
@ -316,10 +344,12 @@ object MediaBrowserTree {
|
||||||
MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 100)
|
MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 100)
|
||||||
LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 100)
|
LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 100)
|
||||||
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
|
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
|
||||||
|
RECENT_SONGS_ID -> automotiveRepository.getRecentlyPlayedSongs(getServerId(),100)
|
||||||
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id)
|
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id)
|
||||||
STARRED_TRACKS_ID -> automotiveRepository.starredSongs
|
STARRED_TRACKS_ID -> automotiveRepository.starredSongs
|
||||||
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
|
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
|
||||||
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id)
|
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id)
|
||||||
|
RANDOM_ID -> automotiveRepository.getRandomSongs(100)
|
||||||
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
|
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
|
||||||
PLAYLIST_ID -> automotiveRepository.getPlaylists(id)
|
PLAYLIST_ID -> automotiveRepository.getPlaylists(id)
|
||||||
PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(100)
|
PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(100)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue