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.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
|
@ -25,8 +24,8 @@ import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
|
|||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.Pair;
|
||||
import org.moire.ultrasonic.util.FragmentBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Pair;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.EntryAdapter;
|
||||
|
||||
|
@ -78,23 +77,14 @@ public class BookmarksFragment extends Fragment {
|
|||
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
|
||||
albumListView = view.findViewById(R.id.select_album_entries_list);
|
||||
|
||||
refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
refreshAlbumListView.setOnRefreshListener(() -> {
|
||||
enableButtons();
|
||||
getBookmarks();
|
||||
}
|
||||
});
|
||||
|
||||
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
|
||||
albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
albumListView.setOnItemClickListener((parent, view17, position, id) -> {
|
||||
if (position >= 0)
|
||||
{
|
||||
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);
|
||||
|
@ -130,58 +119,24 @@ public class BookmarksFragment extends Fragment {
|
|||
playLastButton.setVisibility(View.GONE);
|
||||
oreButton.setVisibility(View.GONE);
|
||||
|
||||
playNowButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
playNow(getSelectedSongs(albumListView));
|
||||
}
|
||||
});
|
||||
playNowButton.setOnClickListener(view16 -> playNow(getSelectedSongs(albumListView)));
|
||||
|
||||
selectButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
selectAllOrNone();
|
||||
}
|
||||
});
|
||||
pinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
selectButton.setOnClickListener(view15 -> selectAllOrNone());
|
||||
pinButton.setOnClickListener(view14 -> {
|
||||
downloadBackground(true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
unpinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
unpinButton.setOnClickListener(view13 -> {
|
||||
unpin();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
downloadButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
downloadButton.setOnClickListener(view12 -> {
|
||||
downloadBackground(false);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
deleteButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
deleteButton.setOnClickListener(view1 -> {
|
||||
delete();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(albumListView);
|
||||
|
@ -230,7 +185,8 @@ public class BookmarksFragment extends Fragment {
|
|||
{
|
||||
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)
|
||||
{
|
||||
if (song == null) continue;
|
||||
DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song);
|
||||
if (downloadFile.isWorkDone())
|
||||
{
|
||||
|
@ -326,11 +283,7 @@ public class BookmarksFragment extends Fragment {
|
|||
|
||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
Runnable onValid = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Runnable onValid = () -> {
|
||||
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
||||
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()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onValid.run();
|
||||
|
|
|
@ -562,7 +562,7 @@ public class SearchFragment extends Fragment {
|
|||
mediaPlayerController.clear();
|
||||
}
|
||||
|
||||
mediaPlayerController.download(Collections.singletonList(song), false, false, false, false, false);
|
||||
mediaPlayerController.addToPlaylist(Collections.singletonList(song), false, false, false, false, false);
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// Scroll to current playing/downloading.
|
||||
// Scroll to current playing.
|
||||
private fun scrollToCurrent() {
|
||||
val adapter = playlistView.adapter
|
||||
if (adapter != null) {
|
||||
|
@ -467,13 +467,6 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
|
|||
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
|
||||
}
|
||||
R.id.menu_remove -> {
|
||||
mediaPlayerController.remove(song!!)
|
||||
mediaPlayerController.removeFromPlaylist(song!!)
|
||||
onDownloadListChanged()
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -517,6 +517,7 @@ class TrackCollectionFragment : Fragment() {
|
|||
var pinnedCount = 0
|
||||
|
||||
for (song in selection) {
|
||||
if (song == null) continue
|
||||
val downloadFile = mediaPlayerController.getDownloadFileForSong(song)
|
||||
if (downloadFile.isWorkDone) {
|
||||
deleteEnabled = true
|
||||
|
|
|
@ -1066,7 +1066,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
|||
}
|
||||
|
||||
private fun playSongs(songs: List<MusicDirectory.Entry?>?) {
|
||||
mediaPlayerController.download(
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save = false,
|
||||
autoPlay = true,
|
||||
|
@ -1077,7 +1077,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
|||
}
|
||||
|
||||
private fun playSong(song: MusicDirectory.Entry) {
|
||||
mediaPlayerController.download(
|
||||
mediaPlayerController.addToPlaylist(
|
||||
listOf(song),
|
||||
save = false,
|
||||
autoPlay = false,
|
||||
|
|
|
@ -40,7 +40,7 @@ import timber.log.Timber
|
|||
class DownloadFile(
|
||||
val song: MusicDirectory.Entry,
|
||||
private val save: Boolean
|
||||
) : KoinComponent {
|
||||
) : KoinComponent, Comparable<DownloadFile> {
|
||||
val partialFile: File
|
||||
val completeFile: File
|
||||
private val saveFile: File = FileUtil.getSongFile(song)
|
||||
|
@ -50,6 +50,8 @@ class DownloadFile(
|
|||
|
||||
private val desiredBitRate: Int = Util.getMaxBitRate()
|
||||
|
||||
var priority = 100
|
||||
|
||||
@Volatile
|
||||
private var isPlaying = false
|
||||
|
||||
|
@ -202,7 +204,6 @@ class DownloadFile(
|
|||
return String.format("DownloadFile (%s)", song)
|
||||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private inner class DownloadTask : CancellableTask() {
|
||||
override fun execute() {
|
||||
var inputStream: InputStream? = null
|
||||
|
@ -291,7 +292,7 @@ class DownloadFile(
|
|||
Util.renameFile(partialFile, completeFile)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (all: Exception) {
|
||||
Util.close(outputStream)
|
||||
Util.delete(completeFile)
|
||||
Util.delete(saveFile)
|
||||
|
@ -300,7 +301,7 @@ class DownloadFile(
|
|||
if (retryCount > 0) {
|
||||
--retryCount
|
||||
}
|
||||
Timber.w(e, "Failed to download '%s'.", song)
|
||||
Timber.w(all, "Failed to download '%s'.", song)
|
||||
}
|
||||
} finally {
|
||||
Util.close(inputStream)
|
||||
|
@ -388,6 +389,10 @@ class DownloadFile(
|
|||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: DownloadFile): Int {
|
||||
return priority.compareTo(other.priority)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_RETRIES = 5
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class MediaPlayerController(
|
|||
autoPlay: Boolean,
|
||||
newPlaylist: Boolean
|
||||
) {
|
||||
download(
|
||||
addToPlaylist(
|
||||
songs,
|
||||
save = false,
|
||||
autoPlay = false,
|
||||
|
@ -167,7 +167,7 @@ class MediaPlayerController(
|
|||
|
||||
@Synchronized
|
||||
@Suppress("LongParameterList")
|
||||
fun download(
|
||||
fun addToPlaylist(
|
||||
songs: List<MusicDirectory.Entry?>?,
|
||||
save: Boolean,
|
||||
autoPlay: Boolean,
|
||||
|
@ -175,10 +175,12 @@ class MediaPlayerController(
|
|||
shuffle: 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()
|
||||
if (shuffle) shuffle()
|
||||
val isLastTrack = (downloader.downloadList.size - 1 == downloader.currentPlayingIndex)
|
||||
val isLastTrack = (downloader.playlist.size - 1 == downloader.currentPlayingIndex)
|
||||
|
||||
if (!playNext && !autoPlay && isLastTrack) {
|
||||
val mediaPlayerService = runningInstance
|
||||
|
@ -188,15 +190,15 @@ class MediaPlayerController(
|
|||
if (autoPlay) {
|
||||
play(0)
|
||||
} else {
|
||||
if (localMediaPlayer.currentPlaying == null && downloader.downloadList.size > 0) {
|
||||
localMediaPlayer.currentPlaying = downloader.downloadList[0]
|
||||
downloader.downloadList[0].setPlaying(true)
|
||||
if (localMediaPlayer.currentPlaying == null && downloader.playlist.size > 0) {
|
||||
localMediaPlayer.currentPlaying = downloader.playlist[0]
|
||||
downloader.playlist[0].setPlaying(true)
|
||||
}
|
||||
downloader.checkDownloads()
|
||||
}
|
||||
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -204,9 +206,11 @@ class MediaPlayerController(
|
|||
|
||||
@Synchronized
|
||||
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(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -237,7 +241,7 @@ class MediaPlayerController(
|
|||
fun shuffle() {
|
||||
downloader.shuffle()
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -267,10 +271,10 @@ class MediaPlayerController(
|
|||
mediaPlayerService.clear(serialize)
|
||||
} else {
|
||||
// If no MediaPlayerService is available, just empty the playlist
|
||||
downloader.clear()
|
||||
downloader.clearPlaylist()
|
||||
if (serialize) {
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex, playerPosition
|
||||
)
|
||||
}
|
||||
|
@ -281,7 +285,7 @@ class MediaPlayerController(
|
|||
@Synchronized
|
||||
fun clearIncomplete() {
|
||||
reset()
|
||||
val iterator = downloader.downloadList.iterator()
|
||||
val iterator = downloader.playlist.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val downloadFile = iterator.next()
|
||||
if (!downloadFile.isCompleteFileAvailable) {
|
||||
|
@ -290,7 +294,7 @@ class MediaPlayerController(
|
|||
}
|
||||
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -299,15 +303,15 @@ class MediaPlayerController(
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(downloadFile: DownloadFile) {
|
||||
fun removeFromPlaylist(downloadFile: DownloadFile) {
|
||||
if (downloadFile == localMediaPlayer.currentPlaying) {
|
||||
reset()
|
||||
currentPlaying = null
|
||||
}
|
||||
downloader.removeDownloadFile(downloadFile)
|
||||
downloader.removeFromPlaylist(downloadFile)
|
||||
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -321,15 +325,17 @@ class MediaPlayerController(
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
// TODO: Make it require not null
|
||||
fun delete(songs: List<MusicDirectory.Entry?>) {
|
||||
for (song in songs) {
|
||||
for (song in songs.filterNotNull()) {
|
||||
downloader.getDownloadFileForSong(song).delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
// TODO: Make it require not null
|
||||
fun unpin(songs: List<MusicDirectory.Entry?>) {
|
||||
for (song in songs) {
|
||||
for (song in songs.filterNotNull()) {
|
||||
downloader.getDownloadFileForSong(song).unpin()
|
||||
}
|
||||
}
|
||||
|
@ -357,12 +363,12 @@ class MediaPlayerController(
|
|||
when (repeatMode) {
|
||||
RepeatMode.SINGLE, RepeatMode.OFF -> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
RepeatMode.ALL -> {
|
||||
play((index + 1) % downloader.downloadList.size)
|
||||
play((index + 1) % downloader.playlist.size)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
|
@ -409,9 +415,7 @@ class MediaPlayerController(
|
|||
reset()
|
||||
|
||||
// Cancel current download, if necessary.
|
||||
if (downloader.currentDownloading != null) {
|
||||
downloader.currentDownloading.cancelDownload()
|
||||
}
|
||||
downloader.clearActiveDownloads()
|
||||
} else {
|
||||
jukeboxMediaPlayer.stopJukeboxService()
|
||||
}
|
||||
|
@ -491,24 +495,21 @@ class MediaPlayerController(
|
|||
}
|
||||
|
||||
val playlistSize: Int
|
||||
get() = downloader.downloadList.size
|
||||
get() = downloader.playlist.size
|
||||
|
||||
val currentPlayingNumberOnPlaylist: Int
|
||||
get() = downloader.currentPlayingIndex
|
||||
|
||||
val currentDownloading: DownloadFile?
|
||||
get() = downloader.currentDownloading
|
||||
|
||||
val playList: List<DownloadFile>
|
||||
get() = downloader.downloadList
|
||||
get() = downloader.playlist
|
||||
|
||||
val playListUpdateRevision: Long
|
||||
get() = downloader.downloadListUpdateRevision
|
||||
get() = downloader.playlistUpdateRevision
|
||||
|
||||
val playListDuration: Long
|
||||
get() = downloader.downloadListDuration
|
||||
|
||||
fun getDownloadFileForSong(song: MusicDirectory.Entry?): DownloadFile {
|
||||
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile {
|
||||
return downloader.getDownloadFileForSong(song)
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
|
|||
// Work-around: Serialize again, as the restore() method creates a
|
||||
// serialization without current playing info.
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
mediaPlayerController.playerPosition
|
||||
)
|
||||
|
@ -94,7 +94,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
|
|||
if (!created) return
|
||||
|
||||
downloadQueueSerializer.serializeDownloadQueueNow(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
mediaPlayerController.playerPosition
|
||||
)
|
||||
|
|
|
@ -88,7 +88,7 @@ class MediaPlayerService : Service() {
|
|||
|
||||
localMediaPlayer.onPrepared = {
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex,
|
||||
playerPosition
|
||||
)
|
||||
|
@ -189,7 +189,7 @@ class MediaPlayerService : Service() {
|
|||
@Synchronized
|
||||
fun setCurrentPlaying(currentPlayingIndex: Int) {
|
||||
try {
|
||||
localMediaPlayer.setCurrentPlaying(downloader.downloadList[currentPlayingIndex])
|
||||
localMediaPlayer.setCurrentPlaying(downloader.playlist[currentPlayingIndex])
|
||||
} catch (ignored: IndexOutOfBoundsException) {
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ class MediaPlayerService : Service() {
|
|||
if (index != -1) {
|
||||
when (repeatMode) {
|
||||
RepeatMode.OFF -> index += 1
|
||||
RepeatMode.ALL -> index = (index + 1) % downloader.downloadList.size
|
||||
RepeatMode.ALL -> index = (index + 1) % downloader.playlist.size
|
||||
RepeatMode.SINGLE -> {
|
||||
}
|
||||
else -> {
|
||||
|
@ -217,8 +217,8 @@ class MediaPlayerService : Service() {
|
|||
}
|
||||
|
||||
localMediaPlayer.clearNextPlaying(false)
|
||||
if (index < downloader.downloadList.size && index != -1) {
|
||||
localMediaPlayer.setNextPlaying(downloader.downloadList[index])
|
||||
if (index < downloader.playlist.size && index != -1) {
|
||||
localMediaPlayer.setNextPlaying(downloader.playlist[index])
|
||||
} else {
|
||||
localMediaPlayer.clearNextPlaying(true)
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ class MediaPlayerService : Service() {
|
|||
@Synchronized
|
||||
fun play(index: Int, start: Boolean) {
|
||||
Timber.v("play requested for %d", index)
|
||||
if (index < 0 || index >= downloader.downloadList.size) {
|
||||
if (index < 0 || index >= downloader.playlist.size) {
|
||||
resetPlayback()
|
||||
} else {
|
||||
setCurrentPlaying(index)
|
||||
|
@ -280,7 +280,7 @@ class MediaPlayerService : Service() {
|
|||
jukeboxMediaPlayer.skip(index, 0)
|
||||
localMediaPlayer.setPlayerState(PlayerState.STARTED)
|
||||
} else {
|
||||
localMediaPlayer.play(downloader.downloadList[index])
|
||||
localMediaPlayer.play(downloader.playlist[index])
|
||||
}
|
||||
}
|
||||
downloader.checkDownloads()
|
||||
|
@ -293,7 +293,7 @@ class MediaPlayerService : Service() {
|
|||
localMediaPlayer.reset()
|
||||
localMediaPlayer.setCurrentPlaying(null)
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex, playerPosition
|
||||
)
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ class MediaPlayerService : Service() {
|
|||
|
||||
if (playerState === PlayerState.PAUSED) {
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList, downloader.currentPlayingIndex, playerPosition
|
||||
downloader.playlist, downloader.currentPlayingIndex, playerPosition
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -408,8 +408,8 @@ class MediaPlayerService : Service() {
|
|||
Util.broadcastPlaybackStatusChange(context, playerState)
|
||||
Util.broadcastA2dpPlayStatusChange(
|
||||
context, playerState, song,
|
||||
downloader.downloadList.size + downloader.backgroundDownloadList.size,
|
||||
downloader.downloadList.indexOf(currentPlaying) + 1, playerPosition
|
||||
downloader.playlist.size,
|
||||
downloader.playlist.indexOf(currentPlaying) + 1, playerPosition
|
||||
)
|
||||
|
||||
// Update widget
|
||||
|
@ -455,7 +455,7 @@ class MediaPlayerService : Service() {
|
|||
if (index != -1) {
|
||||
when (repeatMode) {
|
||||
RepeatMode.OFF -> {
|
||||
if (index + 1 < 0 || index + 1 >= downloader.downloadList.size) {
|
||||
if (index + 1 < 0 || index + 1 >= downloader.playlist.size) {
|
||||
if (Util.getShouldClearPlaylist()) {
|
||||
clear(true)
|
||||
jukeboxMediaPlayer.updatePlaylist()
|
||||
|
@ -466,7 +466,7 @@ class MediaPlayerService : Service() {
|
|||
}
|
||||
}
|
||||
RepeatMode.ALL -> {
|
||||
play((index + 1) % downloader.downloadList.size)
|
||||
play((index + 1) % downloader.playlist.size)
|
||||
}
|
||||
RepeatMode.SINGLE -> play(index)
|
||||
else -> {
|
||||
|
@ -480,12 +480,12 @@ class MediaPlayerService : Service() {
|
|||
@Synchronized
|
||||
fun clear(serialize: Boolean) {
|
||||
localMediaPlayer.reset()
|
||||
downloader.clear()
|
||||
downloader.clearPlaylist()
|
||||
localMediaPlayer.setCurrentPlaying(null)
|
||||
setNextPlaying()
|
||||
if (serialize) {
|
||||
downloadQueueSerializer.serializeDownloadQueue(
|
||||
downloader.downloadList,
|
||||
downloader.playlist,
|
||||
downloader.currentPlayingIndex, playerPosition
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class DownloadHandler(
|
|||
mediaPlayerController.clear()
|
||||
}
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
mediaPlayerController.download(
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save,
|
||||
autoPlay,
|
||||
|
@ -297,7 +297,7 @@ class DownloadHandler(
|
|||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
mediaPlayerController.download(
|
||||
mediaPlayerController.addToPlaylist(
|
||||
songs,
|
||||
save,
|
||||
autoPlay,
|
||||
|
|
|
@ -218,10 +218,13 @@ class SongView(context: Context) : UpdateView(context), Checkable, KoinComponent
|
|||
|
||||
override fun updateBackground() {}
|
||||
|
||||
@Synchronized
|
||||
public override fun update() {
|
||||
updateBackground()
|
||||
|
||||
downloadFile = mediaPlayerController.getDownloadFileForSong(entry)
|
||||
val song = entry ?: return
|
||||
|
||||
downloadFile = mediaPlayerController.getDownloadFileForSong(song)
|
||||
|
||||
updateDownloadStatus(downloadFile!!)
|
||||
|
||||
|
|
Loading…
Reference in New Issue