Clover Coverage Report - Subsonic-Android Coverage Report
Coverage timestamp: ven dic 19 2014 17:57:13 EST
../../../../../img/srcFileCovDistChart9.png 32% of files have more coverage
126   323   53   5,04
48   246   0,42   12,5
25     2,12  
2    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  DownloadFile       Line # 44 52 31 87% 0.8695652
  DownloadFile.DownloadTask       Line # 189 74 22 82,2% 0.8224299
 
No Tests
 
1    /*
2    This file is part of Subsonic.
3   
4    Subsonic is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8   
9    Subsonic is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12    GNU General Public License for more details.
13   
14    You should have received a copy of the GNU General Public License
15    along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
16   
17    Copyright 2009 (C) Sindre Mehus
18    */
19    package net.sourceforge.subsonic.androidapp.service;
20   
21    import java.io.File;
22    import java.io.FileOutputStream;
23    import java.io.IOException;
24    import java.io.InputStream;
25    import java.io.OutputStream;
26   
27    import android.content.Context;
28    import android.os.PowerManager;
29    import android.util.DisplayMetrics;
30    import android.util.Log;
31    import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
32    import net.sourceforge.subsonic.androidapp.util.CancellableTask;
33    import net.sourceforge.subsonic.androidapp.util.FileUtil;
34    import net.sourceforge.subsonic.androidapp.util.Util;
35    import net.sourceforge.subsonic.androidapp.util.CacheCleaner;
36   
37    import org.apache.http.HttpResponse;
38    import org.apache.http.HttpStatus;
39   
40    /**
41    * @author Sindre Mehus
42    * @version $Id$
43    */
 
44    public class DownloadFile {
45   
46    private static final String TAG = DownloadFile.class.getSimpleName();
47    private final Context context;
48    private final MusicDirectory.Entry song;
49    private final File partialFile;
50    private final File completeFile;
51    private final File saveFile;
52   
53    private final MediaStoreService mediaStoreService;
54    private CancellableTask downloadTask;
55    private boolean save;
56    private boolean failed;
57    private int bitRate;
58   
 
59  164 toggle public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) {
60  164 this.context = context;
61  164 this.song = song;
62  164 this.save = save;
63  164 saveFile = FileUtil.getSongFile(context, song);
64  164 bitRate = Util.getMaxBitrate(context);
65  164 partialFile = new File(saveFile.getParent(), FileUtil.getBaseName(saveFile.getName()) +
66    "." + bitRate + ".partial." + FileUtil.getExtension(saveFile.getName()));
67  164 completeFile = new File(saveFile.getParent(), FileUtil.getBaseName(saveFile.getName()) +
68    ".complete." + FileUtil.getExtension(saveFile.getName()));
69  164 mediaStoreService = new MediaStoreService(context);
70    }
71   
 
72  9134 toggle public MusicDirectory.Entry getSong() {
73  9134 return song;
74    }
75   
76    /**
77    * Returns the effective bit rate.
78    */
 
79  30 toggle public int getBitRate() {
80  30 if (bitRate > 0) {
81  0 return bitRate;
82    }
83  30 return song.getBitRate() == null ? 160 : song.getBitRate();
84    }
85   
 
86  21 toggle public synchronized void download() {
87  21 FileUtil.createDirectoryForParent(saveFile);
88  21 failed = false;
89  21 downloadTask = new DownloadTask();
90  21 downloadTask.start();
91    }
92   
 
93  47 toggle public synchronized void cancelDownload() {
94  47 if (downloadTask != null) {
95  20 downloadTask.cancel();
96    }
97    }
98   
 
99  1300 toggle public File getCompleteFile() {
100  1300 if (saveFile.exists()) {
101  0 return saveFile;
102    }
103   
104  1300 if (completeFile.exists()) {
105  320 return completeFile;
106    }
107   
108  980 return saveFile;
109    }
110   
 
111  1366 toggle public File getPartialFile() {
112  1366 return partialFile;
113    }
114   
 
115  408 toggle public boolean isSaved() {
116  408 return saveFile.exists();
117    }
118   
 
119  530 toggle public synchronized boolean isCompleteFileAvailable() {
120  530 return saveFile.exists() || completeFile.exists();
121    }
122   
 
123  920 toggle public synchronized boolean isWorkDone() {
124  920 return saveFile.exists() || (completeFile.exists() && !save);
125    }
126   
 
127  1032 toggle public synchronized boolean isDownloading() {
128  1032 return downloadTask != null && downloadTask.isRunning();
129    }
130   
 
131  56 toggle public synchronized boolean isDownloadCancelled() {
132  56 return downloadTask != null && downloadTask.isCancelled();
133    }
134   
 
135  57 toggle public boolean shouldSave() {
136  57 return save;
137    }
138   
 
139  51 toggle public boolean isFailed() {
140  51 return failed;
141    }
142   
 
143  4 toggle public void delete() {
144  4 cancelDownload();
145  4 Util.delete(partialFile);
146  4 Util.delete(completeFile);
147  4 Util.delete(saveFile);
148  4 mediaStoreService.deleteFromMediaStore(this);
149    }
150   
 
151  0 toggle public void unpin() {
152  0 if (saveFile.exists()) {
153  0 saveFile.renameTo(completeFile);
154    }
155    }
156   
 
157  19 toggle public boolean cleanup() {
158  19 boolean ok = true;
159  19 if (completeFile.exists() || saveFile.exists()) {
160  5 ok = Util.delete(partialFile);
161    }
162  19 if (saveFile.exists()) {
163  0 ok &= Util.delete(completeFile);
164    }
165  19 return ok;
166    }
167   
168    // In support of LRU caching.
 
169  24 toggle public void updateModificationDate() {
170  24 updateModificationDate(saveFile);
171  24 updateModificationDate(partialFile);
172  24 updateModificationDate(completeFile);
173    }
174   
 
175  72 toggle private void updateModificationDate(File file) {
176  72 if (file.exists()) {
177  25 boolean ok = file.setLastModified(System.currentTimeMillis());
178  25 if (!ok) {
179  25 Log.w(TAG, "Failed to set last-modified date on " + file);
180    }
181    }
182    }
183   
 
184  309 toggle @Override
185    public String toString() {
186  309 return "DownloadFile (" + song + ")";
187    }
188   
 
189    private class DownloadTask extends CancellableTask {
190   
 
191  21 toggle @Override
192    public void execute() {
193   
194  21 InputStream in = null;
195  21 FileOutputStream out = null;
196  21 PowerManager.WakeLock wakeLock = null;
197  21 try {
198   
199  21 if (Util.isScreenLitOnDownload(context)) {
200  21 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
201  21 wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, toString());
202  21 wakeLock.acquire();
203  21 Log.i(TAG, "Acquired wake lock " + wakeLock);
204    }
205   
206  21 if (saveFile.exists()) {
207  0 Log.i(TAG, saveFile + " already exists. Skipping.");
208  0 return;
209    }
210  21 if (completeFile.exists()) {
211  0 if (save) {
212  0 Util.atomicCopy(completeFile, saveFile);
213    } else {
214  0 Log.i(TAG, completeFile + " already exists. Skipping.");
215    }
216  0 return;
217    }
218   
219  21 MusicService musicService = MusicServiceFactory.getMusicService(context);
220   
221    // Attempt partial HTTP GET, appending to the file if it exists.
222  21 HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
223  21 in = response.getEntity().getContent();
224  21 boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT;
225  21 if (partial) {
226  3 Log.i(TAG, "Executed partial HTTP GET, skipping " + partialFile.length() + " bytes");
227    }
228   
229  21 out = new FileOutputStream(partialFile, partial);
230  20 long n = copy(in, out);
231  7 Log.i(TAG, "Downloaded " + n + " bytes to " + partialFile);
232  7 out.flush();
233  7 out.close();
234   
235  7 if (isCancelled()) {
236  1 throw new Exception("Download of '" + song + "' was cancelled");
237    }
238   
239  6 downloadAndSaveCoverArt(musicService);
240   
241  6 if (save) {
242  0 Util.atomicCopy(partialFile, saveFile);
243  0 mediaStoreService.saveInMediaStore(DownloadFile.this);
244    } else {
245  6 Util.atomicCopy(partialFile, completeFile);
246    }
247   
248    } catch (Exception x) {
249  14 Util.close(out);
250  14 Util.delete(completeFile);
251  14 Util.delete(saveFile);
252  14 if (!isCancelled()) {
253  1 failed = true;
254  1 Log.w(TAG, "Failed to download '" + song + "'.", x);
255    }
256   
257    } finally {
258  20 Util.close(in);
259  20 Util.close(out);
260  20 if (wakeLock != null) {
261  20 wakeLock.release();
262  20 Log.i(TAG, "Released wake lock " + wakeLock);
263    }
264  20 new CacheCleaner(context, DownloadServiceImpl.getInstance()).clean();
265    }
266    }
267   
 
268  82 toggle @Override
269    public String toString() {
270  82 return "DownloadTask (" + song + ")";
271    }
272   
 
273  6 toggle private void downloadAndSaveCoverArt(MusicService musicService) throws Exception {
274  6 try {
275  6 if (song.getCoverArt() != null) {
276  6 DisplayMetrics metrics = context.getResources().getDisplayMetrics();
277  6 int size = Math.min(metrics.widthPixels, metrics.heightPixels);
278  6 musicService.getCoverArt(context, song, size, true, null);
279    }
280    } catch (Exception x) {
281  0 Log.e(TAG, "Failed to get cover art.", x);
282    }
283    }
284   
 
285  20 toggle private long copy(final InputStream in, OutputStream out) throws IOException, InterruptedException {
286   
287    // Start a thread that will close the input stream if the task is
288    // cancelled, thus causing the copy() method to return.
289  20 new Thread() {
 
290  20 toggle @Override
291    public void run() {
292  20 while (true) {
293  69 Util.sleepQuietly(3000L);
294  68 if (isCancelled()) {
295  14 Util.close(in);
296  14 return;
297    }
298  54 if (!isRunning()) {
299  5 return;
300    }
301    }
302    }
303    }.start();
304   
305  20 byte[] buffer = new byte[1024 * 16];
306  20 long count = 0;
307  20 int n;
308  20 long lastLog = System.currentTimeMillis();
309   
310  ? while (!isCancelled() && (n = in.read(buffer)) != -1) {
311  17376 out.write(buffer, 0, n);
312  17376 count += n;
313   
314  17376 long now = System.currentTimeMillis();
315  17376 if (now - lastLog > 3000L) { // Only every so often.
316  48 Log.i(TAG, "Downloaded " + Util.formatBytes(count) + " of " + song);
317  48 lastLog = now;
318    }
319    }
320  7 return count;
321    }
322    }
323    }