diff options
author | Scott Jackson <daneren2005@gmail.com> | 2013-05-31 21:31:22 -0700 |
---|---|---|
committer | Scott Jackson <daneren2005@gmail.com> | 2013-05-31 21:31:22 -0700 |
commit | 8306a6ad9dd4875801d782caa2ca6df1bb8c2eb9 (patch) | |
tree | 51812ec3f903bdcba26ec236294f57e6eac9613a | |
parent | 63bea6976413bb2e24ea9d606a8fe5cff2e3de44 (diff) | |
download | dsub-8306a6ad9dd4875801d782caa2ca6df1bb8c2eb9.tar.gz dsub-8306a6ad9dd4875801d782caa2ca6df1bb8c2eb9.tar.bz2 dsub-8306a6ad9dd4875801d782caa2ca6df1bb8c2eb9.zip |
Added Genre combo to Shuffle dialog (thanks archrival for parsers)
9 files changed, 300 insertions, 2 deletions
diff --git a/subsonic-android/res/layout/shuffle_dialog.xml b/subsonic-android/res/layout/shuffle_dialog.xml index 2a21dc11..e78aba33 100644 --- a/subsonic-android/res/layout/shuffle_dialog.xml +++ b/subsonic-android/res/layout/shuffle_dialog.xml @@ -66,6 +66,14 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="4dp" - android:hint="@string/shuffle.genre" /> + android:hint="@string/shuffle.genre"/> + + <Button + android:id="@+id/genre_combo" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginLeft="4dp" + android:text="@string/shuffle.genre"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/subsonic-android/res/values/strings.xml b/subsonic-android/res/values/strings.xml index 62172631..01e10d6b 100644 --- a/subsonic-android/res/values/strings.xml +++ b/subsonic-android/res/values/strings.xml @@ -283,6 +283,7 @@ <string name="shuffle.startYear">Start Year:</string>
<string name="shuffle.endYear">End Year:</string>
<string name="shuffle.genre">Genre:</string>
+ <string name="shuffle.pick_genre">Pick a genre</string>
<string name="music_service.retry">A network error occurred. Retrying %1$d of %2$d.</string>
diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java b/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java new file mode 100644 index 00000000..8c705e31 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/domain/Genre.java @@ -0,0 +1,29 @@ +package github.daneren2005.dsub.domain;
+
+import java.io.Serializable;
+
+public class Genre implements Serializable {
+ private String name;
+ private String index;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getIndex() {
+ return index;
+ }
+
+ public void setIndex(String index) {
+ this.index = index;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java index 35a272eb..05b78eab 100644 --- a/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -35,6 +35,7 @@ import android.view.MenuInflater; import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
@@ -46,8 +47,10 @@ import github.daneren2005.dsub.activity.SearchActivity; import github.daneren2005.dsub.activity.SettingsActivity;
import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.domain.Version;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.DownloadServiceImpl;
@@ -379,15 +382,70 @@ public class SubsonicFragment extends SherlockFragment { final EditText startYearBox = (EditText)dialogView.findViewById(R.id.start_year);
final EditText endYearBox = (EditText)dialogView.findViewById(R.id.end_year);
final EditText genreBox = (EditText)dialogView.findViewById(R.id.genre);
+ final Button genreCombo = (Button)dialogView.findViewById(R.id.genre_combo);
final SharedPreferences prefs = Util.getPreferences(context);
final String oldStartYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, "");
final String oldEndYear = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, "");
final String oldGenre = prefs.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, "");
+
+ Version version = Util.getServerRestVersion(context);
+ Version genreVersion = new Version("1.9.0");
+ boolean _useCombo = false;
+ if(version.compareTo(genreVersion) >= 0) {
+ genreBox.setVisibility(View.GONE);
+ genreCombo.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ new LoadingTask<List<Genre>>(context, true) {
+ @Override
+ protected List<Genre> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ return musicService.getGenres(context, this);
+ }
+
+ @Override
+ protected void done(final List<Genre> genres) {
+ List<String> names = new ArrayList<String>();
+ for(Genre genre: genres) {
+ names.add(genre.getName());
+ }
+ final List<String> finalNames = names;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.shuffle_pick_genre)
+ .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ genreCombo.setText(finalNames.get(which));
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ _useCombo = true;
+ } else {
+ genreCombo.setVisibility(View.GONE);
+ }
+ final boolean useCombo = _useCombo;
startYearBox.setText(oldStartYear);
endYearBox.setText(oldEndYear);
genreBox.setText(oldGenre);
+ genreCombo.setText(oldGenre);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Shuffle By")
@@ -397,7 +455,12 @@ public class SubsonicFragment extends SherlockFragment { public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(context, DownloadActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- String genre = genreBox.getText().toString();
+ String genre;
+ if(useCombo) {
+ genre = genreCombo.getText().toString();
+ } else {
+ genre = genreBox.getText().toString();
+ }
String startYear = startYearBox.getText().toString();
String endYear = endYearBox.getText().toString();
diff --git a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java index 3eb4184f..71bbb867 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java @@ -26,6 +26,7 @@ import org.apache.http.HttpResponse; import android.content.Context; import android.graphics.Bitmap; import github.daneren2005.dsub.domain.ChatMessage; +import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; import github.daneren2005.dsub.domain.JukeboxStatus; import github.daneren2005.dsub.domain.Lyrics; @@ -56,6 +57,7 @@ public class CachedMusicService implements MusicService { private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS); + private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS); private String restUrl; public CachedMusicService(MusicService musicService) { @@ -275,6 +277,24 @@ public class CachedMusicService implements MusicService { public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception { musicService.addChatMessage(message, context, progressListener); } + + @Override + public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + List<Genre> result = cachedGenres.get(); + + if (result == null) { + result = musicService.getGenres(context, progressListener); + cachedGenres.set(result); + } + + return result; + } + + @Override + public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception { + return musicService.getSongsByGenre(genre, count, offset, context, progressListener); + } private void checkSettingsChanged(Context context) { String newUrl = Util.getRestUrl(context, null); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java index 88391e53..31e9c23c 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java @@ -25,6 +25,7 @@ import org.apache.http.HttpResponse; import android.content.Context; import android.graphics.Bitmap; import github.daneren2005.dsub.domain.ChatMessage; +import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; import github.daneren2005.dsub.domain.JukeboxStatus; import github.daneren2005.dsub.domain.Lyrics; @@ -110,4 +111,8 @@ public interface MusicService { List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception; void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception; + + List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception; + + public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception; }
\ No newline at end of file diff --git a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java index df501994..97fed19f 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java @@ -38,6 +38,7 @@ import android.graphics.BitmapFactory; import android.media.MediaMetadataRetriever; import android.util.Log; import github.daneren2005.dsub.domain.Artist; +import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; import github.daneren2005.dsub.domain.JukeboxStatus; import github.daneren2005.dsub.domain.Lyrics; @@ -480,6 +481,16 @@ public class OfflineMusicService extends RESTMusicService { public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception { throw new OfflineException("Starring not available in offline mode"); } + + @Override + public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Getting Genres not available in offline mode"); + } + + @Override + public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Getting Songs By Genre not available in offline mode"); + } @Override public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception { diff --git a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java index daa9291a..5fc66190 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -75,6 +75,7 @@ import github.daneren2005.dsub.domain.MusicDirectory.Entry; import github.daneren2005.dsub.service.parser.AlbumListParser; import github.daneren2005.dsub.service.parser.ChatMessageParser; import github.daneren2005.dsub.service.parser.ErrorParser; +import github.daneren2005.dsub.service.parser.GenreParser; import github.daneren2005.dsub.service.parser.IndexesParser; import github.daneren2005.dsub.service.parser.JukeboxStatusParser; import github.daneren2005.dsub.service.parser.LicenseParser; @@ -787,6 +788,44 @@ public class RESTMusicService implements MusicService { Util.close(reader); } } + + @Override + public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception { + checkServerVersion(context, "1.9", "Genres not supported."); + + Reader reader = getReader(context, progressListener, "getGenres", null); + try { + return new GenreParser(context).parse(reader, progressListener); + } finally { + Util.close(reader); + } + } + + @Override + public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception { + checkServerVersion(context, "1.9", "Genres not supported."); + + HttpParams params = new BasicHttpParams(); + HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS); + + List<String> parameterNames = new ArrayList<String>(); + List<Object> parameterValues = new ArrayList<Object>(); + + parameterNames.add("genre"); + parameterValues.add(genre); + parameterNames.add("count"); + parameterValues.add(count); + parameterNames.add("offset"); + parameterValues.add(offset); + + Reader reader = getReader(context, progressListener, "getSongsByGenre", params, parameterNames, parameterValues); + + try { + return new RandomSongsParser(context).parse(reader, progressListener); + } finally { + Util.close(reader); + } + } private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception { return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList()); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java new file mode 100644 index 00000000..1062d3af --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java @@ -0,0 +1,122 @@ +/*
+ 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 2010 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.service.parser;
+
+import android.content.Context;
+import android.util.Log;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Genre;
+import github.daneren2005.dsub.util.ProgressListener;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joshua Bahnsen
+ */
+public class GenreParser extends AbstractParser {
+ private static final String TAG = GenreParser.class.getSimpleName();
+
+ public GenreParser(Context context) {
+ super(context);
+ }
+
+ public List<Genre> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ updateProgress(progressListener, R.string.parser_reading);
+
+ List<Genre> result = new ArrayList<Genre>();
+ StringReader sr = null;
+
+ try {
+ BufferedReader br = new BufferedReader(reader);
+ String xml = null;
+ String line = null;
+
+ while ((line = br.readLine()) != null) {
+ if (xml == null) {
+ xml = line;
+ } else {
+ xml += line;
+ }
+ }
+ br.close();
+
+ // Replace double escaped ampersand (&apos;)
+ xml = xml.replaceAll("(?:&)(amp;|lt;|gt;|#37;|apos;)", "&$1");
+
+ // Replace unescaped ampersand
+ xml = xml.replaceAll("&(?!amp;|lt;|gt;|#37;|apos;)", "&");
+
+ // Replace unescaped percent symbol
+ // No replacements for <> at this time
+ xml = xml.replaceAll("%", "%");
+
+ xml = xml.replaceAll("'", "'");
+
+ sr = new StringReader(xml);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error parsing Genre XML", ioe);
+ }
+
+ if (sr == null) {
+ Log.w(TAG, "Unable to parse Genre XML, returning empty list");
+ return result;
+ }
+
+ init(sr);
+
+ Genre genre = null;
+
+ int eventType;
+ do {
+ eventType = nextParseEvent();
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = getElementName();
+ if ("genre".equals(name)) {
+ genre = new Genre();
+ } else if ("error".equals(name)) {
+ handleError();
+ } else {
+ genre = null;
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ if (genre != null) {
+ String value = getText();
+ if (genre != null) {
+ genre.setName(value);
+ genre.setIndex(value.substring(0, 1));
+ result.add(genre);
+ genre = null;
+ }
+ }
+ }
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ validate();
+ updateProgress(progressListener, R.string.parser_reading_done);
+
+ return result;
+ }
+}
|