213 lines
8.0 KiB
Java
213 lines
8.0 KiB
Java
/*
|
|
This file is part of Subsonic.
|
|
|
|
Subsonic is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Subsonic is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Copyright 2009 (C) Sindre Mehus
|
|
*/
|
|
package net.nullsum.audinaut.util;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.util.Log;
|
|
import net.nullsum.audinaut.domain.MusicDirectory;
|
|
import net.nullsum.audinaut.service.DownloadService;
|
|
import net.nullsum.audinaut.service.MusicService;
|
|
import net.nullsum.audinaut.service.MusicServiceFactory;
|
|
import net.nullsum.audinaut.util.FileUtil;
|
|
|
|
/**
|
|
* @author Sindre Mehus
|
|
* @version $Id$
|
|
*/
|
|
public class ShufflePlayBuffer {
|
|
|
|
private static final String TAG = ShufflePlayBuffer.class.getSimpleName();
|
|
private static final String CACHE_FILENAME = "shuffleBuffer.ser";
|
|
|
|
private ScheduledExecutorService executorService;
|
|
private Runnable runnable;
|
|
private boolean firstRun = true;
|
|
private final ArrayList<MusicDirectory.Entry> buffer = new ArrayList<MusicDirectory.Entry>();
|
|
private int lastCount = -1;
|
|
private DownloadService context;
|
|
private boolean awaitingResults = false;
|
|
private int capacity;
|
|
private int refillThreshold;
|
|
|
|
private SharedPreferences.OnSharedPreferenceChangeListener listener;
|
|
private int currentServer;
|
|
private String currentFolder = "";
|
|
private String genre = "";
|
|
private String startYear = "";
|
|
private String endYear = "";
|
|
|
|
public ShufflePlayBuffer(DownloadService context) {
|
|
this.context = context;
|
|
|
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
|
runnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
refill();
|
|
}
|
|
};
|
|
executorService.scheduleWithFixedDelay(runnable, 1, 10, TimeUnit.SECONDS);
|
|
|
|
// Calculate out the capacity and refill threshold based on the user's random size preference
|
|
int shuffleListSize = Math.max(1, Integer.parseInt(Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20")));
|
|
// ex: default 20 -> 50
|
|
capacity = shuffleListSize * 5 / 2;
|
|
capacity = Math.min(500, capacity);
|
|
|
|
// ex: default 20 -> 40
|
|
refillThreshold = capacity * 4 / 5;
|
|
}
|
|
|
|
public List<MusicDirectory.Entry> get(int size) {
|
|
clearBufferIfnecessary();
|
|
// Make sure fetcher is running if needed
|
|
restart();
|
|
|
|
List<MusicDirectory.Entry> result = new ArrayList<MusicDirectory.Entry>(size);
|
|
synchronized (buffer) {
|
|
boolean removed = false;
|
|
while (!buffer.isEmpty() && result.size() < size) {
|
|
result.add(buffer.remove(buffer.size() - 1));
|
|
removed = true;
|
|
}
|
|
|
|
// Re-cache if anything is taken out
|
|
if(removed) {
|
|
FileUtil.serialize(context, buffer, CACHE_FILENAME);
|
|
}
|
|
}
|
|
Log.i(TAG, "Taking " + result.size() + " songs from shuffle play buffer. " + buffer.size() + " remaining.");
|
|
if(result.isEmpty()) {
|
|
awaitingResults = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void shutdown() {
|
|
executorService.shutdown();
|
|
Util.getPreferences(context).unregisterOnSharedPreferenceChangeListener(listener);
|
|
}
|
|
|
|
private void restart() {
|
|
synchronized(buffer) {
|
|
if(buffer.size() <= refillThreshold && lastCount != 0 && executorService.isShutdown()) {
|
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
|
executorService.scheduleWithFixedDelay(runnable, 0, 10, TimeUnit.SECONDS);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void refill() {
|
|
// Check if active server has changed.
|
|
clearBufferIfnecessary();
|
|
|
|
if (buffer != null && (buffer.size() > refillThreshold || (!Util.isNetworkConnected(context) && !Util.isOffline(context)) || lastCount == 0)) {
|
|
executorService.shutdown();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
MusicService service = MusicServiceFactory.getMusicService(context);
|
|
|
|
// Get capacity based
|
|
int n = capacity - buffer.size();
|
|
String folder = null;
|
|
if(!Util.isTagBrowsing(context)) {
|
|
folder = Util.getSelectedMusicFolderId(context);
|
|
}
|
|
MusicDirectory songs = service.getRandomSongs(n, folder, genre, startYear, endYear, context, null);
|
|
|
|
synchronized (buffer) {
|
|
lastCount = 0;
|
|
for(MusicDirectory.Entry entry: songs.getChildren()) {
|
|
if(!buffer.contains(entry)) {
|
|
buffer.add(entry);
|
|
lastCount++;
|
|
}
|
|
}
|
|
Log.i(TAG, "Refilled shuffle play buffer with " + lastCount + " songs.");
|
|
|
|
// Cache buffer
|
|
FileUtil.serialize(context, buffer, CACHE_FILENAME);
|
|
}
|
|
} catch (Exception x) {
|
|
// Give it one more try before quitting
|
|
if(lastCount != -2) {
|
|
lastCount = -2;
|
|
} else if(lastCount == -2) {
|
|
lastCount = 0;
|
|
}
|
|
Log.w(TAG, "Failed to refill shuffle play buffer.", x);
|
|
}
|
|
|
|
if(awaitingResults) {
|
|
awaitingResults = false;
|
|
context.checkDownloads();
|
|
}
|
|
}
|
|
|
|
private void clearBufferIfnecessary() {
|
|
synchronized (buffer) {
|
|
final SharedPreferences prefs = Util.getPreferences(context);
|
|
if (currentServer != Util.getActiveServer(context)
|
|
|| !Util.equals(currentFolder, Util.getSelectedMusicFolderId(context))
|
|
|| (genre != null && !genre.equals(prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "")))
|
|
|| (startYear != null && !startYear.equals(prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "")))
|
|
|| (endYear != null && !endYear.equals(prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "")))) {
|
|
lastCount = -1;
|
|
currentServer = Util.getActiveServer(context);
|
|
currentFolder = Util.getSelectedMusicFolderId(context);
|
|
genre = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "");
|
|
startYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "");
|
|
endYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "");
|
|
buffer.clear();
|
|
|
|
if(firstRun) {
|
|
ArrayList cacheList = FileUtil.deserialize(context, CACHE_FILENAME, ArrayList.class);
|
|
if(cacheList != null) {
|
|
buffer.addAll(cacheList);
|
|
}
|
|
|
|
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
|
@Override
|
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
|
clearBufferIfnecessary();
|
|
restart();
|
|
}
|
|
};
|
|
prefs.registerOnSharedPreferenceChangeListener(listener);
|
|
firstRun = false;
|
|
} else {
|
|
// Clear cache
|
|
File file = new File(context.getCacheDir(), CACHE_FILENAME);
|
|
file.delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|