1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
|
16 |
|
|
17 |
|
|
18 |
|
|
19 |
|
package net.sourceforge.subsonic.androidapp.service; |
20 |
|
|
21 |
|
import java.io.FileOutputStream; |
22 |
|
import java.io.IOException; |
23 |
|
import java.io.InputStream; |
24 |
|
import java.io.InputStreamReader; |
25 |
|
import java.io.OutputStream; |
26 |
|
import java.io.Reader; |
27 |
|
import java.net.URLEncoder; |
28 |
|
import java.util.ArrayList; |
29 |
|
import java.util.Arrays; |
30 |
|
import java.util.Collections; |
31 |
|
import java.util.LinkedList; |
32 |
|
import java.util.List; |
33 |
|
import java.util.concurrent.atomic.AtomicReference; |
34 |
|
|
35 |
|
import org.apache.http.Header; |
36 |
|
import org.apache.http.HttpEntity; |
37 |
|
import org.apache.http.HttpHost; |
38 |
|
import org.apache.http.HttpResponse; |
39 |
|
import org.apache.http.NameValuePair; |
40 |
|
import org.apache.http.auth.AuthScope; |
41 |
|
import org.apache.http.auth.UsernamePasswordCredentials; |
42 |
|
import org.apache.http.client.entity.UrlEncodedFormEntity; |
43 |
|
import org.apache.http.client.methods.HttpPost; |
44 |
|
import org.apache.http.client.methods.HttpUriRequest; |
45 |
|
import org.apache.http.conn.params.ConnManagerParams; |
46 |
|
import org.apache.http.conn.params.ConnPerRouteBean; |
47 |
|
import org.apache.http.conn.scheme.PlainSocketFactory; |
48 |
|
import org.apache.http.conn.scheme.Scheme; |
49 |
|
import org.apache.http.conn.scheme.SchemeRegistry; |
50 |
|
import org.apache.http.conn.scheme.SocketFactory; |
51 |
|
import org.apache.http.impl.client.DefaultHttpClient; |
52 |
|
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; |
53 |
|
import org.apache.http.message.BasicHeader; |
54 |
|
import org.apache.http.message.BasicNameValuePair; |
55 |
|
import org.apache.http.params.BasicHttpParams; |
56 |
|
import org.apache.http.params.HttpConnectionParams; |
57 |
|
import org.apache.http.params.HttpParams; |
58 |
|
import org.apache.http.protocol.BasicHttpContext; |
59 |
|
import org.apache.http.protocol.ExecutionContext; |
60 |
|
import org.apache.http.protocol.HttpContext; |
61 |
|
|
62 |
|
import android.content.Context; |
63 |
|
import android.content.SharedPreferences; |
64 |
|
import android.content.pm.PackageInfo; |
65 |
|
import android.graphics.Bitmap; |
66 |
|
import android.graphics.BitmapFactory; |
67 |
|
import android.net.ConnectivityManager; |
68 |
|
import android.net.NetworkInfo; |
69 |
|
import android.util.Log; |
70 |
|
import net.sourceforge.subsonic.androidapp.R; |
71 |
|
import net.sourceforge.subsonic.androidapp.domain.Indexes; |
72 |
|
import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; |
73 |
|
import net.sourceforge.subsonic.androidapp.domain.Lyrics; |
74 |
|
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; |
75 |
|
import net.sourceforge.subsonic.androidapp.domain.MusicFolder; |
76 |
|
import net.sourceforge.subsonic.androidapp.domain.Playlist; |
77 |
|
import net.sourceforge.subsonic.androidapp.domain.SearchCritera; |
78 |
|
import net.sourceforge.subsonic.androidapp.domain.SearchResult; |
79 |
|
import net.sourceforge.subsonic.androidapp.domain.ServerInfo; |
80 |
|
import net.sourceforge.subsonic.androidapp.domain.Version; |
81 |
|
import net.sourceforge.subsonic.androidapp.service.parser.AlbumListParser; |
82 |
|
import net.sourceforge.subsonic.androidapp.service.parser.ErrorParser; |
83 |
|
import net.sourceforge.subsonic.androidapp.service.parser.IndexesParser; |
84 |
|
import net.sourceforge.subsonic.androidapp.service.parser.JukeboxStatusParser; |
85 |
|
import net.sourceforge.subsonic.androidapp.service.parser.LicenseParser; |
86 |
|
import net.sourceforge.subsonic.androidapp.service.parser.LyricsParser; |
87 |
|
import net.sourceforge.subsonic.androidapp.service.parser.MusicDirectoryParser; |
88 |
|
import net.sourceforge.subsonic.androidapp.service.parser.MusicFoldersParser; |
89 |
|
import net.sourceforge.subsonic.androidapp.service.parser.PlaylistParser; |
90 |
|
import net.sourceforge.subsonic.androidapp.service.parser.PlaylistsParser; |
91 |
|
import net.sourceforge.subsonic.androidapp.service.parser.RandomSongsParser; |
92 |
|
import net.sourceforge.subsonic.androidapp.service.parser.SearchResult2Parser; |
93 |
|
import net.sourceforge.subsonic.androidapp.service.parser.SearchResultParser; |
94 |
|
import net.sourceforge.subsonic.androidapp.service.parser.VersionParser; |
95 |
|
import net.sourceforge.subsonic.androidapp.service.ssl.SSLSocketFactory; |
96 |
|
import net.sourceforge.subsonic.androidapp.service.ssl.TrustSelfSignedStrategy; |
97 |
|
import net.sourceforge.subsonic.androidapp.util.CancellableTask; |
98 |
|
import net.sourceforge.subsonic.androidapp.util.Constants; |
99 |
|
import net.sourceforge.subsonic.androidapp.util.FileUtil; |
100 |
|
import net.sourceforge.subsonic.androidapp.util.ProgressListener; |
101 |
|
import net.sourceforge.subsonic.androidapp.util.Util; |
102 |
|
|
103 |
|
|
104 |
|
@author |
105 |
|
|
|
|
| 77,2% |
Uncovered Elements: 89 (390) |
Complexity: 83 |
Complexity Density: 0,29 |
|
106 |
|
public class RESTMusicService implements MusicService { |
107 |
|
|
108 |
|
private static final String TAG = RESTMusicService.class.getSimpleName(); |
109 |
|
|
110 |
|
private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000; |
111 |
|
private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000; |
112 |
|
private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000; |
113 |
|
private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000; |
114 |
|
private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000; |
115 |
|
|
116 |
|
|
117 |
|
private static final double TIMEOUT_MILLIS_PER_OFFSET_BYTE = 20000.0 / 1000000.0; |
118 |
|
|
119 |
|
|
120 |
|
|
121 |
|
|
122 |
|
private static final String VERSION_URL = "http://subsonic.org/backend/version.view"; |
123 |
|
|
124 |
|
private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5; |
125 |
|
private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L; |
126 |
|
|
127 |
|
private final DefaultHttpClient httpClient; |
128 |
|
private long redirectionLastChecked; |
129 |
|
private int redirectionNetworkType = -1; |
130 |
|
private String redirectFrom; |
131 |
|
private String redirectTo; |
132 |
|
private final ThreadSafeClientConnManager connManager; |
133 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (11) |
Complexity: 1 |
Complexity Density: 0,09 |
|
134 |
2
|
public RESTMusicService() {... |
135 |
|
|
136 |
|
|
137 |
2
|
HttpParams params = new BasicHttpParams(); |
138 |
2
|
ConnManagerParams.setMaxTotalConnections(params, 20); |
139 |
2
|
ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(20)); |
140 |
2
|
HttpConnectionParams.setConnectionTimeout(params, SOCKET_CONNECT_TIMEOUT); |
141 |
2
|
HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_DEFAULT); |
142 |
|
|
143 |
|
|
144 |
|
|
145 |
2
|
HttpConnectionParams.setStaleCheckingEnabled(params, false); |
146 |
|
|
147 |
|
|
148 |
2
|
SchemeRegistry schemeRegistry = new SchemeRegistry(); |
149 |
2
|
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); |
150 |
2
|
schemeRegistry.register(new Scheme("https", createSSLSocketFactory(), 443)); |
151 |
|
|
152 |
|
|
153 |
|
|
154 |
|
|
155 |
2
|
connManager = new ThreadSafeClientConnManager(params, schemeRegistry); |
156 |
2
|
httpClient = new DefaultHttpClient(connManager, params); |
157 |
|
} |
158 |
|
|
|
|
| 50% |
Uncovered Elements: 2 (4) |
Complexity: 2 |
Complexity Density: 0,5 |
|
159 |
2
|
private SocketFactory createSSLSocketFactory() {... |
160 |
2
|
try { |
161 |
2
|
return new SSLSocketFactory(new TrustSelfSignedStrategy(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); |
162 |
|
} catch (Throwable x) { |
163 |
0
|
Log.e(TAG, "Failed to create custom SSL socket factory, using default.", x); |
164 |
0
|
return org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); |
165 |
|
} |
166 |
|
} |
167 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
168 |
2
|
@Override... |
169 |
|
public void ping(Context context, ProgressListener progressListener) throws Exception { |
170 |
2
|
Reader reader = getReader(context, progressListener, "ping", null); |
171 |
2
|
try { |
172 |
2
|
new ErrorParser(context).parse(reader); |
173 |
|
} finally { |
174 |
2
|
Util.close(reader); |
175 |
|
} |
176 |
|
} |
177 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (5) |
Complexity: 1 |
Complexity Density: 0,2 |
|
178 |
1
|
@Override... |
179 |
|
public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { |
180 |
1
|
Reader reader = getReader(context, progressListener, "getLicense", null); |
181 |
1
|
try { |
182 |
1
|
ServerInfo serverInfo = new LicenseParser(context).parse(reader); |
183 |
1
|
return serverInfo.isLicenseValid(); |
184 |
|
} finally { |
185 |
1
|
Util.close(reader); |
186 |
|
} |
187 |
|
} |
188 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
189 |
2
|
public List<MusicFolder> getMusicFolders(Context context, ProgressListener progressListener) throws Exception {... |
190 |
2
|
Reader reader = getReader(context, progressListener, "getMusicFolders", null); |
191 |
2
|
try { |
192 |
2
|
return new MusicFoldersParser(context).parse(reader, progressListener); |
193 |
|
} finally { |
194 |
2
|
Util.close(reader); |
195 |
|
} |
196 |
|
} |
197 |
|
|
|
|
| 87% |
Uncovered Elements: 3 (23) |
Complexity: 4 |
Complexity Density: 0,24 |
|
198 |
3
|
@Override... |
199 |
|
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { |
200 |
3
|
Indexes cachedIndexes = readCachedIndexes(context, musicFolderId); |
201 |
3
|
long lastModified = cachedIndexes == null ? 0L : cachedIndexes.getLastModified(); |
202 |
|
|
203 |
3
|
List<String> parameterNames = new ArrayList<String>(); |
204 |
3
|
List<Object> parameterValues = new ArrayList<Object>(); |
205 |
|
|
206 |
3
|
parameterNames.add("ifModifiedSince"); |
207 |
3
|
parameterValues.add(lastModified); |
208 |
|
|
209 |
3
|
if (musicFolderId != null) { |
210 |
0
|
parameterNames.add("musicFolderId"); |
211 |
0
|
parameterValues.add(musicFolderId); |
212 |
|
} |
213 |
|
|
214 |
3
|
Reader reader = getReader(context, progressListener, "getIndexes", null, parameterNames, parameterValues); |
215 |
3
|
try { |
216 |
3
|
Indexes indexes = new IndexesParser(context).parse(reader, progressListener); |
217 |
3
|
if (indexes != null) { |
218 |
1
|
writeCachedIndexes(context, indexes, musicFolderId); |
219 |
1
|
return indexes; |
220 |
|
} |
221 |
2
|
return cachedIndexes; |
222 |
|
} finally { |
223 |
3
|
Util.close(reader); |
224 |
|
} |
225 |
|
} |
226 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0,5 |
|
227 |
3
|
private Indexes readCachedIndexes(Context context, String musicFolderId) {... |
228 |
3
|
String filename = getCachedIndexesFilename(context, musicFolderId); |
229 |
3
|
return FileUtil.deserialize(context, filename); |
230 |
|
} |
231 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0,5 |
|
232 |
1
|
private void writeCachedIndexes(Context context, Indexes indexes, String musicFolderId) {... |
233 |
1
|
String filename = getCachedIndexesFilename(context, musicFolderId); |
234 |
1
|
FileUtil.serialize(context, indexes, filename); |
235 |
|
} |
236 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0,5 |
|
237 |
4
|
private String getCachedIndexesFilename(Context context, String musicFolderId) {... |
238 |
4
|
String s = Util.getRestUrl(context, null) + musicFolderId; |
239 |
4
|
return "indexes-" + Math.abs(s.hashCode()) + ".ser"; |
240 |
|
} |
241 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
242 |
5
|
@Override... |
243 |
|
public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { |
244 |
5
|
Reader reader = getReader(context, progressListener, "getMusicDirectory", null, "id", id); |
245 |
5
|
try { |
246 |
5
|
return new MusicDirectoryParser(context).parse(reader, progressListener); |
247 |
|
} finally { |
248 |
5
|
Util.close(reader); |
249 |
|
} |
250 |
|
} |
251 |
|
|
|
|
| 66,7% |
Uncovered Elements: 1 (3) |
Complexity: 2 |
Complexity Density: 0,67 |
|
252 |
1
|
@Override... |
253 |
|
public SearchResult search(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception { |
254 |
1
|
try { |
255 |
1
|
return searchNew(critera, context, progressListener); |
256 |
|
} catch (ServerTooOldException x) { |
257 |
|
|
258 |
0
|
return searchOld(critera, context, progressListener); |
259 |
|
} |
260 |
|
} |
261 |
|
|
262 |
|
|
263 |
|
|
264 |
|
|
|
|
| 0% |
Uncovered Elements: 6 (6) |
Complexity: 1 |
Complexity Density: 0,17 |
|
265 |
0
|
private SearchResult searchOld(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception {... |
266 |
0
|
List<String> parameterNames = Arrays.asList("any", "songCount"); |
267 |
0
|
List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getSongCount()); |
268 |
0
|
Reader reader = getReader(context, progressListener, "search", null, parameterNames, parameterValues); |
269 |
0
|
try { |
270 |
0
|
return new SearchResultParser(context).parse(reader, progressListener); |
271 |
|
} finally { |
272 |
0
|
Util.close(reader); |
273 |
|
} |
274 |
|
} |
275 |
|
|
276 |
|
|
277 |
|
|
278 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (7) |
Complexity: 1 |
Complexity Density: 0,14 |
|
279 |
1
|
private SearchResult searchNew(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception {... |
280 |
1
|
checkServerVersion(context, "1.4", null); |
281 |
|
|
282 |
1
|
List<String> parameterNames = Arrays.asList("query", "artistCount", "albumCount", "songCount"); |
283 |
1
|
List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getArtistCount(), |
284 |
|
critera.getAlbumCount(), critera.getSongCount()); |
285 |
1
|
Reader reader = getReader(context, progressListener, "search2", null, parameterNames, parameterValues); |
286 |
1
|
try { |
287 |
1
|
return new SearchResult2Parser(context).parse(reader, progressListener); |
288 |
|
} finally { |
289 |
1
|
Util.close(reader); |
290 |
|
} |
291 |
|
} |
292 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (6) |
Complexity: 1 |
Complexity Density: 0,17 |
|
293 |
2
|
@Override... |
294 |
|
public MusicDirectory getPlaylist(String id, Context context, ProgressListener progressListener) throws Exception { |
295 |
2
|
HttpParams params = new BasicHttpParams(); |
296 |
2
|
HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_PLAYLIST); |
297 |
|
|
298 |
2
|
Reader reader = getReader(context, progressListener, "getPlaylist", params, "id", id); |
299 |
2
|
try { |
300 |
2
|
return new PlaylistParser(context).parse(reader, progressListener); |
301 |
|
} finally { |
302 |
2
|
Util.close(reader); |
303 |
|
} |
304 |
|
} |
305 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
306 |
2
|
@Override... |
307 |
|
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { |
308 |
2
|
Reader reader = getReader(context, progressListener, "getPlaylists", null); |
309 |
2
|
try { |
310 |
2
|
return new PlaylistsParser(context).parse(reader, progressListener); |
311 |
|
} finally { |
312 |
2
|
Util.close(reader); |
313 |
|
} |
314 |
|
} |
315 |
|
|
|
|
| 78,9% |
Uncovered Elements: 4 (19) |
Complexity: 3 |
Complexity Density: 0,2 |
|
316 |
1
|
@Override... |
317 |
|
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { |
318 |
1
|
List<String> parameterNames = new LinkedList<String>(); |
319 |
1
|
List<Object> parameterValues = new LinkedList<Object>(); |
320 |
|
|
321 |
1
|
if (id != null) { |
322 |
0
|
parameterNames.add("playlistId"); |
323 |
0
|
parameterValues.add(id); |
324 |
|
} |
325 |
1
|
if (name != null) { |
326 |
1
|
parameterNames.add("name"); |
327 |
1
|
parameterValues.add(name); |
328 |
|
} |
329 |
1
|
for (MusicDirectory.Entry entry : entries) { |
330 |
8
|
parameterNames.add("songId"); |
331 |
8
|
parameterValues.add(entry.getId()); |
332 |
|
} |
333 |
|
|
334 |
1
|
Reader reader = getReader(context, progressListener, "createPlaylist", null, parameterNames, parameterValues); |
335 |
1
|
try { |
336 |
1
|
new ErrorParser(context).parse(reader); |
337 |
|
} finally { |
338 |
1
|
Util.close(reader); |
339 |
|
} |
340 |
|
} |
341 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
342 |
1
|
@Override... |
343 |
|
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { |
344 |
1
|
Reader reader = getReader(context, progressListener, "getLyrics", null, Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title)); |
345 |
1
|
try { |
346 |
1
|
return new LyricsParser(context).parse(reader, progressListener); |
347 |
|
} finally { |
348 |
1
|
Util.close(reader); |
349 |
|
} |
350 |
|
} |
351 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (5) |
Complexity: 1 |
Complexity Density: 0,2 |
|
352 |
4
|
@Override... |
353 |
|
public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { |
354 |
4
|
checkServerVersion(context, "1.5", "Scrobbling not supported."); |
355 |
4
|
Reader reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission)); |
356 |
4
|
try { |
357 |
4
|
new ErrorParser(context).parse(reader); |
358 |
|
} finally { |
359 |
4
|
Util.close(reader); |
360 |
|
} |
361 |
|
} |
362 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
363 |
3
|
@Override... |
364 |
|
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { |
365 |
3
|
Reader reader = getReader(context, progressListener, "getAlbumList", |
366 |
|
null, Arrays.asList("type", "size", "offset"), Arrays.<Object>asList(type, size, offset)); |
367 |
3
|
try { |
368 |
3
|
return new AlbumListParser(context).parse(reader, progressListener); |
369 |
|
} finally { |
370 |
3
|
Util.close(reader); |
371 |
|
} |
372 |
|
} |
373 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (6) |
Complexity: 1 |
Complexity Density: 0,17 |
|
374 |
5
|
@Override... |
375 |
|
public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { |
376 |
5
|
HttpParams params = new BasicHttpParams(); |
377 |
5
|
HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS); |
378 |
|
|
379 |
5
|
Reader reader = getReader(context, progressListener, "getRandomSongs", params, "size", size); |
380 |
5
|
try { |
381 |
5
|
return new RandomSongsParser(context).parse(reader, progressListener); |
382 |
|
} finally { |
383 |
5
|
Util.close(reader); |
384 |
|
} |
385 |
|
} |
386 |
|
|
|
|
| 0% |
Uncovered Elements: 2 (2) |
Complexity: 1 |
Complexity Density: 0,5 |
|
387 |
0
|
@Override... |
388 |
|
public Version getLocalVersion(Context context) throws Exception { |
389 |
0
|
PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.sourceforge.subsonic.androidapp", 0); |
390 |
0
|
return new Version(packageInfo.versionName); |
391 |
|
} |
392 |
|
|
|
|
| 0% |
Uncovered Elements: 4 (4) |
Complexity: 1 |
Complexity Density: 0,25 |
|
393 |
0
|
@Override... |
394 |
|
public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { |
395 |
0
|
Reader reader = getReaderForURL(context, VERSION_URL, null, null, null, progressListener); |
396 |
0
|
try { |
397 |
0
|
return new VersionParser().parse(reader); |
398 |
|
} finally { |
399 |
0
|
Util.close(reader); |
400 |
|
} |
401 |
|
} |
402 |
|
|
|
|
| 71,4% |
Uncovered Elements: 2 (7) |
Complexity: 2 |
Complexity Density: 0,4 |
|
403 |
9
|
private void checkServerVersion(Context context, String version, String text) throws ServerTooOldException {... |
404 |
9
|
Version serverVersion = Util.getServerRestVersion(context); |
405 |
9
|
Version requiredVersion = new Version(version); |
406 |
9
|
boolean ok = serverVersion == null || serverVersion.compareTo(requiredVersion) >= 0; |
407 |
|
|
408 |
9
|
if (!ok) { |
409 |
0
|
throw new ServerTooOldException(text, serverVersion, requiredVersion); |
410 |
|
} |
411 |
|
} |
412 |
|
|
|
|
| 90% |
Uncovered Elements: 3 (30) |
Complexity: 5 |
Complexity Density: 0,21 |
|
413 |
38
|
@Override... |
414 |
|
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { |
415 |
|
|
416 |
|
|
417 |
38
|
synchronized (entry) { |
418 |
|
|
419 |
|
|
420 |
38
|
Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); |
421 |
38
|
if (bitmap != null) { |
422 |
8
|
return bitmap; |
423 |
|
} |
424 |
|
|
425 |
30
|
String url = Util.getRestUrl(context, "getCoverArt"); |
426 |
|
|
427 |
30
|
InputStream in = null; |
428 |
30
|
try { |
429 |
30
|
List<String> parameterNames = Arrays.asList("id", "size"); |
430 |
30
|
List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt(), size); |
431 |
30
|
HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener); |
432 |
29
|
in = entity.getContent(); |
433 |
|
|
434 |
|
|
435 |
30
|
String contentType = Util.getContentType(entity); |
436 |
30
|
if (contentType != null && contentType.startsWith("text/xml")) { |
437 |
0
|
new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); |
438 |
0
|
return null; |
439 |
|
} |
440 |
|
|
441 |
30
|
byte[] bytes = Util.toByteArray(in); |
442 |
|
|
443 |
30
|
if (saveToFile) { |
444 |
8
|
OutputStream out = null; |
445 |
8
|
try { |
446 |
8
|
out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry)); |
447 |
8
|
out.write(bytes); |
448 |
|
} finally { |
449 |
8
|
Util.close(out); |
450 |
|
} |
451 |
|
} |
452 |
|
|
453 |
30
|
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); |
454 |
|
|
455 |
|
} finally { |
456 |
30
|
Util.close(in); |
457 |
|
} |
458 |
|
} |
459 |
|
} |
460 |
|
|
|
|
| 76,2% |
Uncovered Elements: 5 (21) |
Complexity: 4 |
Complexity Density: 0,24 |
|
461 |
21
|
@Override... |
462 |
|
public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { |
463 |
|
|
464 |
21
|
String url = Util.getRestUrl(context, "stream"); |
465 |
|
|
466 |
|
|
467 |
|
|
468 |
|
|
469 |
21
|
HttpParams params = new BasicHttpParams(); |
470 |
21
|
int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE); |
471 |
21
|
HttpConnectionParams.setSoTimeout(params, timeout); |
472 |
|
|
473 |
|
|
474 |
21
|
List<Header> headers = new ArrayList<Header>(); |
475 |
21
|
if (offset > 0) { |
476 |
3
|
headers.add(new BasicHeader("Range", "bytes=" + offset + "-")); |
477 |
|
} |
478 |
21
|
List<String> parameterNames = Arrays.asList("id", "maxBitRate"); |
479 |
21
|
List<Object> parameterValues = Arrays.<Object>asList(song.getId(), maxBitrate); |
480 |
21
|
HttpResponse response = getResponseForURL(context, url, params, parameterNames, parameterValues, headers, null, task); |
481 |
|
|
482 |
|
|
483 |
21
|
String contentType = Util.getContentType(response.getEntity()); |
484 |
21
|
if (contentType != null && contentType.startsWith("text/xml")) { |
485 |
0
|
InputStream in = response.getEntity().getContent(); |
486 |
0
|
try { |
487 |
0
|
new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); |
488 |
|
} finally { |
489 |
0
|
Util.close(in); |
490 |
|
} |
491 |
|
} |
492 |
|
|
493 |
21
|
return response; |
494 |
|
} |
495 |
|
|
|
|
| 0% |
Uncovered Elements: 7 (7) |
Complexity: 1 |
Complexity Density: 0,14 |
|
496 |
0
|
@Override... |
497 |
|
public String getVideoUrl(Context context, String id) { |
498 |
0
|
StringBuilder builder = new StringBuilder(Util.getRestUrl(context, "videoPlayer")); |
499 |
0
|
builder.append("&id=").append(id); |
500 |
0
|
builder.append("&maxBitRate=500"); |
501 |
0
|
builder.append("&autoplay=true"); |
502 |
|
|
503 |
0
|
String url = rewriteUrlWithRedirect(context, builder.toString()); |
504 |
0
|
Log.i(TAG, "Using video URL: " + url); |
505 |
0
|
return url; |
506 |
|
} |
507 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (11) |
Complexity: 2 |
Complexity Density: 0,22 |
|
508 |
2
|
@Override... |
509 |
|
public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { |
510 |
2
|
int n = ids.size(); |
511 |
2
|
List<String> parameterNames = new ArrayList<String>(n + 1); |
512 |
2
|
parameterNames.add("action"); |
513 |
42
|
for (int i = 0; i < n; i++) { |
514 |
40
|
parameterNames.add("id"); |
515 |
|
} |
516 |
2
|
List<Object> parameterValues = new ArrayList<Object>(); |
517 |
2
|
parameterValues.add("set"); |
518 |
2
|
parameterValues.addAll(ids); |
519 |
|
|
520 |
2
|
return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); |
521 |
|
} |
522 |
|
|
|
|
| 0% |
Uncovered Elements: 3 (3) |
Complexity: 1 |
Complexity Density: 0,33 |
|
523 |
0
|
@Override... |
524 |
|
public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { |
525 |
0
|
List<String> parameterNames = Arrays.asList("action", "index", "offset"); |
526 |
0
|
List<Object> parameterValues = Arrays.<Object>asList("skip", index, offsetSeconds); |
527 |
0
|
return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); |
528 |
|
} |
529 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
530 |
2
|
@Override... |
531 |
|
public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { |
532 |
2
|
return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("stop")); |
533 |
|
} |
534 |
|
|
|
|
| 0% |
Uncovered Elements: 1 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
535 |
0
|
@Override... |
536 |
|
public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { |
537 |
0
|
return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("start")); |
538 |
|
} |
539 |
|
|
|
|
| 0% |
Uncovered Elements: 1 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
540 |
0
|
@Override... |
541 |
|
public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { |
542 |
0
|
return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("status")); |
543 |
|
} |
544 |
|
|
|
|
| 0% |
Uncovered Elements: 3 (3) |
Complexity: 1 |
Complexity Density: 0,33 |
|
545 |
0
|
@Override... |
546 |
|
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { |
547 |
0
|
List<String> parameterNames = Arrays.asList("action", "gain"); |
548 |
0
|
List<Object> parameterValues = Arrays.<Object>asList("setGain", gain); |
549 |
0
|
return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); |
550 |
|
|
551 |
|
} |
552 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (5) |
Complexity: 1 |
Complexity Density: 0,2 |
|
553 |
4
|
private JukeboxStatus executeJukeboxCommand(Context context, ProgressListener progressListener, List<String> parameterNames, List<Object> parameterValues) throws Exception {... |
554 |
4
|
checkServerVersion(context, "1.7", "Jukebox not supported."); |
555 |
4
|
Reader reader = getReader(context, progressListener, "jukeboxControl", null, parameterNames, parameterValues); |
556 |
4
|
try { |
557 |
4
|
return new JukeboxStatusParser(context).parse(reader); |
558 |
|
} finally { |
559 |
4
|
Util.close(reader); |
560 |
|
} |
561 |
|
} |
562 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
563 |
7
|
private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception {... |
564 |
7
|
return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList()); |
565 |
|
} |
566 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
567 |
12
|
private Reader getReader(Context context, ProgressListener progressListener, String method,... |
568 |
|
HttpParams requestParams, String parameterName, Object parameterValue) throws Exception { |
569 |
12
|
return getReader(context, progressListener, method, requestParams, Arrays.asList(parameterName), Arrays.<Object>asList(parameterValue)); |
570 |
|
} |
571 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (6) |
Complexity: 2 |
Complexity Density: 0,5 |
|
572 |
36
|
private Reader getReader(Context context, ProgressListener progressListener, String method,... |
573 |
|
HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues) throws Exception { |
574 |
|
|
575 |
36
|
if (progressListener != null) { |
576 |
22
|
progressListener.updateProgress(R.string.service_connecting); |
577 |
|
} |
578 |
|
|
579 |
36
|
String url = Util.getRestUrl(context, method); |
580 |
36
|
return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener); |
581 |
|
} |
582 |
|
|
|
|
| 71,4% |
Uncovered Elements: 2 (7) |
Complexity: 2 |
Complexity Density: 0,4 |
|
583 |
36
|
private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,... |
584 |
|
List<Object> parameterValues, ProgressListener progressListener) throws Exception { |
585 |
36
|
HttpEntity entity = getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener); |
586 |
36
|
if (entity == null) { |
587 |
0
|
throw new RuntimeException("No entity received for URL " + url); |
588 |
|
} |
589 |
|
|
590 |
36
|
InputStream in = entity.getContent(); |
591 |
36
|
return new InputStreamReader(in, Constants.UTF_8); |
592 |
|
} |
593 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
594 |
66
|
private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,... |
595 |
|
List<Object> parameterValues, ProgressListener progressListener) throws Exception { |
596 |
66
|
return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, null).getEntity(); |
597 |
|
} |
598 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (15) |
Complexity: 4 |
Complexity Density: 0,36 |
|
599 |
87
|
private HttpResponse getResponseForURL(Context context, String url, HttpParams requestParams,... |
600 |
|
List<String> parameterNames, List<Object> parameterValues, |
601 |
|
List<Header> headers, ProgressListener progressListener, CancellableTask task) throws Exception { |
602 |
87
|
Log.d(TAG, "Connections in pool: " + connManager.getConnectionsInPool()); |
603 |
|
|
604 |
|
|
605 |
|
|
606 |
|
|
607 |
87
|
if (parameterNames != null && parameterNames.size() < 10) { |
608 |
85
|
StringBuilder builder = new StringBuilder(url); |
609 |
236
|
for (int i = 0; i < parameterNames.size(); i++) { |
610 |
151
|
builder.append("&").append(parameterNames.get(i)).append("="); |
611 |
151
|
builder.append(URLEncoder.encode(String.valueOf(parameterValues.get(i)), "UTF-8")); |
612 |
|
} |
613 |
85
|
url = builder.toString(); |
614 |
85
|
parameterNames = null; |
615 |
85
|
parameterValues = null; |
616 |
|
} |
617 |
|
|
618 |
87
|
String rewrittenUrl = rewriteUrlWithRedirect(context, url); |
619 |
87
|
return executeWithRetry(context, rewrittenUrl, url, requestParams, parameterNames, parameterValues, headers, progressListener, task); |
620 |
|
} |
621 |
|
|
|
|
| 75% |
Uncovered Elements: 13 (52) |
Complexity: 10 |
Complexity Density: 0,26 |
|
622 |
87
|
private HttpResponse executeWithRetry(Context context, String url, String originalUrl, HttpParams requestParams,... |
623 |
|
List<String> parameterNames, List<Object> parameterValues, |
624 |
|
List<Header> headers, ProgressListener progressListener, CancellableTask task) throws IOException { |
625 |
87
|
Log.i(TAG, "Using URL " + url); |
626 |
|
|
627 |
87
|
final AtomicReference<Boolean> cancelled = new AtomicReference<Boolean>(false); |
628 |
87
|
int attempts = 0; |
629 |
87
|
while (true) { |
630 |
87
|
attempts++; |
631 |
87
|
HttpContext httpContext = new BasicHttpContext(); |
632 |
87
|
final HttpPost request = new HttpPost(url); |
633 |
|
|
634 |
87
|
if (task != null) { |
635 |
|
|
636 |
21
|
task.setOnCancelListener(new CancellableTask.OnCancelListener() { |
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0,5 |
|
637 |
20
|
@Override... |
638 |
|
public void onCancel() { |
639 |
20
|
cancelled.set(true); |
640 |
20
|
request.abort(); |
641 |
|
} |
642 |
|
}); |
643 |
|
} |
644 |
|
|
645 |
87
|
if (parameterNames != null) { |
646 |
2
|
List<NameValuePair> params = new ArrayList<NameValuePair>(); |
647 |
44
|
for (int i = 0; i < parameterNames.size(); i++) { |
648 |
42
|
params.add(new BasicNameValuePair(parameterNames.get(i), String.valueOf(parameterValues.get(i)))); |
649 |
|
} |
650 |
2
|
request.setEntity(new UrlEncodedFormEntity(params, Constants.UTF_8)); |
651 |
|
} |
652 |
|
|
653 |
87
|
if (requestParams != null) { |
654 |
28
|
request.setParams(requestParams); |
655 |
28
|
Log.d(TAG, "Socket read timeout: " + HttpConnectionParams.getSoTimeout(requestParams) + " ms."); |
656 |
|
} |
657 |
|
|
658 |
87
|
if (headers != null) { |
659 |
21
|
for (Header header : headers) { |
660 |
3
|
request.addHeader(header); |
661 |
|
} |
662 |
|
} |
663 |
|
|
664 |
|
|
665 |
87
|
SharedPreferences prefs = Util.getPreferences(context); |
666 |
87
|
int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); |
667 |
87
|
String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null); |
668 |
87
|
String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null); |
669 |
87
|
httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), |
670 |
|
new UsernamePasswordCredentials(username, password)); |
671 |
|
|
672 |
87
|
try { |
673 |
87
|
HttpResponse response = httpClient.execute(request, httpContext); |
674 |
87
|
detectRedirect(originalUrl, context, httpContext); |
675 |
86
|
return response; |
676 |
|
} catch (IOException x) { |
677 |
0
|
request.abort(); |
678 |
0
|
if (attempts >= HTTP_REQUEST_MAX_ATTEMPTS || cancelled.get()) { |
679 |
0
|
throw x; |
680 |
|
} |
681 |
0
|
if (progressListener != null) { |
682 |
0
|
String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1); |
683 |
0
|
progressListener.updateProgress(msg); |
684 |
|
} |
685 |
0
|
Log.w(TAG, "Got IOException (" + attempts + "), will retry", x); |
686 |
0
|
increaseTimeouts(requestParams); |
687 |
0
|
Util.sleepQuietly(2000L); |
688 |
|
} |
689 |
|
} |
690 |
|
} |
691 |
|
|
|
|
| 0% |
Uncovered Elements: 13 (13) |
Complexity: 4 |
Complexity Density: 0,57 |
|
692 |
0
|
private void increaseTimeouts(HttpParams requestParams) {... |
693 |
0
|
if (requestParams != null) { |
694 |
0
|
int connectTimeout = HttpConnectionParams.getConnectionTimeout(requestParams); |
695 |
0
|
if (connectTimeout != 0) { |
696 |
0
|
HttpConnectionParams.setConnectionTimeout(requestParams, (int) (connectTimeout * 1.3F)); |
697 |
|
} |
698 |
0
|
int readTimeout = HttpConnectionParams.getSoTimeout(requestParams); |
699 |
0
|
if (readTimeout != 0) { |
700 |
0
|
HttpConnectionParams.setSoTimeout(requestParams, (int) (readTimeout * 1.5F)); |
701 |
|
} |
702 |
|
} |
703 |
|
} |
704 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (8) |
Complexity: 1 |
Complexity Density: 0,12 |
|
705 |
87
|
private void detectRedirect(String originalUrl, Context context, HttpContext httpContext) {... |
706 |
87
|
HttpUriRequest request = (HttpUriRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST); |
707 |
87
|
HttpHost host = (HttpHost) httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST); |
708 |
87
|
String redirectedUrl = host.toURI() + request.getURI(); |
709 |
|
|
710 |
87
|
redirectFrom = originalUrl.substring(0, originalUrl.indexOf("/rest/")); |
711 |
87
|
redirectTo = redirectedUrl.substring(0, redirectedUrl.indexOf("/rest/")); |
712 |
|
|
713 |
87
|
Log.i(TAG, redirectFrom + " redirects to " + redirectTo); |
714 |
87
|
redirectionLastChecked = System.currentTimeMillis(); |
715 |
87
|
redirectionNetworkType = getCurrentNetworkType(context); |
716 |
|
} |
717 |
|
|
|
|
| 69,2% |
Uncovered Elements: 4 (13) |
Complexity: 5 |
Complexity Density: 0,71 |
|
718 |
87
|
private String rewriteUrlWithRedirect(Context context, String url) {... |
719 |
|
|
720 |
|
|
721 |
87
|
if (System.currentTimeMillis() - redirectionLastChecked > REDIRECTION_CHECK_INTERVAL_MILLIS) { |
722 |
1
|
return url; |
723 |
|
} |
724 |
|
|
725 |
|
|
726 |
86
|
if (redirectionNetworkType != getCurrentNetworkType(context)) { |
727 |
0
|
return url; |
728 |
|
} |
729 |
|
|
730 |
86
|
if (redirectFrom == null || redirectTo == null) { |
731 |
0
|
return url; |
732 |
|
} |
733 |
|
|
734 |
86
|
return url.replace(redirectFrom, redirectTo); |
735 |
|
} |
736 |
|
|
|
|
| 80% |
Uncovered Elements: 1 (5) |
Complexity: 2 |
Complexity Density: 0,67 |
|
737 |
173
|
private int getCurrentNetworkType(Context context) {... |
738 |
173
|
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); |
739 |
173
|
NetworkInfo networkInfo = manager.getActiveNetworkInfo(); |
740 |
173
|
return networkInfo == null ? -1 : networkInfo.getType(); |
741 |
|
} |
742 |
|
} |