diff options
6 files changed, 305 insertions, 487 deletions
diff --git a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java index 00fb4624..9fd26fe5 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java @@ -20,6 +20,7 @@ package github.daneren2005.dsub.service; import java.io.File; import java.io.IOException; +import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -27,8 +28,6 @@ import java.util.List; import java.util.ListIterator; import java.util.concurrent.TimeUnit; -import org.apache.http.HttpResponse; - import android.content.Context; import android.graphics.Bitmap; import android.util.Log; @@ -733,11 +732,16 @@ public class CachedMusicService implements MusicService { @Override public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { - return musicService.getCoverArt(context, entry, size, progressListener, task); + Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); + if (bitmap != null) { + return bitmap; + } else { + return musicService.getCoverArt(context, entry, size, progressListener, task); + } } @Override - public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { + public HttpURLConnection getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task); } @@ -1157,7 +1161,12 @@ public class CachedMusicService implements MusicService { @Override public Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { - return musicService.getAvatar(username, size, context, progressListener, task); + Bitmap bitmap = FileUtil.getAvatarBitmap(context, username, size); + if(bitmap != null) { + return bitmap; + } else { + return musicService.getAvatar(username, size, context, progressListener, task); + } } @Override @@ -1188,7 +1197,12 @@ public class CachedMusicService implements MusicService { @Override public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { - return musicService.getBitmap(url, size, context, progressListener, task); + Bitmap bitmap = FileUtil.getMiscBitmap(context, url, size); + if(bitmap != null) { + return bitmap; + } else { + return musicService.getBitmap(url, size, context, progressListener, task); + } } @Override diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java index d1c594ce..30e06982 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; +import java.net.HttpURLConnection; import android.content.Context; import android.net.wifi.WifiManager; @@ -39,15 +40,6 @@ import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.util.CacheCleaner; import github.daneren2005.serverproxy.BufferFile; -import org.apache.http.Header; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; - -/** - * @author Sindre Mehus - * @version $Id$ - */ public class DownloadFile implements BufferFile { private static final String TAG = DownloadFile.class.getSimpleName(); private static final int MAX_FAILURES = 5; @@ -467,17 +459,15 @@ public class DownloadFile implements BufferFile { } if(compare) { // Attempt partial HTTP GET, appending to the file if it exists. - HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this); - Header contentLengthHeader = response.getFirstHeader("Content-Length"); - if(contentLengthHeader != null) { - String contentLengthString = contentLengthHeader.getValue(); - if(contentLengthString != null) { - Log.i(TAG, "Content Length: " + contentLengthString); - contentLength = Long.parseLong(contentLengthString); - } + HttpURLConnection connection = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this); + long contentLength = connection.getContentLength(); + if(contentLength > 0) { + Log.i(TAG, "Content Length: " + contentLength); + DownloadFile.this.contentLength = contentLength; } - in = response.getEntity().getContent(); - boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT; + + in = connection.getInputStream(); + boolean partial = connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL; if (partial) { Log.i(TAG, "Executed partial HTTP GET, skipping " + partialFile.length() + " bytes"); } diff --git a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java index 1e275108..6a58e340 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java @@ -18,15 +18,14 @@ */ package github.daneren2005.dsub.service; +import java.net.HttpURLConnection; import java.util.List; -import org.apache.http.HttpResponse; import android.content.Context; import android.graphics.Bitmap; import github.daneren2005.dsub.domain.ArtistInfo; -import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.ChatMessage; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; @@ -104,7 +103,7 @@ public interface MusicService { Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception; - HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception; + HttpURLConnection getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception; String getMusicUrl(Context context, MusicDirectory.Entry song, int maxBitrate) throws Exception; diff --git a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java index 2c439ec4..da6c37f1 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java @@ -21,6 +21,7 @@ package github.daneren2005.dsub.service; import java.io.File; import java.io.Reader; import java.io.FileReader; +import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -34,8 +35,6 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.util.Log; -import org.apache.http.HttpResponse; - import github.daneren2005.dsub.domain.Artist; import github.daneren2005.dsub.domain.ArtistInfo; import github.daneren2005.dsub.domain.ChatMessage; @@ -227,7 +226,7 @@ public class OfflineMusicService implements MusicService { } @Override - public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { + public HttpURLConnection getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { throw new OfflineException(ERRORMSG); } diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java index 6ccf562c..cc475d4f 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java @@ -24,27 +24,16 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; + import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.params.ConnManagerParams; import org.apache.http.conn.params.ConnPerRouteBean; import org.apache.http.conn.scheme.PlainSocketFactory; @@ -53,21 +42,15 @@ import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.os.Looper; import android.util.Log; import github.daneren2005.dsub.R; @@ -102,7 +85,6 @@ import github.daneren2005.dsub.service.parser.UserParser; import github.daneren2005.dsub.service.parser.VideosParser; import github.daneren2005.dsub.service.ssl.SSLSocketFactory; import github.daneren2005.dsub.service.ssl.TrustSelfSignedStrategy; -import github.daneren2005.dsub.util.BackgroundTask; import github.daneren2005.dsub.util.Pair; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.Constants; @@ -113,9 +95,10 @@ import github.daneren2005.dsub.util.Util; import java.io.*; import java.util.zip.GZIPInputStream; -/** - * @author Sindre Mehus - */ +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; + public class RESTMusicService implements MusicService { private static final String TAG = RESTMusicService.class.getSimpleName(); @@ -123,7 +106,6 @@ public class RESTMusicService implements MusicService { private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000; private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000; private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000; - private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000; private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000; // Allow 20 seconds extra timeout per MB offset. @@ -132,6 +114,7 @@ public class RESTMusicService implements MusicService { private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5; private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L; + private HostnameVerifier selfSignedHostnameVerifier; private final DefaultHttpClient httpClient; private long redirectionLastChecked; private int redirectionNetworkType = -1; @@ -141,6 +124,11 @@ public class RESTMusicService implements MusicService { private Integer instance; public RESTMusicService() { + selfSignedHostnameVerifier = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; // Create and initialize default HTTP parameters HttpParams params = new BasicHttpParams(); @@ -176,7 +164,7 @@ public class RESTMusicService implements MusicService { @Override public void ping(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "ping", null); + Reader reader = getReader(context, progressListener, "ping"); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -187,7 +175,7 @@ public class RESTMusicService implements MusicService { @Override public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getLicense", null); + Reader reader = getReader(context, progressListener, "getLicense"); try { ServerInfo serverInfo = new LicenseParser(context, getInstance(context)).parse(reader); return serverInfo.isLicenseValid(); @@ -197,7 +185,7 @@ public class RESTMusicService implements MusicService { } public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getMusicFolders", null); + Reader reader = getReader(context, progressListener, "getMusicFolders"); try { return new MusicFoldersParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -207,7 +195,7 @@ public class RESTMusicService implements MusicService { @Override public void startRescan(Context context, ProgressListener listener) throws Exception { - Reader reader = getReader(context, listener, "startRescan", null); + Reader reader = getReader(context, listener, "startRescan"); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -217,7 +205,7 @@ public class RESTMusicService implements MusicService { // Now check if still running boolean done = false; while(!done) { - reader = getReader(context, null, "scanstatus", null); + reader = getReader(context, null, "scanstatus"); try { boolean running = new ScanStatusParser(context, getInstance(context)).parse(reader, listener); if(running) { @@ -244,7 +232,7 @@ public class RESTMusicService implements MusicService { parameterValues.add(musicFolderId); } - Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, getInstance(context)) ? "getArtists" : "getIndexes", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, getInstance(context)) ? "getArtists" : "getIndexes", parameterNames, parameterValues); try { return new IndexesParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -290,7 +278,7 @@ public class RESTMusicService implements MusicService { } private MusicDirectory getMusicDirectoryImpl(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getMusicDirectory", null, "id", id); + Reader reader = getReader(context, progressListener, "getMusicDirectory", "id", id); try { return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener); } finally { @@ -300,7 +288,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getArtist", null, "id", id); + Reader reader = getReader(context, progressListener, "getArtist", "id", id); try { return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener); } finally { @@ -310,7 +298,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getAlbum", null, "id", id); + Reader reader = getReader(context, progressListener, "getAlbum", "id", id); try { return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener); } finally { @@ -334,7 +322,7 @@ public class RESTMusicService implements MusicService { private SearchResult searchOld(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception { List<String> parameterNames = Arrays.asList("any", "songCount"); List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getSongCount()); - Reader reader = getReader(context, progressListener, "search", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "search", parameterNames, parameterValues); try { return new SearchResultParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -366,7 +354,7 @@ public class RESTMusicService implements MusicService { method = "search2"; } } - Reader reader = getReader(context, progressListener, method, null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, method, parameterNames, parameterValues); try { return new SearchResult2Parser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -376,10 +364,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception { - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_PLAYLIST); - - Reader reader = getReader(context, progressListener, "getPlaylist", params, "id", id); + Reader reader = getReader(context, progressListener, "getPlaylist", "id", id, SOCKET_READ_TIMEOUT_GET_PLAYLIST); try { return new PlaylistParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -389,7 +374,7 @@ public class RESTMusicService implements MusicService { @Override public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getPlaylists", null); + Reader reader = getReader(context, progressListener, "getPlaylists"); try { return new PlaylistsParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -415,7 +400,7 @@ public class RESTMusicService implements MusicService { parameterValues.add(getOfflineSongId(entry.getId(), context, progressListener)); } - Reader reader = getReader(context, progressListener, "createPlaylist", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "createPlaylist", parameterNames, parameterValues); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -425,7 +410,7 @@ public class RESTMusicService implements MusicService { @Override public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "deletePlaylist", null, "id", id); + Reader reader = getReader(context, progressListener, "deletePlaylist", "id", id); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -444,7 +429,7 @@ public class RESTMusicService implements MusicService { names.add("songIdToAdd"); values.add(getOfflineSongId(song.getId(), context, progressListener)); } - Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values); + Reader reader = getReader(context, progressListener, "updatePlaylist", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -463,7 +448,7 @@ public class RESTMusicService implements MusicService { names.add("songIndexToRemove"); values.add(song); } - Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values); + Reader reader = getReader(context, progressListener, "updatePlaylist", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -488,7 +473,7 @@ public class RESTMusicService implements MusicService { names.add("songIndexToRemove"); values.add(i); } - Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values); + Reader reader = getReader(context, progressListener, "updatePlaylist", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -499,7 +484,7 @@ public class RESTMusicService implements MusicService { @Override public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.8", "Updating playlists is not supported."); - Reader reader = getReader(context, progressListener, "updatePlaylist", null, Arrays.asList("playlistId", "name", "comment", "public"), Arrays.<Object>asList(id, name, comment, pub)); + Reader reader = getReader(context, progressListener, "updatePlaylist", Arrays.asList("playlistId", "name", "comment", "public"), Arrays.<Object>asList(id, name, comment, pub)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -509,7 +494,7 @@ public class RESTMusicService implements MusicService { @Override public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getLyrics", null, Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title)); + Reader reader = getReader(context, progressListener, "getLyrics", Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title)); try { return new LyricsParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -528,10 +513,10 @@ public class RESTMusicService implements MusicService { Reader reader; if(time > 0){ checkServerVersion(context, "1.8", "Scrobbling with a time not supported."); - reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission", "time"), Arrays.<Object>asList(id, submission, time)); + reader = getReader(context, progressListener, "scrobble", Arrays.asList("id", "submission", "time"), Arrays.<Object>asList(id, submission, time)); } else - reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission)); + reader = getReader(context, progressListener, "scrobble", Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -572,7 +557,7 @@ public class RESTMusicService implements MusicService { method = "getAlbumList"; } - Reader reader = getReader(context, progressListener, method, null, names, values, true); + Reader reader = getReader(context, progressListener, method, names, values, true); try { return new EntryListParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -638,7 +623,7 @@ public class RESTMusicService implements MusicService { method = "getAlbumList"; } - Reader reader = getReader(context, progressListener, method, null, names, values, true); + Reader reader = getReader(context, progressListener, method, names, values, true); try { return new EntryListParser(context, instance).parse(reader, progressListener); } finally { @@ -674,7 +659,7 @@ public class RESTMusicService implements MusicService { method = "getNewaddedSongs"; } - Reader reader = getReader(context, progressListener, method, null, names, values, true); + Reader reader = getReader(context, progressListener, method, names, values, true); try { return new EntryListParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -713,7 +698,7 @@ public class RESTMusicService implements MusicService { } } - Reader reader = getReader(context, progressListener, method, null, names, values); + Reader reader = getReader(context, progressListener, method, names, values); try { return new RandomSongsParser(context, instance).parse(reader, progressListener); } finally { @@ -747,7 +732,7 @@ public class RESTMusicService implements MusicService { method = "getStarred"; } - Reader reader = getReader(context, progressListener, method, null, names, values, true); + Reader reader = getReader(context, progressListener, method, names, values, true); try { return new StarredListParser(context, instance).parse(reader, progressListener); } finally { @@ -757,10 +742,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getRandomSongs(int size, String musicFolderId, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception { - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS); - - List<String> names = new ArrayList<String>(); + List<String> names = new ArrayList<String>(); List<Object> values = new ArrayList<Object>(); names.add("size"); @@ -799,7 +781,7 @@ public class RESTMusicService implements MusicService { values.add(endYear); } - Reader reader = getReader(context, progressListener, "getRandomSongs", params, names, values); + Reader reader = getReader(context, progressListener, "getRandomSongs", names, values); try { return new RandomSongsParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -829,87 +811,24 @@ public class RESTMusicService implements MusicService { @Override public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { - // Synchronize on the entry so that we don't download concurrently for the same song. synchronized (entry) { + String url = getRestUrl(context, "getCoverArt"); + List<String> parameterNames = Arrays.asList("id"); + List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt()); - // Use cached file, if existing. - Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); - if (bitmap != null) { - return bitmap; - } - - String url = getRestUrl(context, "getCoverArt"); - - InputStream in = null; - try { - List<String> parameterNames = Arrays.asList("id"); - List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt()); - HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener, task); - - in = entity.getContent(); - Header contentEncoding = entity.getContentEncoding(); - if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { - in = new GZIPInputStream(in); - } - - // If content type is XML, an error occured. Get it. - String contentType = Util.getContentType(entity); - if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { - new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); - return null; // Never reached. - } - - byte[] bytes = Util.toByteArray(in); - - // Handle case where partial was downloaded before being cancelled - if(task != null && task.isCancelled()) { - return null; - } - - OutputStream out = null; - try { - out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry)); - out.write(bytes); - } finally { - Util.close(out); - } - - // Size == 0 -> only want to download - if(size == 0) { - return null; - } else { - return FileUtil.getSampledBitmap(bytes, size); - } - } finally { - Util.close(in); - } + return getBitmapFromUrl(context, url, parameterNames, parameterValues, size, FileUtil.getAlbumArtFile(context, entry), true, progressListener, task); } } @Override - public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { - + public HttpURLConnection getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception { String url = getRestUrl(context, "stream"); - - // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is - // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server. - // In that case, the server uses a long time before sending any data, causing the client to time out. - HttpParams params = new BasicHttpParams(); - int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE); - HttpConnectionParams.setSoTimeout(params, timeout); - - // Add "Range" header if offset is given. - List<Header> headers = new ArrayList<Header>(); - if (offset > 0) { - headers.add(new BasicHeader("Range", "bytes=" + offset + "-")); - } - List<String> parameterNames = new ArrayList<String>(); parameterNames.add("id"); parameterNames.add("maxBitRate"); - List<Object> parameterValues = new ArrayList<Object>(); + List<Object> parameterValues = new ArrayList<>(); parameterValues.add(song.getId()); parameterValues.add(maxBitrate); @@ -929,24 +848,34 @@ public class RESTMusicService implements MusicService { parameterValues.add("raw"); } } - HttpResponse response = getResponseForURL(context, url, params, parameterNames, parameterValues, headers, null, task, false); - // If content type is XML, an error occurred. Get it. - String contentType = Util.getContentType(response.getEntity()); - if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { - InputStream in = response.getEntity().getContent(); - Header contentEncoding = response.getEntity().getContentEncoding(); - if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { - in = new GZIPInputStream(in); + // TODO: Use task + // TODO: Add range header + // Add "Range" header if offset is given. + /*List<Header> headers = new ArrayList<Header>(); + if (offset > 0) { + headers.add(new BasicHeader("Range", "bytes=" + offset + "-")); + }*/ + + // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is + // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server. + // In that case, the server uses a long time before sending any data, causing the client to time out. + int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE); + HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, timeout); + + // If content type is XML, an error occurred. Get it. + String contentType = connection.getContentType(); + if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { + InputStream in = getInputStreamFromConnection(connection); + + try { + new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); + } finally { + Util.close(in); } - try { - new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); - } finally { - Util.close(in); - } - } + } - return response; + return connection; } @Override @@ -1057,7 +986,7 @@ public class RESTMusicService implements MusicService { private RemoteStatus executeJukeboxCommand(Context context, ProgressListener progressListener, List<String> parameterNames, List<Object> parameterValues) throws Exception { checkServerVersion(context, "1.7", "Jukebox not supported."); - Reader reader = getReader(context, progressListener, "jukeboxControl", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "jukeboxControl", parameterNames, parameterValues); try { return new JukeboxStatusParser(context, getInstance(context)).parse(reader); } finally { @@ -1096,7 +1025,7 @@ public class RESTMusicService implements MusicService { } } - Reader reader = getReader(context, progressListener, starred ? "star" : "unstar", null, names, values); + Reader reader = getReader(context, progressListener, starred ? "star" : "unstar", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1108,7 +1037,7 @@ public class RESTMusicService implements MusicService { public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.6", "Shares not supported."); - Reader reader = getReader(context, progressListener, "getShares", null); + Reader reader = getReader(context, progressListener, "getShares"); try { return new ShareParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1136,7 +1065,7 @@ public class RESTMusicService implements MusicService { parameterValues.add(expires); } - Reader reader = getReader(context, progressListener, "createShare", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "createShare", parameterNames, parameterValues); try { return new ShareParser(context, getInstance(context)).parse(reader, progressListener); } @@ -1149,16 +1078,13 @@ public class RESTMusicService implements MusicService { public void deleteShare(String id, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.6", "Shares 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("id"); parameterValues.add(id); - Reader reader = getReader(context, progressListener, "deleteShare", params, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "deleteShare", parameterNames, parameterValues); try { new ErrorParser(context, getInstance(context)).parse(reader); @@ -1172,9 +1098,6 @@ public class RESTMusicService implements MusicService { public void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.6", "Updating share 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>(); @@ -1189,7 +1112,7 @@ public class RESTMusicService implements MusicService { parameterNames.add("expires"); parameterValues.add(expires); - Reader reader = getReader(context, progressListener, "updateShare", params, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "updateShare", parameterNames, parameterValues); try { new ErrorParser(context, getInstance(context)).parse(reader); } @@ -1202,16 +1125,13 @@ public class RESTMusicService implements MusicService { public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.2", "Chat 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("since"); parameterValues.add(since); - Reader reader = getReader(context, progressListener, "getChatMessages", params, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "getChatMessages", parameterNames, parameterValues); try { return new ChatMessageParser(context, getInstance(context)).parse(reader, progressListener); @@ -1224,16 +1144,13 @@ public class RESTMusicService implements MusicService { public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.2", "Chat 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("message"); parameterValues.add(message); - Reader reader = getReader(context, progressListener, "addChatMessage", params, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "addChatMessage", parameterNames, parameterValues); try { new ErrorParser(context, getInstance(context)).parse(reader); @@ -1246,7 +1163,7 @@ public class RESTMusicService implements MusicService { public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Genres not supported."); - Reader reader = getReader(context, progressListener, "getGenres", null); + Reader reader = getReader(context, progressListener, "getGenres"); try { return new GenreParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1258,9 +1175,6 @@ public class RESTMusicService implements MusicService { 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>(); @@ -1281,7 +1195,7 @@ public class RESTMusicService implements MusicService { } } - Reader reader = getReader(context, progressListener, "getSongsByGenre", params, parameterNames, parameterValues, true); + Reader reader = getReader(context, progressListener, "getSongsByGenre", parameterNames, parameterValues, true); try { return new RandomSongsParser(context, instance).parse(reader, progressListener); } finally { @@ -1300,7 +1214,7 @@ public class RESTMusicService implements MusicService { parameterValues.add(size); String method = ServerInfo.isMadsonic(context, getInstance(context)) ? "getTopTrackSongs" : "getTopSongs"; - Reader reader = getReader(context, progressListener, method, null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, method, parameterNames, parameterValues); try { return new TopSongsParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1312,7 +1226,7 @@ public class RESTMusicService implements MusicService { public List<PodcastChannel> getPodcastChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.6", "Podcasts not supported."); - Reader reader = getReader(context, progressListener, "getPodcasts", null, Arrays.asList("includeEpisodes"), Arrays.<Object>asList("false")); + Reader reader = getReader(context, progressListener, "getPodcasts", Arrays.asList("includeEpisodes"), Arrays.<Object>asList("false")); try { List<PodcastChannel> channels = new PodcastChannelParser(context, getInstance(context)).parse(reader, progressListener); @@ -1334,7 +1248,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getPodcastEpisodes(boolean refresh, String id, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getPodcasts", null, Arrays.asList("id"), Arrays.<Object>asList(id)); + Reader reader = getReader(context, progressListener, "getPodcasts", Arrays.asList("id"), Arrays.<Object>asList(id)); try { return new PodcastEntryParser(context, getInstance(context)).parse(id, reader, progressListener); } finally { @@ -1344,7 +1258,7 @@ public class RESTMusicService implements MusicService { @Override public MusicDirectory getNewestPodcastEpisodes(boolean refresh, Context context, ProgressListener progressListener, int count) throws Exception { - Reader reader = getReader(context, progressListener, "getNewestPodcasts", null, Arrays.asList("count"), Arrays.<Object>asList(count), true); + Reader reader = getReader(context, progressListener, "getNewestPodcasts", Arrays.asList("count"), Arrays.<Object>asList(count), true); try { return new PodcastEntryParser(context, getInstance(context)).parse(null, reader, progressListener); @@ -1357,7 +1271,7 @@ public class RESTMusicService implements MusicService { public void refreshPodcasts(Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Refresh podcasts not supported."); - Reader reader = getReader(context, progressListener, "refreshPodcasts", null); + Reader reader = getReader(context, progressListener, "refreshPodcasts"); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1369,7 +1283,7 @@ public class RESTMusicService implements MusicService { public void createPodcastChannel(String url, Context context, ProgressListener progressListener) throws Exception{ checkServerVersion(context, "1.9", "Creating podcasts not supported."); - Reader reader = getReader(context, progressListener, "createPodcastChannel", null, "url", url); + Reader reader = getReader(context, progressListener, "createPodcastChannel", "url", url); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1381,7 +1295,7 @@ public class RESTMusicService implements MusicService { public void deletePodcastChannel(String id, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Deleting podcasts not supported."); - Reader reader = getReader(context, progressListener, "deletePodcastChannel", null, "id", id); + Reader reader = getReader(context, progressListener, "deletePodcastChannel", "id", id); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1393,7 +1307,7 @@ public class RESTMusicService implements MusicService { public void downloadPodcastEpisode(String id, Context context, ProgressListener progressListener) throws Exception{ checkServerVersion(context, "1.9", "Downloading podcasts not supported."); - Reader reader = getReader(context, progressListener, "downloadPodcastEpisode", null, "id", id); + Reader reader = getReader(context, progressListener, "downloadPodcastEpisode", "id", id); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1405,7 +1319,7 @@ public class RESTMusicService implements MusicService { public void deletePodcastEpisode(String id, String parent, ProgressListener progressListener, Context context) throws Exception{ checkServerVersion(context, "1.9", "Deleting podcasts not supported."); - Reader reader = getReader(context, progressListener, "deletePodcastEpisode", null, "id", id); + Reader reader = getReader(context, progressListener, "deletePodcastEpisode", "id", id); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1417,7 +1331,7 @@ public class RESTMusicService implements MusicService { public void setRating(MusicDirectory.Entry entry, int rating, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.6", "Setting ratings not supported."); - Reader reader = getReader(context, progressListener, "setRating", null, Arrays.asList("id", "rating"), Arrays.<Object>asList(entry.getId(), rating)); + Reader reader = getReader(context, progressListener, "setRating", Arrays.asList("id", "rating"), Arrays.<Object>asList(entry.getId(), rating)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1429,7 +1343,7 @@ public class RESTMusicService implements MusicService { public MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Bookmarks not supported."); - Reader reader = getReader(context, progressListener, "getBookmarks", null); + Reader reader = getReader(context, progressListener, "getBookmarks"); try { return new BookmarkParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1441,7 +1355,7 @@ public class RESTMusicService implements MusicService { public void createBookmark(MusicDirectory.Entry entry, int position, String comment, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Creating bookmarks not supported."); - Reader reader = getReader(context, progressListener, "createBookmark", null, Arrays.asList("id", "position", "comment"), Arrays.<Object>asList(entry.getId(), position, comment)); + Reader reader = getReader(context, progressListener, "createBookmark", Arrays.asList("id", "position", "comment"), Arrays.<Object>asList(entry.getId(), position, comment)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1453,7 +1367,7 @@ public class RESTMusicService implements MusicService { public void deleteBookmark(MusicDirectory.Entry entry, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", "Deleting bookmarks not supported."); - Reader reader = getReader(context, progressListener, "deleteBookmark", null, Arrays.asList("id"), Arrays.<Object>asList(entry.getId())); + Reader reader = getReader(context, progressListener, "deleteBookmark", Arrays.asList("id"), Arrays.<Object>asList(entry.getId())); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1463,7 +1377,7 @@ public class RESTMusicService implements MusicService { @Override public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getUser", null, Arrays.asList("username"), Arrays.<Object>asList(username)); + Reader reader = getReader(context, progressListener, "getUser", Arrays.asList("username"), Arrays.<Object>asList(username)); try { List<User> users = new UserParser(context, getInstance(context)).parse(reader, progressListener); if(users.size() > 0) { @@ -1481,7 +1395,7 @@ public class RESTMusicService implements MusicService { public List<User> getUsers(boolean refresh, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.8", "Getting user list is not supported"); - Reader reader = getReader(context, progressListener, "getUsers", null); + Reader reader = getReader(context, progressListener, "getUsers"); try { return new UserParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1515,7 +1429,7 @@ public class RESTMusicService implements MusicService { } } - Reader reader = getReader(context, progressListener, "createUser", null, names, values); + Reader reader = getReader(context, progressListener, "createUser", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1549,7 +1463,7 @@ public class RESTMusicService implements MusicService { } } - Reader reader = getReader(context, progressListener, "updateUser", null, names, values); + Reader reader = getReader(context, progressListener, "updateUser", names, values); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1559,7 +1473,7 @@ public class RESTMusicService implements MusicService { @Override public void deleteUser(String username, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "deleteUser", null, Arrays.asList("username"), Arrays.<Object>asList(username)); + Reader reader = getReader(context, progressListener, "deleteUser", Arrays.asList("username"), Arrays.<Object>asList(username)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1569,7 +1483,7 @@ public class RESTMusicService implements MusicService { @Override public void changeEmail(String username, String email, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "updateUser", null, Arrays.asList("username", "email"), Arrays.<Object>asList(username, email)); + Reader reader = getReader(context, progressListener, "updateUser", Arrays.asList("username", "email"), Arrays.<Object>asList(username, email)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1579,7 +1493,7 @@ public class RESTMusicService implements MusicService { @Override public void changePassword(String username, String password, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "changePassword", null, Arrays.asList("username", "password"), Arrays.<Object>asList(username, password)); + Reader reader = getReader(context, progressListener, "changePassword", Arrays.asList("username", "password"), Arrays.<Object>asList(username, password)); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1597,55 +1511,11 @@ public class RESTMusicService implements MusicService { // Synchronize on the username so that we don't download concurrently for // the same user. synchronized (username) { - // Use cached file, if existing. - Bitmap bitmap = FileUtil.getAvatarBitmap(context, username, size); - if(bitmap != null) { - return bitmap; - } - String url = Util.getRestUrl(context, "getAvatar"); - InputStream in = null; - try - { - List<String> parameterNames; - List<Object> parameterValues; - - parameterNames = Collections.singletonList("username"); - parameterValues = Arrays.<Object>asList(username); - - HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener, task); - in = entity.getContent(); - Header contentEncoding = entity.getContentEncoding(); - if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { - in = new GZIPInputStream(in); - } - - // If content type is XML, an error occurred. Get it. - String contentType = Util.getContentType(entity); - if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { - new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); - return null; // Never reached. - } + List<String> parameterNames = Collections.singletonList("username"); + List<Object> parameterValues = Arrays.<Object>asList(username); - byte[] bytes = Util.toByteArray(in); - if(task != null && task.isCancelled()) { - // Handle case where partial is downloaded and cancelled - return null; - } - - OutputStream out = null; - try { - out = new FileOutputStream(FileUtil.getAvatarFile(context, username)); - out.write(bytes); - } finally { - Util.close(out); - } - - return FileUtil.getSampledBitmap(bytes, size, false); - } - finally { - Util.close(in); - } + return getBitmapFromUrl(context, url, parameterNames, parameterValues, size, FileUtil.getAvatarFile(context, username), false, progressListener, task); } } @@ -1665,7 +1535,7 @@ public class RESTMusicService implements MusicService { method = "getArtistInfo"; } - Reader reader = getReader(context, progressListener, method, null, Arrays.asList("id", "includeNotPresent"), Arrays.<Object>asList(id, "true")); + Reader reader = getReader(context, progressListener, method, Arrays.asList("id", "includeNotPresent"), Arrays.<Object>asList(id, "true")); try { return new ArtistInfoParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1677,53 +1547,13 @@ public class RESTMusicService implements MusicService { public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { // Synchronize on the url so that we don't download concurrently synchronized (url) { - // Use cached file, if existing. - Bitmap bitmap = FileUtil.getMiscBitmap(context, url, size); - if(bitmap != null) { - return bitmap; - } - - InputStream in = null; - try { - HttpEntity entity = getEntityForURL(context, url, null, null, null, progressListener, task); - in = entity.getContent(); - Header contentEncoding = entity.getContentEncoding(); - if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { - in = new GZIPInputStream(in); - } - - // If content type is XML, an error occurred. Get it. - String contentType = Util.getContentType(entity); - if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { - new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); - return null; // Never reached. - } - - byte[] bytes = Util.toByteArray(in); - if(task != null && task.isCancelled()) { - // Handle case where partial is downloaded and cancelled - return null; - } - - OutputStream out = null; - try { - out = new FileOutputStream(FileUtil.getMiscFile(context, url)); - out.write(bytes); - } finally { - Util.close(out); - } - - return FileUtil.getSampledBitmap(bytes, size, false); - } - finally { - Util.close(in); - } + return getBitmapFromUrl(context, url, null, null, size, FileUtil.getMiscFile(context, url), false, progressListener, task); } } @Override public MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getVideos", null, true); + Reader reader = getReader(context, progressListener, "getVideos"); try { return new VideosParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1747,7 +1577,7 @@ public class RESTMusicService implements MusicService { parameterNames.add("position"); parameterValues.add(position); - Reader reader = getReader(context, progressListener, "savePlayQueue", null, parameterNames, parameterValues); + Reader reader = getReader(context, progressListener, "savePlayQueue", parameterNames, parameterValues); try { new ErrorParser(context, getInstance(context)).parse(reader); } finally { @@ -1757,7 +1587,7 @@ public class RESTMusicService implements MusicService { @Override public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getPlayQueue", null); + Reader reader = getReader(context, progressListener, "getPlayQueue"); try { return new PlayQueueParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1769,7 +1599,7 @@ public class RESTMusicService implements MusicService { public List<InternetRadioStation> getInternetRadioStations(boolean refresh, Context context, ProgressListener progressListener) throws Exception { checkServerVersion(context, "1.9", null); - Reader reader = getReader(context, progressListener, "getInternetRadioStations", null); + Reader reader = getReader(context, progressListener, "getInternetRadioStations"); try { return new InternetRadioStationParser(context, getInstance(context)).parse(reader, progressListener); } finally { @@ -1889,210 +1719,196 @@ public class RESTMusicService implements MusicService { this.instance = instance; } - private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception { - return getReader(context, progressListener, method, requestParams, false); + protected Bitmap getBitmapFromUrl(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int size, File saveToFile, boolean allowUnscaled, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { + InputStream in = null; + try { + HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, progressListener, true); + in = getInputStreamFromConnection(connection); + + String contentType = connection.getContentType(); + if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) { + new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8)); + } + + byte[] bytes = Util.toByteArray(in); + + // Handle case where partial was downloaded before being cancelled + if(task != null && task.isCancelled()) { + return null; + } + + OutputStream out = null; + try { + out = new FileOutputStream(saveToFile); + out.write(bytes); + } finally { + Util.close(out); + } + + // Size == 0 -> only want to download + if(size == 0) { + return null; + } else { + return FileUtil.getSampledBitmap(bytes, size, allowUnscaled); + } + } finally { + Util.close(in); + } } - private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams, boolean throwsError) throws Exception { - return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList(), throwsError); - } - private Reader getReader(Context context, ProgressListener progressListener, String method, - HttpParams requestParams, String parameterName, Object parameterValue) throws Exception { - return getReader(context, progressListener, method, requestParams, Arrays.asList(parameterName), Arrays.<Object>asList(parameterValue)); - } + // Helper classes to get a reader for the request + private Reader getReader(Context context, ProgressListener progressListener, String method) throws Exception { + return getReader(context, progressListener, method, (List<String>)null, null); + } - private Reader getReader(Context context, ProgressListener progressListener, String method, - HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues) throws Exception { - return getReader(context, progressListener, method, requestParams, parameterNames, parameterValues, false); + private Reader getReader(Context context, ProgressListener progressListener, String method, String parameterName, Object parameterValue) throws Exception { + return getReader(context, progressListener, method, parameterName, parameterValue, 0); } - private Reader getReader(Context context, ProgressListener progressListener, String method, - HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues, boolean throwErrors) throws Exception { + private Reader getReader(Context context, ProgressListener progressListener, String method, String parameterName, Object parameterValue, int minNetworkTimeout) throws Exception { + return getReader(context, progressListener, method, Arrays.asList(parameterName), Arrays.asList(parameterValue), minNetworkTimeout); + } + private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues) throws Exception { + return getReader(context, progressListener, method, parameterNames, parameterValues, 0); + } + private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout) throws Exception { + return getReader(context, progressListener, method, parameterNames, parameterValues, minNetworkTimeout, false); + } + private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, boolean throwErrors) throws Exception { + return getReader(context, progressListener, method, parameterNames, parameterValues, 0, throwErrors); + } + private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, boolean throwErrors) throws Exception { if (progressListener != null) { progressListener.updateProgress(R.string.service_connecting); } String url = getRestUrl(context, method); - return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, throwErrors); + return getReaderForURL(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwErrors); } - private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener) throws Exception { - return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, true); + private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener) throws Exception { + return getReaderForURL(context, url, parameterNames, parameterValues, progressListener, true); } - private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception { - HttpEntity entity = getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, throwErrors); - if (entity == null) { - throw new RuntimeException("No entity received for URL " + url); - } + private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception { + return getReaderForURL(context, url, parameterNames, parameterValues, 0, progressListener, throwErrors); + } + private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception { + InputStream in = getInputStream(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwErrors); + return new InputStreamReader(in, Constants.UTF_8); + } - InputStream in = entity.getContent(); - Header contentEncoding = entity.getContentEncoding(); - if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) { + // Helper classes to open a connection to a server + private InputStream getInputStream(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwsErrors) throws Exception { + return getInputStream(context, url, parameterNames, parameterValues, 0, progressListener, throwsErrors); + } + private InputStream getInputStream(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwsErrors) throws Exception { + HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwsErrors); + return getInputStreamFromConnection(connection); + } + private InputStream getInputStreamFromConnection(HttpURLConnection connection) throws Exception { + InputStream in = connection.getInputStream(); + if("gzip".equals(connection.getContentEncoding())) { in = new GZIPInputStream(in); } - return new InputStreamReader(in, Constants.UTF_8); - } - private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception { + return in; + } + + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues) throws Exception { + return getConnection(context, url, parameterNames, parameterValues, null, true); + } + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout) throws Exception { + return getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, null, true); + } + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception { + return getConnection(context, url, parameterNames, parameterValues, 0, progressListener, throwErrors); + } + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception { + if(throwErrors) { + return getConnectionDirect(context, url, parameterNames, parameterValues, minNetworkTimeout); + } else { + return getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, HTTP_REQUEST_MAX_ATTEMPTS, 0); + } + } - return getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, null, throwErrors); + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception { + return getConnection(context, url, parameterNames, parameterValues, 0, progressListener, retriesLeft, attempts); } + private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception { + try { + return getConnectionDirect(context, url, parameterNames, parameterValues, minNetworkTimeout); + } catch (IOException x) { + if(retriesLeft > 0) { + if (progressListener != null) { + String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1); + progressListener.updateProgress(msg); + } + + Log.w(TAG, "Got IOException " + x + " (" + attempts + "), will retry"); + Thread.sleep(2000L); - private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener, SilentBackgroundTask task) throws Exception { - return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, task, false).getEntity(); + // TODO: Increase timeouts on failures + // TODO: Detect task cancellation + return getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, retriesLeft--, attempts + 1); + } else { + throw x; + } + } } - private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener, SilentBackgroundTask task, boolean throwsError) throws Exception { - return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, task, throwsError).getEntity(); - } - private HttpResponse getResponseForURL(Context context, String url, HttpParams requestParams, - List<String> parameterNames, List<Object> parameterValues, - List<Header> headers, ProgressListener progressListener, SilentBackgroundTask task, boolean throwsErrors) throws Exception { - // If not too many parameters, extract them to the URL rather than relying on the HTTP POST request being - // received intact. Remember, HTTP POST requests are converted to GET requests during HTTP redirects, thus - // loosing its entity. - if (parameterNames != null && parameterNames.size() < 10) { - StringBuilder builder = new StringBuilder(url); - for (int i = 0; i < parameterNames.size(); i++) { - builder.append("&").append(parameterNames.get(i)).append("="); + private HttpURLConnection getConnectionDirect(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout) throws Exception { + // Add params to query + if (parameterNames != null) { + StringBuilder builder = new StringBuilder(url); + for (int i = 0; i < parameterNames.size(); i++) { + builder.append("&").append(parameterNames.get(i)).append("="); String part = URLEncoder.encode(String.valueOf(parameterValues.get(i)), "UTF-8"); part = part.replaceAll("\\%27", "'"); - builder.append(part); - } - url = builder.toString(); - parameterNames = null; - parameterValues = null; - } + builder.append(part); + } + url = builder.toString(); + } - String rewrittenUrl = rewriteUrlWithRedirect(context, url); - return executeWithRetry(context, rewrittenUrl, url, requestParams, parameterNames, parameterValues, headers, progressListener, task, throwsErrors); - } + // Rewrite url based on redirects + String rewrittenUrl = rewriteUrlWithRedirect(context, url); + if(rewrittenUrl.indexOf("scanstatus") == -1) { + Log.i(TAG, stripUrlInfo(rewrittenUrl)); + } - private HttpResponse executeWithRetry(final Context context, String url, String originalUrl, HttpParams requestParams, - List<String> parameterNames, List<Object> parameterValues, - List<Header> headers, ProgressListener progressListener, SilentBackgroundTask task, boolean throwErrors) throws Exception { - // Strip out sensitive information from log - if(url.indexOf("scanstatus") == -1) { - Log.i(TAG, stripUrlInfo(url)); + return getConnectionDirect(context, rewrittenUrl, minNetworkTimeout); + } + + private HttpURLConnection getConnectionDirect(Context context, String url, int minNetworkTimeout) throws Exception { + // Connect and add headers + URL urlObj = new URL(url); + HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection(); + if(url.indexOf("getCoverArt") == -1 && url.indexOf("stream") == -1 && url.indexOf("getAvatar") == -1) { + connection.addRequestProperty("Accept-Encoding", "gzip"); } + connection.addRequestProperty("User-Agent", Constants.REST_CLIENT_ID); + // Set timeout SharedPreferences prefs = Util.getPreferences(context); - int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, "15000")); - HttpParams newParams = httpClient.getParams(); - HttpConnectionParams.setSoTimeout(newParams, networkTimeout); - httpClient.setParams(newParams); - - final AtomicReference<Boolean> isCancelled = new AtomicReference<Boolean>(false); - int attempts = 0; - while (true) { - attempts++; - HttpContext httpContext = new BasicHttpContext(); - final HttpRequestBase request = (url.indexOf("rest") == -1) ? new HttpGet(url) : new HttpPost(url); - - if (task != null) { - // Attempt to abort the HTTP request if the task is cancelled. - task.setOnCancelListener(new BackgroundTask.OnCancelListener() { - @Override - public void onCancel() { - try { - isCancelled.set(true); - if(Thread.currentThread() == Looper.getMainLooper().getThread()) { - new SilentBackgroundTask<Void>(context) { - @Override - protected Void doInBackground() throws Throwable { - request.abort(); - return null; - } - }.execute(); - } else { - request.abort(); - } - } catch(Exception e) { - Log.e(TAG, "Failed to stop http task", e); - } - } - }); - } - - if (parameterNames != null && request instanceof HttpPost) { - List<NameValuePair> params = new ArrayList<NameValuePair>(); - for (int i = 0; i < parameterNames.size(); i++) { - params.add(new BasicNameValuePair(parameterNames.get(i), String.valueOf(parameterValues.get(i)))); - } - ((HttpPost) request).setEntity(new UrlEncodedFormEntity(params, Constants.UTF_8)); - } - - if (requestParams != null) { - request.setParams(requestParams); - } - - if (headers != null) { - for (Header header : headers) { - request.addHeader(header); - } - } - if(url.indexOf("getCoverArt") == -1 && url.indexOf("stream") == -1 && url.indexOf("getAvatar") == -1) { - request.addHeader("Accept-Encoding", "gzip"); - } - request.addHeader("User-Agent", Constants.REST_CLIENT_ID); - - // Set credentials to get through apache proxies that require authentication. - int instance = getInstance(context); - String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null); - String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null); - httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), - new UsernamePasswordCredentials(username, password)); - - try { - HttpResponse response = httpClient.execute(request, httpContext); - detectRedirect(originalUrl, context, httpContext); - return response; - } catch (IOException x) { - request.abort(); - if (attempts >= HTTP_REQUEST_MAX_ATTEMPTS || isCancelled.get() || throwErrors) { - throw x; - } - if (progressListener != null) { - String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1); - progressListener.updateProgress(msg); - } - Log.w(TAG, "Got IOException " + x + " (" + attempts + "), will retry"); - increaseTimeouts(requestParams); - Thread.sleep(2000L); - } - } - } + int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_CONNECT_TIMEOUT + "")); + connection.setConnectTimeout(Math.max(minNetworkTimeout, networkTimeout)); + connection.setReadTimeout(SOCKET_READ_TIMEOUT_DEFAULT); - private void increaseTimeouts(HttpParams requestParams) { - if (requestParams != null) { - int connectTimeout = HttpConnectionParams.getConnectionTimeout(requestParams); - if (connectTimeout != 0) { - HttpConnectionParams.setConnectionTimeout(requestParams, (int) (connectTimeout * 1.3F)); - } - int readTimeout = HttpConnectionParams.getSoTimeout(requestParams); - if (readTimeout != 0) { - HttpConnectionParams.setSoTimeout(requestParams, (int) (readTimeout * 1.5F)); - } - } - } - - private void detectRedirect(String originalUrl, Context context, HttpContext httpContext) throws Exception { - HttpUriRequest request = (HttpUriRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST); - HttpHost host = (HttpHost) httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST); - - // Sometimes the request doesn't contain the "http://host" part - String redirectedUrl; - if (request.getURI().getScheme() == null) { - redirectedUrl = host.toURI() + request.getURI(); - } else { - redirectedUrl = request.getURI().toString(); + if(connection instanceof HttpsURLConnection) { + HttpsURLConnection sslConnection = (HttpsURLConnection) connection; + sslConnection.setHostnameVerifier(selfSignedHostnameVerifier); } + detectRedirect(context, urlObj, connection); + return connection; + } + + private void detectRedirect(Context context, URL originalUrl, HttpURLConnection connection) throws Exception { + detectRedirect(context, originalUrl, connection.getURL()); + } + private void detectRedirect(Context context, URL originalUrl, URL redirectedUrl) throws Exception { + detectRedirect(context, originalUrl.toExternalForm(), redirectedUrl.toExternalForm()); + } + private void detectRedirect(Context context, String originalUrl, String redirectedUrl) throws Exception { if(redirectedUrl != null && "http://subsonic.org/pages/".equals(redirectedUrl)) { throw new Exception("Invalid url, redirects to http://subsonic.org/pages/"); } @@ -2109,7 +1925,7 @@ public class RESTMusicService implements MusicService { redirectionLastChecked = System.currentTimeMillis(); redirectionNetworkType = getCurrentNetworkType(context); } - } + } private String rewriteUrlWithRedirect(Context context, String url) { diff --git a/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java index afb05928..1b389f80 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java +++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java @@ -27,7 +27,6 @@ import java.io.Reader; * @author Sindre Mehus */ public class ErrorParser extends AbstractParser { - public ErrorParser(Context context, int instance) { super(context, instance); } @@ -45,5 +44,6 @@ public class ErrorParser extends AbstractParser { } while (eventType != XmlPullParser.END_DOCUMENT); validate(); + reader.close(); } }
\ No newline at end of file |