Separate the functions for creating a mediaItem from a Child object and from a Download object

This commit is contained in:
antonio 2023-03-12 16:34:38 +01:00
parent e3f088468c
commit e337c6faf2
15 changed files with 103 additions and 141 deletions

View file

@ -32,7 +32,7 @@ public class DownloaderManager {
private final Context context;
private final DataSource.Factory dataSourceFactory;
private final HashMap<Uri, Download> downloads;
private final HashMap<String, Download> downloads;
private final DownloadIndex downloadIndex;
public DownloaderManager(Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
@ -46,27 +46,29 @@ public class DownloaderManager {
}
private DownloadRequest buildDownloadRequest(MediaItem mediaItem) {
return DownloadHelper.forMediaItem(
context,
mediaItem,
DownloadUtil.buildRenderersFactory(context, false),
dataSourceFactory
).getDownloadRequest(Util.getUtf8Bytes(checkNotNull(mediaItem.mediaId)));
return DownloadHelper
.forMediaItem(
context,
mediaItem,
DownloadUtil.buildRenderersFactory(context, false),
dataSourceFactory)
.getDownloadRequest(Util.getUtf8Bytes(checkNotNull(mediaItem.mediaId)))
.copyWithId(mediaItem.mediaId);
}
public boolean isDownloaded(Uri uri) {
@Nullable Download download = downloads.get(uri);
public boolean isDownloaded(String mediaId) {
@Nullable Download download = downloads.get(mediaId);
return download != null && download.state != Download.STATE_FAILED;
}
public boolean isDownloaded(MediaItem mediaItem) {
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
@Nullable Download download = downloads.get(mediaItem.mediaId);
return download != null && download.state != Download.STATE_FAILED;
}
public boolean areDownloaded(List<MediaItem> mediaItems) {
for (MediaItem mediaItem : mediaItems) {
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
@Nullable Download download = downloads.get(mediaItem.mediaId);
if (download != null && download.state != Download.STATE_FAILED) {
return true;
}
@ -101,7 +103,7 @@ public class DownloaderManager {
try (DownloadCursor loadedDownloads = downloadIndex.getDownloads()) {
while (loadedDownloads.moveToNext()) {
Download download = loadedDownloads.getDownload();
downloads.put(download.request.uri, download);
downloads.put(download.request.id, download);
}
} catch (IOException e) {
Log.w(TAG, "Failed to query downloads", e);

View file

@ -1,7 +1,5 @@
package com.cappielloantonio.play.service;
import android.content.Context;
import androidx.media3.common.MediaItem;
import androidx.media3.session.MediaBrowser;
@ -19,8 +17,6 @@ import java.util.List;
import java.util.concurrent.ExecutionException;
public class MediaManager {
private static final String TAG = "MediaManager";
public static void reset(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
@ -82,7 +78,7 @@ public class MediaManager {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems();
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(media, true));
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(media));
mediaBrowserListenableFuture.get().seekTo(getQueueRepository().getLastPlayedMediaIndex(), getQueueRepository().getLastPlayedMediaTimestamp());
mediaBrowserListenableFuture.get().prepare();
}
@ -93,83 +89,13 @@ public class MediaManager {
}
}
public static void prepare(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().prepare();
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void play(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().play();
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void pause(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().pause();
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void stop(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().stop();
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void clearQueue(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems();
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void startQueue(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int startIndex) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems();
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(media, true));
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(media));
mediaBrowserListenableFuture.get().prepare();
mediaBrowserListenableFuture.get().seekTo(startIndex, 0);
mediaBrowserListenableFuture.get().play();
@ -188,7 +114,7 @@ public class MediaManager {
try {
if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems();
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(media, true));
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(media));
mediaBrowserListenableFuture.get().prepare();
mediaBrowserListenableFuture.get().play();
enqueueDatabase(media, true, 0);
@ -207,10 +133,10 @@ public class MediaManager {
if (mediaBrowserListenableFuture.isDone()) {
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(media, true));
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(media));
} else {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media, true));
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media));
}
}
} catch (ExecutionException | InterruptedException e) {
@ -227,10 +153,10 @@ public class MediaManager {
if (mediaBrowserListenableFuture.isDone()) {
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(media, true));
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(media));
} else {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media, true));
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media));
}
}
} catch (ExecutionException | InterruptedException e) {
@ -298,15 +224,19 @@ public class MediaManager {
}
public static void scrobble(MediaItem mediaItem) {
if (mediaItem != null)
if (getQueueRepository().isMediaPlayingPlausible(mediaItem))
if (mediaItem != null) {
if (getQueueRepository().isMediaPlayingPlausible(mediaItem)) {
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"));
}
}
}
public static void saveChronology(MediaItem mediaItem) {
if (mediaItem != null)
if (getQueueRepository().isMediaPlayingPlausible(mediaItem))
if (mediaItem != null) {
if (getQueueRepository().isMediaPlayingPlausible(mediaItem)) {
getChronologyRepository().insert(new Chronology(mediaItem));
}
}
}
private static QueueRepository getQueueRepository() {

View file

@ -54,7 +54,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
holder.item.trackNumberTextView.setText(String.valueOf(song.getTrack()));
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(MappingUtil.mapMediaItem(song, false))) {
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.VISIBLE);
} else {
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.GONE);

View file

@ -63,7 +63,7 @@ public class StarredSyncDialog extends DialogFragment {
starredSyncViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
if (songs != null) {
DownloadUtil.getDownloadTracker(context).download(
MappingUtil.mapMediaItems(songs, false),
MappingUtil.mapDownloads(songs),
songs.stream().map(Download::new).collect(Collectors.toList())
);
}

View file

@ -107,7 +107,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
if (item.getItemId() == R.id.action_download_album) {
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> {
DownloadUtil.getDownloadTracker(requireContext()).download(
MappingUtil.mapMediaItems(songs, false),
MappingUtil.mapDownloads(songs),
songs.stream().map(Download::new).collect(Collectors.toList())
);
});

View file

@ -156,7 +156,9 @@ public class PlayerControllerFragment extends Fragment {
private void setMediaInfo(MediaMetadata mediaMetadata) {
if (mediaMetadata.extras != null) {
String extension = mediaMetadata.extras.getString("suffix", "Unknown format");
String bitrate = mediaMetadata.extras.getInt("bitrate", 0) != 0 ? mediaMetadata.extras.getInt("bitrate", 0) + "kbps" : "Original";
String bitrate = mediaMetadata.extras.getInt("bitrate", 0) != 0
? mediaMetadata.extras.getInt("bitrate", 0) + "kbps"
: "Original";
playerMediaExtension.setText(extension);
@ -170,7 +172,9 @@ public class PlayerControllerFragment extends Fragment {
}
String transcodingExtension = MusicUtil.getTranscodingFormatPreference();
String transcodingBitrate = Integer.parseInt(MusicUtil.getBitratePreference()) != 0 ? Integer.parseInt(MusicUtil.getBitratePreference()) + "kbps" : "Original";
String transcodingBitrate = Integer.parseInt(MusicUtil.getBitratePreference()) != 0
? Integer.parseInt(MusicUtil.getBitratePreference()) + "kbps"
: "Original";
if (transcodingExtension.equals("raw") && transcodingBitrate.equals("Original")) {
playerMediaTranscodingIcon.setVisibility(View.GONE);
@ -184,6 +188,12 @@ public class PlayerControllerFragment extends Fragment {
playerMediaTranscodedExtension.setText(transcodingExtension);
playerMediaTranscodedBitrate.setText(transcodingBitrate);
}
if (mediaMetadata.extras != null && mediaMetadata.extras.getString("uri", "").contains(Constants.DOWNLOAD_URI)) {
playerMediaTranscodingIcon.setVisibility(View.GONE);
playerMediaTranscodedBitrate.setVisibility(View.GONE);
playerMediaTranscodedExtension.setVisibility(View.GONE);
}
}
private void setMediaControllerUI(MediaBrowser mediaBrowser) {

View file

@ -92,7 +92,7 @@ public class PlayerCoverFragment extends Fragment {
if (song != null && bind != null) {
bind.innerButtonTopLeft.setOnClickListener(view -> {
DownloadUtil.getDownloadTracker(requireContext()).download(
MappingUtil.mapMediaItem(song, false),
MappingUtil.mapDownload(song),
new Download(song)
);
});

View file

@ -106,7 +106,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> {
if (isVisible() && getActivity() != null) {
DownloadUtil.getDownloadTracker(requireContext()).download(
MappingUtil.mapMediaItems(songs, false),
MappingUtil.mapDownloads(songs),
songs.stream().map(child -> {
Download toDownload = new Download(child);
toDownload.setPlaylistId(playlistPageViewModel.getPlaylist().getId());

View file

@ -156,7 +156,7 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
TextView removeAll = view.findViewById(R.id.remove_all_text_view);
albumBottomSheetViewModel.getAlbumTracks().observe(getViewLifecycleOwner(), songs -> {
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs, false);
List<MediaItem> mediaItems = MappingUtil.mapDownloads(songs);
List<Download> downloads = songs.stream().map(Download::new).collect(Collectors.toList());
downloadAll.setOnClickListener(v -> {

View file

@ -156,7 +156,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
TextView download = view.findViewById(R.id.download_text_view);
download.setOnClickListener(v -> {
DownloadUtil.getDownloadTracker(requireContext()).download(
MappingUtil.mapMediaItem(song, false),
MappingUtil.mapDownload(song),
new Download(song)
);
dismissBottomSheet();
@ -165,7 +165,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
TextView remove = view.findViewById(R.id.remove_text_view);
remove.setOnClickListener(v -> {
DownloadUtil.getDownloadTracker(requireContext()).remove(
MappingUtil.mapMediaItem(song, false),
MappingUtil.mapDownload(song),
new Download(song)
);
dismissBottomSheet();
@ -220,7 +220,7 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
}
private void initDownloadUI(TextView download, TextView remove) {
if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(MappingUtil.mapMediaItem(song, false))) {
if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(song.getId())) {
download.setVisibility(View.GONE);
remove.setVisibility(View.VISIBLE);
} else {

View file

@ -63,4 +63,6 @@ object Constants {
const val MEDIA_MIX = "MEDIA_MIX"
const val MEDIA_CHRONOLOGY = "MEDIA_CHRONOLOGY"
const val MEDIA_BEST_OF = "MEDIA_BEST_OF"
const val DOWNLOAD_URI = "rest/download"
}

View file

@ -17,18 +17,18 @@ import java.util.List;
@OptIn(markerClass = UnstableApi.class)
public class MappingUtil {
public static ArrayList<MediaItem> mapMediaItems(List<Child> items, boolean stream) {
ArrayList<MediaItem> mediaItems = new ArrayList();
public static List<MediaItem> mapMediaItems(List<Child> items) {
ArrayList<MediaItem> mediaItems = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
mediaItems.add(mapMediaItem(items.get(i), stream));
mediaItems.add(mapMediaItem(items.get(i)));
}
return mediaItems;
}
public static MediaItem mapMediaItem(Child media, boolean stream) {
boolean isDownloaded = DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(MusicUtil.getDownloadUri(media.getId()));
public static MediaItem mapMediaItem(Child media) {
Uri uri = getUri(media);
Bundle bundle = new Bundle();
bundle.putString("id", media.getId());
@ -62,6 +62,7 @@ public class MappingUtil {
bundle.putLong("bookmarkPosition", media.getBookmarkPosition() != null ? media.getBookmarkPosition() : 0);
bundle.putInt("originalWidth", media.getOriginalWidth() != null ? media.getOriginalWidth() : 0);
bundle.putInt("originalHeight", media.getOriginalHeight() != null ? media.getOriginalHeight() : 0);
bundle.putString("uri", uri.toString());
return new MediaItem.Builder()
.setMediaId(media.getId())
@ -78,20 +79,51 @@ public class MappingUtil {
)
.setRequestMetadata(
new MediaItem.RequestMetadata.Builder()
.setMediaUri(getUri(media, stream && !isDownloaded))
.setMediaUri(uri)
.setExtras(bundle)
.build()
)
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
.setUri(getUri(media, stream && !isDownloaded))
.setUri(uri)
.build();
}
private static Uri getUri(Child media, boolean stream) {
if (stream) {
return MusicUtil.getStreamUri(media.getId());
} else {
return MusicUtil.getDownloadUri(media.getId());
public static List<MediaItem> mapDownloads(List<Child> items) {
ArrayList<MediaItem> downloads = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
downloads.add(mapDownload(items.get(i)));
}
return downloads;
}
public static MediaItem mapDownload(Child media) {
return new MediaItem.Builder()
.setMediaId(media.getId())
.setMediaMetadata(
new MediaMetadata.Builder()
.setTitle(MusicUtil.getReadableString(media.getTitle()))
.setTrackNumber(media.getTrack())
.setDiscNumber(media.getDiscNumber())
.setReleaseYear(media.getYear())
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
.setArtist(MusicUtil.getReadableString(media.getArtist()))
.build()
)
.setRequestMetadata(
new MediaItem.RequestMetadata.Builder()
.setMediaUri(MusicUtil.getDownloadUri(media.getId()))
.build()
)
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
.setUri(MusicUtil.getDownloadUri(media.getId()))
.build();
}
private static Uri getUri(Child media) {
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(media.getId())
? MusicUtil.getDownloadUri(media.getId())
: MusicUtil.getStreamUri(media.getId());
}
}

View file

@ -6,10 +6,9 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.text.Html;
import android.util.Log;
import com.cappielloantonio.play.App;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.glide.CustomGlideRequest;
import java.util.ArrayList;
import java.util.List;
@ -45,6 +44,8 @@ public class MusicUtil {
uri.append("&format=").append(getTranscodingFormatPreference());
uri.append("&id=").append(id);
Log.d(TAG, "getStreamUri: " + uri);
return Uri.parse(uri.toString());
}
@ -54,7 +55,7 @@ public class MusicUtil {
StringBuilder uri = new StringBuilder();
uri.append(App.getSubsonicClientInstance(false).getUrl());
uri.append("stream");
uri.append("download");
if (params.containsKey("u") && params.get("u") != null)
uri.append("?u=").append(params.get("u"));
@ -71,6 +72,8 @@ public class MusicUtil {
uri.append("&id=").append(id);
Log.d(TAG, "getDownloadUri: " + uri);
return Uri.parse(uri.toString());
}

View file

@ -61,7 +61,7 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
if (Preferences.isStarredSyncEnabled()) {
DownloadUtil.getDownloadTracker(context).download(
MappingUtil.mapMediaItem(song, false),
MappingUtil.mapDownload(song),
new Download(song)
);
}

View file

@ -1,17 +0,0 @@
package com.cappielloantonio.play;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}