mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
Reimplemented a basic track download functionality
This commit is contained in:
parent
b7a77cf32b
commit
dee845ebff
17 changed files with 154 additions and 214 deletions
|
|
@ -19,6 +19,8 @@ import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.service.MediaManager;
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.MusicUtil;
|
import com.cappielloantonio.play.util.MusicUtil;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
|
@ -59,11 +61,11 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||||
holder.songSubtitle.setText(context.getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtistName()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
holder.songSubtitle.setText(context.getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtistName()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||||
holder.trackNumber.setText(String.valueOf(song.getTrackNumber()));
|
holder.trackNumber.setText(String.valueOf(song.getTrackNumber()));
|
||||||
|
|
||||||
/*if (DownloadUtil.getDownloadTracker(context).isDownloaded(song)) {
|
if (DownloadUtil.getDownloadTracker(context).isDownloaded(MappingUtil.mapMediaItem(context, song, false))) {
|
||||||
holder.downloadIndicator.setVisibility(View.VISIBLE);
|
holder.downloadIndicator.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
holder.downloadIndicator.setVisibility(View.GONE);
|
holder.downloadIndicator.setVisibility(View.GONE);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (isCoverVisible) CustomGlideRequest.Builder
|
if (isCoverVisible) CustomGlideRequest.Builder
|
||||||
.from(context, song.getPrimary(), CustomGlideRequest.SONG_PIC, null)
|
.from(context, song.getPrimary(), CustomGlideRequest.SONG_PIC, null)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.NotificationUtil;
|
import androidx.media3.common.util.NotificationUtil;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
|
@ -30,6 +31,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||||
super(FOREGROUND_NOTIFICATION_ID, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID, R.string.exo_download_notification_channel_name, 0);
|
super(FOREGROUND_NOTIFICATION_ID, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID, R.string.exo_download_notification_channel_name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected DownloadManager getDownloadManager() {
|
protected DownloadManager getDownloadManager() {
|
||||||
DownloadManager downloadManager = DownloadUtil.getDownloadManager(this);
|
DownloadManager downloadManager = DownloadUtil.getDownloadManager(this);
|
||||||
|
|
@ -38,17 +40,18 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||||
return downloadManager;
|
return downloadManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected Scheduler getScheduler() {
|
protected Scheduler getScheduler() {
|
||||||
return new PlatformScheduler(this, JOB_ID);
|
return new PlatformScheduler(this, JOB_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected Notification getForegroundNotification(List<Download> downloads, @Requirements.RequirementFlags int notMetRequirements) {
|
protected Notification getForegroundNotification(@NonNull List<Download> downloads, @Requirements.RequirementFlags int notMetRequirements) {
|
||||||
return DownloadUtil.getDownloadNotificationHelper(this).buildProgressNotification(this, R.drawable.ic_download, null, null, downloads, notMetRequirements);
|
return DownloadUtil.getDownloadNotificationHelper(this).buildProgressNotification(this, R.drawable.ic_download, null, null, downloads, notMetRequirements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final DownloadNotificationHelper notificationHelper;
|
private final DownloadNotificationHelper notificationHelper;
|
||||||
|
|
@ -63,7 +66,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadChanged(DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
|
public void onDownloadChanged(@NonNull DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
|
||||||
Notification notification;
|
Notification notification;
|
||||||
|
|
||||||
if (download.state == Download.STATE_COMPLETED) {
|
if (download.state == Download.STATE_COMPLETED) {
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,12 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.FragmentManager;
|
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
|
||||||
import androidx.media3.exoplayer.offline.Download;
|
import androidx.media3.exoplayer.offline.Download;
|
||||||
import androidx.media3.exoplayer.offline.DownloadCursor;
|
import androidx.media3.exoplayer.offline.DownloadCursor;
|
||||||
import androidx.media3.exoplayer.offline.DownloadHelper;
|
import androidx.media3.exoplayer.offline.DownloadHelper;
|
||||||
|
|
@ -22,39 +17,31 @@ import androidx.media3.exoplayer.offline.DownloadIndex;
|
||||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||||
import androidx.media3.exoplayer.offline.DownloadRequest;
|
import androidx.media3.exoplayer.offline.DownloadRequest;
|
||||||
import androidx.media3.exoplayer.offline.DownloadService;
|
import androidx.media3.exoplayer.offline.DownloadService;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
|
||||||
import androidx.media3.exoplayer.trackselection.MappingTrackSelector;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
public class DownloaderTracker {
|
public class DownloaderTracker {
|
||||||
private static final String TAG = "DownloadTracker";
|
private static final String TAG = "DownloadTracker";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final HttpDataSource.Factory httpDataSourceFactory;
|
|
||||||
private final CopyOnWriteArraySet<Listener> listeners;
|
private final CopyOnWriteArraySet<Listener> listeners;
|
||||||
private final HashMap<Uri, Download> downloads;
|
private final HashMap<Uri, Download> downloads;
|
||||||
private final DownloadIndex downloadIndex;
|
private final DownloadIndex downloadIndex;
|
||||||
private final DefaultTrackSelector.Parameters trackSelectorParameters;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private StartDownloadDialogHelper startDownloadDialogHelper;
|
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
void onDownloadsChanged();
|
void onDownloadsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
public DownloaderTracker(Context context, HttpDataSource.Factory httpDataSourceFactory, DownloadManager downloadManager) {
|
public DownloaderTracker(Context context, DownloadManager downloadManager) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.httpDataSourceFactory = httpDataSourceFactory;
|
|
||||||
|
|
||||||
listeners = new CopyOnWriteArraySet<>();
|
listeners = new CopyOnWriteArraySet<>();
|
||||||
downloads = new HashMap<>();
|
downloads = new HashMap<>();
|
||||||
downloadIndex = downloadManager.getDownloadIndex();
|
downloadIndex = downloadManager.getDownloadIndex();
|
||||||
trackSelectorParameters = DownloadHelper.getDefaultTrackSelectorParameters(context);
|
|
||||||
|
|
||||||
downloadManager.addListener(new DownloadManagerListener());
|
downloadManager.addListener(new DownloadManagerListener());
|
||||||
loadDownloads();
|
loadDownloads();
|
||||||
|
|
@ -70,28 +57,48 @@ public class DownloaderTracker {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
private DownloadRequest buildDownloadRequest(MediaItem mediaItem) {
|
||||||
|
return DownloadHelper.forMediaItem(context, mediaItem).getDownloadRequest(Util.getUtf8Bytes(checkNotNull(mediaItem.mediaId)));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
public boolean isDownloaded(MediaItem mediaItem) {
|
public boolean isDownloaded(MediaItem mediaItem) {
|
||||||
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
|
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
|
||||||
return download != null && download.state != Download.STATE_FAILED;
|
return download != null && download.state != Download.STATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
public DownloadRequest getDownloadRequest(Uri uri) {
|
public boolean areDownloaded(List<MediaItem> mediaItems) {
|
||||||
@Nullable Download download = downloads.get(uri);
|
for (MediaItem mediaItem : mediaItems) {
|
||||||
return download != null && download.state != Download.STATE_FAILED ? download.request : null;
|
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
|
||||||
|
if (download != null && download.state != Download.STATE_FAILED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
public void toggleDownload(FragmentManager fragmentManager, MediaItem mediaItem, RenderersFactory renderersFactory) {
|
public void download(MediaItem mediaItem) {
|
||||||
@Nullable Download download = downloads.get(checkNotNull(mediaItem.localConfiguration).uri);
|
DownloadService.sendAddDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem), false);
|
||||||
if (download != null && download.state != Download.STATE_FAILED) {
|
}
|
||||||
androidx.media3.exoplayer.offline.DownloadService.sendRemoveDownload(context, DownloaderService.class, download.request.id, false);
|
|
||||||
} else {
|
public void download(List<MediaItem> mediaItems) {
|
||||||
if (startDownloadDialogHelper != null) {
|
for (MediaItem mediaItem : mediaItems) {
|
||||||
startDownloadDialogHelper.release();
|
download(mediaItem);
|
||||||
}
|
}
|
||||||
startDownloadDialogHelper = new StartDownloadDialogHelper(fragmentManager, DownloadHelper.forMediaItem(context, mediaItem, renderersFactory, httpDataSourceFactory), mediaItem);
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
public void remove(MediaItem mediaItem) {
|
||||||
|
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(List<MediaItem> mediaItems) {
|
||||||
|
for (MediaItem mediaItem : mediaItems) {
|
||||||
|
remove(mediaItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,109 +131,4 @@ public class DownloaderTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class StartDownloadDialogHelper implements DownloadHelper.Callback, DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
|
||||||
private final FragmentManager fragmentManager;
|
|
||||||
private final DownloadHelper downloadHelper;
|
|
||||||
private final MediaItem mediaItem;
|
|
||||||
|
|
||||||
private TrackSelectionDialog trackSelectionDialog;
|
|
||||||
private MappingTrackSelector.MappedTrackInfo mappedTrackInfo;
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
public StartDownloadDialogHelper(FragmentManager fragmentManager, DownloadHelper downloadHelper, MediaItem mediaItem) {
|
|
||||||
this.fragmentManager = fragmentManager;
|
|
||||||
this.downloadHelper = downloadHelper;
|
|
||||||
this.mediaItem = mediaItem;
|
|
||||||
downloadHelper.prepare(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
public void release() {
|
|
||||||
downloadHelper.release();
|
|
||||||
if (trackSelectionDialog != null) {
|
|
||||||
trackSelectionDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Override
|
|
||||||
public void onPrepared(DownloadHelper helper) {
|
|
||||||
onDownloadPrepared(helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Override
|
|
||||||
public void onPrepareError(DownloadHelper helper, IOException e) {
|
|
||||||
Toast.makeText(context, R.string.download_start_error, Toast.LENGTH_LONG).show();
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialogInterface.OnClickListener implementation.
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
for (int periodIndex = 0; periodIndex < downloadHelper.getPeriodCount(); periodIndex++) {
|
|
||||||
downloadHelper.clearTrackSelections(periodIndex);
|
|
||||||
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
|
|
||||||
if (!trackSelectionDialog.getIsDisabled(i)) {
|
|
||||||
downloadHelper.addTrackSelectionForSingleRenderer(periodIndex, i, trackSelectorParameters, trackSelectionDialog.getOverrides(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadRequest downloadRequest = buildDownloadRequest();
|
|
||||||
|
|
||||||
if (downloadRequest.streamKeys.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startDownload(downloadRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Override
|
|
||||||
public void onDismiss(DialogInterface dialogInterface) {
|
|
||||||
trackSelectionDialog = null;
|
|
||||||
downloadHelper.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
private void onDownloadPrepared(DownloadHelper helper) {
|
|
||||||
if (helper.getPeriodCount() == 0) {
|
|
||||||
Log.d(TAG, "No periods found. Downloading entire stream.");
|
|
||||||
startDownload();
|
|
||||||
downloadHelper.release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mappedTrackInfo = downloadHelper.getMappedTrackInfo(0);
|
|
||||||
|
|
||||||
if (!TrackSelectionDialog.willHaveContent(mappedTrackInfo)) {
|
|
||||||
Log.d(TAG, "No dialog content. Downloading entire stream.");
|
|
||||||
startDownload();
|
|
||||||
downloadHelper.release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
trackSelectionDialog = TrackSelectionDialog.createForMappedTrackInfoAndParameters(R.string.exo_download_description, mappedTrackInfo, trackSelectorParameters, false, true, this, this);
|
|
||||||
|
|
||||||
trackSelectionDialog.show(fragmentManager, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startDownload() {
|
|
||||||
startDownload(buildDownloadRequest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
private void startDownload(DownloadRequest downloadRequest) {
|
|
||||||
DownloadService.sendAddDownload(context, DownloaderService.class, downloadRequest, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
private DownloadRequest buildDownloadRequest() {
|
|
||||||
return downloadHelper.getDownloadRequest(Util.getUtf8Bytes(checkNotNull(mediaItem.mediaMetadata.title.toString()))).copyWithKeySetId(keySetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ public class MediaManager {
|
||||||
try {
|
try {
|
||||||
if (mediaBrowserListenableFuture.isDone()) {
|
if (mediaBrowserListenableFuture.isDone()) {
|
||||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||||
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(context, songs));
|
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(context, songs, true));
|
||||||
mediaBrowserListenableFuture.get().seekTo(getQueueRepository().getLastPlayedSongIndex(), 0);
|
mediaBrowserListenableFuture.get().seekTo(getQueueRepository().getLastPlayedSongIndex(), 0);
|
||||||
mediaBrowserListenableFuture.get().prepare();
|
mediaBrowserListenableFuture.get().prepare();
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +148,7 @@ public class MediaManager {
|
||||||
try {
|
try {
|
||||||
if (mediaBrowserListenableFuture.isDone()) {
|
if (mediaBrowserListenableFuture.isDone()) {
|
||||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||||
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(context, songs));
|
mediaBrowserListenableFuture.get().setMediaItems(MappingUtil.mapMediaItems(context, songs, true));
|
||||||
mediaBrowserListenableFuture.get().prepare();
|
mediaBrowserListenableFuture.get().prepare();
|
||||||
mediaBrowserListenableFuture.get().seekTo(startIndex, 0);
|
mediaBrowserListenableFuture.get().seekTo(startIndex, 0);
|
||||||
mediaBrowserListenableFuture.get().play();
|
mediaBrowserListenableFuture.get().play();
|
||||||
|
|
@ -167,7 +167,7 @@ public class MediaManager {
|
||||||
try {
|
try {
|
||||||
if (mediaBrowserListenableFuture.isDone()) {
|
if (mediaBrowserListenableFuture.isDone()) {
|
||||||
mediaBrowserListenableFuture.get().clearMediaItems();
|
mediaBrowserListenableFuture.get().clearMediaItems();
|
||||||
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(context, song));
|
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(context, song, true));
|
||||||
mediaBrowserListenableFuture.get().prepare();
|
mediaBrowserListenableFuture.get().prepare();
|
||||||
mediaBrowserListenableFuture.get().play();
|
mediaBrowserListenableFuture.get().play();
|
||||||
enqueueDatabase(song, true, 0);
|
enqueueDatabase(song, true, 0);
|
||||||
|
|
@ -186,10 +186,10 @@ public class MediaManager {
|
||||||
if (mediaBrowserListenableFuture.isDone()) {
|
if (mediaBrowserListenableFuture.isDone()) {
|
||||||
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
||||||
enqueueDatabase(songs, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
enqueueDatabase(songs, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
||||||
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(context, songs));
|
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(context, songs, true));
|
||||||
} else {
|
} else {
|
||||||
enqueueDatabase(songs, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
enqueueDatabase(songs, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
||||||
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(context, songs));
|
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(context, songs, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
|
@ -206,10 +206,10 @@ public class MediaManager {
|
||||||
if (mediaBrowserListenableFuture.isDone()) {
|
if (mediaBrowserListenableFuture.isDone()) {
|
||||||
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) {
|
||||||
enqueueDatabase(song, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
enqueueDatabase(song, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex());
|
||||||
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(context, song));
|
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(context, song, true));
|
||||||
} else {
|
} else {
|
||||||
enqueueDatabase(song, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
enqueueDatabase(song, false, mediaBrowserListenableFuture.get().getMediaItemCount());
|
||||||
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(context, song));
|
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(context, song, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,15 @@ import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
|
import androidx.media3.exoplayer.source.MediaSourceFactory;
|
||||||
import androidx.media3.session.MediaLibraryService;
|
import androidx.media3.session.MediaLibraryService;
|
||||||
import androidx.media3.session.MediaSession;
|
import androidx.media3.session.MediaSession;
|
||||||
|
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
|
||||||
public class MediaService extends MediaLibraryService {
|
public class MediaService extends MediaLibraryService {
|
||||||
private static final String TAG = "MediaService";
|
private static final String TAG = "MediaService";
|
||||||
|
|
@ -21,11 +25,14 @@ public class MediaService extends MediaLibraryService {
|
||||||
public static final int REQUEST_CODE = 432;
|
public static final int REQUEST_CODE = 432;
|
||||||
|
|
||||||
private ExoPlayer player;
|
private ExoPlayer player;
|
||||||
|
private DataSource.Factory dataSourceFactory;
|
||||||
|
private MediaSourceFactory mediaSourceFactory;
|
||||||
private MediaLibrarySession mediaLibrarySession;
|
private MediaLibrarySession mediaLibrarySession;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
initializeMediaSource();
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
initializePlayerListener();
|
initializePlayerListener();
|
||||||
}
|
}
|
||||||
|
|
@ -42,9 +49,16 @@ public class MediaService extends MediaLibraryService {
|
||||||
return mediaLibrarySession;
|
return mediaLibrarySession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
private void initializeMediaSource() {
|
||||||
|
dataSourceFactory = DownloadUtil.getDataSourceFactory(this);
|
||||||
|
mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
private void initializePlayer() {
|
private void initializePlayer() {
|
||||||
player = new ExoPlayer.Builder(this)
|
player = new ExoPlayer.Builder(this)
|
||||||
|
.setMediaSourceFactory(mediaSourceFactory)
|
||||||
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||||
.setHandleAudioBecomingNoisy(true)
|
.setHandleAudioBecomingNoisy(true)
|
||||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,41 @@ package com.cappielloantonio.play.ui.activity.base;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.exoplayer.offline.DownloadService;
|
||||||
import androidx.media3.common.MediaMetadata;
|
|
||||||
import androidx.media3.session.MediaBrowser;
|
import androidx.media3.session.MediaBrowser;
|
||||||
import androidx.media3.session.SessionToken;
|
import androidx.media3.session.SessionToken;
|
||||||
|
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.service.DownloaderService;
|
||||||
|
import com.cappielloantonio.play.service.DownloaderTracker;
|
||||||
import com.cappielloantonio.play.service.MediaService;
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
public class BaseActivity extends AppCompatActivity implements DownloaderTracker.Listener {
|
||||||
|
|
||||||
public class BaseActivity extends AppCompatActivity {
|
|
||||||
private static final String TAG = "BaseActivity";
|
private static final String TAG = "BaseActivity";
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
private DownloaderTracker downloaderTracker;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
initializeDownloader();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
initializeBrowser();
|
initializeBrowser();
|
||||||
|
addDownloadListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -42,9 +49,16 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
releaseBrowser();
|
releaseBrowser();
|
||||||
|
removeDownloadListener();
|
||||||
super.onStop();
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadsChanged() {
|
||||||
|
// TODO Notificare all'item scaricato che lo stato di download è cambiato
|
||||||
|
// sampleAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private void checkBatteryOptimization() {
|
private void checkBatteryOptimization() {
|
||||||
if (detectBatteryOptimization()) {
|
if (detectBatteryOptimization()) {
|
||||||
showBatteryOptimizationDialog();
|
showBatteryOptimizationDialog();
|
||||||
|
|
@ -85,4 +99,23 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
public ListenableFuture<MediaBrowser> getMediaBrowserListenableFuture() {
|
public ListenableFuture<MediaBrowser> getMediaBrowserListenableFuture() {
|
||||||
return mediaBrowserListenableFuture;
|
return mediaBrowserListenableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
private void initializeDownloader() {
|
||||||
|
downloaderTracker = DownloadUtil.getDownloadTracker(this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DownloadService.start(this, DownloaderService.class);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
DownloadService.startForeground(this, DownloaderService.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDownloadListener() {
|
||||||
|
downloaderTracker.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDownloadListener() {
|
||||||
|
downloaderTracker.removeListener(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import androidx.lifecycle.ViewModelProvider;
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.databinding.DialogConnectionAlertBinding;
|
import com.cappielloantonio.play.databinding.DialogConnectionAlertBinding;
|
||||||
import com.cappielloantonio.play.util.DownloadUtil;
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
import com.cappielloantonio.play.viewmodel.StarredSyncViewModel;
|
import com.cappielloantonio.play.viewmodel.StarredSyncViewModel;
|
||||||
|
|
||||||
|
|
@ -34,8 +35,10 @@ public class StarredSyncDialog extends DialogFragment {
|
||||||
|
|
||||||
builder.setView(bind.getRoot())
|
builder.setView(bind.getRoot())
|
||||||
.setTitle(R.string.starred_sync_dialog_title)
|
.setTitle(R.string.starred_sync_dialog_title)
|
||||||
.setPositiveButton(R.string.starred_sync_dialog_positive_button, (dialog, id) -> { })
|
.setPositiveButton(R.string.starred_sync_dialog_positive_button, (dialog, id) -> {
|
||||||
.setNegativeButton(R.string.starred_sync_dialog_negative_button, (dialog, id) -> { });
|
})
|
||||||
|
.setNegativeButton(R.string.starred_sync_dialog_negative_button, (dialog, id) -> {
|
||||||
|
});
|
||||||
|
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +63,7 @@ public class StarredSyncDialog extends DialogFragment {
|
||||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
starredSyncViewModel.getStarredTracks(requireActivity()).observe(requireActivity(), songs -> {
|
starredSyncViewModel.getStarredTracks(requireActivity()).observe(requireActivity(), songs -> {
|
||||||
if (songs != null) {
|
if (songs != null) {
|
||||||
// DownloadUtil.getDownloadTracker(context).download(songs, null, null);
|
DownloadUtil.getDownloadTracker(context).download(MappingUtil.mapMediaItems(context, songs, false));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.play.service.MediaManager;
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
import com.cappielloantonio.play.service.MediaService;
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.MusicUtil;
|
import com.cappielloantonio.play.util.MusicUtil;
|
||||||
import com.cappielloantonio.play.viewmodel.AlbumPageViewModel;
|
import com.cappielloantonio.play.viewmodel.AlbumPageViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
@ -106,7 +108,7 @@ public class AlbumPageFragment extends Fragment {
|
||||||
if (item.getItemId() == R.id.action_download_album) {
|
if (item.getItemId() == R.id.action_download_album) {
|
||||||
albumPageViewModel.getAlbumSongLiveList(requireActivity()).observe(requireActivity(), songs -> {
|
albumPageViewModel.getAlbumSongLiveList(requireActivity()).observe(requireActivity(), songs -> {
|
||||||
if (isVisible() && getActivity() != null) {
|
if (isVisible() && getActivity() != null) {
|
||||||
// DownloadUtil.getDownloadTracker(requireContext()).download(songs, null, null);
|
DownloadUtil.getDownloadTracker(requireContext()).download(MappingUtil.mapMediaItems(requireContext(), songs, false));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,13 @@ import androidx.media3.session.SessionToken;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||||
import com.cappielloantonio.play.App;
|
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.adapter.SongHorizontalAdapter;
|
import com.cappielloantonio.play.adapter.SongHorizontalAdapter;
|
||||||
import com.cappielloantonio.play.databinding.FragmentPlaylistPageBinding;
|
import com.cappielloantonio.play.databinding.FragmentPlaylistPageBinding;
|
||||||
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.play.repository.QueueRepository;
|
|
||||||
import com.cappielloantonio.play.service.MediaManager;
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
import com.cappielloantonio.play.service.MediaService;
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.play.util.DownloadUtil;
|
|
||||||
import com.cappielloantonio.play.util.MusicUtil;
|
import com.cappielloantonio.play.util.MusicUtil;
|
||||||
import com.cappielloantonio.play.viewmodel.PlaylistPageViewModel;
|
import com.cappielloantonio.play.viewmodel.PlaylistPageViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import android.widget.ToggleButton;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.session.MediaBrowser;
|
import androidx.media3.session.MediaBrowser;
|
||||||
import androidx.media3.session.SessionToken;
|
import androidx.media3.session.SessionToken;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
|
|
@ -29,6 +30,8 @@ import com.cappielloantonio.play.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.play.service.MediaManager;
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
import com.cappielloantonio.play.service.MediaService;
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.MusicUtil;
|
import com.cappielloantonio.play.util.MusicUtil;
|
||||||
import com.cappielloantonio.play.viewmodel.AlbumBottomSheetViewModel;
|
import com.cappielloantonio.play.viewmodel.AlbumBottomSheetViewModel;
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
|
|
@ -153,19 +156,21 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
TextView removeAll = view.findViewById(R.id.remove_all_text_view);
|
TextView removeAll = view.findViewById(R.id.remove_all_text_view);
|
||||||
|
|
||||||
albumBottomSheetViewModel.getAlbumTracks().observe(requireActivity(), songs -> {
|
albumBottomSheetViewModel.getAlbumTracks().observe(requireActivity(), songs -> {
|
||||||
|
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(requireContext(), songs, false);
|
||||||
|
|
||||||
downloadAll.setOnClickListener(v -> {
|
downloadAll.setOnClickListener(v -> {
|
||||||
// DownloadUtil.getDownloadTracker(requireContext()).download(songs, null, null);
|
DownloadUtil.getDownloadTracker(requireContext()).download(mediaItems);
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
/*if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(songs)) {
|
if (DownloadUtil.getDownloadTracker(requireContext()).areDownloaded(mediaItems)) {
|
||||||
removeAll.setOnClickListener(v -> {
|
removeAll.setOnClickListener(v -> {
|
||||||
DownloadUtil.getDownloadTracker(requireContext()).remove(songs);
|
DownloadUtil.getDownloadTracker(requireContext()).remove(mediaItems);
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
removeAll.setVisibility(View.GONE);
|
removeAll.setVisibility(View.GONE);
|
||||||
}*/
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
TextView goToArtist = view.findViewById(R.id.go_to_artist_text_view);
|
TextView goToArtist = view.findViewById(R.id.go_to_artist_text_view);
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ import com.cappielloantonio.play.service.MediaService;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.play.ui.dialog.PlaylistChooserDialog;
|
import com.cappielloantonio.play.ui.dialog.PlaylistChooserDialog;
|
||||||
import com.cappielloantonio.play.ui.dialog.RatingDialog;
|
import com.cappielloantonio.play.ui.dialog.RatingDialog;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.MusicUtil;
|
import com.cappielloantonio.play.util.MusicUtil;
|
||||||
import com.cappielloantonio.play.viewmodel.SongBottomSheetViewModel;
|
import com.cappielloantonio.play.viewmodel.SongBottomSheetViewModel;
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
|
|
@ -155,13 +157,13 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
|
|
||||||
TextView download = view.findViewById(R.id.download_text_view);
|
TextView download = view.findViewById(R.id.download_text_view);
|
||||||
download.setOnClickListener(v -> {
|
download.setOnClickListener(v -> {
|
||||||
// DownloadUtil.getDownloadTracker(requireContext()).download(Collections.singletonList(song), null, null);
|
DownloadUtil.getDownloadTracker(requireContext()).download(MappingUtil.mapMediaItem(requireContext(), song, false));
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
TextView remove = view.findViewById(R.id.remove_text_view);
|
TextView remove = view.findViewById(R.id.remove_text_view);
|
||||||
remove.setOnClickListener(v -> {
|
remove.setOnClickListener(v -> {
|
||||||
// DownloadUtil.getDownloadTracker(requireContext()).remove(Collections.singletonList(song));
|
DownloadUtil.getDownloadTracker(requireContext()).remove(MappingUtil.mapMediaItem(requireContext(), song, false));
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -214,13 +216,13 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDownloadUI(TextView download, TextView remove) {
|
private void initDownloadUI(TextView download, TextView remove) {
|
||||||
/*if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(song)) {
|
if (DownloadUtil.getDownloadTracker(requireContext()).isDownloaded(MappingUtil.mapMediaItem(requireContext(), song, false))) {
|
||||||
download.setVisibility(View.GONE);
|
download.setVisibility(View.GONE);
|
||||||
remove.setVisibility(View.VISIBLE);
|
remove.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
download.setVisibility(View.VISIBLE);
|
download.setVisibility(View.VISIBLE);
|
||||||
remove.setVisibility(View.GONE);
|
remove.setVisibility(View.GONE);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import androidx.media3.datasource.cache.Cache;
|
||||||
import androidx.media3.datasource.cache.CacheDataSource;
|
import androidx.media3.datasource.cache.CacheDataSource;
|
||||||
import androidx.media3.datasource.cache.NoOpCacheEvictor;
|
import androidx.media3.datasource.cache.NoOpCacheEvictor;
|
||||||
import androidx.media3.datasource.cache.SimpleCache;
|
import androidx.media3.datasource.cache.SimpleCache;
|
||||||
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
|
||||||
import androidx.media3.exoplayer.offline.ActionFileUpgradeUtil;
|
import androidx.media3.exoplayer.offline.ActionFileUpgradeUtil;
|
||||||
import androidx.media3.exoplayer.offline.DefaultDownloadIndex;
|
import androidx.media3.exoplayer.offline.DefaultDownloadIndex;
|
||||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||||
|
|
@ -48,26 +46,7 @@ public final class DownloadUtil {
|
||||||
private static DownloaderTracker downloaderTracker;
|
private static DownloaderTracker downloaderTracker;
|
||||||
private static DownloadNotificationHelper downloadNotificationHelper;
|
private static DownloadNotificationHelper downloadNotificationHelper;
|
||||||
|
|
||||||
/**
|
public static synchronized HttpDataSource.Factory getHttpDataSourceFactory() {
|
||||||
* Returns whether extension renderers should be used.
|
|
||||||
*/
|
|
||||||
public static boolean useExtensionRenderers() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
public static RenderersFactory buildRenderersFactory(Context context, boolean preferExtensionRenderer) {
|
|
||||||
@DefaultRenderersFactory.ExtensionRendererMode
|
|
||||||
int extensionRendererMode = useExtensionRenderers()
|
|
||||||
? (preferExtensionRenderer
|
|
||||||
? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
|
||||||
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
|
|
||||||
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;
|
|
||||||
|
|
||||||
return new DefaultRenderersFactory(context.getApplicationContext()).setExtensionRendererMode(extensionRendererMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized HttpDataSource.Factory getHttpDataSourceFactory(Context context) {
|
|
||||||
if (httpDataSourceFactory == null) {
|
if (httpDataSourceFactory == null) {
|
||||||
CookieManager cookieManager = new CookieManager();
|
CookieManager cookieManager = new CookieManager();
|
||||||
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||||
|
|
@ -81,7 +60,7 @@ public final class DownloadUtil {
|
||||||
public static synchronized DataSource.Factory getDataSourceFactory(Context context) {
|
public static synchronized DataSource.Factory getDataSourceFactory(Context context) {
|
||||||
if (dataSourceFactory == null) {
|
if (dataSourceFactory == null) {
|
||||||
context = context.getApplicationContext();
|
context = context.getApplicationContext();
|
||||||
DefaultDataSource.Factory upstreamFactory = new DefaultDataSource.Factory(context, getHttpDataSourceFactory(context));
|
DefaultDataSource.Factory upstreamFactory = new DefaultDataSource.Factory(context, getHttpDataSourceFactory());
|
||||||
dataSourceFactory = buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache(context));
|
dataSourceFactory = buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +92,7 @@ public final class DownloadUtil {
|
||||||
File downloadContentDirectory = new File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY);
|
File downloadContentDirectory = new File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY);
|
||||||
downloadCache = new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider(context));
|
downloadCache = new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
return downloadCache;
|
return downloadCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,10 +100,10 @@ public final class DownloadUtil {
|
||||||
private static synchronized void ensureDownloadManagerInitialized(Context context) {
|
private static synchronized void ensureDownloadManagerInitialized(Context context) {
|
||||||
if (downloadManager == null) {
|
if (downloadManager == null) {
|
||||||
DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context));
|
DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context));
|
||||||
upgradeActionFile(context, DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false);
|
upgradeActionFile(context, DOWNLOAD_ACTION_FILE, downloadIndex, false);
|
||||||
upgradeActionFile(context, DOWNLOAD_TRACKER_ACTION_FILE, downloadIndex, true);
|
upgradeActionFile(context, DOWNLOAD_TRACKER_ACTION_FILE, downloadIndex, true);
|
||||||
downloadManager = new DownloadManager(context, getDatabaseProvider(context), getDownloadCache(context), getHttpDataSourceFactory(context), Executors.newFixedThreadPool(/* nThreads= */ 6));
|
downloadManager = new DownloadManager(context, getDatabaseProvider(context), getDownloadCache(context), getHttpDataSourceFactory(), Executors.newFixedThreadPool(6));
|
||||||
downloaderTracker = new DownloaderTracker(context, getHttpDataSourceFactory(context), downloadManager);
|
downloaderTracker = new DownloaderTracker(context, downloadManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,12 +121,13 @@ public final class DownloadUtil {
|
||||||
if (databaseProvider == null) {
|
if (databaseProvider == null) {
|
||||||
databaseProvider = new StandaloneDatabaseProvider(context);
|
databaseProvider = new StandaloneDatabaseProvider(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return databaseProvider;
|
return databaseProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static synchronized File getDownloadDirectory(Context context) {
|
private static synchronized File getDownloadDirectory(Context context) {
|
||||||
if (downloadDirectory == null) {
|
if (downloadDirectory == null) {
|
||||||
downloadDirectory = context.getExternalFilesDir(/* type= */ null);
|
downloadDirectory = context.getExternalFilesDir(null);
|
||||||
if (downloadDirectory == null) {
|
if (downloadDirectory == null) {
|
||||||
downloadDirectory = context.getFilesDir();
|
downloadDirectory = context.getFilesDir();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ public class MappingUtil {
|
||||||
return genres;
|
return genres;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MediaItem mapMediaItem(Context context, Song song) {
|
public static MediaItem mapMediaItem(Context context, Song song, boolean isForStreaming) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("id", song.getId());
|
bundle.putString("id", song.getId());
|
||||||
bundle.putString("albumId", song.getAlbumId());
|
bundle.putString("albumId", song.getAlbumId());
|
||||||
|
|
@ -207,7 +207,7 @@ public class MappingUtil {
|
||||||
.setMediaId(song.getId())
|
.setMediaId(song.getId())
|
||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setMediaUri(MusicUtil.getSongStreamUri(context, song))
|
.setMediaUri(isForStreaming ? MusicUtil.getSongStreamUri(context, song) : MusicUtil.getSongDownloadUri(song))
|
||||||
.setTitle(MusicUtil.getReadableString(song.getTitle()))
|
.setTitle(MusicUtil.getReadableString(song.getTitle()))
|
||||||
.setTrackNumber(song.getTrackNumber())
|
.setTrackNumber(song.getTrackNumber())
|
||||||
.setDiscNumber(song.getDiscNumber())
|
.setDiscNumber(song.getDiscNumber())
|
||||||
|
|
@ -217,14 +217,15 @@ public class MappingUtil {
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
.setUri(isForStreaming ? MusicUtil.getSongStreamUri(context, song) : MusicUtil.getSongDownloadUri(song))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<MediaItem> mapMediaItems(Context context, List<Song> songs) {
|
public static ArrayList<MediaItem> mapMediaItems(Context context, List<Song> songs, boolean isForStreaming) {
|
||||||
ArrayList<MediaItem> mediaItems = new ArrayList();
|
ArrayList<MediaItem> mediaItems = new ArrayList();
|
||||||
|
|
||||||
for (Song song : songs) {
|
for (Song song : songs) {
|
||||||
mediaItems.add(mapMediaItem(context, song));
|
mediaItems.add(mapMediaItem(context, song, isForStreaming));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mediaItems;
|
return mediaItems;
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import android.net.Uri;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.media3.common.MediaItem;
|
|
||||||
|
|
||||||
import com.cappielloantonio.play.App;
|
import com.cappielloantonio.play.App;
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
|
|
@ -45,7 +43,7 @@ public class MusicUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
public static MediaItem getSongDownloadItem(Song song) {
|
public static Uri getSongDownloadUri(Song song) {
|
||||||
Map<String, String> params = App.getSubsonicClientInstance(App.getInstance(), false).getParams();
|
Map<String, String> params = App.getSubsonicClientInstance(App.getInstance(), false).getParams();
|
||||||
|
|
||||||
String uri = App.getSubsonicClientInstance(App.getInstance(), false).getUrl() +
|
String uri = App.getSubsonicClientInstance(App.getInstance(), false).getUrl() +
|
||||||
|
|
@ -57,7 +55,9 @@ public class MusicUtil {
|
||||||
"&c=" + params.get("c") +
|
"&c=" + params.get("c") +
|
||||||
"&id=" + song.getId();
|
"&id=" + song.getId();
|
||||||
|
|
||||||
return MediaItem.fromUri(uri);
|
Log.d(TAG, "getSongDownloadUri(): " + uri);
|
||||||
|
|
||||||
|
return Uri.parse(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getReadableDurationString(long duration, boolean millis) {
|
public static String getReadableDurationString(long duration, boolean millis) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.repository.ArtistRepository;
|
import com.cappielloantonio.play.repository.ArtistRepository;
|
||||||
import com.cappielloantonio.play.repository.QueueRepository;
|
import com.cappielloantonio.play.repository.QueueRepository;
|
||||||
import com.cappielloantonio.play.repository.SongRepository;
|
import com.cappielloantonio.play.repository.SongRepository;
|
||||||
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -45,11 +47,6 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||||
return queueRepository.getLiveQueue();
|
return queueRepository.getLiveQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Song getCurrentSong() {
|
|
||||||
// return MusicPlayerRemote.getCurrentSong();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFavorite(Context context, Song song) {
|
public void setFavorite(Context context, Song song) {
|
||||||
if (song != null) {
|
if (song != null) {
|
||||||
if (song.isFavorite()) {
|
if (song.isFavorite()) {
|
||||||
|
|
@ -60,7 +57,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||||
song.setFavorite(true);
|
song.setFavorite(true);
|
||||||
|
|
||||||
if (PreferenceUtil.getInstance(context).isStarredSyncEnabled()) {
|
if (PreferenceUtil.getInstance(context).isStarredSyncEnabled()) {
|
||||||
// DownloadUtil.getDownloadTracker(context).download(Collections.singletonList(song), null, null);
|
DownloadUtil.getDownloadTracker(context).download(MappingUtil.mapMediaItem(context, song, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +68,6 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshSongInfo(LifecycleOwner owner, Song song) {
|
public void refreshSongInfo(LifecycleOwner owner, Song song) {
|
||||||
// songLiveData.postValue(song);
|
|
||||||
songRepository.getSongLyrics(song).observe(owner, lyricsLiveData::postValue);
|
songRepository.getSongLyrics(song).observe(owner, lyricsLiveData::postValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.preference.Preference;
|
|
||||||
|
|
||||||
import com.cappielloantonio.play.model.Album;
|
import com.cappielloantonio.play.model.Album;
|
||||||
import com.cappielloantonio.play.model.Artist;
|
import com.cappielloantonio.play.model.Artist;
|
||||||
|
|
@ -15,10 +14,9 @@ import com.cappielloantonio.play.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.play.repository.ArtistRepository;
|
import com.cappielloantonio.play.repository.ArtistRepository;
|
||||||
import com.cappielloantonio.play.repository.SongRepository;
|
import com.cappielloantonio.play.repository.SongRepository;
|
||||||
import com.cappielloantonio.play.util.DownloadUtil;
|
import com.cappielloantonio.play.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class SongBottomSheetViewModel extends AndroidViewModel {
|
public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final SongRepository songRepository;
|
private final SongRepository songRepository;
|
||||||
private final AlbumRepository albumRepository;
|
private final AlbumRepository albumRepository;
|
||||||
|
|
@ -50,8 +48,8 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
|
||||||
songRepository.star(song.getId());
|
songRepository.star(song.getId());
|
||||||
song.setFavorite(true);
|
song.setFavorite(true);
|
||||||
|
|
||||||
if(PreferenceUtil.getInstance(context).isStarredSyncEnabled()) {
|
if (PreferenceUtil.getInstance(context).isStarredSyncEnabled()) {
|
||||||
// DownloadUtil.getDownloadTracker(context).download(Collections.singletonList(song), null, null);
|
DownloadUtil.getDownloadTracker(context).download(MappingUtil.mapMediaItem(context, song, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
<string name="connection_alert_dialog_summary">Access to the Subsonic server on connections other than Wi-Fi has been restricted. To prevent this alert dialod from reappearing, disable the connection check in the app settings.</string>
|
<string name="connection_alert_dialog_summary">Access to the Subsonic server on connections other than Wi-Fi has been restricted. To prevent this alert dialod from reappearing, disable the connection check in the app settings.</string>
|
||||||
<string name="download_info_empty_subtitle">Once you download a song, you\'ll find it here</string>
|
<string name="download_info_empty_subtitle">Once you download a song, you\'ll find it here</string>
|
||||||
<string name="download_info_empty_title">No downloads yet!</string>
|
<string name="download_info_empty_title">No downloads yet!</string>
|
||||||
|
<string name="download_start_error">Failed to start download</string>
|
||||||
<string name="download_title_album_section">Albums</string>
|
<string name="download_title_album_section">Albums</string>
|
||||||
<string name="download_title_album_see_all">See all</string>
|
<string name="download_title_album_see_all">See all</string>
|
||||||
<string name="download_title_artist_section">Artists</string>
|
<string name="download_title_artist_section">Artists</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue