aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2015-02-16 19:10:39 -0800
committerScott Jackson <daneren2005@gmail.com>2015-02-16 19:10:39 -0800
commitf0879be88ce68f3c1ce197ff35141f2d68edd046 (patch)
treeef7832001da878d4f172d3dfc38bdd734946d190
parentdf58a876833f6be97e4ba297dd473f2e227d2561 (diff)
downloaddsub-f0879be88ce68f3c1ce197ff35141f2d68edd046.tar.gz
dsub-f0879be88ce68f3c1ce197ff35141f2d68edd046.tar.bz2
dsub-f0879be88ce68f3c1ce197ff35141f2d68edd046.zip
#435 Add Artist Radio
-rw-r--r--res/drawable-hdpi/ic_menu_radio_dark.pngbin0 -> 768 bytes
-rw-r--r--res/drawable-hdpi/ic_menu_radio_light.pngbin0 -> 878 bytes
-rw-r--r--res/drawable-mdpi/ic_menu_radio_dark.pngbin0 -> 578 bytes
-rw-r--r--res/drawable-mdpi/ic_menu_radio_light.pngbin0 -> 675 bytes
-rw-r--r--res/drawable-xhdpi/ic_menu_radio_dark.pngbin0 -> 1131 bytes
-rw-r--r--res/drawable-xhdpi/ic_menu_radio_light.pngbin0 -> 1376 bytes
-rw-r--r--res/drawable-xxhdpi/ic_menu_radio_dark.pngbin0 -> 1992 bytes
-rw-r--r--res/drawable-xxhdpi/ic_menu_radio_light.pngbin0 -> 2310 bytes
-rw-r--r--res/menu/select_album.xml6
-rw-r--r--res/values/attrs.xml1
-rw-r--r--res/values/strings.xml1
-rw-r--r--res/values/themes.xml2
-rw-r--r--src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java27
-rw-r--r--src/github/daneren2005/dsub/service/CachedMusicService.java5
-rw-r--r--src/github/daneren2005/dsub/service/DownloadService.java100
-rw-r--r--src/github/daneren2005/dsub/service/MusicService.java1
-rw-r--r--src/github/daneren2005/dsub/service/OfflineMusicService.java7
-rw-r--r--src/github/daneren2005/dsub/service/RESTMusicService.java22
-rw-r--r--src/github/daneren2005/dsub/util/ArtistRadioBuffer.java150
-rw-r--r--src/github/daneren2005/dsub/util/Constants.java3
20 files changed, 309 insertions, 16 deletions
diff --git a/res/drawable-hdpi/ic_menu_radio_dark.png b/res/drawable-hdpi/ic_menu_radio_dark.png
new file mode 100644
index 00000000..a801dce0
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_radio_light.png b/res/drawable-hdpi/ic_menu_radio_light.png
new file mode 100644
index 00000000..b723d574
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_radio_dark.png b/res/drawable-mdpi/ic_menu_radio_dark.png
new file mode 100644
index 00000000..bab20118
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_radio_light.png b/res/drawable-mdpi/ic_menu_radio_light.png
new file mode 100644
index 00000000..72578d54
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_radio_dark.png b/res/drawable-xhdpi/ic_menu_radio_dark.png
new file mode 100644
index 00000000..3a4114a3
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_radio_light.png b/res/drawable-xhdpi/ic_menu_radio_light.png
new file mode 100644
index 00000000..5bcc9261
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_radio_dark.png b/res/drawable-xxhdpi/ic_menu_radio_dark.png
new file mode 100644
index 00000000..0c63afbe
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_radio_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_radio_light.png b/res/drawable-xxhdpi/ic_menu_radio_light.png
new file mode 100644
index 00000000..133772f8
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_radio_light.png
Binary files differ
diff --git a/res/menu/select_album.xml b/res/menu/select_album.xml
index 39eb2206..a9741d84 100644
--- a/res/menu/select_album.xml
+++ b/res/menu/select_album.xml
@@ -6,6 +6,12 @@
android:icon="?media_button_start"
android:title="@string/menu.play"
compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_radio"
+ android:icon="?attr/radio"
+ android:title="@string/menu.start_radio"
+ compat:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/menu_shuffle"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e0a3d00d..9667117c 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -25,6 +25,7 @@
<attr name="password" format="reference"/>
<attr name="rating_bad" format="reference"/>
<attr name="rating_good" format="reference"/>
+ <attr name="radio" format="reference"/>
<attr name="drawerItemsIcons" format="reference"/>
<declare-styleable name="SeekBarPreference">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 43c4650b..eed2f1c7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -107,6 +107,7 @@
<string name="menu.top_tracks">Last.FM Top Tracks</string>
<string name="menu.similar_artists">Similar Artists</string>
<string name="menu.show_missing">Show missing</string>
+ <string name="menu.start_radio">Start Radio</string>
<string name="playlist.label">Playlists</string>
<string name="playlist.update_info">Update Information</string>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 70f30e56..78a2c34d 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -28,6 +28,7 @@
<item name="password">@drawable/ic_menu_password_light</item>
<item name="rating_bad">@drawable/ic_action_rating_bad_light</item>
<item name="rating_good">@drawable/ic_action_rating_good_light</item>
+ <item name="radio">@drawable/ic_menu_radio_light</item>
<item name="drawerItemsIcons">@array/drawerItemIconsLight</item>
<item name="android:textViewStyle">@style/DSub.TextViewStyle</item>
<item name="android:buttonStyle">@style/DSub.ButtonStyle.Light</item>
@@ -62,6 +63,7 @@
<item name="password">@drawable/ic_menu_password_dark</item>
<item name="rating_bad">@drawable/ic_action_rating_bad_dark</item>
<item name="rating_good">@drawable/ic_action_rating_good_dark</item>
+ <item name="radio">@drawable/ic_menu_radio_dark</item>
<item name="drawerItemsIcons">@array/drawerItemIconsDark</item>
<item name="android:textViewStyle">@style/DSub.TextViewStyle</item>
<item name="android:buttonStyle">@style/DSub.ButtonStyle.Dark</item>
diff --git a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index 9e6608cf..b8a1006b 100644
--- a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -42,6 +42,7 @@ import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.domain.Share;
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.view.AlbumGridAdapter;
import github.daneren2005.dsub.view.EntryAdapter;
@@ -227,8 +228,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
if(!ServerInfo.isMadsonic(context)) {
menu.removeItem(R.id.menu_top_tracks);
}
- if(!ServerInfo.checkServerVersion(context, "1.11")) {
+ if(!ServerInfo.checkServerVersion(context, "1.11") || !ServerInfo.isStockSubsonic(context)) {
menu.removeItem(R.id.menu_similar_artists);
+ menu.removeItem(R.id.menu_radio);
}
} else {
if(podcastId == null) {
@@ -320,6 +322,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
case R.id.menu_similar_artists:
showSimilarArtists(id);
return true;
+ case R.id.menu_radio:
+ startArtistRadio(id);
+ return true;
}
return super.onOptionsItemSelected(item);
@@ -1268,6 +1273,26 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
replaceFragment(fragment, true);
}
+ private void startArtistRadio(final String artistId) {
+ new LoadingTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ DownloadService downloadService = getDownloadService();
+ downloadService.setArtistRadio(artistId);
+ if(downloadService.size() == 0) {
+ Log.e(TAG, "Failed to create artist radio");
+ throw new Exception("Failed to create artist radio");
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ }
+ }.execute();
+ }
+
private View createHeader() {
View header = entryList.findViewById(R.id.select_album_header);
boolean add = false;
diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/src/github/daneren2005/dsub/service/CachedMusicService.java
index 124ae615..09347d1d 100644
--- a/src/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/src/github/daneren2005/dsub/service/CachedMusicService.java
@@ -466,6 +466,11 @@ public class CachedMusicService implements MusicService {
}
@Override
+ public MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception {
+ return musicService.getRandomSongs(size, artistId, context, progressListener);
+ }
+
+ @Override
public MusicDirectory getStarredList(Context context, ProgressListener progressListener) throws Exception {
MusicDirectory dir = musicService.getStarredList(context, progressListener);
diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java
index 8c6016ba..5726b696 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/src/github/daneren2005/dsub/service/DownloadService.java
@@ -41,6 +41,7 @@ import github.daneren2005.dsub.domain.RemoteControlState;
import github.daneren2005.dsub.domain.RepeatMode;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
+import github.daneren2005.dsub.util.ArtistRadioBuffer;
import github.daneren2005.dsub.util.Notifications;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Constants;
@@ -100,6 +101,9 @@ public class DownloadService extends Service {
public static final int REWIND = 10000;
private static final double DELETE_CUTOFF = 0.84;
private static final int REQUIRED_ALBUM_MATCHES = 4;
+ private static final int SHUFFLE_MODE_NONE = 0;
+ private static final int SHUFFLE_MODE_ALL = 1;
+ private static final int SHUFFLE_MODE_ARTIST = 2;
private RemoteControlClientHelper mRemoteControl;
@@ -116,6 +120,7 @@ public class DownloadService extends Service {
private Handler mediaPlayerHandler;
private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
private ShufflePlayBuffer shufflePlayBuffer;
+ private ArtistRadioBuffer artistRadioBuffer;
private final LruCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LruCache<MusicDirectory.Entry, DownloadFile>(100);
private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>();
@@ -131,6 +136,7 @@ public class DownloadService extends Service {
private PlayerState nextPlayerState = IDLE;
private boolean removePlayed;
private boolean shufflePlay;
+ private boolean artistRadio;
private long revision;
private static DownloadService instance;
private String suggestedPlaylistName;
@@ -229,6 +235,7 @@ public class DownloadService extends Service {
instance = this;
lifecycleSupport.onCreate();
shufflePlayBuffer = new ShufflePlayBuffer(this);
+ artistRadioBuffer = new ArtistRadioBuffer(this);
}
@Override
@@ -313,6 +320,7 @@ public class DownloadService extends Service {
}
public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, int start, int position) {
setShufflePlayEnabled(false);
+ setArtistRadio(null);
int offset = 1;
boolean noNetwork = !Util.isOffline(this) && !Util.isNetworkConnected(this);
boolean warnNetwork = false;
@@ -355,7 +363,7 @@ public class DownloadService extends Service {
}
revision++;
}
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
if(shuffle) {
shuffle();
@@ -406,7 +414,7 @@ public class DownloadService extends Service {
lifecycleSupport.serializeDownloadQueue();
}
- private void updateJukeboxPlaylist() {
+ private void updateRemotePlaylist() {
if (remoteState != LOCAL && remoteController != null) {
remoteController.updatePlaylist();
}
@@ -422,12 +430,17 @@ public class DownloadService extends Service {
if(prefs.getBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, false)) {
removePlayed = true;
}
- boolean startShufflePlay = prefs.getBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, false);
+ int startShufflePlay = prefs.getInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, SHUFFLE_MODE_NONE);
download(songs, false, false, false, false);
- if(startShufflePlay) {
- shufflePlay = true;
+ if(startShufflePlay != SHUFFLE_MODE_NONE) {
+ if(startShufflePlay == SHUFFLE_MODE_ALL) {
+ shufflePlay = true;
+ } else {
+ artistRadio = true;
+ artistRadioBuffer.restoreArtist(prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_MODE_EXTRA, null));
+ }
SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, true);
+ editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, startShufflePlay);
editor.commit();
}
if (currentPlayingIndex != -1) {
@@ -470,7 +483,7 @@ public class DownloadService extends Service {
checkDownloads();
}
SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled);
+ editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled ? SHUFFLE_MODE_ALL : SHUFFLE_MODE_NONE);
editor.commit();
}
@@ -478,6 +491,20 @@ public class DownloadService extends Service {
return shufflePlay;
}
+ public void setArtistRadio(String artistId) {
+ if(artistId == null) {
+ artistRadio = false;
+ } else {
+ artistRadio = true;
+ artistRadioBuffer.setArtist(artistId);
+ }
+
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, (artistId != null) ? SHUFFLE_MODE_ARTIST : SHUFFLE_MODE_NONE);
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_MODE_EXTRA, artistId);
+ editor.commit();
+ }
+
public synchronized void shuffle() {
Collections.shuffle(downloadList);
currentPlayingIndex = downloadList.indexOf(currentPlaying);
@@ -488,7 +515,7 @@ public class DownloadService extends Service {
}
revision++;
lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
setNextPlaying();
}
@@ -575,7 +602,7 @@ public class DownloadService extends Service {
}
}
lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
}
public void setOnline(final boolean online) {
@@ -587,6 +614,9 @@ public class DownloadService extends Service {
if(shufflePlay) {
setShufflePlayEnabled(false);
}
+ if(artistRadio) {
+ setArtistRadio(null);
+ }
lifecycleSupport.post(new Runnable() {
@Override
@@ -645,7 +675,7 @@ public class DownloadService extends Service {
if (serialize) {
lifecycleSupport.serializeDownloadQueue();
}
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
setNextPlaying();
if(proxy != null) {
proxy.stop();
@@ -675,7 +705,7 @@ public class DownloadService extends Service {
backgroundDownloadList.remove(downloadFile);
revision++;
lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
if(downloadFile == nextPlaying) {
setNextPlaying();
}
@@ -1742,7 +1772,7 @@ public class DownloadService extends Service {
list.add(to, movedSong);
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if(remoteState != LOCAL && mainList) {
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
} else if(mainList && (movedSong == nextPlaying || movedSong == currentPlaying || (currentPlayingIndex + 1) == to)) {
// Moving next playing, current playing, or moving a song to be next playing
setNextPlaying();
@@ -1787,6 +1817,9 @@ public class DownloadService extends Service {
if (shufflePlay) {
checkShufflePlay();
}
+ if(artistRadio) {
+ checkArtistRadio();
+ }
if (!Util.isNetworkConnected(this, true) || Util.isOffline(this)) {
return;
@@ -1913,7 +1946,48 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if (revisionBefore != revision) {
- updateJukeboxPlaylist();
+ updateRemotePlaylist();
+ }
+
+ if (wasEmpty && !downloadList.isEmpty()) {
+ play(0);
+ }
+ }
+
+ private synchronized void checkArtistRadio() {
+ // Get users desired random playlist size
+ SharedPreferences prefs = Util.getPreferences(this);
+ int listSize = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20"));
+ boolean wasEmpty = downloadList.isEmpty();
+
+ long revisionBefore = revision;
+
+ // First, ensure that list is at least 20 songs long.
+ int size = size();
+ if (size < listSize) {
+ for (MusicDirectory.Entry song : artistRadioBuffer.get(listSize - size)) {
+ DownloadFile downloadFile = new DownloadFile(this, song, false);
+ downloadList.add(downloadFile);
+ revision++;
+ }
+ }
+
+ int currIndex = currentPlaying == null ? 0 : getCurrentPlayingIndex();
+
+ // Only shift playlist if playing song #5 or later.
+ if (currIndex > 4) {
+ int songsToShift = currIndex - 2;
+ for (MusicDirectory.Entry song : artistRadioBuffer.get(songsToShift)) {
+ downloadList.add(new DownloadFile(this, song, false));
+ downloadList.get(0).cancelDownload();
+ downloadList.remove(0);
+ revision++;
+ }
+ }
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+
+ if (revisionBefore != revision) {
+ updateRemotePlaylist();
}
if (wasEmpty && !downloadList.isEmpty()) {
diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/src/github/daneren2005/dsub/service/MusicService.java
index 854a0aa4..2c5a7f2a 100644
--- a/src/github/daneren2005/dsub/service/MusicService.java
+++ b/src/github/daneren2005/dsub/service/MusicService.java
@@ -93,6 +93,7 @@ public interface MusicService {
MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception;
String getCoverArtUrl(Context context, MusicDirectory.Entry entry) throws Exception;
diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/src/github/daneren2005/dsub/service/OfflineMusicService.java
index 254592da..c0fc89fe 100644
--- a/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -531,7 +531,12 @@ public class OfflineMusicService implements MusicService {
throw new OfflineException(ERRORMSG);
}
- @Override
+ @Override
+ public MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception {
+ throw new OfflineException(ERRORMSG);
+ }
+
+ @Override
public String getVideoUrl(int maxBitrate, Context context, String id) {
return null;
}
diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java
index 8db42677..715d07da 100644
--- a/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/src/github/daneren2005/dsub/service/RESTMusicService.java
@@ -571,6 +571,28 @@ public class RESTMusicService implements MusicService {
}
@Override
+ public MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception {
+ checkServerVersion(context, "1.11", "Artist radio is not supported");
+
+ List<String> names = new ArrayList<String>();
+ List<Object> values = new ArrayList<Object>();
+
+ names.add("id");
+ names.add("count");
+
+ values.add(artistId);
+ values.add(size);
+
+ int instance = getInstance(context);
+ Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, instance) ? "getSimilarSongs2" : "getSimilarSongs", null, names, values);
+ try {
+ return new RandomSongsParser(context, instance).parse(reader, progressListener);
+ } finally {
+ Util.close(reader);
+ }
+ }
+
+ @Override
public MusicDirectory getStarredList(Context context, ProgressListener progressListener) throws Exception {
Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, getInstance(context)) ? "getStarred2" : "getStarred", null);
try {
diff --git a/src/github/daneren2005/dsub/util/ArtistRadioBuffer.java b/src/github/daneren2005/dsub/util/ArtistRadioBuffer.java
new file mode 100644
index 00000000..829478f7
--- /dev/null
+++ b/src/github/daneren2005/dsub/util/ArtistRadioBuffer.java
@@ -0,0 +1,150 @@
+/*
+ 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 2014 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+
+public class ArtistRadioBuffer {
+ private static final String TAG = ArtistRadioBuffer.class.getSimpleName();
+
+ private ScheduledExecutorService executorService;
+ private Runnable runnable;
+ 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 String artistId;
+
+ public ArtistRadioBuffer(DownloadService context) {
+ this.context = context;
+
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ runnable = new Runnable() {
+ @Override
+ public void run() {
+ refill();
+ }
+ };
+
+ // Calculate out the capacity and refill threshold based on the user's random size preference
+ int shuffleListSize = 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 void setArtist(String artistId) {
+ if(!Util.equals(this.artistId, artistId)) {
+ buffer.clear();
+ }
+
+ context.clear();
+ this.artistId = artistId;
+ awaitingResults = true;
+ refill();
+ }
+ public void restoreArtist(String artistId) {
+ this.artistId = artistId;
+ awaitingResults = false;
+ restart();
+ }
+
+ public List<MusicDirectory.Entry> get(int size) {
+ // Make sure fetcher is running if needed
+ restart();
+
+ List<MusicDirectory.Entry> result = new ArrayList<MusicDirectory.Entry>(size);
+ synchronized (buffer) {
+ while (!buffer.isEmpty() && result.size() < size) {
+ result.add(buffer.remove(buffer.size() - 1));
+ }
+ }
+ 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();
+ }
+
+ 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() {
+ 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();
+ MusicDirectory songs = service.getRandomSongs(n, artistId, context, null);
+
+ synchronized (buffer) {
+ lastCount = 0;
+ for(MusicDirectory.Entry entry: songs.getChildren()) {
+ if(!buffer.contains(entry)) {
+ buffer.add(entry);
+ lastCount++;
+ }
+ }
+ Log.i(TAG, "Refilled artist radio buffer with " + lastCount + " songs.");
+ }
+ } 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 artist radio buffer.", x);
+ }
+
+ if(awaitingResults) {
+ awaitingResults = false;
+ context.checkDownloads();
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/util/Constants.java b/src/github/daneren2005/dsub/util/Constants.java
index 7298c7b4..d97fe8d0 100644
--- a/src/github/daneren2005/dsub/util/Constants.java
+++ b/src/github/daneren2005/dsub/util/Constants.java
@@ -113,7 +113,8 @@ public final class Constants {
public static final String PREFERENCES_KEY_PERSISTENT_NOTIFICATION = "persistentNotification";
public static final String PREFERENCES_KEY_GAPLESS_PLAYBACK = "gaplessPlayback";
public static final String PREFERENCES_KEY_REMOVE_PLAYED = "removePlayed";
- public static final String PREFERENCES_KEY_SHUFFLE_MODE = "shuffleMode";
+ public static final String PREFERENCES_KEY_SHUFFLE_MODE = "shuffleMode2";
+ public static final String PREFERENCES_KEY_SHUFFLE_MODE_EXTRA = "shuffleModeExtra";
public static final String PREFERENCES_KEY_CHAT_REFRESH = "chatRefreshRate";
public static final String PREFERENCES_KEY_CHAT_ENABLED = "chatEnabled";
public static final String PREFERENCES_KEY_VIDEO_PLAYER = "videoPlayer";