Merge branch 'develop' into semanticDownloadView
This commit is contained in:
commit
9f1315b6dc
|
@ -4,7 +4,6 @@ import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
@ -25,8 +24,8 @@ import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
|
||||||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||||
import org.moire.ultrasonic.util.CancellationToken;
|
import org.moire.ultrasonic.util.CancellationToken;
|
||||||
import org.moire.ultrasonic.util.Constants;
|
import org.moire.ultrasonic.util.Constants;
|
||||||
import org.moire.ultrasonic.util.Pair;
|
|
||||||
import org.moire.ultrasonic.util.FragmentBackgroundTask;
|
import org.moire.ultrasonic.util.FragmentBackgroundTask;
|
||||||
|
import org.moire.ultrasonic.util.Pair;
|
||||||
import org.moire.ultrasonic.util.Util;
|
import org.moire.ultrasonic.util.Util;
|
||||||
import org.moire.ultrasonic.view.EntryAdapter;
|
import org.moire.ultrasonic.view.EntryAdapter;
|
||||||
|
|
||||||
|
@ -78,23 +77,14 @@ public class BookmarksFragment extends Fragment {
|
||||||
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
|
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
|
||||||
albumListView = view.findViewById(R.id.select_album_entries_list);
|
albumListView = view.findViewById(R.id.select_album_entries_list);
|
||||||
|
|
||||||
refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
refreshAlbumListView.setOnRefreshListener(() -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onRefresh()
|
|
||||||
{
|
|
||||||
enableButtons();
|
enableButtons();
|
||||||
getBookmarks();
|
getBookmarks();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||||
|
|
||||||
albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
albumListView.setOnItemClickListener((parent, view17, position, id) -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
|
||||||
{
|
|
||||||
if (position >= 0)
|
if (position >= 0)
|
||||||
{
|
{
|
||||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
|
MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
|
||||||
|
@ -111,7 +101,6 @@ public class BookmarksFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageView selectButton = view.findViewById(R.id.select_album_select);
|
ImageView selectButton = view.findViewById(R.id.select_album_select);
|
||||||
|
@ -130,58 +119,24 @@ public class BookmarksFragment extends Fragment {
|
||||||
playLastButton.setVisibility(View.GONE);
|
playLastButton.setVisibility(View.GONE);
|
||||||
oreButton.setVisibility(View.GONE);
|
oreButton.setVisibility(View.GONE);
|
||||||
|
|
||||||
playNowButton.setOnClickListener(new View.OnClickListener()
|
playNowButton.setOnClickListener(view16 -> playNow(getSelectedSongs(albumListView)));
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
playNow(getSelectedSongs(albumListView));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
selectButton.setOnClickListener(new View.OnClickListener()
|
selectButton.setOnClickListener(view15 -> selectAllOrNone());
|
||||||
{
|
pinButton.setOnClickListener(view14 -> {
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
selectAllOrNone();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
pinButton.setOnClickListener(new View.OnClickListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
downloadBackground(true);
|
downloadBackground(true);
|
||||||
selectAll(false, false);
|
selectAll(false, false);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
unpinButton.setOnClickListener(new View.OnClickListener()
|
unpinButton.setOnClickListener(view13 -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
unpin();
|
unpin();
|
||||||
selectAll(false, false);
|
selectAll(false, false);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
downloadButton.setOnClickListener(new View.OnClickListener()
|
downloadButton.setOnClickListener(view12 -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
downloadBackground(false);
|
downloadBackground(false);
|
||||||
selectAll(false, false);
|
selectAll(false, false);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
deleteButton.setOnClickListener(new View.OnClickListener()
|
deleteButton.setOnClickListener(view1 -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(View view)
|
|
||||||
{
|
|
||||||
delete();
|
delete();
|
||||||
selectAll(false, false);
|
selectAll(false, false);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
registerForContextMenu(albumListView);
|
registerForContextMenu(albumListView);
|
||||||
|
@ -230,7 +185,8 @@ public class BookmarksFragment extends Fragment {
|
||||||
{
|
{
|
||||||
if (albumListView.isItemChecked(i))
|
if (albumListView.isItemChecked(i))
|
||||||
{
|
{
|
||||||
songs.add((MusicDirectory.Entry) albumListView.getItemAtPosition(i));
|
MusicDirectory.Entry song = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
|
||||||
|
if (song != null) songs.add(song);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,6 +247,7 @@ public class BookmarksFragment extends Fragment {
|
||||||
|
|
||||||
for (MusicDirectory.Entry song : selection)
|
for (MusicDirectory.Entry song : selection)
|
||||||
{
|
{
|
||||||
|
if (song == null) continue;
|
||||||
DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song);
|
DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song);
|
||||||
if (downloadFile.isWorkDone())
|
if (downloadFile.isWorkDone())
|
||||||
{
|
{
|
||||||
|
@ -326,11 +283,7 @@ public class BookmarksFragment extends Fragment {
|
||||||
|
|
||||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||||
{
|
{
|
||||||
Runnable onValid = new Runnable()
|
Runnable onValid = () -> {
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
||||||
mediaPlayerController.getValue().downloadBackground(songs, save);
|
mediaPlayerController.getValue().downloadBackground(songs, save);
|
||||||
|
|
||||||
|
@ -342,7 +295,6 @@ public class BookmarksFragment extends Fragment {
|
||||||
{
|
{
|
||||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onValid.run();
|
onValid.run();
|
||||||
|
|
|
@ -562,7 +562,7 @@ public class SearchFragment extends Fragment {
|
||||||
mediaPlayerController.clear();
|
mediaPlayerController.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPlayerController.download(Collections.singletonList(song), false, false, false, false, false);
|
mediaPlayerController.addToPlaylist(Collections.singletonList(song), false, false, false, false, false);
|
||||||
|
|
||||||
if (true)
|
if (true)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,445 +0,0 @@
|
||||||
package org.moire.ultrasonic.service;
|
|
||||||
|
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
|
||||||
import org.moire.ultrasonic.util.LRUCache;
|
|
||||||
import org.moire.ultrasonic.util.ShufflePlayBuffer;
|
|
||||||
import org.moire.ultrasonic.util.Util;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import kotlin.Lazy;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static org.koin.java.KoinJavaComponent.inject;
|
|
||||||
import static org.moire.ultrasonic.domain.PlayerState.DOWNLOADING;
|
|
||||||
import static org.moire.ultrasonic.domain.PlayerState.STARTED;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is responsible for maintaining the playlist and downloading
|
|
||||||
* its items from the network to the filesystem.
|
|
||||||
*/
|
|
||||||
public class Downloader
|
|
||||||
{
|
|
||||||
public final List<DownloadFile> downloadList = new ArrayList<>();
|
|
||||||
public final List<DownloadFile> backgroundDownloadList = new ArrayList<>();
|
|
||||||
public DownloadFile currentDownloading;
|
|
||||||
|
|
||||||
private final ShufflePlayBuffer shufflePlayBuffer;
|
|
||||||
private final ExternalStorageMonitor externalStorageMonitor;
|
|
||||||
private final LocalMediaPlayer localMediaPlayer;
|
|
||||||
|
|
||||||
// TODO: This is a circular reference, try to remove
|
|
||||||
private final Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class);
|
|
||||||
|
|
||||||
private final List<DownloadFile> cleanupCandidates = new ArrayList<>();
|
|
||||||
private final LRUCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LRUCache<>(100);
|
|
||||||
private ScheduledExecutorService executorService;
|
|
||||||
private long revision;
|
|
||||||
|
|
||||||
public Downloader(ShufflePlayBuffer shufflePlayBuffer, ExternalStorageMonitor externalStorageMonitor,
|
|
||||||
LocalMediaPlayer localMediaPlayer)
|
|
||||||
{
|
|
||||||
this.shufflePlayBuffer = shufflePlayBuffer;
|
|
||||||
this.externalStorageMonitor = externalStorageMonitor;
|
|
||||||
this.localMediaPlayer = localMediaPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCreate()
|
|
||||||
{
|
|
||||||
Runnable downloadChecker = () -> {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
checkDownloads();
|
|
||||||
}
|
|
||||||
catch (Throwable x)
|
|
||||||
{
|
|
||||||
Timber.e(x,"checkDownloads() failed.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
executorService.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS);
|
|
||||||
Timber.i("Downloader created");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDestroy()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
clear();
|
|
||||||
clearBackground();
|
|
||||||
Timber.i("Downloader destroyed");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop()
|
|
||||||
{
|
|
||||||
if (executorService != null) executorService.shutdown();
|
|
||||||
Timber.i("Downloader stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void checkDownloads()
|
|
||||||
{
|
|
||||||
if (!Util.isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shufflePlayBuffer.isEnabled)
|
|
||||||
{
|
|
||||||
checkShufflePlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jukeboxMediaPlayer.getValue().isEnabled() || !Util.isNetworkConnected())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadList.isEmpty() && backgroundDownloadList.isEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to download current playing?
|
|
||||||
if (localMediaPlayer.currentPlaying != null && localMediaPlayer.currentPlaying != currentDownloading && !localMediaPlayer.currentPlaying.isWorkDone())
|
|
||||||
{
|
|
||||||
// Cancel current download, if necessary.
|
|
||||||
if (currentDownloading != null)
|
|
||||||
{
|
|
||||||
currentDownloading.cancelDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDownloading = localMediaPlayer.currentPlaying;
|
|
||||||
currentDownloading.download();
|
|
||||||
cleanupCandidates.add(currentDownloading);
|
|
||||||
|
|
||||||
// Delete obsolete .partial and .complete files.
|
|
||||||
cleanup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a suitable target for download.
|
|
||||||
if (currentDownloading != null &&
|
|
||||||
!currentDownloading.isWorkDone() &&
|
|
||||||
(!currentDownloading.isFailed() || (downloadList.isEmpty() && backgroundDownloadList.isEmpty())))
|
|
||||||
{
|
|
||||||
cleanup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a target to download
|
|
||||||
currentDownloading = null;
|
|
||||||
int n = downloadList.size();
|
|
||||||
|
|
||||||
int preloaded = 0;
|
|
||||||
|
|
||||||
if (n != 0)
|
|
||||||
{
|
|
||||||
int start = localMediaPlayer.currentPlaying == null ? 0 : getCurrentPlayingIndex();
|
|
||||||
if (start == -1) start = 0;
|
|
||||||
|
|
||||||
int i = start;
|
|
||||||
// Check all DownloadFiles on the playlist
|
|
||||||
do
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = downloadList.get(i);
|
|
||||||
if (!downloadFile.isWorkDone())
|
|
||||||
{
|
|
||||||
if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount())
|
|
||||||
{
|
|
||||||
currentDownloading = downloadFile;
|
|
||||||
currentDownloading.download();
|
|
||||||
cleanupCandidates.add(currentDownloading);
|
|
||||||
if (i == (start + 1))
|
|
||||||
{
|
|
||||||
// The next file on the playlist is currently downloading
|
|
||||||
localMediaPlayer.setNextPlayerState(DOWNLOADING);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (localMediaPlayer.currentPlaying != downloadFile)
|
|
||||||
{
|
|
||||||
preloaded++;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = (i + 1) % n;
|
|
||||||
} while (i != start);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the downloadList contains no work, check the backgroundDownloadList
|
|
||||||
if ((preloaded + 1 == n || preloaded >= Util.getPreloadCount() || downloadList.isEmpty()) && !backgroundDownloadList.isEmpty())
|
|
||||||
{
|
|
||||||
for (int i = 0; i < backgroundDownloadList.size(); i++)
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = backgroundDownloadList.get(i);
|
|
||||||
if (downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved()))
|
|
||||||
{
|
|
||||||
Util.scanMedia(downloadFile.getCompleteFile());
|
|
||||||
|
|
||||||
// Don't need to keep list like active song list
|
|
||||||
backgroundDownloadList.remove(i);
|
|
||||||
revision++;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
else if (downloadFile.isFailed() && !downloadFile.shouldRetry()) {
|
|
||||||
// Don't continue to attempt to download forever
|
|
||||||
backgroundDownloadList.remove(i);
|
|
||||||
revision++;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentDownloading = downloadFile;
|
|
||||||
currentDownloading.download();
|
|
||||||
cleanupCandidates.add(currentDownloading);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete obsolete .partial and .complete files.
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized int getCurrentPlayingIndex()
|
|
||||||
{
|
|
||||||
return downloadList.indexOf(localMediaPlayer.currentPlaying);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDownloadListDuration()
|
|
||||||
{
|
|
||||||
long totalDuration = 0;
|
|
||||||
|
|
||||||
for (DownloadFile downloadFile : downloadList)
|
|
||||||
{
|
|
||||||
MusicDirectory.Entry entry = downloadFile.getSong();
|
|
||||||
|
|
||||||
if (!entry.isDirectory())
|
|
||||||
{
|
|
||||||
if (entry.getArtist() != null)
|
|
||||||
{
|
|
||||||
Integer duration = entry.getDuration();
|
|
||||||
|
|
||||||
if (duration != null)
|
|
||||||
{
|
|
||||||
totalDuration += duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<DownloadFile> getDownloads()
|
|
||||||
{
|
|
||||||
List<DownloadFile> temp = new ArrayList<>();
|
|
||||||
temp.addAll(downloadList);
|
|
||||||
temp.addAll(backgroundDownloadList);
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDownloadListUpdateRevision()
|
|
||||||
{
|
|
||||||
return revision;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void clear()
|
|
||||||
{
|
|
||||||
downloadList.clear();
|
|
||||||
revision++;
|
|
||||||
if (currentDownloading != null)
|
|
||||||
{
|
|
||||||
currentDownloading.cancelDownload();
|
|
||||||
currentDownloading = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearBackground()
|
|
||||||
{
|
|
||||||
if (currentDownloading != null && backgroundDownloadList.contains(currentDownloading))
|
|
||||||
{
|
|
||||||
currentDownloading.cancelDownload();
|
|
||||||
currentDownloading = null;
|
|
||||||
}
|
|
||||||
backgroundDownloadList.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void removeDownloadFile(DownloadFile downloadFile)
|
|
||||||
{
|
|
||||||
if (downloadFile == currentDownloading)
|
|
||||||
{
|
|
||||||
currentDownloading.cancelDownload();
|
|
||||||
currentDownloading = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadList.remove(downloadFile);
|
|
||||||
backgroundDownloadList.remove(downloadFile);
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoPlay, boolean playNext, boolean newPlaylist)
|
|
||||||
{
|
|
||||||
shufflePlayBuffer.isEnabled = false;
|
|
||||||
int offset = 1;
|
|
||||||
|
|
||||||
if (songs.isEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newPlaylist)
|
|
||||||
{
|
|
||||||
downloadList.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playNext)
|
|
||||||
{
|
|
||||||
if (autoPlay && getCurrentPlayingIndex() >= 0)
|
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MusicDirectory.Entry song : songs)
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = new DownloadFile(song, save);
|
|
||||||
downloadList.add(getCurrentPlayingIndex() + offset, downloadFile);
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (MusicDirectory.Entry song : songs)
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = new DownloadFile(song, save);
|
|
||||||
downloadList.add(downloadFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save)
|
|
||||||
{
|
|
||||||
for (MusicDirectory.Entry song : songs)
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = new DownloadFile(song, save);
|
|
||||||
backgroundDownloadList.add(downloadFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
revision++;
|
|
||||||
|
|
||||||
checkDownloads();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void shuffle()
|
|
||||||
{
|
|
||||||
Collections.shuffle(downloadList);
|
|
||||||
if (localMediaPlayer.currentPlaying != null)
|
|
||||||
{
|
|
||||||
downloadList.remove(localMediaPlayer.currentPlaying);
|
|
||||||
downloadList.add(0, localMediaPlayer.currentPlaying);
|
|
||||||
}
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized DownloadFile getDownloadFileForSong(MusicDirectory.Entry song)
|
|
||||||
{
|
|
||||||
for (DownloadFile downloadFile : downloadList)
|
|
||||||
{
|
|
||||||
if (downloadFile.getSong().equals(song) && ((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone()))
|
|
||||||
{
|
|
||||||
return downloadFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (DownloadFile downloadFile : backgroundDownloadList)
|
|
||||||
{
|
|
||||||
if (downloadFile.getSong().equals(song))
|
|
||||||
{
|
|
||||||
return downloadFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadFile downloadFile = downloadFileCache.get(song);
|
|
||||||
if (downloadFile == null)
|
|
||||||
{
|
|
||||||
downloadFile = new DownloadFile(song, false);
|
|
||||||
downloadFileCache.put(song, downloadFile);
|
|
||||||
}
|
|
||||||
return downloadFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void cleanup()
|
|
||||||
{
|
|
||||||
Iterator<DownloadFile> iterator = cleanupCandidates.iterator();
|
|
||||||
while (iterator.hasNext())
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = iterator.next();
|
|
||||||
if (downloadFile != localMediaPlayer.currentPlaying && downloadFile != currentDownloading)
|
|
||||||
{
|
|
||||||
if (downloadFile.cleanup())
|
|
||||||
{
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void checkShufflePlay()
|
|
||||||
{
|
|
||||||
// Get users desired random playlist size
|
|
||||||
int listSize = Util.getMaxSongs();
|
|
||||||
boolean wasEmpty = downloadList.isEmpty();
|
|
||||||
|
|
||||||
long revisionBefore = revision;
|
|
||||||
|
|
||||||
// First, ensure that list is at least 20 songs long.
|
|
||||||
int size = downloadList.size();
|
|
||||||
if (size < listSize)
|
|
||||||
{
|
|
||||||
for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size))
|
|
||||||
{
|
|
||||||
DownloadFile downloadFile = new DownloadFile(song, false);
|
|
||||||
downloadList.add(downloadFile);
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int currIndex = localMediaPlayer.currentPlaying == null ? 0 : getCurrentPlayingIndex();
|
|
||||||
|
|
||||||
// Only shift playlist if playing song #5 or later.
|
|
||||||
if (currIndex > 4)
|
|
||||||
{
|
|
||||||
int songsToShift = currIndex - 2;
|
|
||||||
for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift))
|
|
||||||
{
|
|
||||||
downloadList.add(new DownloadFile(song, false));
|
|
||||||
downloadList.get(0).cancelDownload();
|
|
||||||
downloadList.remove(0);
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revisionBefore != revision)
|
|
||||||
{
|
|
||||||
jukeboxMediaPlayer.getValue().updatePlaylist();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasEmpty && !downloadList.isEmpty())
|
|
||||||
{
|
|
||||||
if (jukeboxMediaPlayer.getValue().isEnabled())
|
|
||||||
{
|
|
||||||
jukeboxMediaPlayer.getValue().skip(0, 0);
|
|
||||||
localMediaPlayer.setPlayerState(STARTED);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
localMediaPlayer.play(downloadList.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,353 @@
|
||||||
|
package org.moire.ultrasonic.service
|
||||||
|
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.PriorityQueue
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
|
import org.moire.ultrasonic.domain.PlayerState
|
||||||
|
import org.moire.ultrasonic.util.LRUCache
|
||||||
|
import org.moire.ultrasonic.util.ShufflePlayBuffer
|
||||||
|
import org.moire.ultrasonic.util.Util.getMaxSongs
|
||||||
|
import org.moire.ultrasonic.util.Util.getPreloadCount
|
||||||
|
import org.moire.ultrasonic.util.Util.isExternalStoragePresent
|
||||||
|
import org.moire.ultrasonic.util.Util.isNetworkConnected
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for maintaining the playlist and downloading
|
||||||
|
* its items from the network to the filesystem.
|
||||||
|
*
|
||||||
|
* TODO: Implement LiveData
|
||||||
|
* TODO: Move away from managing the queue with scheduled checks, instead use callbacks when
|
||||||
|
* Downloads are finished
|
||||||
|
*/
|
||||||
|
class Downloader(
|
||||||
|
private val shufflePlayBuffer: ShufflePlayBuffer,
|
||||||
|
private val externalStorageMonitor: ExternalStorageMonitor,
|
||||||
|
private val localMediaPlayer: LocalMediaPlayer
|
||||||
|
) : KoinComponent {
|
||||||
|
val playlist: MutableList<DownloadFile> = ArrayList()
|
||||||
|
private val downloadQueue: PriorityQueue<DownloadFile> = PriorityQueue<DownloadFile>()
|
||||||
|
private val activelyDownloading: MutableList<DownloadFile> = ArrayList()
|
||||||
|
|
||||||
|
private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject()
|
||||||
|
|
||||||
|
private val downloadFileCache = LRUCache<MusicDirectory.Entry, DownloadFile>(100)
|
||||||
|
|
||||||
|
private var executorService: ScheduledExecutorService? = null
|
||||||
|
|
||||||
|
var playlistUpdateRevision: Long = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
val downloadChecker = Runnable {
|
||||||
|
try {
|
||||||
|
Timber.w("checking Downloads")
|
||||||
|
checkDownloadsInternal()
|
||||||
|
} catch (all: Exception) {
|
||||||
|
Timber.e(all, "checkDownloads() failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCreate() {
|
||||||
|
executorService = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
executorService!!.scheduleWithFixedDelay(
|
||||||
|
downloadChecker, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS
|
||||||
|
)
|
||||||
|
Timber.i("Downloader created")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDestroy() {
|
||||||
|
stop()
|
||||||
|
clearPlaylist()
|
||||||
|
clearBackground()
|
||||||
|
Timber.i("Downloader destroyed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
if (executorService != null) executorService!!.shutdown()
|
||||||
|
Timber.i("Downloader stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkDownloads() {
|
||||||
|
executorService?.execute(downloadChecker)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun checkDownloadsInternal() {
|
||||||
|
if (!isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (shufflePlayBuffer.isEnabled) {
|
||||||
|
checkShufflePlay()
|
||||||
|
}
|
||||||
|
if (jukeboxMediaPlayer.isEnabled || !isNetworkConnected()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the active downloads for failures or completions and remove them
|
||||||
|
cleanupActiveDownloads()
|
||||||
|
|
||||||
|
// Check if need to preload more from playlist
|
||||||
|
val preloadCount = getPreloadCount()
|
||||||
|
|
||||||
|
// Start preloading at the current playing song
|
||||||
|
var start = currentPlayingIndex
|
||||||
|
if (start == -1) start = 0
|
||||||
|
|
||||||
|
val end = (start + preloadCount).coerceAtMost(playlist.size)
|
||||||
|
|
||||||
|
for (i in start until end) {
|
||||||
|
val download = playlist[i]
|
||||||
|
|
||||||
|
// Set correct priority (the lower the number, the higher the priority)
|
||||||
|
download.priority = i
|
||||||
|
|
||||||
|
// Add file to queue if not in one of the queues already.
|
||||||
|
if (!download.isWorkDone &&
|
||||||
|
!activelyDownloading.contains(download) &&
|
||||||
|
!downloadQueue.contains(download)
|
||||||
|
) {
|
||||||
|
downloadQueue.add(download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill up active List with waiting tasks
|
||||||
|
while (activelyDownloading.size < PARALLEL_DOWNLOADS && downloadQueue.size > 0) {
|
||||||
|
val task = downloadQueue.remove()
|
||||||
|
activelyDownloading.add(task)
|
||||||
|
task.download()
|
||||||
|
|
||||||
|
// The next file on the playlist is currently downloading
|
||||||
|
if (playlist.indexOf(task) == 1) {
|
||||||
|
localMediaPlayer.setNextPlayerState(PlayerState.DOWNLOADING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupActiveDownloads() {
|
||||||
|
activelyDownloading.retainAll {
|
||||||
|
when {
|
||||||
|
it.isDownloading -> true
|
||||||
|
it.isFailed && it.shouldRetry() -> {
|
||||||
|
// Add it back to queue
|
||||||
|
downloadQueue.add(it)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
it.cleanup()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val currentPlayingIndex: Int
|
||||||
|
get() = playlist.indexOf(localMediaPlayer.currentPlaying)
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val downloadListDuration: Long
|
||||||
|
get() {
|
||||||
|
var totalDuration: Long = 0
|
||||||
|
for (downloadFile in playlist) {
|
||||||
|
val song = downloadFile.song
|
||||||
|
if (!song.isDirectory) {
|
||||||
|
if (song.artist != null) {
|
||||||
|
if (song.duration != null) {
|
||||||
|
totalDuration += song.duration!!.toLong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
val downloads: List<DownloadFile?>
|
||||||
|
get() {
|
||||||
|
val temp: MutableList<DownloadFile?> = ArrayList()
|
||||||
|
temp.addAll(playlist)
|
||||||
|
temp.addAll(activelyDownloading)
|
||||||
|
temp.addAll(downloadQueue)
|
||||||
|
return temp.distinct()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun clearPlaylist() {
|
||||||
|
playlist.clear()
|
||||||
|
|
||||||
|
// Cancel all active downloads with a high priority
|
||||||
|
for (download in activelyDownloading) {
|
||||||
|
if (download.priority < 100)
|
||||||
|
download.cancelDownload()
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistUpdateRevision++
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun clearBackground() {
|
||||||
|
// Clear the pending queue
|
||||||
|
downloadQueue.clear()
|
||||||
|
|
||||||
|
// Cancel all active downloads with a low priority
|
||||||
|
for (download in activelyDownloading) {
|
||||||
|
if (download.priority >= 100)
|
||||||
|
download.cancelDownload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun clearActiveDownloads() {
|
||||||
|
// Cancel all active downloads with a low priority
|
||||||
|
for (download in activelyDownloading) {
|
||||||
|
download.cancelDownload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun removeFromPlaylist(downloadFile: DownloadFile) {
|
||||||
|
if (activelyDownloading.contains(downloadFile)) {
|
||||||
|
downloadFile.cancelDownload()
|
||||||
|
}
|
||||||
|
playlist.remove(downloadFile)
|
||||||
|
playlistUpdateRevision++
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun addToPlaylist(
|
||||||
|
songs: List<MusicDirectory.Entry?>,
|
||||||
|
save: Boolean,
|
||||||
|
autoPlay: Boolean,
|
||||||
|
playNext: Boolean,
|
||||||
|
newPlaylist: Boolean
|
||||||
|
) {
|
||||||
|
shufflePlayBuffer.isEnabled = false
|
||||||
|
var offset = 1
|
||||||
|
if (songs.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (newPlaylist) {
|
||||||
|
playlist.clear()
|
||||||
|
}
|
||||||
|
if (playNext) {
|
||||||
|
if (autoPlay && currentPlayingIndex >= 0) {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
for (song in songs) {
|
||||||
|
val downloadFile = DownloadFile(song!!, save)
|
||||||
|
playlist.add(currentPlayingIndex + offset, downloadFile)
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (song in songs) {
|
||||||
|
val downloadFile = DownloadFile(song!!, save)
|
||||||
|
playlist.add(downloadFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playlistUpdateRevision++
|
||||||
|
checkDownloads()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun downloadBackground(songs: List<MusicDirectory.Entry>, save: Boolean) {
|
||||||
|
|
||||||
|
// Because of the priority handling we add the songs in the reverse order they
|
||||||
|
// were requested, then it is correct in the end.
|
||||||
|
for (song in songs.asReversed()) {
|
||||||
|
downloadQueue.add(DownloadFile(song, save))
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDownloads()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun shuffle() {
|
||||||
|
playlist.shuffle()
|
||||||
|
|
||||||
|
// Move the current song to the top..
|
||||||
|
if (localMediaPlayer.currentPlaying != null) {
|
||||||
|
playlist.remove(localMediaPlayer.currentPlaying)
|
||||||
|
playlist.add(0, localMediaPlayer.currentPlaying!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistUpdateRevision++
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
@Suppress("ReturnCount")
|
||||||
|
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile {
|
||||||
|
for (downloadFile in playlist) {
|
||||||
|
if (downloadFile.song == song) {
|
||||||
|
return downloadFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (downloadFile in activelyDownloading) {
|
||||||
|
if (downloadFile.song == song) {
|
||||||
|
return downloadFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (downloadFile in downloadQueue) {
|
||||||
|
if (downloadFile.song == song) {
|
||||||
|
return downloadFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var downloadFile = downloadFileCache[song]
|
||||||
|
if (downloadFile == null) {
|
||||||
|
downloadFile = DownloadFile(song, false)
|
||||||
|
downloadFileCache.put(song, downloadFile)
|
||||||
|
}
|
||||||
|
return downloadFile
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun checkShufflePlay() {
|
||||||
|
// Get users desired random playlist size
|
||||||
|
val listSize = getMaxSongs()
|
||||||
|
val wasEmpty = playlist.isEmpty()
|
||||||
|
val revisionBefore = playlistUpdateRevision
|
||||||
|
|
||||||
|
// First, ensure that list is at least 20 songs long.
|
||||||
|
val size = playlist.size
|
||||||
|
if (size < listSize) {
|
||||||
|
for (song in shufflePlayBuffer[listSize - size]) {
|
||||||
|
val downloadFile = DownloadFile(song, false)
|
||||||
|
playlist.add(downloadFile)
|
||||||
|
playlistUpdateRevision++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currIndex = if (localMediaPlayer.currentPlaying == null) 0 else currentPlayingIndex
|
||||||
|
|
||||||
|
// Only shift playlist if playing song #5 or later.
|
||||||
|
if (currIndex > SHUFFLE_BUFFER_LIMIT) {
|
||||||
|
val songsToShift = currIndex - 2
|
||||||
|
for (song in shufflePlayBuffer[songsToShift]) {
|
||||||
|
playlist.add(DownloadFile(song, false))
|
||||||
|
playlist[0].cancelDownload()
|
||||||
|
playlist.removeAt(0)
|
||||||
|
playlistUpdateRevision++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (revisionBefore != playlistUpdateRevision) {
|
||||||
|
jukeboxMediaPlayer.updatePlaylist()
|
||||||
|
}
|
||||||
|
if (wasEmpty && playlist.isNotEmpty()) {
|
||||||
|
if (jukeboxMediaPlayer.isEnabled) {
|
||||||
|
jukeboxMediaPlayer.skip(0, 0)
|
||||||
|
localMediaPlayer.setPlayerState(PlayerState.STARTED)
|
||||||
|
} else {
|
||||||
|
localMediaPlayer.play(playlist[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
const val PARALLEL_DOWNLOADS = 3
|
||||||
|
const val CHECK_INTERVAL = 5L
|
||||||
|
const val SHUFFLE_BUFFER_LIMIT = 4
|
||||||
|
}
|
||||||
|
}
|
|
@ -456,7 +456,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
|
||||||
requireActivity().invalidateOptionsMenu()
|
requireActivity().invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to current playing/downloading.
|
// Scroll to current playing.
|
||||||
private fun scrollToCurrent() {
|
private fun scrollToCurrent() {
|
||||||
val adapter = playlistView.adapter
|
val adapter = playlistView.adapter
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
|
@ -467,13 +467,6 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val currentDownloading = mediaPlayerController.currentDownloading
|
|
||||||
for (i in 0 until count) {
|
|
||||||
if (currentDownloading == playlistView.getItemAtPosition(i)) {
|
|
||||||
playlistView.smoothScrollToPositionFromTop(i, 40)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,7 +636,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_remove -> {
|
R.id.menu_remove -> {
|
||||||
mediaPlayerController.remove(song!!)
|
mediaPlayerController.removeFromPlaylist(song!!)
|
||||||
onDownloadListChanged()
|
onDownloadListChanged()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -517,6 +517,7 @@ class TrackCollectionFragment : Fragment() {
|
||||||
var pinnedCount = 0
|
var pinnedCount = 0
|
||||||
|
|
||||||
for (song in selection) {
|
for (song in selection) {
|
||||||
|
if (song == null) continue
|
||||||
val downloadFile = mediaPlayerController.getDownloadFileForSong(song)
|
val downloadFile = mediaPlayerController.getDownloadFileForSong(song)
|
||||||
if (downloadFile.isWorkDone) {
|
if (downloadFile.isWorkDone) {
|
||||||
deleteEnabled = true
|
deleteEnabled = true
|
||||||
|
|
|
@ -1066,7 +1066,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun playSongs(songs: List<MusicDirectory.Entry?>?) {
|
private fun playSongs(songs: List<MusicDirectory.Entry?>?) {
|
||||||
mediaPlayerController.download(
|
mediaPlayerController.addToPlaylist(
|
||||||
songs,
|
songs,
|
||||||
save = false,
|
save = false,
|
||||||
autoPlay = true,
|
autoPlay = true,
|
||||||
|
@ -1077,7 +1077,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun playSong(song: MusicDirectory.Entry) {
|
private fun playSong(song: MusicDirectory.Entry) {
|
||||||
mediaPlayerController.download(
|
mediaPlayerController.addToPlaylist(
|
||||||
listOf(song),
|
listOf(song),
|
||||||
save = false,
|
save = false,
|
||||||
autoPlay = false,
|
autoPlay = false,
|
||||||
|
|
|
@ -40,7 +40,7 @@ import timber.log.Timber
|
||||||
class DownloadFile(
|
class DownloadFile(
|
||||||
val song: MusicDirectory.Entry,
|
val song: MusicDirectory.Entry,
|
||||||
private val save: Boolean
|
private val save: Boolean
|
||||||
) : KoinComponent {
|
) : KoinComponent, Comparable<DownloadFile> {
|
||||||
val partialFile: File
|
val partialFile: File
|
||||||
val completeFile: File
|
val completeFile: File
|
||||||
private val saveFile: File = FileUtil.getSongFile(song)
|
private val saveFile: File = FileUtil.getSongFile(song)
|
||||||
|
@ -50,6 +50,8 @@ class DownloadFile(
|
||||||
|
|
||||||
private val desiredBitRate: Int = Util.getMaxBitRate()
|
private val desiredBitRate: Int = Util.getMaxBitRate()
|
||||||
|
|
||||||
|
var priority = 100
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var isPlaying = false
|
private var isPlaying = false
|
||||||
|
|
||||||
|
@ -202,7 +204,6 @@ class DownloadFile(
|
||||||
return String.format("DownloadFile (%s)", song)
|
return String.format("DownloadFile (%s)", song)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
private inner class DownloadTask : CancellableTask() {
|
private inner class DownloadTask : CancellableTask() {
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
var inputStream: InputStream? = null
|
var inputStream: InputStream? = null
|
||||||
|
@ -291,7 +292,7 @@ class DownloadFile(
|
||||||
Util.renameFile(partialFile, completeFile)
|
Util.renameFile(partialFile, completeFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (all: Exception) {
|
||||||
Util.close(outputStream)
|
Util.close(outputStream)
|
||||||
Util.delete(completeFile)
|
Util.delete(completeFile)
|
||||||
Util.delete(saveFile)
|
Util.delete(saveFile)
|
||||||
|
@ -300,7 +301,7 @@ class DownloadFile(
|
||||||
if (retryCount > 0) {
|
if (retryCount > 0) {
|
||||||
--retryCount
|
--retryCount
|
||||||
}
|
}
|
||||||
Timber.w(e, "Failed to download '%s'.", song)
|
Timber.w(all, "Failed to download '%s'.", song)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Util.close(inputStream)
|
Util.close(inputStream)
|
||||||
|
@ -388,6 +389,10 @@ class DownloadFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: DownloadFile): Int {
|
||||||
|
return priority.compareTo(other.priority)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_RETRIES = 5
|
const val MAX_RETRIES = 5
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ class MediaPlayerController(
|
||||||
autoPlay: Boolean,
|
autoPlay: Boolean,
|
||||||
newPlaylist: Boolean
|
newPlaylist: Boolean
|
||||||
) {
|
) {
|
||||||
download(
|
addToPlaylist(
|
||||||
songs,
|
songs,
|
||||||
save = false,
|
save = false,
|
||||||
autoPlay = false,
|
autoPlay = false,
|
||||||
|
@ -167,7 +167,7 @@ class MediaPlayerController(
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
fun download(
|
fun addToPlaylist(
|
||||||
songs: List<MusicDirectory.Entry?>?,
|
songs: List<MusicDirectory.Entry?>?,
|
||||||
save: Boolean,
|
save: Boolean,
|
||||||
autoPlay: Boolean,
|
autoPlay: Boolean,
|
||||||
|
@ -175,10 +175,12 @@ class MediaPlayerController(
|
||||||
shuffle: Boolean,
|
shuffle: Boolean,
|
||||||
newPlaylist: Boolean
|
newPlaylist: Boolean
|
||||||
) {
|
) {
|
||||||
downloader.download(songs, save, autoPlay, playNext, newPlaylist)
|
if (songs == null) return
|
||||||
|
val filteredSongs = songs.filterNotNull()
|
||||||
|
downloader.addToPlaylist(filteredSongs, save, autoPlay, playNext, newPlaylist)
|
||||||
jukeboxMediaPlayer.updatePlaylist()
|
jukeboxMediaPlayer.updatePlaylist()
|
||||||
if (shuffle) shuffle()
|
if (shuffle) shuffle()
|
||||||
val isLastTrack = (downloader.downloadList.size - 1 == downloader.currentPlayingIndex)
|
val isLastTrack = (downloader.playlist.size - 1 == downloader.currentPlayingIndex)
|
||||||
|
|
||||||
if (!playNext && !autoPlay && isLastTrack) {
|
if (!playNext && !autoPlay && isLastTrack) {
|
||||||
val mediaPlayerService = runningInstance
|
val mediaPlayerService = runningInstance
|
||||||
|
@ -188,15 +190,15 @@ class MediaPlayerController(
|
||||||
if (autoPlay) {
|
if (autoPlay) {
|
||||||
play(0)
|
play(0)
|
||||||
} else {
|
} else {
|
||||||
if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size > 0) {
|
if (localMediaPlayer.currentPlaying == null && downloader.playlist.size > 0) {
|
||||||
localMediaPlayer.currentPlaying = downloader.downloadList[0]
|
localMediaPlayer.currentPlaying = downloader.playlist[0]
|
||||||
downloader.downloadList[0].setPlaying(true)
|
downloader.playlist[0].setPlaying(true)
|
||||||
}
|
}
|
||||||
downloader.checkDownloads()
|
downloader.checkDownloads()
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -204,9 +206,11 @@ class MediaPlayerController(
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun downloadBackground(songs: List<MusicDirectory.Entry?>?, save: Boolean) {
|
fun downloadBackground(songs: List<MusicDirectory.Entry?>?, save: Boolean) {
|
||||||
downloader.downloadBackground(songs, save)
|
if (songs == null) return
|
||||||
|
val filteredSongs = songs.filterNotNull()
|
||||||
|
downloader.downloadBackground(filteredSongs, save)
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -237,7 +241,7 @@ class MediaPlayerController(
|
||||||
fun shuffle() {
|
fun shuffle() {
|
||||||
downloader.shuffle()
|
downloader.shuffle()
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -267,10 +271,10 @@ class MediaPlayerController(
|
||||||
mediaPlayerService.clear(serialize)
|
mediaPlayerService.clear(serialize)
|
||||||
} else {
|
} else {
|
||||||
// If no MediaPlayerService is available, just empty the playlist
|
// If no MediaPlayerService is available, just empty the playlist
|
||||||
downloader.clear()
|
downloader.clearPlaylist()
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex, playerPosition
|
downloader.currentPlayingIndex, playerPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -281,7 +285,7 @@ class MediaPlayerController(
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clearIncomplete() {
|
fun clearIncomplete() {
|
||||||
reset()
|
reset()
|
||||||
val iterator = downloader.downloadList.iterator()
|
val iterator = downloader.playlist.iterator()
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val downloadFile = iterator.next()
|
val downloadFile = iterator.next()
|
||||||
if (!downloadFile.isCompleteFileAvailable) {
|
if (!downloadFile.isCompleteFileAvailable) {
|
||||||
|
@ -290,7 +294,7 @@ class MediaPlayerController(
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -299,15 +303,15 @@ class MediaPlayerController(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun remove(downloadFile: DownloadFile) {
|
fun removeFromPlaylist(downloadFile: DownloadFile) {
|
||||||
if (downloadFile == localMediaPlayer.currentPlaying) {
|
if (downloadFile == localMediaPlayer.currentPlaying) {
|
||||||
reset()
|
reset()
|
||||||
currentPlaying = null
|
currentPlaying = null
|
||||||
}
|
}
|
||||||
downloader.removeDownloadFile(downloadFile)
|
downloader.removeFromPlaylist(downloadFile)
|
||||||
|
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -321,15 +325,17 @@ class MediaPlayerController(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
// TODO: Make it require not null
|
||||||
fun delete(songs: List<MusicDirectory.Entry?>) {
|
fun delete(songs: List<MusicDirectory.Entry?>) {
|
||||||
for (song in songs) {
|
for (song in songs.filterNotNull()) {
|
||||||
downloader.getDownloadFileForSong(song).delete()
|
downloader.getDownloadFileForSong(song).delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
// TODO: Make it require not null
|
||||||
fun unpin(songs: List<MusicDirectory.Entry?>) {
|
fun unpin(songs: List<MusicDirectory.Entry?>) {
|
||||||
for (song in songs) {
|
for (song in songs.filterNotNull()) {
|
||||||
downloader.getDownloadFileForSong(song).unpin()
|
downloader.getDownloadFileForSong(song).unpin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,12 +363,12 @@ class MediaPlayerController(
|
||||||
when (repeatMode) {
|
when (repeatMode) {
|
||||||
RepeatMode.SINGLE, RepeatMode.OFF -> {
|
RepeatMode.SINGLE, RepeatMode.OFF -> {
|
||||||
// Play next if exists
|
// Play next if exists
|
||||||
if (index + 1 >= 0 && index + 1 < downloader.downloadList.size) {
|
if (index + 1 >= 0 && index + 1 < downloader.playlist.size) {
|
||||||
play(index + 1)
|
play(index + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RepeatMode.ALL -> {
|
RepeatMode.ALL -> {
|
||||||
play((index + 1) % downloader.downloadList.size)
|
play((index + 1) % downloader.playlist.size)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
|
@ -409,9 +415,7 @@ class MediaPlayerController(
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
// Cancel current download, if necessary.
|
// Cancel current download, if necessary.
|
||||||
if (downloader.currentDownloading != null) {
|
downloader.clearActiveDownloads()
|
||||||
downloader.currentDownloading.cancelDownload()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
jukeboxMediaPlayer.stopJukeboxService()
|
jukeboxMediaPlayer.stopJukeboxService()
|
||||||
}
|
}
|
||||||
|
@ -491,24 +495,21 @@ class MediaPlayerController(
|
||||||
}
|
}
|
||||||
|
|
||||||
val playlistSize: Int
|
val playlistSize: Int
|
||||||
get() = downloader.downloadList.size
|
get() = downloader.playlist.size
|
||||||
|
|
||||||
val currentPlayingNumberOnPlaylist: Int
|
val currentPlayingNumberOnPlaylist: Int
|
||||||
get() = downloader.currentPlayingIndex
|
get() = downloader.currentPlayingIndex
|
||||||
|
|
||||||
val currentDownloading: DownloadFile?
|
|
||||||
get() = downloader.currentDownloading
|
|
||||||
|
|
||||||
val playList: List<DownloadFile>
|
val playList: List<DownloadFile>
|
||||||
get() = downloader.downloadList
|
get() = downloader.playlist
|
||||||
|
|
||||||
val playListUpdateRevision: Long
|
val playListUpdateRevision: Long
|
||||||
get() = downloader.downloadListUpdateRevision
|
get() = downloader.playlistUpdateRevision
|
||||||
|
|
||||||
val playListDuration: Long
|
val playListDuration: Long
|
||||||
get() = downloader.downloadListDuration
|
get() = downloader.downloadListDuration
|
||||||
|
|
||||||
fun getDownloadFileForSong(song: MusicDirectory.Entry?): DownloadFile {
|
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile {
|
||||||
return downloader.getDownloadFileForSong(song)
|
return downloader.getDownloadFileForSong(song)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
|
||||||
// Work-around: Serialize again, as the restore() method creates a
|
// Work-around: Serialize again, as the restore() method creates a
|
||||||
// serialization without current playing info.
|
// serialization without current playing info.
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
mediaPlayerController.playerPosition
|
mediaPlayerController.playerPosition
|
||||||
)
|
)
|
||||||
|
@ -94,7 +94,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
|
||||||
if (!created) return
|
if (!created) return
|
||||||
|
|
||||||
downloadQueueSerializer.serializeDownloadQueueNow(
|
downloadQueueSerializer.serializeDownloadQueueNow(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
mediaPlayerController.playerPosition
|
mediaPlayerController.playerPosition
|
||||||
)
|
)
|
||||||
|
|
|
@ -88,7 +88,7 @@ class MediaPlayerService : Service() {
|
||||||
|
|
||||||
localMediaPlayer.onPrepared = {
|
localMediaPlayer.onPrepared = {
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex,
|
downloader.currentPlayingIndex,
|
||||||
playerPosition
|
playerPosition
|
||||||
)
|
)
|
||||||
|
@ -189,7 +189,7 @@ class MediaPlayerService : Service() {
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun setCurrentPlaying(currentPlayingIndex: Int) {
|
fun setCurrentPlaying(currentPlayingIndex: Int) {
|
||||||
try {
|
try {
|
||||||
localMediaPlayer.setCurrentPlaying(downloader.downloadList[currentPlayingIndex])
|
localMediaPlayer.setCurrentPlaying(downloader.playlist[currentPlayingIndex])
|
||||||
} catch (ignored: IndexOutOfBoundsException) {
|
} catch (ignored: IndexOutOfBoundsException) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ class MediaPlayerService : Service() {
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
when (repeatMode) {
|
when (repeatMode) {
|
||||||
RepeatMode.OFF -> index += 1
|
RepeatMode.OFF -> index += 1
|
||||||
RepeatMode.ALL -> index = (index + 1) % downloader.downloadList.size
|
RepeatMode.ALL -> index = (index + 1) % downloader.playlist.size
|
||||||
RepeatMode.SINGLE -> {
|
RepeatMode.SINGLE -> {
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -217,8 +217,8 @@ class MediaPlayerService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
localMediaPlayer.clearNextPlaying(false)
|
localMediaPlayer.clearNextPlaying(false)
|
||||||
if (index < downloader.downloadList.size && index != -1) {
|
if (index < downloader.playlist.size && index != -1) {
|
||||||
localMediaPlayer.setNextPlaying(downloader.downloadList[index])
|
localMediaPlayer.setNextPlaying(downloader.playlist[index])
|
||||||
} else {
|
} else {
|
||||||
localMediaPlayer.clearNextPlaying(true)
|
localMediaPlayer.clearNextPlaying(true)
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ class MediaPlayerService : Service() {
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun play(index: Int, start: Boolean) {
|
fun play(index: Int, start: Boolean) {
|
||||||
Timber.v("play requested for %d", index)
|
Timber.v("play requested for %d", index)
|
||||||
if (index < 0 || index >= downloader.downloadList.size) {
|
if (index < 0 || index >= downloader.playlist.size) {
|
||||||
resetPlayback()
|
resetPlayback()
|
||||||
} else {
|
} else {
|
||||||
setCurrentPlaying(index)
|
setCurrentPlaying(index)
|
||||||
|
@ -280,7 +280,7 @@ class MediaPlayerService : Service() {
|
||||||
jukeboxMediaPlayer.skip(index, 0)
|
jukeboxMediaPlayer.skip(index, 0)
|
||||||
localMediaPlayer.setPlayerState(PlayerState.STARTED)
|
localMediaPlayer.setPlayerState(PlayerState.STARTED)
|
||||||
} else {
|
} else {
|
||||||
localMediaPlayer.play(downloader.downloadList[index])
|
localMediaPlayer.play(downloader.playlist[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downloader.checkDownloads()
|
downloader.checkDownloads()
|
||||||
|
@ -293,7 +293,7 @@ class MediaPlayerService : Service() {
|
||||||
localMediaPlayer.reset()
|
localMediaPlayer.reset()
|
||||||
localMediaPlayer.setCurrentPlaying(null)
|
localMediaPlayer.setCurrentPlaying(null)
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex, playerPosition
|
downloader.currentPlayingIndex, playerPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,7 @@ class MediaPlayerService : Service() {
|
||||||
|
|
||||||
if (playerState === PlayerState.PAUSED) {
|
if (playerState === PlayerState.PAUSED) {
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList, downloader.currentPlayingIndex, playerPosition
|
downloader.playlist, downloader.currentPlayingIndex, playerPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,8 +408,8 @@ class MediaPlayerService : Service() {
|
||||||
Util.broadcastPlaybackStatusChange(context, playerState)
|
Util.broadcastPlaybackStatusChange(context, playerState)
|
||||||
Util.broadcastA2dpPlayStatusChange(
|
Util.broadcastA2dpPlayStatusChange(
|
||||||
context, playerState, song,
|
context, playerState, song,
|
||||||
downloader.downloadList.size + downloader.backgroundDownloadList.size,
|
downloader.playlist.size,
|
||||||
downloader.downloadList.indexOf(currentPlaying) + 1, playerPosition
|
downloader.playlist.indexOf(currentPlaying) + 1, playerPosition
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update widget
|
// Update widget
|
||||||
|
@ -455,7 +455,7 @@ class MediaPlayerService : Service() {
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
when (repeatMode) {
|
when (repeatMode) {
|
||||||
RepeatMode.OFF -> {
|
RepeatMode.OFF -> {
|
||||||
if (index + 1 < 0 || index + 1 >= downloader.downloadList.size) {
|
if (index + 1 < 0 || index + 1 >= downloader.playlist.size) {
|
||||||
if (Util.getShouldClearPlaylist()) {
|
if (Util.getShouldClearPlaylist()) {
|
||||||
clear(true)
|
clear(true)
|
||||||
jukeboxMediaPlayer.updatePlaylist()
|
jukeboxMediaPlayer.updatePlaylist()
|
||||||
|
@ -466,7 +466,7 @@ class MediaPlayerService : Service() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RepeatMode.ALL -> {
|
RepeatMode.ALL -> {
|
||||||
play((index + 1) % downloader.downloadList.size)
|
play((index + 1) % downloader.playlist.size)
|
||||||
}
|
}
|
||||||
RepeatMode.SINGLE -> play(index)
|
RepeatMode.SINGLE -> play(index)
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -480,12 +480,12 @@ class MediaPlayerService : Service() {
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clear(serialize: Boolean) {
|
fun clear(serialize: Boolean) {
|
||||||
localMediaPlayer.reset()
|
localMediaPlayer.reset()
|
||||||
downloader.clear()
|
downloader.clearPlaylist()
|
||||||
localMediaPlayer.setCurrentPlaying(null)
|
localMediaPlayer.setCurrentPlaying(null)
|
||||||
setNextPlaying()
|
setNextPlaying()
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
downloadQueueSerializer.serializeDownloadQueue(
|
downloadQueueSerializer.serializeDownloadQueue(
|
||||||
downloader.downloadList,
|
downloader.playlist,
|
||||||
downloader.currentPlayingIndex, playerPosition
|
downloader.currentPlayingIndex, playerPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ class DownloadHandler(
|
||||||
mediaPlayerController.clear()
|
mediaPlayerController.clear()
|
||||||
}
|
}
|
||||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||||
mediaPlayerController.download(
|
mediaPlayerController.addToPlaylist(
|
||||||
songs,
|
songs,
|
||||||
save,
|
save,
|
||||||
autoPlay,
|
autoPlay,
|
||||||
|
@ -297,7 +297,7 @@ class DownloadHandler(
|
||||||
if (unpin) {
|
if (unpin) {
|
||||||
mediaPlayerController.unpin(songs)
|
mediaPlayerController.unpin(songs)
|
||||||
} else {
|
} else {
|
||||||
mediaPlayerController.download(
|
mediaPlayerController.addToPlaylist(
|
||||||
songs,
|
songs,
|
||||||
save,
|
save,
|
||||||
autoPlay,
|
autoPlay,
|
||||||
|
|
|
@ -218,10 +218,13 @@ class SongView(context: Context) : UpdateView(context), Checkable, KoinComponent
|
||||||
|
|
||||||
override fun updateBackground() {}
|
override fun updateBackground() {}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
public override fun update() {
|
public override fun update() {
|
||||||
updateBackground()
|
updateBackground()
|
||||||
|
|
||||||
downloadFile = mediaPlayerController.getDownloadFileForSong(entry)
|
val song = entry ?: return
|
||||||
|
|
||||||
|
downloadFile = mediaPlayerController.getDownloadFileForSong(song)
|
||||||
|
|
||||||
updateDownloadStatus(downloadFile!!)
|
updateDownloadStatus(downloadFile!!)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue