summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh/antennapod/service')
-rw-r--r--src/de/danoeh/antennapod/service/PlaybackService.java386
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java66
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadStatus.java7
-rw-r--r--src/de/danoeh/antennapod/service/download/HttpDownloader.java333
4 files changed, 469 insertions, 323 deletions
diff --git a/src/de/danoeh/antennapod/service/PlaybackService.java b/src/de/danoeh/antennapod/service/PlaybackService.java
index 7c306984c..556a58ee8 100644
--- a/src/de/danoeh/antennapod/service/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/PlaybackService.java
@@ -45,9 +45,13 @@ import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.DuckType;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.util.playback.AudioPlayer;
+import de.danoeh.antennapod.util.playback.IPlayer;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
+import de.danoeh.antennapod.util.playback.VideoPlayer;
import de.danoeh.antennapod.util.playback.PlaybackController;
/**
@@ -119,7 +123,12 @@ public class PlaybackService extends Service {
*/
public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7;
- /**
+ /**
+ * Playback speed has changed
+ * */
+ public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
+
+ /**
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
* is in an invalid state.
*/
@@ -132,13 +141,12 @@ public class PlaybackService extends Service {
private static final int NOTIFICATION_ID = 1;
+ private volatile IPlayer player;
+ private RemoteControlClient remoteControlClient;
private AudioManager audioManager;
private ComponentName mediaButtonReceiver;
- private MediaPlayer player;
- private RemoteControlClient remoteControlClient;
-
- private Playable media;
+ private volatile Playable media;
/**
* True if media should be streamed (Extracted from Intent Extra) .
@@ -252,7 +260,6 @@ public class PlaybackService extends Service {
}
);
dbLoaderExecutor = Executors.newSingleThreadExecutor();
- player = createMediaPlayer();
mediaButtonReceiver = new ComponentName(getPackageName(),
MediaButtonReceiver.class.getName());
@@ -273,22 +280,43 @@ public class PlaybackService extends Service {
loadQueue();
}
- private MediaPlayer createMediaPlayer() {
- return createMediaPlayer(new MediaPlayer());
- }
-
- private MediaPlayer createMediaPlayer(MediaPlayer mp) {
- if (mp != null) {
- mp.setOnPreparedListener(preparedListener);
- mp.setOnCompletionListener(completionListener);
- mp.setOnSeekCompleteListener(onSeekCompleteListener);
- mp.setOnErrorListener(onErrorListener);
- mp.setOnBufferingUpdateListener(onBufferingUpdateListener);
- mp.setOnInfoListener(onInfoListener);
+ private IPlayer createMediaPlayer() {
+ IPlayer player;
+ if (media == null || media.getMediaType() == MediaType.VIDEO) {
+ player = new VideoPlayer();
+ } else {
+ player = new AudioPlayer(this);
}
- return mp;
+ return createMediaPlayer(player);
}
+ private IPlayer createMediaPlayer(IPlayer mp) {
+ if (mp != null && media != null) {
+ if (media.getMediaType() == MediaType.AUDIO) {
+ ((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener);
+ ((AudioPlayer) mp)
+ .setOnCompletionListener(audioCompletionListener);
+ ((AudioPlayer) mp)
+ .setOnSeekCompleteListener(audioSeekCompleteListener);
+ ((AudioPlayer) mp).setOnErrorListener(audioErrorListener);
+ ((AudioPlayer) mp)
+ .setOnBufferingUpdateListener(audioBufferingUpdateListener);
+ ((AudioPlayer) mp).setOnInfoListener(audioInfoListener);
+ } else {
+ ((VideoPlayer) mp).setOnPreparedListener(videoPreparedListener);
+ ((VideoPlayer) mp)
+ .setOnCompletionListener(videoCompletionListener);
+ ((VideoPlayer) mp)
+ .setOnSeekCompleteListener(videoSeekCompleteListener);
+ ((VideoPlayer) mp).setOnErrorListener(videoErrorListener);
+ ((VideoPlayer) mp)
+ .setOnBufferingUpdateListener(videoBufferingUpdateListener);
+ ((VideoPlayer) mp).setOnInfoListener(videoInfoListener);
+ }
+ }
+ return mp;
+ }
+
@SuppressLint("NewApi")
@Override
public void onDestroy() {
@@ -475,7 +503,7 @@ public class PlaybackService extends Service {
seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
break;
}
- }
+ }
}
/**
@@ -568,6 +596,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Setting up media player");
try {
MediaType mediaType = media.getMediaType();
+ player = createMediaPlayer();
if (mediaType == MediaType.AUDIO) {
if (AppConfig.DEBUG)
Log.d(TAG, "Mime type is audio");
@@ -662,105 +691,169 @@ public class PlaybackService extends Service {
}
}
- private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
- @Override
- public void onPrepared(MediaPlayer mp) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Resource prepared");
- mp.seekTo(media.getPosition());
- if (media.getDuration() == 0) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Setting duration of media");
- media.setDuration(mp.getDuration());
- }
- setStatus(PlayerStatus.PREPARED);
- if (chapterLoader != null) {
- chapterLoader.interrupt();
- }
- chapterLoader = new Thread() {
- @Override
- public void run() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Chapter loader started");
- if (media != null && media.getChapters() == null) {
- media.loadChapterMarks();
- if (!isInterrupted() && media.getChapters() != null) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
- 0);
- }
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "Chapter loader stopped");
- }
- };
- chapterLoader.start();
+ private final com.aocate.media.MediaPlayer.OnPreparedListener audioPreparedListener = new com.aocate.media.MediaPlayer.OnPreparedListener() {
+ @Override
+ public void onPrepared(com.aocate.media.MediaPlayer mp) {
+ genericOnPrepared(mp);
+ }
+ };
- if (startWhenPrepared) {
- play();
- }
- }
- };
+ private final android.media.MediaPlayer.OnPreparedListener videoPreparedListener = new android.media.MediaPlayer.OnPreparedListener() {
+ @Override
+ public void onPrepared(android.media.MediaPlayer mp) {
+ genericOnPrepared(mp);
+ }
+ };
+
+ private final void genericOnPrepared(Object inObj) {
+ IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Resource prepared");
+ mp.seekTo(media.getPosition());
+ if (media.getDuration() == 0) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Setting duration of media");
+ media.setDuration(mp.getDuration());
+ }
+ setStatus(PlayerStatus.PREPARED);
+ if (chapterLoader != null) {
+ chapterLoader.interrupt();
+ }
+ chapterLoader = new Thread() {
+ @Override
+ public void run() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Chapter loader started");
+ if (media != null && media.getChapters() == null) {
+ media.loadChapterMarks();
+ if (!isInterrupted() && media.getChapters() != null) {
+ sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
+ 0);
+ }
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Chapter loader stopped");
+ }
+ };
+ chapterLoader.start();
- private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
+ if (startWhenPrepared) {
+ play();
+ }
+ }
- @Override
- public void onSeekComplete(MediaPlayer mp) {
- if (status == PlayerStatus.SEEKING) {
- setStatus(statusBeforeSeek);
- }
+ private final com.aocate.media.MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener = new com.aocate.media.MediaPlayer.OnSeekCompleteListener() {
+ @Override
+ public void onSeekComplete(com.aocate.media.MediaPlayer mp) {
+ genericSeekCompleteListener();
+ }
+ };
- }
- };
+ private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() {
+ @Override
+ public void onSeekComplete(android.media.MediaPlayer mp) {
+ genericSeekCompleteListener();
+ }
+ };
- private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
+ private final void genericSeekCompleteListener() {
+ if (status == PlayerStatus.SEEKING) {
+ setStatus(statusBeforeSeek);
+ }
+ }
- @Override
- public boolean onInfo(MediaPlayer mp, int what, int extra) {
- switch (what) {
- case MediaPlayer.MEDIA_INFO_BUFFERING_START:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
- return true;
- case MediaPlayer.MEDIA_INFO_BUFFERING_END:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
- return true;
- default:
- return false;
- }
- }
- };
+ private final com.aocate.media.MediaPlayer.OnInfoListener audioInfoListener = new com.aocate.media.MediaPlayer.OnInfoListener() {
+ @Override
+ public boolean onInfo(com.aocate.media.MediaPlayer mp, int what,
+ int extra) {
+ return genericInfoListener(what);
+ }
+ };
- private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
- private static final String TAG = "PlaybackService.onErrorListener";
+ private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() {
+ @Override
+ public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) {
+ return genericInfoListener(what);
+ }
+ };
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- Log.w(TAG, "An error has occured: " + what);
- if (mp.isPlaying()) {
- pause(true, true);
- }
- sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
- setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
- stopSelf();
- return true;
- }
- };
+ private boolean genericInfoListener(int what) {
+ switch (what) {
+ case MediaPlayer.MEDIA_INFO_BUFFERING_START:
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
+ return true;
+ case MediaPlayer.MEDIA_INFO_BUFFERING_END:
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
+ return true;
+ default:
+ return false;
+ }
+ }
- private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
+ private final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(com.aocate.media.MediaPlayer mp, int what,
+ int extra) {
+ return genericOnError(mp, what, extra);
+ }
+ };
- @Override
- public void onCompletion(MediaPlayer mp) {
- endPlayback(true);
- }
- };
+ private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(android.media.MediaPlayer mp, int what, int extra) {
+ return genericOnError(mp, what, extra);
+ }
+ };
- private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
+ private boolean genericOnError(Object inObj, int what, int extra) {
+ final String TAG = "PlaybackService.onErrorListener";
+ Log.w(TAG, "An error has occured: " + what + " " + extra);
+ IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
+ if (mp.isPlaying()) {
+ pause(true, true);
+ }
+ sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
+ setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
+ stopSelf();
+ return true;
+ }
- @Override
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
+ private final com.aocate.media.MediaPlayer.OnCompletionListener audioCompletionListener = new com.aocate.media.MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(com.aocate.media.MediaPlayer mp) {
+ genericOnCompletion();
+ }
+ };
- }
- };
+ private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener = new android.media.MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(android.media.MediaPlayer mp) {
+ genericOnCompletion();
+ }
+ };
+
+ private void genericOnCompletion() {
+ endPlayback(true);
+ }
+
+ private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() {
+ @Override
+ public void onBufferingUpdate(com.aocate.media.MediaPlayer mp,
+ int percent) {
+ genericOnBufferingUpdate(percent);
+ }
+ };
+
+ private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener = new android.media.MediaPlayer.OnBufferingUpdateListener() {
+ @Override
+ public void onBufferingUpdate(android.media.MediaPlayer mp, int percent) {
+ genericOnBufferingUpdate(percent);
+ }
+ };
+
+ private void genericOnBufferingUpdate(int percent) {
+ sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
+ }
private void endPlayback(boolean playNextEpisode) {
if (AppConfig.DEBUG)
@@ -783,7 +876,6 @@ public class PlaybackService extends Service {
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
}
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
- DBWriter.setFeedMedia(PlaybackService.this, (FeedMedia) media);
long autoDeleteMediaId = ((FeedComponent) media).getId();
if (shouldStream) {
autoDeleteMediaId = -1;
@@ -863,7 +955,7 @@ public class PlaybackService extends Service {
/**
* Saves the current position and pauses playback. Note that, if audiofocus
* is abandoned, the lockscreen controls will also disapear.
- *
+ *
* @param abandonFocus
* is true if the service should release audio focus
* @param reinit
@@ -939,6 +1031,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Resuming/Starting playback");
writePlaybackPreferences();
+ setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed()));
player.start();
if (status != PlayerStatus.PAUSED) {
player.seekTo((int) media.getPosition());
@@ -1124,7 +1217,7 @@ public class PlaybackService extends Service {
/**
* Seek a specific position from the current position
- *
+ *
* @param delta
* offset from current position (positive or negative)
* */
@@ -1282,18 +1375,20 @@ public class PlaybackService extends Service {
isPlaying = true;
}
- Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
- i.putExtra("id", 1);
- i.putExtra("artist", "");
- i.putExtra("album", media.getFeedTitle());
- i.putExtra("track", media.getEpisodeTitle());
- i.putExtra("playing", isPlaying);
- if (queue != null) {
- i.putExtra("ListSize", queue.size());
+ if (media != null) {
+ Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
+ i.putExtra("id", 1);
+ i.putExtra("artist", "");
+ i.putExtra("album", media.getFeedTitle());
+ i.putExtra("track", media.getEpisodeTitle());
+ i.putExtra("playing", isPlaying);
+ if (queue != null) {
+ i.putExtra("ListSize", queue.size());
+ }
+ i.putExtra("duration", media.getDuration());
+ i.putExtra("position", media.getPosition());
+ sendBroadcast(i);
}
- i.putExtra("duration", media.getDuration());
- i.putExtra("position", media.getPosition());
- sendBroadcast(i);
}
/**
@@ -1370,7 +1465,7 @@ public class PlaybackService extends Service {
}
}
}
- };
+ };
/** Periodically saves the position of the media file */
class PositionSaver implements Runnable {
@@ -1472,7 +1567,7 @@ public class PlaybackService extends Service {
return media;
}
- public MediaPlayer getPlayer() {
+ public IPlayer getPlayer() {
return player;
}
@@ -1485,6 +1580,53 @@ public class PlaybackService extends Service {
postStatusUpdateIntent();
}
+ public boolean canSetSpeed() {
+ if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
+ return ((AudioPlayer) player).canSetSpeed();
+ }
+ return false;
+ }
+
+ public boolean canSetPitch() {
+ if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
+ return ((AudioPlayer) player).canSetPitch();
+ }
+ return false;
+ }
+
+ public void setSpeed(float speed) {
+ if (media != null && media.getMediaType() == MediaType.AUDIO) {
+ AudioPlayer audioPlayer = (AudioPlayer) player;
+ if (audioPlayer.canSetSpeed()) {
+ audioPlayer.setPlaybackSpeed((float) speed);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Playback speed was set to " + speed);
+ sendNotificationBroadcast(
+ NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
+ }
+ }
+ }
+
+ public void setPitch(float pitch) {
+ if (media != null && media.getMediaType() == MediaType.AUDIO) {
+ AudioPlayer audioPlayer = (AudioPlayer) player;
+ if (audioPlayer.canSetPitch()) {
+ audioPlayer.setPlaybackPitch((float) pitch);
+ }
+ }
+ }
+
+ public float getCurrentPlaybackSpeed() {
+ if (media.getMediaType() == MediaType.AUDIO
+ && player instanceof AudioPlayer) {
+ AudioPlayer audioPlayer = (AudioPlayer) player;
+ if (audioPlayer.canSetSpeed()) {
+ return audioPlayer.getCurrentSpeedMultiplier();
+ }
+ }
+ return -1;
+ }
+
/**
* call getDuration() on mediaplayer or return INVALID_TIME if player is in
* an invalid state. This method should be used instead of calling
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index c84a6f913..4040c85a8 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -184,7 +184,7 @@ public class DownloadService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getParcelableExtra(EXTRA_REQUEST) != null) {
onDownloadQueued(intent);
- } else if (numberOfDownloads.equals(0)) {
+ } else if (numberOfDownloads.get() == 0) {
stopSelf();
}
return Service.START_NOT_STICKY;
@@ -421,52 +421,24 @@ public class DownloadService extends Service {
return null;
}
- @SuppressLint("NewApi")
- public void onDownloadCompleted(final Downloader downloader) {
- final AsyncTask<Void, Void, Void> handlerTask = new AsyncTask<Void, Void, Void>() {
- boolean successful;
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- if (!successful) {
- queryDownloads();
- }
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- removeDownload(downloader);
- }
-
- @Override
- protected Void doInBackground(Void... params) {
-
-
- return null;
- }
- };
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- handlerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- handlerTask.execute();
- }
- }
-
/**
* Remove download from the DownloadRequester list and from the
* DownloadService list.
*/
private void removeDownload(final Downloader d) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Removing downloader: "
- + d.getDownloadRequest().getSource());
- boolean rc = downloads.remove(d);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Result of downloads.remove: " + rc);
- DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
- sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Removing downloader: "
+ + d.getDownloadRequest().getSource());
+ boolean rc = downloads.remove(d);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Result of downloads.remove: " + rc);
+ DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
+ sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ }
+ });
}
/**
@@ -828,8 +800,9 @@ public class DownloadService extends Service {
media.setFile_url(request.getDestination());
// Get duration
- MediaPlayer mediaplayer = new MediaPlayer();
+ MediaPlayer mediaplayer = null;
try {
+ mediaplayer = new MediaPlayer();
mediaplayer.setDataSource(media.getFile_url());
mediaplayer.prepare();
media.setDuration(mediaplayer.getDuration());
@@ -838,8 +811,13 @@ public class DownloadService extends Service {
mediaplayer.reset();
} catch (IOException e) {
e.printStackTrace();
+ } catch (RuntimeException e) {
+ // Thrown by MediaPlayer initialization on some devices
+ e.printStackTrace();
} finally {
- mediaplayer.release();
+ if (mediaplayer != null) {
+ mediaplayer.release();
+ }
}
if (media.getItem().getChapters() == null) {
diff --git a/src/de/danoeh/antennapod/service/download/DownloadStatus.java b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
index 62e54cbb4..487c3b3de 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadStatus.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
@@ -52,7 +52,7 @@ public class DownloadStatus {
this.feedfileId = feedfileId;
this.reason = reason;
this.successful = successful;
- this.completionDate = completionDate;
+ this.completionDate = (Date) completionDate.clone();
this.reasonDetailed = reasonDetailed;
this.feedfileType = feedfileType;
}
@@ -133,7 +133,7 @@ public class DownloadStatus {
}
public Date getCompletionDate() {
- return completionDate;
+ return (Date) completionDate.clone();
}
public long getFeedfileId() {
@@ -162,6 +162,7 @@ public class DownloadStatus {
this.successful = false;
this.reason = reason;
this.reasonDetailed = reasonDetailed;
+ this.done = true;
}
public void setCancelled() {
@@ -172,7 +173,7 @@ public class DownloadStatus {
}
public void setCompletionDate(Date completionDate) {
- this.completionDate = completionDate;
+ this.completionDate = (Date) completionDate.clone();
}
public void setId(long id) {
diff --git a/src/de/danoeh/antennapod/service/download/HttpDownloader.java b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
index c9671ceb3..582fb9575 100644
--- a/src/de/danoeh/antennapod/service/download/HttpDownloader.java
+++ b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
@@ -6,12 +6,12 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -30,161 +30,186 @@ import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.StorageUtils;
public class HttpDownloader extends Downloader {
- private static final String TAG = "HttpDownloader";
-
- private static final int MAX_REDIRECTS = 5;
-
- private static final int BUFFER_SIZE = 8 * 1024;
- private static final int CONNECTION_TIMEOUT = 30000;
- private static final int SOCKET_TIMEOUT = 30000;
-
- public HttpDownloader(DownloadRequest request) {
- super(request);
- }
-
- private DefaultHttpClient createHttpClient() {
- DefaultHttpClient httpClient = new DefaultHttpClient();
- HttpParams params = httpClient.getParams();
- params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS);
- params.setBooleanParameter("http.protocol.reject-relative-redirect",
- false);
- HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
- HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
- HttpClientParams.setRedirecting(params, true);
-
- // Workaround for broken URLs in redirection
- ((AbstractHttpClient) httpClient)
- .setRedirectHandler(new APRedirectHandler());
- return httpClient;
- }
-
- @Override
- protected void download() {
- DefaultHttpClient httpClient = null;
- OutputStream out = null;
- InputStream connection = null;
- try {
- HttpGet httpGet = new HttpGet(request.getSource());
- httpClient = createHttpClient();
- HttpResponse response = httpClient.execute(httpGet);
- HttpEntity httpEntity = response.getEntity();
- int responseCode = response.getStatusLine().getStatusCode();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Response code is " + responseCode);
- if (responseCode == HttpURLConnection.HTTP_OK && httpEntity != null) {
- if (StorageUtils.storageAvailable(PodcastApp.getInstance())) {
- File destination = new File(request.getDestination());
- if (!destination.exists()) {
- connection = AndroidHttpClient
- .getUngzippedContent(httpEntity);
- InputStream in = new BufferedInputStream(connection);
- out = new BufferedOutputStream(new FileOutputStream(
- destination));
- byte[] buffer = new byte[BUFFER_SIZE];
- int count = 0;
- request.setStatusMsg(R.string.download_running);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Getting size of download");
- request.setSize(httpEntity.getContentLength());
- if (AppConfig.DEBUG)
- Log.d(TAG, "Size is " + request.getSize());
- if (request.getSize() < 0) {
- request.setSize(DownloadStatus.SIZE_UNKNOWN);
- }
-
- long freeSpace = StorageUtils.getFreeSpaceAvailable();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Free space is " + freeSpace);
- if (request.getSize() == DownloadStatus.SIZE_UNKNOWN
- || request.getSize() <= freeSpace) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Starting download");
- while (!cancelled
- && (count = in.read(buffer)) != -1) {
- out.write(buffer, 0, count);
- request.setSoFar(request.getSoFar() + count);
- request.setProgressPercent((int) (((double) request
- .getSoFar() / (double) request
- .getSize()) * 100));
- }
- if (cancelled) {
- onCancelled();
- } else {
- onSuccess();
- }
- } else {
- onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
- }
- } else {
- Log.w(TAG, "File already exists");
- onFail(DownloadError.ERROR_FILE_EXISTS, null);
- }
- } else {
- onFail(DownloadError.ERROR_DEVICE_NOT_FOUND, null);
- }
- } else {
- onFail(DownloadError.ERROR_HTTP_DATA_ERROR,
- String.valueOf(responseCode));
- }
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_MALFORMED_URL, e.getMessage());
- } catch (SocketTimeoutException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_CONNECTION_ERROR, e.getMessage());
- } catch (UnknownHostException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
- } catch (IOException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
- } catch (NullPointerException e) {
- // might be thrown by connection.getInputStream()
- e.printStackTrace();
- onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
- } finally {
- IOUtils.closeQuietly(out);
- if (httpClient != null) {
- httpClient.getConnectionManager().shutdown();
- }
- }
- }
-
- private void onSuccess() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Download was successful");
- result.setSuccessful();
- }
-
- private void onFail(DownloadError reason, String reasonDetailed) {
- if (AppConfig.DEBUG) {
- Log.d(TAG, "Download failed");
- }
+ private static final String TAG = "HttpDownloader";
+
+ private static final int MAX_REDIRECTS = 5;
+
+ private static final int BUFFER_SIZE = 8 * 1024;
+ private static final int CONNECTION_TIMEOUT = 30000;
+ private static final int SOCKET_TIMEOUT = 30000;
+
+ public HttpDownloader(DownloadRequest request) {
+ super(request);
+ }
+
+ private DefaultHttpClient createHttpClient() {
+ DefaultHttpClient httpClient = new DefaultHttpClient();
+ HttpParams params = httpClient.getParams();
+ params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS);
+ params.setBooleanParameter("http.protocol.reject-relative-redirect",
+ false);
+ HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
+ HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
+ HttpClientParams.setRedirecting(params, true);
+
+ // Workaround for broken URLs in redirection
+ ((AbstractHttpClient) httpClient)
+ .setRedirectHandler(new APRedirectHandler());
+ return httpClient;
+ }
+
+ @Override
+ protected void download() {
+ DefaultHttpClient httpClient = null;
+ BufferedOutputStream out = null;
+ InputStream connection = null;
+ try {
+ HttpGet httpGet = new HttpGet(request.getSource());
+ httpClient = createHttpClient();
+ HttpResponse response = httpClient.execute(httpGet);
+ HttpEntity httpEntity = response.getEntity();
+ int responseCode = response.getStatusLine().getStatusCode();
+ Header contentEncodingHeader = response.getFirstHeader("Content-Encoding");
+
+ final boolean isGzip = contentEncodingHeader != null &&
+ contentEncodingHeader.getValue().equalsIgnoreCase("gzip");
+
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Response code is " + responseCode);
+
+ if (responseCode != HttpURLConnection.HTTP_OK || httpEntity == null) {
+ onFail(DownloadError.ERROR_HTTP_DATA_ERROR,
+ String.valueOf(responseCode));
+ return;
+ }
+
+ if (!StorageUtils.storageAvailable(PodcastApp.getInstance())) {
+ onFail(DownloadError.ERROR_DEVICE_NOT_FOUND, null);
+ return;
+ }
+
+ File destination = new File(request.getDestination());
+ if (destination.exists()) {
+ Log.w(TAG, "File already exists");
+ onFail(DownloadError.ERROR_FILE_EXISTS, null);
+ return;
+ }
+
+ connection = new BufferedInputStream(AndroidHttpClient
+ .getUngzippedContent(httpEntity));
+ out = new BufferedOutputStream(new FileOutputStream(
+ destination));
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ request.setStatusMsg(R.string.download_running);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Getting size of download");
+ request.setSize(httpEntity.getContentLength());
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Size is " + request.getSize());
+ if (request.getSize() < 0) {
+ request.setSize(DownloadStatus.SIZE_UNKNOWN);
+ }
+
+ long freeSpace = StorageUtils.getFreeSpaceAvailable();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Free space is " + freeSpace);
+
+ if (request.getSize() != DownloadStatus.SIZE_UNKNOWN
+ && request.getSize() > freeSpace) {
+ onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
+ return;
+ }
+
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Starting download");
+ while (!cancelled
+ && (count = connection.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ request.setSoFar(request.getSoFar() + count);
+ request.setProgressPercent((int) (((double) request
+ .getSoFar() / (double) request
+ .getSize()) * 100));
+ }
+ if (cancelled) {
+ onCancelled();
+ } else {
+ out.flush();
+ // check if size specified in the response header is the same as the size of the
+ // written file. This check cannot be made if compression was used
+ if (!isGzip && request.getSize() != DownloadStatus.SIZE_UNKNOWN &&
+ request.getSoFar() != request.getSize()) {
+ onFail(DownloadError.ERROR_IO_ERROR,
+ "Download completed but size: " +
+ request.getSoFar() +
+ " does not equal expected size " +
+ request.getSize());
+ return;
+ }
+ onSuccess();
+ }
+
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_MALFORMED_URL, e.getMessage());
+ } catch (SocketTimeoutException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_CONNECTION_ERROR, e.getMessage());
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
+ } catch (IOException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
+ } catch (NullPointerException e) {
+ // might be thrown by connection.getInputStream()
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
+ } finally {
+ IOUtils.closeQuietly(out);
+ if (httpClient != null) {
+ httpClient.getConnectionManager().shutdown();
+ }
+ }
+ }
+
+ private void onSuccess() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Download was successful");
+ result.setSuccessful();
+ }
+
+ private void onFail(DownloadError reason, String reasonDetailed) {
+ if (AppConfig.DEBUG) {
+ Log.d(TAG, "Download failed");
+ }
result.setFailed(reason, reasonDetailed);
- cleanup();
- }
+ cleanup();
+ }
- private void onCancelled() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Download was cancelled");
+ private void onCancelled() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Download was cancelled");
result.setCancelled();
- cleanup();
- }
-
- /** Deletes unfinished downloads. */
- private void cleanup() {
- if (request.getDestination() != null) {
- File dest = new File(request.getDestination());
- if (dest.exists()) {
- boolean rc = dest.delete();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Deleted file " + dest.getName() + "; Result: "
- + rc);
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG, "cleanup() didn't delete file: does not exist.");
- }
- }
- }
+ cleanup();
+ }
+
+ /**
+ * Deletes unfinished downloads.
+ */
+ private void cleanup() {
+ if (request.getDestination() != null) {
+ File dest = new File(request.getDestination());
+ if (dest.exists()) {
+ boolean rc = dest.delete();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Deleted file " + dest.getName() + "; Result: "
+ + rc);
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "cleanup() didn't delete file: does not exist.");
+ }
+ }
+ }
}