From 8306a6ad9dd4875801d782caa2ca6df1bb8c2eb9 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 31 May 2013 21:31:22 -0700 Subject: Added Genre combo to Shuffle dialog (thanks archrival for parsers) --- .../src/github/daneren2005/dsub/domain/Genre.java | 29 +++++ .../dsub/fragments/SubsonicFragment.java | 65 ++++++++++- .../dsub/service/CachedMusicService.java | 20 ++++ .../daneren2005/dsub/service/MusicService.java | 5 + .../dsub/service/OfflineMusicService.java | 11 ++ .../daneren2005/dsub/service/RESTMusicService.java | 39 +++++++ .../dsub/service/parser/GenreParser.java | 122 +++++++++++++++++++++ 7 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 subsonic-android/src/github/daneren2005/dsub/domain/Genre.java create mode 100644 subsonic-android/src/github/daneren2005/dsub/service/parser/GenreParser.java (limited to 'subsonic-android/src/github/daneren2005') 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>(context, true) { + @Override + protected List doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + return musicService.getGenres(context, this); + } + + @Override + protected void done(final List genres) { + List names = new ArrayList(); + for(Genre genre: genres) { + names.add(genre.getName()); + } + final List 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 cachedIndexes = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache> cachedPlaylists = new TimeLimitedCache>(3600, TimeUnit.SECONDS); private final TimeLimitedCache> cachedMusicFolders = new TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS); + private final TimeLimitedCache> cachedGenres = new TimeLimitedCache>(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 getGenres(Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + List 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 getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception; void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception; + + List 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 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 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 parameterNames = new ArrayList(); + List parameterValues = new ArrayList(); + + 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.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 . + + 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 parse(Reader reader, ProgressListener progressListener) throws Exception { + updateProgress(progressListener, R.string.parser_reading); + + List result = new ArrayList(); + 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; + } +} -- cgit v1.2.3