summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2012-08-18 00:53:09 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2012-08-18 00:53:09 +0200
commitfdb9a296adeab9f5f169b98c8488354ec821ebc6 (patch)
tree286000e4685554e6aa9874c255a3724ad2e6ec57 /src/de/danoeh/antennapod
parent1b15ad65b65f1bc9b0bb74b7c9118796048713d5 (diff)
parentbcbc1624123b4c7be974d9f67cd1bd31114d1d42 (diff)
downloadAntennaPod-fdb9a296adeab9f5f169b98c8488354ec821ebc6.zip
Merge branch 'downloadmanager' into develop
Diffstat (limited to 'src/de/danoeh/antennapod')
-rw-r--r--src/de/danoeh/antennapod/activity/AddFeedActivity.java2
-rw-r--r--src/de/danoeh/antennapod/activity/DownloadActivity.java155
-rw-r--r--src/de/danoeh/antennapod/activity/DownloadLogActivity.java34
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java11
-rw-r--r--src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java4
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadObserver.java18
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadStatus.java60
-rw-r--r--src/de/danoeh/antennapod/feed/FeedFile.java20
-rw-r--r--src/de/danoeh/antennapod/feed/FeedManager.java35
-rw-r--r--src/de/danoeh/antennapod/fragment/FeedlistFragment.java12
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemlistFragment.java2
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java (renamed from src/de/danoeh/antennapod/service/DownloadService.java)576
-rw-r--r--src/de/danoeh/antennapod/service/download/Downloader.java59
-rw-r--r--src/de/danoeh/antennapod/service/download/HttpDownloader.java136
-rw-r--r--src/de/danoeh/antennapod/storage/DownloadRequester.java154
-rw-r--r--src/de/danoeh/antennapod/syndication/util/SyndDateUtils.java55
-rw-r--r--src/de/danoeh/antennapod/util/DownloadError.java22
-rw-r--r--src/de/danoeh/antennapod/util/StorageUtils.java11
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java3
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java2
21 files changed, 870 insertions, 503 deletions
diff --git a/src/de/danoeh/antennapod/activity/AddFeedActivity.java b/src/de/danoeh/antennapod/activity/AddFeedActivity.java
index a5185bf9b..c17ef4996 100644
--- a/src/de/danoeh/antennapod/activity/AddFeedActivity.java
+++ b/src/de/danoeh/antennapod/activity/AddFeedActivity.java
@@ -23,7 +23,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.DownloadStatus;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedManager;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.ConnectionTester;
import de.danoeh.antennapod.util.DownloadError;
diff --git a/src/de/danoeh/antennapod/activity/DownloadActivity.java b/src/de/danoeh/antennapod/activity/DownloadActivity.java
index 363084a81..404cb49c6 100644
--- a/src/de/danoeh/antennapod/activity/DownloadActivity.java
+++ b/src/de/danoeh/antennapod/activity/DownloadActivity.java
@@ -1,8 +1,15 @@
package de.danoeh.antennapod.activity;
+import java.util.List;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
@@ -18,9 +25,9 @@ import com.actionbarsherlock.view.MenuItem;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadlistAdapter;
-import de.danoeh.antennapod.asynctask.DownloadObserver;
import de.danoeh.antennapod.asynctask.DownloadStatus;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
+import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.storage.DownloadRequester;
/**
@@ -28,7 +35,7 @@ import de.danoeh.antennapod.storage.DownloadRequester;
* objects created by a DownloadObserver.
*/
public class DownloadActivity extends SherlockListActivity implements
- ActionMode.Callback, DownloadObserver.Callback {
+ ActionMode.Callback {
private static final String TAG = "DownloadActivity";
private static final int MENU_SHOW_LOG = 0;
@@ -38,7 +45,11 @@ public class DownloadActivity extends SherlockListActivity implements
private ActionMode mActionMode;
private DownloadStatus selectedDownload;
- private DownloadObserver downloadObserver;
+
+ private DownloadService downloadService = null;
+ boolean mIsBound;
+
+ private AsyncTask<Void, Void, Void> contentRefresher;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -47,24 +58,25 @@ public class DownloadActivity extends SherlockListActivity implements
Log.d(TAG, "Creating Activity");
requester = DownloadRequester.getInstance();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
}
@Override
protected void onPause() {
super.onPause();
unbindService(mConnection);
- if (downloadObserver != null) {
- downloadObserver.unregisterCallback(DownloadActivity.this);
- }
+ unregisterReceiver(contentChanged);
}
@Override
protected void onResume() {
super.onResume();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Trying to bind service");
+ registerReceiver(contentChanged, new IntentFilter(
+ DownloadService.ACTION_DOWNLOADS_CONTENT_CHANGED));
bindService(new Intent(this, DownloadService.class), mConnection, 0);
+ startContentRefresher();
+ if (dla != null) {
+ dla.notifyDataSetChanged();
+ }
}
@Override
@@ -72,6 +84,71 @@ public class DownloadActivity extends SherlockListActivity implements
super.onStop();
if (AppConfig.DEBUG)
Log.d(TAG, "Stopping Activity");
+ stopContentRefresher();
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceDisconnected(ComponentName className) {
+ downloadService = null;
+ mIsBound = false;
+ Log.i(TAG, "Closed connection with DownloadService.");
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ downloadService = ((DownloadService.LocalBinder) service)
+ .getService();
+ mIsBound = true;
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Connection to service established");
+ dla = new DownloadlistAdapter(DownloadActivity.this, 0,
+ downloadService.getDownloads());
+ setListAdapter(dla);
+ dla.notifyDataSetChanged();
+ }
+ };
+
+ @SuppressLint("NewApi")
+ private void startContentRefresher() {
+ if (contentRefresher != null) {
+ contentRefresher.cancel(true);
+ }
+ contentRefresher = new AsyncTask<Void, Void, Void>() {
+ private final int WAITING_INTERVALL = 1000;
+
+ @Override
+ protected void onProgressUpdate(Void... values) {
+ super.onProgressUpdate(values);
+ if (dla != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Refreshing content automatically");
+ dla.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ while (!isCancelled()) {
+ try {
+ Thread.sleep(WAITING_INTERVALL);
+ publishProgress();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ return null;
+ }
+ };
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ contentRefresher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ contentRefresher.execute();
+ }
+ }
+
+ private void stopContentRefresher() {
+ if (contentRefresher != null) {
+ contentRefresher.cancel(true);
+ }
}
@Override
@@ -82,7 +159,7 @@ public class DownloadActivity extends SherlockListActivity implements
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int position, long id) {
- DownloadStatus selection = dla.getItem(position);
+ DownloadStatus selection = dla.getItem(position).getStatus();
if (selection != null && mActionMode != null) {
mActionMode.finish();
}
@@ -142,8 +219,7 @@ public class DownloadActivity extends SherlockListActivity implements
boolean handled = false;
switch (item.getItemId()) {
case R.id.cancel_download_item:
- requester.cancelDownload(this, selectedDownload.getFeedFile()
- .getDownloadId());
+ requester.cancelDownload(this, selectedDownload.getFeedFile());
handled = true;
break;
}
@@ -158,53 +234,16 @@ public class DownloadActivity extends SherlockListActivity implements
dla.setSelectedItemIndex(DownloadlistAdapter.SELECTION_NONE);
}
- private DownloadService downloadService = null;
- boolean mIsBound;
+ private BroadcastReceiver contentChanged = new BroadcastReceiver() {
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- downloadService = ((DownloadService.LocalBinder) service)
- .getService();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Connection to service established");
- dla = new DownloadlistAdapter(DownloadActivity.this, 0,
- downloadService.getDownloadObserver().getStatusList());
- setListAdapter(dla);
- downloadObserver = downloadService.getDownloadObserver();
- downloadObserver.registerCallback(DownloadActivity.this);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- downloadService = null;
- mIsBound = false;
- Log.i(TAG, "Closed connection with DownloadService.");
- }
- };
-
- @Override
- public void onProgressUpdate() {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (dla != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Refreshing content");
dla.notifyDataSetChanged();
-
- }
- });
- }
-
- @Override
- public void onFinish() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Observer has finished, clearing adapter");
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- dla.clear();
- dla.notifyDataSetInvalidated();
}
- });
+ }
+ };
- }
}
diff --git a/src/de/danoeh/antennapod/activity/DownloadLogActivity.java b/src/de/danoeh/antennapod/activity/DownloadLogActivity.java
index 11a15accb..2c19b5649 100644
--- a/src/de/danoeh/antennapod/activity/DownloadLogActivity.java
+++ b/src/de/danoeh/antennapod/activity/DownloadLogActivity.java
@@ -1,5 +1,9 @@
package de.danoeh.antennapod.activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockListActivity;
@@ -9,7 +13,10 @@ import com.actionbarsherlock.view.MenuItem;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.feed.FeedManager;
-/** Displays completed and failed downloads in a list. The data comes from the FeedManager. */
+/**
+ * Displays completed and failed downloads in a list. The data comes from the
+ * FeedManager.
+ */
public class DownloadLogActivity extends SherlockListActivity {
private static final String TAG = "DownloadLogActivity";
@@ -27,6 +34,20 @@ public class DownloadLogActivity extends SherlockListActivity {
}
@Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(contentUpdate);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(contentUpdate, new IntentFilter(
+ FeedManager.ACTION_DOWNLOADLOG_UPDATE));
+ dla.notifyDataSetChanged();
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@@ -43,4 +64,15 @@ public class DownloadLogActivity extends SherlockListActivity {
return true;
}
+ private BroadcastReceiver contentUpdate = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction()
+ .equals(FeedManager.ACTION_DOWNLOADLOG_UPDATE)) {
+ dla.notifyDataSetChanged();
+ }
+ }
+ };
+
}
diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java
index eab7b6ea0..d6db17740 100644
--- a/src/de/danoeh/antennapod/activity/MainActivity.java
+++ b/src/de/danoeh/antennapod/activity/MainActivity.java
@@ -23,8 +23,8 @@ import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.fragment.FeedlistFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.UnreadItemlistFragment;
-import de.danoeh.antennapod.service.DownloadService;
import de.danoeh.antennapod.service.PlaybackService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.AppConfig;
diff --git a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index d103787f5..4346de927 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -14,16 +14,17 @@ import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.R;
-public class DownloadlistAdapter extends ArrayAdapter<DownloadStatus> {
+public class DownloadlistAdapter extends ArrayAdapter<Downloader> {
private int selectedItemIndex;
public static final int SELECTION_NONE = -1;
public DownloadlistAdapter(Context context, int textViewResourceId,
- List<DownloadStatus> objects) {
+ List<Downloader> objects) {
super(context, textViewResourceId, objects);
this.selectedItemIndex = SELECTION_NONE;
}
@@ -31,7 +32,7 @@ public class DownloadlistAdapter extends ArrayAdapter<DownloadStatus> {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
- DownloadStatus status = getItem(position);
+ DownloadStatus status = getItem(position).getStatus();
FeedFile feedFile = status.getFeedFile();
// Inflate layout
if (convertView == null) {
@@ -77,7 +78,9 @@ public class DownloadlistAdapter extends ArrayAdapter<DownloadStatus> {
}
}
holder.title.setText(titleText);
- holder.message.setText(status.getStatusMsg());
+ if (status.getStatusMsg() != 0) {
+ holder.message.setText(status.getStatusMsg());
+ }
holder.downloaded.setText(Converter.byteToString(status.getSoFar())
+ " / " + Converter.byteToString(status.getSize()));
holder.percent.setText(status.getProgressPercent() + "%");
diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index 568768058..9dbc225d0 100644
--- a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -5,6 +5,7 @@ import java.util.List;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
+import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.R;
import android.widget.ArrayAdapter;
@@ -121,7 +122,8 @@ public class FeedItemlistAdapter extends ArrayAdapter<FeedItem> {
holder.downloaded.setVisibility(View.GONE);
}
- if (item.getMedia().isDownloading()) {
+ if (DownloadRequester.getInstance().isDownloadingFile(
+ item.getMedia())) {
holder.downloading.setVisibility(View.VISIBLE);
} else {
holder.downloading.setVisibility(View.GONE);
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
index 5125e6b91..40ab869f3 100644
--- a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
+++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.asynctask;
+/*package de.danoeh.antennapod.asynctask;
import java.util.ArrayList;
import java.util.Collections;
@@ -16,16 +16,17 @@ import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
-/** Observes the status of a specific Download */
-public class DownloadObserver extends AsyncTask<Void, Void, Void> {
+*//** Observes the status of a specific Download *//*
+public class DownloadObserver{
+ *//******
private static final String TAG = "DownloadObserver";
- /** Types of downloads to observe. */
+ /** Types of downloads to observe. *//*
public static final int TYPE_FEED = 0;
public static final int TYPE_IMAGE = 1;
public static final int TYPE_MEDIA = 2;
- /** Error codes */
+ *//** Error codes *//*
public static final int ALREADY_DOWNLOADED = 1;
public static final int NO_DOWNLOAD_FOUND = 2;
@@ -165,7 +166,7 @@ public class DownloadObserver extends AsyncTask<Void, Void, Void> {
}
- /** Request a cursor with all running Feedfile downloads */
+ *//** Request a cursor with all running Feedfile downloads *//*
private Cursor getDownloadCursor() {
// Collect download ids
@@ -186,7 +187,7 @@ public class DownloadObserver extends AsyncTask<Void, Void, Void> {
return result;
}
- /** Return value of a specific column */
+ *//** Return value of a specific column *//*
private int getDownloadStatus(Cursor c, String column) {
int status = c.getInt(c.getColumnIndex(column));
return status;
@@ -205,7 +206,7 @@ public class DownloadObserver extends AsyncTask<Void, Void, Void> {
return context;
}
- /** Find a DownloadStatus entry by its FeedFile */
+ *//** Find a DownloadStatus entry by its FeedFile *//*
public DownloadStatus findDownloadStatus(FeedFile f) {
for (DownloadStatus status : statusList) {
if (status.feedfile == f) {
@@ -238,3 +239,4 @@ public class DownloadObserver extends AsyncTask<Void, Void, Void> {
}
}
+*/ \ No newline at end of file
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadStatus.java b/src/de/danoeh/antennapod/asynctask/DownloadStatus.java
index 67cf4a6d8..e5b2bcf5c 100644
--- a/src/de/danoeh/antennapod/asynctask/DownloadStatus.java
+++ b/src/de/danoeh/antennapod/asynctask/DownloadStatus.java
@@ -14,6 +14,9 @@ public class DownloadStatus {
/** Unique id for storing the object in database. */
protected long id;
+ /** Used by DownloadService to check if the status has been updated. */
+ protected volatile boolean updateAvailable;
+
protected FeedFile feedfile;
protected int progressPercent;
protected long soFar;
@@ -29,8 +32,8 @@ public class DownloadStatus {
}
/** Constructor for restoring Download status entries from DB. */
- public DownloadStatus(long id, FeedFile feedfile, boolean successful, int reason,
- Date completionDate) {
+ public DownloadStatus(long id, FeedFile feedfile, boolean successful,
+ int reason, Date completionDate) {
this.id = id;
this.feedfile = feedfile;
progressPercent = 100;
@@ -41,11 +44,9 @@ public class DownloadStatus {
this.done = true;
this.completionDate = completionDate;
}
-
-
+
/** Constructor for creating new completed downloads. */
- public DownloadStatus(FeedFile feedfile, int reason,
- boolean successful) {
+ public DownloadStatus(FeedFile feedfile, int reason, boolean successful) {
this(0, feedfile, successful, reason, new Date());
}
@@ -88,8 +89,49 @@ public class DownloadStatus {
public boolean isDone() {
return done;
}
-
-
-
+
+ public void setFeedfile(FeedFile feedfile) {
+ this.feedfile = feedfile;
+ }
+
+ public void setProgressPercent(int progressPercent) {
+ this.progressPercent = progressPercent;
+ }
+
+ public void setSoFar(long soFar) {
+ this.soFar = soFar;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public void setStatusMsg(int statusMsg) {
+ this.statusMsg = statusMsg;
+ }
+
+ public void setReason(int reason) {
+ this.reason = reason;
+ }
+
+ public void setSuccessful(boolean successful) {
+ this.successful = successful;
+ }
+
+ public void setDone(boolean done) {
+ this.done = done;
+ }
+
+ public void setCompletionDate(Date completionDate) {
+ this.completionDate = completionDate;
+ }
+
+ public boolean isUpdateAvailable() {
+ return updateAvailable;
+ }
+
+ public void setUpdateAvailable(boolean updateAvailable) {
+ this.updateAvailable = updateAvailable;
+ }
} \ No newline at end of file
diff --git a/src/de/danoeh/antennapod/feed/FeedFile.java b/src/de/danoeh/antennapod/feed/FeedFile.java
index c7a9b7bc1..5ec52d7d2 100644
--- a/src/de/danoeh/antennapod/feed/FeedFile.java
+++ b/src/de/danoeh/antennapod/feed/FeedFile.java
@@ -1,10 +1,9 @@
package de.danoeh.antennapod.feed;
-/** Represents a component of a Feed that has to be downloaded*/
+/** Represents a component of a Feed that has to be downloaded */
public abstract class FeedFile extends FeedComponent {
protected String file_url;
protected String download_url;
- protected long downloadId; // temporary id given by the Android DownloadManager
protected boolean downloaded;
public FeedFile(String file_url, String download_url, boolean downloaded) {
@@ -21,24 +20,19 @@ public abstract class FeedFile extends FeedComponent {
public String getFile_url() {
return file_url;
}
+
public void setFile_url(String file_url) {
this.file_url = file_url;
}
+
public String getDownload_url() {
return download_url;
}
+
public void setDownload_url(String download_url) {
this.download_url = download_url;
}
- public long getDownloadId() {
- return downloadId;
- }
-
- public void setDownloadId(long downloadId) {
- this.downloadId = downloadId;
- }
-
public boolean isDownloaded() {
return downloaded;
}
@@ -46,10 +40,4 @@ public abstract class FeedFile extends FeedComponent {
public void setDownloaded(boolean downloaded) {
this.downloaded = downloaded;
}
-
- public boolean isDownloading() {
- return downloadId != 0;
- }
-
-
}
diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java
index 6479f9864..5582d9620 100644
--- a/src/de/danoeh/antennapod/feed/FeedManager.java
+++ b/src/de/danoeh/antennapod/feed/FeedManager.java
@@ -37,6 +37,7 @@ public class FeedManager {
public static final String ACITON_FEED_LIST_UPDATE = "de.danoeh.antennapod.action.feed.feedlistUpdate";
public static final String ACTION_UNREAD_ITEMS_UPDATE = "de.danoeh.antennapod.action.feed.unreadItemsUpdate";
public static final String ACTION_QUEUE_UPDATE = "de.danoeh.antennapod.action.feed.queueUpdate";
+ public static final String ACTION_DOWNLOADLOG_UPDATE = "de.danoeh.antennapod.action.feed.downloadLogUpdate";
public static final String EXTRA_FEED_ITEM_ID = "de.danoeh.antennapod.extra.feed.feedItemId";
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feed.feedId";
@@ -167,8 +168,7 @@ public class FeedManager {
imageFile.delete();
}
} else if (requester.isDownloadingFile(feed.getImage())) {
- requester.cancelDownload(context, feed.getImage()
- .getDownloadId());
+ requester.cancelDownload(context, feed.getImage());
}
// delete stored media files and mark them as read
for (FeedItem item : feed.getItems()) {
@@ -184,8 +184,7 @@ public class FeedManager {
mediaFile.delete();
} else if (item.getMedia() != null
&& requester.isDownloadingFile(item.getMedia())) {
- requester.cancelDownload(context, item.getMedia()
- .getDownloadId());
+ requester.cancelDownload(context, item.getMedia());
}
}
@@ -334,20 +333,34 @@ public class FeedManager {
public void addDownloadStatus(final Context context,
final DownloadStatus status) {
- downloadLog.add(status);
- dbExec.execute(new Runnable() {
+ contentChanger.post(new Runnable() {
@Override
public void run() {
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
+ downloadLog.add(status);
+ final DownloadStatus removedStatus;
if (downloadLog.size() > DOWNLOAD_LOG_SIZE) {
- adapter.removeDownloadStatus(downloadLog.remove(0));
+ removedStatus = downloadLog.remove(0);
+ } else {
+ removedStatus = null;
}
- adapter.setDownloadStatus(status);
- adapter.close();
+ context.sendBroadcast(new Intent(ACTION_DOWNLOADLOG_UPDATE));
+ dbExec.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ if (removedStatus != null) {
+ adapter.removeDownloadStatus(removedStatus);
+ }
+ adapter.setDownloadStatus(status);
+ adapter.close();
+ }
+ });
}
});
+
}
public void addQueueItem(final Context context, final FeedItem item) {
diff --git a/src/de/danoeh/antennapod/fragment/FeedlistFragment.java b/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
index b6abdeab7..038b16f36 100644
--- a/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/FeedlistFragment.java
@@ -5,7 +5,7 @@ import de.danoeh.antennapod.adapter.FeedlistAdapter;
import de.danoeh.antennapod.asynctask.FeedRemover;
import de.danoeh.antennapod.dialog.ConfirmationDialog;
import de.danoeh.antennapod.feed.*;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.AppConfig;
@@ -140,15 +140,7 @@ public class FeedlistFragment extends SherlockFragment implements
@Override
public void run() {
- if (intent.getAction().equals(
- DownloadService.ACTION_DOWNLOAD_HANDLED)) {
- int type = intent.getIntExtra(DownloadService.EXTRA_DOWNLOAD_TYPE, 0);
- if (type == DownloadService.DOWNLOAD_TYPE_IMAGE) {
- fla.notifyDataSetChanged();
- }
- } else {
- fla.notifyDataSetChanged();
- }
+ fla.notifyDataSetChanged();
}
});
}
diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
index ba810507d..e8df221f2 100644
--- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -27,7 +27,7 @@ import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.EpisodeFilter;
import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler;
diff --git a/src/de/danoeh/antennapod/service/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index f1b056499..96846e6ed 100644
--- a/src/de/danoeh/antennapod/service/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -3,60 +3,60 @@
* to complete, then stops
* */
-package de.danoeh.antennapod.service;
+package de.danoeh.antennapod.service.download;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
-import de.danoeh.antennapod.AppConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.activity.DownloadActivity;
-import de.danoeh.antennapod.activity.AudioplayerActivity;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.asynctask.DownloadObserver;
-import de.danoeh.antennapod.asynctask.DownloadStatus;
-import de.danoeh.antennapod.feed.*;
-import de.danoeh.antennapod.service.PlaybackService.LocalBinder;
-import de.danoeh.antennapod.storage.DownloadRequester;
-import de.danoeh.antennapod.syndication.handler.FeedHandler;
-import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
-import de.danoeh.antennapod.util.DownloadError;
-import de.danoeh.antennapod.util.InvalidFeedException;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
-import android.app.DownloadManager;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.IBinder;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.database.Cursor;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.support.v4.app.NotificationCompat;
-import android.util.Log;
+import android.media.MediaPlayer;
import android.os.AsyncTask;
import android.os.Binder;
-import android.os.Debug;
import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.preference.PreferenceManager;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+import android.webkit.URLUtil;
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.PodcastApp;
+import de.danoeh.antennapod.activity.DownloadActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.asynctask.DownloadStatus;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedFile;
+import de.danoeh.antennapod.feed.FeedImage;
+import de.danoeh.antennapod.feed.FeedManager;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.storage.DownloadRequester;
+import de.danoeh.antennapod.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
+import de.danoeh.antennapod.util.DownloadError;
+import de.danoeh.antennapod.util.InvalidFeedException;
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
@@ -67,13 +67,27 @@ public class DownloadService extends Service {
* If the DownloadService receives this intent, it will execute
* queryDownloads()
*/
- public static final String ACTION_NOTIFY_DOWNLOADS_CHANGED = "action.de.danoeh.antennapod.service.notifyDownloadsChanged";
+ public static final String ACTION_ENQUEUE_DOWNLOAD = "action.de.danoeh.antennapod.service.enqueueDownload";
+ public static final String ACTION_CANCEL_DOWNLOAD = "action.de.danoeh.antennapod.service.cancelDownload";
+ public static final String ACTION_CANCEL_ALL_DOWNLOADS = "action.de.danoeh.antennapod.service.cancelAllDownloads";
+
+ /** Is used for sending the delete intent for the report notification */
+ private static final String ACTION_REPORT_DELETED = "action.de.danoeh.antennapod.service.reportDeleted";
+
+ /** Extra for ACTION_CANCEL_DOWNLOAD */
+ public static final String EXTRA_DOWNLOAD_URL = "downloadUrl";
public static final String ACTION_DOWNLOAD_HANDLED = "action.de.danoeh.antennapod.service.download_handled";
- /** True if handled feed has an image. */
- public static final String EXTRA_FEED_HAS_IMAGE = "extra.de.danoeh.antennapod.service.feed_has_image";
+ /**
+ * Sent by the DownloadService when the content of the downloads list
+ * changes.
+ */
+ public static final String ACTION_DOWNLOADS_CONTENT_CHANGED = "action.de.danoeh.antennapod.service.downloadsContentChanged";
+
public static final String EXTRA_DOWNLOAD_ID = "extra.de.danoeh.antennapod.service.download_id";
- public static final String EXTRA_IMAGE_DOWNLOAD_ID = "extra.de.danoeh.antennapod.service.image_download_id";
+
+ /** Extra for ACTION_ENQUEUE_DOWNLOAD intent. */
+ public static final String EXTRA_REQUEST = "request";
// Download types for ACTION_DOWNLOAD_HANDLED
public static final String EXTRA_DOWNLOAD_TYPE = "extra.de.danoeh.antennapod.service.downloadType";
@@ -81,9 +95,11 @@ public class DownloadService extends Service {
public static final int DOWNLOAD_TYPE_MEDIA = 2;
public static final int DOWNLOAD_TYPE_IMAGE = 3;
- private ArrayList<DownloadStatus> completedDownloads;
+ private CopyOnWriteArrayList<DownloadStatus> completedDownloads;
private ExecutorService syncExecutor;
+ private ExecutorService downloadExecutor;
+
private DownloadRequester requester;
private FeedManager manager;
private NotificationCompat.Builder notificationBuilder;
@@ -91,16 +107,14 @@ public class DownloadService extends Service {
private int REPORT_ID = 3;
/** Needed to determine the duration of a media file */
private MediaPlayer mediaplayer;
- private DownloadManager downloadManager;
- private DownloadObserver downloadObserver;
+ private List<Downloader> downloads;
private volatile boolean shutdownInitiated = false;
/** True if service is running. */
public static boolean isRunning = false;
- /** Is started when service waits for shutdown. */
- private Thread waiter;
+ private Handler handler;
private final IBinder mBinder = new LocalBinder();
@@ -112,11 +126,10 @@ public class DownloadService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (waiter != null) {
- waiter.interrupt();
+ if (intent.getParcelableExtra(EXTRA_REQUEST) != null) {
+ onDownloadQueued(intent);
}
- queryDownloads();
- return super.onStartCommand(intent, flags, startId);
+ return Service.START_NOT_STICKY;
}
@SuppressLint("NewApi")
@@ -125,10 +138,18 @@ public class DownloadService extends Service {
if (AppConfig.DEBUG)
Log.d(TAG, "Service started");
isRunning = true;
- completedDownloads = new ArrayList<DownloadStatus>();
- registerReceiver(downloadReceiver, createIntentFilter());
- registerReceiver(onDownloadsChanged, new IntentFilter(
- ACTION_NOTIFY_DOWNLOADS_CHANGED));
+ handler = new Handler();
+ completedDownloads = new CopyOnWriteArrayList<DownloadStatus>(
+ new ArrayList<DownloadStatus>());
+ downloads = new ArrayList<Downloader>();
+ registerReceiver(downloadQueued, new IntentFilter(
+ ACTION_ENQUEUE_DOWNLOAD));
+
+ IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
+ cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_ALL_DOWNLOADS);
+ cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_DOWNLOAD);
+ registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter);
+ registerReceiver(reportDeleted, new IntentFilter(ACTION_REPORT_DELETED));
syncExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
@@ -147,17 +168,20 @@ public class DownloadService extends Service {
return t;
}
});
+ downloadExecutor = Executors.newFixedThreadPool(2, new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setPriority(Thread.MIN_PRIORITY);
+ return t;
+ }
+ });
manager = FeedManager.getInstance();
requester = DownloadRequester.getInstance();
mediaplayer = new MediaPlayer();
- downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
- downloadObserver = new DownloadObserver(this);
setupNotification();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- downloadObserver.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- downloadObserver.execute();
- }
+
}
@Override
@@ -171,46 +195,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Service shutting down");
isRunning = false;
mediaplayer.release();
- unregisterReceiver(downloadReceiver);
- unregisterReceiver(onDownloadsChanged);
- downloadObserver.cancel(true);
- createReport();
- }
-
- private IntentFilter createIntentFilter() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
- return filter;
- }
-
- /** Shuts down Executor service and prepares for shutdown */
- private void initiateShutdown() {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Initiating shutdown");
- // Wait until PoolExecutor is done
- waiter = new Thread() {
- @Override
- public void run() {
- syncExecutor.shutdown();
- try {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Starting to wait for termination");
- boolean b = syncExecutor.awaitTermination(20L,
- TimeUnit.SECONDS);
- if (AppConfig.DEBUG)
- Log.d(TAG,
- "Stopping waiting for termination; Result : "
- + b);
- stopForeground(true);
- stopSelf();
- } catch (InterruptedException e) {
- e.printStackTrace();
- Log.i(TAG, "Service shutdown was interrupted.");
- shutdownInitiated = false;
- }
- }
- };
- waiter.start();
+ unregisterReceiver(cancelDownloadReceiver);
+ unregisterReceiver(downloadQueued);
}
private void setupNotification() {
@@ -232,103 +218,163 @@ public class DownloadService extends Service {
Log.d(TAG, "Notification set up");
}
- private BroadcastReceiver onDownloadsChanged = new BroadcastReceiver() {
+ private Downloader getDownloader(String downloadUrl) {
+ for (Downloader downloader : downloads) {
+ if (downloader.getStatus().getFeedFile().getDownload_url()
+ .equals(downloadUrl)) {
+ return downloader;
+ }
+ }
+ return null;
+ }
+
+ private BroadcastReceiver cancelDownloadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_NOTIFY_DOWNLOADS_CHANGED)) {
- queryDownloads();
+ if (intent.getAction().equals(ACTION_CANCEL_DOWNLOAD)) {
+ String url = intent.getStringExtra(EXTRA_DOWNLOAD_URL);
+ if (url == null) {
+ throw new IllegalArgumentException(
+ "ACTION_CANCEL_DOWNLOAD intent needs download url extra");
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Cancelling download with url " + url);
+ Downloader d = getDownloader(url);
+ if (d != null) {
+ d.interrupt();
+ removeDownload(d);
+ } else {
+ Log.e(TAG, "Could not cancel download with url " + url);
+ }
+
+ } else if (intent.getAction().equals(ACTION_CANCEL_ALL_DOWNLOADS)) {
+ for (Downloader d : downloads) {
+ d.interrupt();
+ DownloadRequester.getInstance().removeDownload(
+ d.getStatus().getFeedFile());
+ d.getStatus().getFeedFile().setFile_url(null);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Cancelled all downloads");
+ }
+ downloads.clear();
+ sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+
}
+ queryDownloads();
}
+
};
- private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
- @SuppressLint("NewApi")
+ private void onDownloadQueued(Intent intent) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Received enqueue request");
+ Request request = intent.getParcelableExtra(EXTRA_REQUEST);
+ if (request == null) {
+ throw new IllegalArgumentException(
+ "ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
+ }
+ if (shutdownInitiated) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Cancelling shutdown; new download was queued");
+ shutdownInitiated = false;
+ setupNotification();
+ }
+
+ DownloadRequester requester = DownloadRequester.getInstance();
+ FeedFile feedfile = requester.getDownload(request.source);
+ if (feedfile != null) {
+
+ DownloadStatus status = new DownloadStatus(feedfile);
+ Downloader downloader = getDownloader(status);
+ if (downloader != null) {
+ downloads.add(downloader);
+ downloadExecutor.submit(downloader);
+ sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ }
+ } else {
+ Log.e(TAG,
+ "Could not find feedfile in download requester when trying to enqueue new download");
+ queryDownloads();
+
+ }
+ }
+
+ private BroadcastReceiver downloadQueued = new BroadcastReceiver() {
+
@Override
- public void onReceive(final Context context, final Intent intent) {
- AsyncTask<Void, Void, Void> handler = new AsyncTask<Void, Void, Void>() {
-
- @Override
- protected Void doInBackground(Void... params) {
- int status = -1;
- String file_url = null;
- boolean successful = false;
- int reason = 0;
- if (AppConfig.DEBUG)
- Log.d(TAG, "Received 'Download Complete' - message.");
- long downloadId = intent.getLongExtra(
- DownloadManager.EXTRA_DOWNLOAD_ID, 0);
- // get status
- DownloadManager.Query q = new DownloadManager.Query();
- q.setFilterById(downloadId);
- Cursor c = downloadManager.query(q);
- if (c.moveToFirst()) {
- status = c.getInt(c
- .getColumnIndex(DownloadManager.COLUMN_STATUS));
- String uriString = c
- .getString(c
- .getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
- if (uriString != null) {
- Uri file_uri = Uri.parse(uriString);
- file_url = file_uri.getPath();
- } else {
- Log.w(TAG,
- "DownloadManager didn't provide a destination URI for downloaded file");
+ public void onReceive(Context context, Intent intent) {
+ onDownloadQueued(intent);
+ }
+
+ };
+
+ private Downloader getDownloader(DownloadStatus status) {
+ if (URLUtil.isHttpUrl(status.getFeedFile().getDownload_url())) {
+ return new HttpDownloader(this, status);
+ }
+ Log.e(TAG, "Could not find appropriate downloader for "
+ + status.getFeedFile().getDownload_url());
+ return null;
+ }
+
+ @SuppressLint("NewApi")
+ public void onDownloadCompleted(final Downloader downloader) {
+ final AsyncTask<Void, Void, Void> handlerTask = new AsyncTask<Void, Void, Void>() {
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Received 'Download Complete' - message.");
+ DownloadStatus status = downloader.getStatus();
+ status.setCompletionDate(new Date());
+ boolean successful = status.isSuccessful();
+ int reason = status.getReason();
+
+ FeedFile download = status.getFeedFile();
+ if (download != null) {
+ if (successful) {
+ if (download.getClass() == Feed.class) {
+ handleCompletedFeedDownload(status);
+ } else if (download.getClass() == FeedImage.class) {
+ handleCompletedImageDownload(status);
+ } else if (download.getClass() == FeedMedia.class) {
+ handleCompletedFeedMediaDownload(status);
}
- if (AppConfig.DEBUG)
- Log.d(TAG, "File url given by download manager is "
- + file_url);
- }
- if (downloadId == 0) {
- Log.d(TAG, "Download ID was null");
- }
- FeedFile download = requester.getFeedFile(downloadId);
- if (download != null) {
- if (status == DownloadManager.STATUS_SUCCESSFUL) {
- if (file_url != null) {
- download.setFile_url(file_url);
- }
- if (download.getClass() == Feed.class) {
- handleCompletedFeedDownload(context,
- (Feed) download);
- } else if (download.getClass() == FeedImage.class) {
- handleCompletedImageDownload(context,
- (FeedImage) download);
- } else if (download.getClass() == FeedMedia.class) {
- handleCompletedFeedMediaDownload(context,
- (FeedMedia) download);
- }
- successful = true;
-
- } else if (status == DownloadManager.STATUS_FAILED) {
- reason = c
- .getInt(c
- .getColumnIndex(DownloadManager.COLUMN_REASON));
+ } else {
+ if (!successful
+ && reason != DownloadError.ERROR_DOWNLOAD_CANCELLED) {
Log.e(TAG, "Download failed");
- Log.e(TAG, "reason code is " + reason);
- successful = false;
- saveDownloadStatus(new DownloadStatus(download,
- reason, successful));
- requester.removeDownload(download);
- sendDownloadHandledIntent(download.getDownloadId(),
- false, 0, getDownloadType(download));
- download.setDownloadId(0);
-
}
- queryDownloads();
+ saveDownloadStatus(status);
+ sendDownloadHandledIntent(getDownloadType(download));
+
}
- c.close();
- return null;
}
- };
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- handler.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- handler.execute();
+ removeDownload(downloader);
+ if (!successful) {
+ queryDownloads();
+ }
+ 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) {
+ downloads.remove(d);
+ DownloadRequester.getInstance().removeDownload(
+ d.getStatus().getFeedFile());
+ sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ }
/**
* Adds a new DownloadStatus object to the list of completed downloads and
@@ -355,41 +401,32 @@ public class DownloadService extends Service {
}
}
- private void sendDownloadHandledIntent(long downloadId,
- boolean feedHasImage, long imageDownloadId, int type) {
+ private void sendDownloadHandledIntent(int type) {
Intent intent = new Intent(ACTION_DOWNLOAD_HANDLED);
- intent.putExtra(EXTRA_DOWNLOAD_ID, downloadId);
- intent.putExtra(EXTRA_FEED_HAS_IMAGE, feedHasImage);
intent.putExtra(EXTRA_DOWNLOAD_TYPE, type);
- if (feedHasImage) {
- intent.putExtra(EXTRA_IMAGE_DOWNLOAD_ID, imageDownloadId);
- }
+
sendBroadcast(intent);
}
+ private BroadcastReceiver reportDeleted = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ACTION_REPORT_DELETED)) {
+ completedDownloads.clear();
+ }
+ }
+ };
+
/**
* Creates a notification at the end of the service lifecycle to notify the
* user about the number of completed downloads. A report will only be
* created if the number of feeds is > 1 or if at least one media file was
* downloaded.
*/
- private void createReport() {
+ private void updateReport() {
// check if report should be created
- boolean createReport = false;
- int feedCount = 0;
- for (DownloadStatus status : completedDownloads) {
- if (status.getFeedFile().getClass() == Feed.class) {
- feedCount++;
- if (feedCount > 1) {
- createReport = true;
- break;
- }
- } else if (status.getFeedFile().getClass() == FeedMedia.class) {
- createReport = true;
- break;
- }
- }
- if (createReport) {
+ if (!completedDownloads.isEmpty()) {
if (AppConfig.DEBUG)
Log.d(TAG, "Creating report");
int successfulDownloads = 0;
@@ -417,7 +454,12 @@ public class DownloadService extends Service {
.setContentIntent(
PendingIntent.getActivity(this, 0, new Intent(this,
MainActivity.class), 0))
- .setAutoCancel(true).getNotification();
+ .setAutoCancel(true)
+ .setDeleteIntent(
+ PendingIntent.getBroadcast(this, 0, new Intent(
+ ACTION_REPORT_DELETED),
+ PendingIntent.FLAG_UPDATE_CURRENT))
+ .getNotification();
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(REPORT_ID, notification);
@@ -428,11 +470,18 @@ public class DownloadService extends Service {
}
/** Check if there's something else to download, otherwise stop */
- private void queryDownloads() {
- int numOfDownloads = requester.getNumberOfDownloads();
- if (!shutdownInitiated && numOfDownloads == 0) {
+ void queryDownloads() {
+ int numOfDownloads = downloads.size();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, numOfDownloads + " downloads left");
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "ShutdownInitiated: " + shutdownInitiated);
+
+ if (numOfDownloads == 0) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Starting shutdown");
shutdownInitiated = true;
- initiateShutdown();
+ stopForeground(true);
} else {
// update notification
notificationBuilder.setContentText(numOfDownloads
@@ -440,29 +489,29 @@ public class DownloadService extends Service {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, notificationBuilder.getNotification());
}
+ updateReport();
}
/** Is called whenever a Feed is downloaded */
- private void handleCompletedFeedDownload(Context context, Feed feed) {
+ private void handleCompletedFeedDownload(DownloadStatus status) {
if (AppConfig.DEBUG)
Log.d(TAG, "Handling completed Feed Download");
- syncExecutor.execute(new FeedSyncThread(feed, this));
+ syncExecutor.execute(new FeedSyncThread(status));
}
/** Is called whenever a Feed-Image is downloaded */
- private void handleCompletedImageDownload(Context context, FeedImage image) {
+ private void handleCompletedImageDownload(DownloadStatus status) {
if (AppConfig.DEBUG)
Log.d(TAG, "Handling completed Image Download");
- syncExecutor.execute(new ImageHandlerThread(image, this));
+ syncExecutor.execute(new ImageHandlerThread(status));
}
/** Is called whenever a FeedMedia is downloaded. */
- private void handleCompletedFeedMediaDownload(Context context,
- FeedMedia media) {
+ private void handleCompletedFeedMediaDownload(DownloadStatus status) {
if (AppConfig.DEBUG)
Log.d(TAG, "Handling completed FeedMedia Download");
- syncExecutor.execute(new MediaHandlerThread(media, this));
+ syncExecutor.execute(new MediaHandlerThread(status));
}
/**
@@ -473,21 +522,18 @@ public class DownloadService extends Service {
private static final String TAG = "FeedSyncThread";
private Feed feed;
- private DownloadService service;
+ private DownloadStatus status;
private int reason;
private boolean successful;
- public FeedSyncThread(Feed feed, DownloadService service) {
- this.feed = feed;
- this.service = service;
+ public FeedSyncThread(DownloadStatus status) {
+ this.feed = (Feed) status.getFeedFile();
+ this.status = status;
}
public void run() {
Feed savedFeed = null;
- long imageId = 0;
- boolean hasImage = false;
- long downloadId = feed.getDownloadId();
reason = 0;
successful = true;
FeedManager manager = FeedManager.getInstance();
@@ -501,18 +547,16 @@ public class DownloadService extends Service {
if (checkFeedData(feed) == false) {
throw new InvalidFeedException();
}
- feed.setDownloadId(0);
// Save information of feed in DB
- savedFeed = manager.updateFeed(service, feed);
+ savedFeed = manager.updateFeed(DownloadService.this, feed);
// Download Feed Image if provided and not downloaded
if (savedFeed.getImage() != null
&& savedFeed.getImage().isDownloaded() == false) {
if (AppConfig.DEBUG)
Log.d(TAG, "Feed has image; Downloading....");
savedFeed.getImage().setFeed(savedFeed);
- imageId = requester.downloadImage(service,
+ requester.downloadImage(DownloadService.this,
savedFeed.getImage());
- hasImage = true;
}
} catch (SAXException e) {
@@ -537,14 +581,13 @@ public class DownloadService extends Service {
reason = DownloadError.ERROR_PARSER_EXCEPTION;
}
- requester.removeDownload(feed);
// cleanup();
if (savedFeed == null) {
savedFeed = feed;
}
+
saveDownloadStatus(new DownloadStatus(savedFeed, reason, successful));
- sendDownloadHandledIntent(downloadId, hasImage, imageId,
- DOWNLOAD_TYPE_FEED);
+ sendDownloadHandledIntent(DOWNLOAD_TYPE_FEED);
queryDownloads();
}
@@ -579,25 +622,22 @@ public class DownloadService extends Service {
/** Handles a completed image download. */
class ImageHandlerThread implements Runnable {
private FeedImage image;
- private DownloadService service;
+ private DownloadStatus status;
- public ImageHandlerThread(FeedImage image, DownloadService service) {
- this.image = image;
- this.service = service;
+ public ImageHandlerThread(DownloadStatus status) {
+ this.image = (FeedImage) status.getFeedFile();
+ this.status = status;
}
@Override
public void run() {
image.setDownloaded(true);
- requester.removeDownload(image);
- saveDownloadStatus(new DownloadStatus(image, 0, true));
- sendDownloadHandledIntent(image.getDownloadId(), false, 0,
- DOWNLOAD_TYPE_IMAGE);
- image.setDownloadId(0);
- manager.setFeedImage(service, image);
+ saveDownloadStatus(status);
+ sendDownloadHandledIntent(DOWNLOAD_TYPE_IMAGE);
+ manager.setFeedImage(DownloadService.this, image);
if (image.getFeed() != null) {
- manager.setFeed(service, image.getFeed());
+ manager.setFeed(DownloadService.this, image.getFeed());
} else {
Log.e(TAG,
"Image has no feed, image might not be saved correctly!");
@@ -609,17 +649,16 @@ public class DownloadService extends Service {
/** Handles a completed media download. */
class MediaHandlerThread implements Runnable {
private FeedMedia media;
- private DownloadService service;
+ private DownloadStatus status;
- public MediaHandlerThread(FeedMedia media, DownloadService service) {
+ public MediaHandlerThread(DownloadStatus status) {
super();
- this.media = media;
- this.service = service;
+ this.media = (FeedMedia) status.getFeedFile();
+ this.status = status;
}
@Override
public void run() {
- requester.removeDownload(media);
media.setDownloaded(true);
// Get duration
try {
@@ -632,11 +671,10 @@ public class DownloadService extends Service {
if (AppConfig.DEBUG)
Log.d(TAG, "Duration of file is " + media.getDuration());
mediaplayer.reset();
- saveDownloadStatus(new DownloadStatus(media, 0, true));
- sendDownloadHandledIntent(media.getDownloadId(), false, 0,
- DOWNLOAD_TYPE_MEDIA);
- media.setDownloadId(0);
- manager.setFeedMedia(service, media);
+
+ saveDownloadStatus(status);
+ sendDownloadHandledIntent(DOWNLOAD_TYPE_MEDIA);
+ manager.setFeedMedia(DownloadService.this, media);
boolean autoQueue = PreferenceManager.getDefaultSharedPreferences(
getApplicationContext()).getBoolean(
PodcastApp.PREF_AUTO_QUEUE, true);
@@ -655,13 +693,59 @@ public class DownloadService extends Service {
if (AppConfig.DEBUG)
Log.d(TAG, "Item is already in queue");
}
-
queryDownloads();
}
}
- public DownloadObserver getDownloadObserver() {
- return downloadObserver;
+ /** Is used to request a new download. */
+ public static class Request implements Parcelable {
+ private String destination;
+ private String source;
+
+ public Request(String destination, String source) {
+ super();
+ this.destination = destination;
+ this.source = source;
+ }
+
+ private Request(Parcel in) {
+ destination = in.readString();
+ source = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(destination);
+ dest.writeString(source);
+ }
+
+ public static final Parcelable.Creator<Request> CREATOR = new Parcelable.Creator<Request>() {
+ public Request createFromParcel(Parcel in) {
+ return new Request(in);
+ }
+
+ public Request[] newArray(int size) {
+ return new Request[size];
+ }
+ };
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ }
+
+ public List<Downloader> getDownloads() {
+ return downloads;
}
}
diff --git a/src/de/danoeh/antennapod/service/download/Downloader.java b/src/de/danoeh/antennapod/service/download/Downloader.java
new file mode 100644
index 000000000..13ee8896c
--- /dev/null
+++ b/src/de/danoeh/antennapod/service/download/Downloader.java
@@ -0,0 +1,59 @@
+package de.danoeh.antennapod.service.download;
+
+import de.danoeh.antennapod.asynctask.DownloadStatus;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.StatFs;
+
+/** Downloads files */
+public abstract class Downloader extends Thread {
+ private static final String TAG = "Downloader";
+ private Handler handler;
+ private DownloadService downloadService;
+
+ protected boolean finished;
+
+ protected volatile DownloadStatus status;
+
+ public Downloader(DownloadService downloadService, DownloadStatus status) {
+ super();
+ this.downloadService = downloadService;
+ this.status = status;
+ handler = new Handler();
+ }
+
+ /**
+ * This method must be called when the download was completed, failed, or
+ * was cancelled
+ */
+ protected void finish() {
+ if (!finished) {
+ finished = true;
+ handler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ downloadService.onDownloadCompleted(Downloader.this);
+ }
+
+ });
+ }
+ }
+
+ protected void publishProgress() {
+ status.setUpdateAvailable(true);
+ }
+
+ protected abstract void download();
+
+ @Override
+ public final void run() {
+ download();
+ finish();
+ }
+
+ public DownloadStatus getStatus() {
+ return status;
+ }
+
+} \ No newline at end of file
diff --git a/src/de/danoeh/antennapod/service/download/HttpDownloader.java b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
new file mode 100644
index 000000000..fb9d0c6ec
--- /dev/null
+++ b/src/de/danoeh/antennapod/service/download/HttpDownloader.java
@@ -0,0 +1,136 @@
+package de.danoeh.antennapod.service.download;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+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.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+
+import android.util.Log;
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.DownloadStatus;
+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 BUFFER_SIZE = 8 * 1024;
+ private static final int CONNECTION_TIMEOUT = 5000;
+
+ public HttpDownloader(DownloadService downloadService, DownloadStatus status) {
+ super(downloadService, status);
+ }
+
+ @Override
+ protected void download() {
+ HttpURLConnection connection = null;
+ OutputStream out = null;
+ try {
+ status.setStatusMsg(R.string.download_pending);
+ publishProgress();
+ URL url = new URL(status.getFeedFile().getDownload_url());
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setConnectTimeout(CONNECTION_TIMEOUT);
+ if (AppConfig.DEBUG) {
+ Log.d(TAG, "Connected to resource");
+ }
+ if (StorageUtils.externalStorageMounted()) {
+ File destination = new File(status.getFeedFile().getFile_url());
+ if (!destination.exists()) {
+ InputStream in = new BufferedInputStream(
+ connection.getInputStream());
+ out = new BufferedOutputStream(new FileOutputStream(
+ destination));
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int count = 0;
+ status.setStatusMsg(R.string.download_running);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Getting size of download");
+ status.setSize(connection.getContentLength());
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Size is " + status.getSize());
+ if (status.getSize() == -1
+ || status.getSize() <= StorageUtils
+ .getFreeSpaceAvailable()) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Size is " + status.getSize());
+ publishProgress();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Starting download");
+ while ((count = in.read(buffer)) != -1
+ && !isInterrupted()) {
+ out.write(buffer, 0, count);
+ status.setSoFar(status.getSoFar() + count);
+ status.setProgressPercent((int) (((double) status
+ .getSoFar() / (double) status.getSize()) * 100));
+ }
+ if (isInterrupted()) {
+ onCancelled();
+ } else {
+ onSuccess();
+ }
+ } else {
+ onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE);
+ }
+ } else {
+ onFail(DownloadError.ERROR_FILE_EXISTS);
+ }
+ } else {
+ onFail(DownloadError.ERROR_DEVICE_NOT_FOUND);
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_MALFORMED_URL);
+ } catch (SocketTimeoutException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_CONNECTION_ERROR);
+ } catch (IOException e) {
+ e.printStackTrace();
+ onFail(DownloadError.ERROR_IO_ERROR);
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void onSuccess() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Download was successful");
+ status.setSuccessful(true);
+ status.setDone(true);
+ }
+
+ private void onFail(int reason) {
+ if (AppConfig.DEBUG) {
+ Log.d(TAG, "Download failed");
+ }
+ status.setReason(reason);
+ status.setDone(true);
+ status.setSuccessful(false);
+ }
+
+ private void onCancelled() {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Download was cancelled");
+ status.setReason(DownloadError.ERROR_DOWNLOAD_CANCELLED);
+ status.setDone(true);
+ status.setSuccessful(false);
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/storage/DownloadRequester.java b/src/de/danoeh/antennapod/storage/DownloadRequester.java
index 5e2af1ba5..13c8f58d0 100644
--- a/src/de/danoeh/antennapod/storage/DownloadRequester.java
+++ b/src/de/danoeh/antennapod/storage/DownloadRequester.java
@@ -1,17 +1,11 @@
package de.danoeh.antennapod.storage;
import java.io.File;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
-import android.annotation.SuppressLint;
-import android.app.DownloadManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
-import android.net.Uri;
-import android.os.IBinder;
import android.util.Log;
import android.webkit.URLUtil;
import de.danoeh.antennapod.AppConfig;
@@ -19,31 +13,28 @@ import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedMedia;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.util.NumberGenerator;
import de.danoeh.antennapod.util.URLChecker;
-public class DownloadRequester {// TODO handle externalstorage missing
+public class DownloadRequester {
private static final String TAG = "DownloadRequester";
- private static final int currentApi = android.os.Build.VERSION.SDK_INT;
public static String EXTRA_DOWNLOAD_ID = "extra.de.danoeh.antennapod.storage.download_id";
public static String EXTRA_ITEM_ID = "extra.de.danoeh.antennapod.storage.item_id";
public static String ACTION_DOWNLOAD_QUEUED = "action.de.danoeh.antennapod.storage.downloadQueued";
- private static boolean STORE_ON_SD = true;
public static String IMAGE_DOWNLOADPATH = "images/";
public static String FEED_DOWNLOADPATH = "cache/";
public static String MEDIA_DOWNLOADPATH = "media/";
private static DownloadRequester downloader;
- private DownloadManager manager;
- private List<FeedFile> downloads;
+ Map<String, FeedFile> downloads;
private DownloadRequester() {
- downloads = new CopyOnWriteArrayList<FeedFile>();
+ downloads = new ConcurrentHashMap<String, FeedFile>();
}
public static DownloadRequester getInstance() {
@@ -53,8 +44,7 @@ public class DownloadRequester {// TODO handle externalstorage missing
return downloader;
}
- @SuppressLint("NewApi")
- private long download(Context context, FeedFile item, File dest) {
+ private void download(Context context, FeedFile item, File dest) {
if (!isDownloadingFile(item)) {
if (dest.exists()) {
if (AppConfig.DEBUG)
@@ -64,106 +54,75 @@ public class DownloadRequester {// TODO handle externalstorage missing
if (AppConfig.DEBUG)
Log.d(TAG,
"Requesting download of url " + item.getDownload_url());
- downloads.add(item);
item.setDownload_url(URLChecker.prepareURL(item.getDownload_url()));
- DownloadManager.Request request = new DownloadManager.Request(
- Uri.parse(item.getDownload_url())).setDestinationUri(Uri
- .fromFile(dest));
- if (AppConfig.DEBUG)
- Log.d(TAG, "Version is " + currentApi);
- if (currentApi >= 11) {
- request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
+ item.setFile_url(dest.toString());
+ downloads.put(item.getDownload_url(), item);
+
+ DownloadService.Request request = new DownloadService.Request(
+ item.getFile_url(), item.getDownload_url());
+
+ if (!DownloadService.isRunning) {
+ Intent launchIntent = new Intent(context, DownloadService.class);
+ launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
+ context.startService(launchIntent);
} else {
- request.setVisibleInDownloadsUi(false);
- request.setShowRunningNotification(false);
+ Intent queueIntent = new Intent(
+ DownloadService.ACTION_ENQUEUE_DOWNLOAD);
+ queueIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
+ context.sendBroadcast(queueIntent);
}
-
- // TODO Set Allowed Network Types
- DownloadManager manager = (DownloadManager) context
- .getSystemService(Context.DOWNLOAD_SERVICE);
-
- long downloadId = manager.enqueue(request);
- item.setDownloadId(downloadId);
- item.setFile_url(dest.toString());
- context.startService(new Intent(context, DownloadService.class));
context.sendBroadcast(new Intent(ACTION_DOWNLOAD_QUEUED));
- return downloadId;
} else {
Log.e(TAG, "URL " + item.getDownload_url()
+ " is already being downloaded");
- return 0;
}
}
- public long downloadFeed(Context context, Feed feed) {
- return download(context, feed, new File(getFeedfilePath(context),
+ public void downloadFeed(Context context, Feed feed) {
+ download(context, feed, new File(getFeedfilePath(context),
getFeedfileName(feed)));
}
- public long downloadImage(Context context, FeedImage image) {
- return download(context, image, new File(getImagefilePath(context),
+ public void downloadImage(Context context, FeedImage image) {
+ download(context, image, new File(getImagefilePath(context),
getImagefileName(image)));
}
- public long downloadMedia(Context context, FeedMedia feedmedia) {
- return download(context, feedmedia,
+ public void downloadMedia(Context context, FeedMedia feedmedia) {
+ download(context, feedmedia,
new File(getMediafilePath(context, feedmedia),
getMediafilename(feedmedia)));
}
/**
* Cancels a running download.
- *
- * @param context
- * A context needed to get the DownloadManager service
- * @param id
- * ID of the download to cancel
* */
- public void cancelDownload(final Context context, final long id) {
+ public void cancelDownload(final Context context, final FeedFile f) {
+ cancelDownload(context, f.getDownload_url());
+ }
+
+ /**
+ * Cancels a running download.
+ * */
+ public void cancelDownload(final Context context, final String downloadUrl) {
if (AppConfig.DEBUG)
- Log.d(TAG, "Cancelling download with id " + id);
- DownloadManager dm = (DownloadManager) context
- .getSystemService(Context.DOWNLOAD_SERVICE);
- int removed = dm.remove(id);
- if (removed > 0) {
- FeedFile f = getFeedFile(id);
- if (f != null) {
- downloads.remove(f);
- f.setFile_url(null);
- f.setDownloadId(0);
- }
- notifyDownloadService(context);
- }
+ Log.d(TAG, "Cancelling download with url " + downloadUrl);
+ Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD);
+ cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, downloadUrl);
+ context.sendBroadcast(cancelIntent);
}
/** Cancels all running downloads */
public void cancelAllDownloads(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Cancelling all running downloads");
- DownloadManager dm = (DownloadManager) context
- .getSystemService(Context.DOWNLOAD_SERVICE);
- for (FeedFile f : downloads) {
- dm.remove(f.getDownloadId());
- f.setFile_url(null);
- f.setDownloadId(0);
- }
- downloads.clear();
- notifyDownloadService(context);
- }
-
- /** Get a feedfile by its download id */
- public FeedFile getFeedFile(long id) {
- for (FeedFile f : downloads) {
- if (f.getDownloadId() == id) {
- return f;
- }
- }
- return null;
+ context.sendBroadcast(new Intent(
+ DownloadService.ACTION_CANCEL_ALL_DOWNLOADS));
}
/** Returns true if there is at least one Feed in the downloads queue. */
public boolean isDownloadingFeeds() {
- for (FeedFile f : downloads) {
+ for (FeedFile f : downloads.values()) {
if (f.getClass() == Feed.class) {
return true;
}
@@ -173,22 +132,19 @@ public class DownloadRequester {// TODO handle externalstorage missing
/** Checks if feedfile is in the downloads list */
public boolean isDownloadingFile(FeedFile item) {
- for (FeedFile f : downloads) {
- if (f.getDownload_url().equals(item.getDownload_url())) {
- return true;
- }
+ if (item.getDownload_url() != null) {
+ return downloads.containsKey(item.getDownload_url());
}
return false;
}
+ public FeedFile getDownload(String downloadUrl) {
+ return downloads.get(downloadUrl);
+ }
+
/** Checks if feedfile with the given download url is in the downloads list */
public boolean isDownloadingFile(String downloadUrl) {
- for (FeedFile f : downloads) {
- if (f.getDownload_url().equals(downloadUrl)) {
- return true;
- }
- }
- return false;
+ return downloads.get(downloadUrl) != null;
}
public boolean hasNoDownloads() {
@@ -201,11 +157,9 @@ public class DownloadRequester {// TODO handle externalstorage missing
/** Remove an object from the downloads-list of the requester. */
public void removeDownload(FeedFile f) {
- downloads.remove(f);
- }
-
- public List<FeedFile> getDownloads() {
- return downloads;
+ if (downloads.remove(f.getDownload_url()) == null) {
+ Log.e(TAG, "Could not remove object with url " + f.getDownload_url());
+ }
}
/** Get the number of uncompleted Downloads */
@@ -241,8 +195,4 @@ public class DownloadRequester {// TODO handle externalstorage missing
media.getMime_type());
}
- /** Notifies the DownloadService to check if there are any Downloads left */
- public void notifyDownloadService(Context context) {
- context.sendBroadcast(new Intent(DownloadService.ACTION_NOTIFY_DOWNLOADS_CHANGED));
- }
}
diff --git a/src/de/danoeh/antennapod/syndication/util/SyndDateUtils.java b/src/de/danoeh/antennapod/syndication/util/SyndDateUtils.java
index 00f77d65a..139cdf650 100644
--- a/src/de/danoeh/antennapod/syndication/util/SyndDateUtils.java
+++ b/src/de/danoeh/antennapod/syndication/util/SyndDateUtils.java
@@ -10,49 +10,52 @@ import android.util.Log;
/** Parses several date formats. */
public class SyndDateUtils {
private static final String TAG = "DateUtils";
- public static final String RFC822 = "dd MMM yyyy HH:mm:ss Z";
- /** RFC 822 date format with day of the week. */
- public static final String RFC822DAY = "EEE, " + RFC822;
+
+ public static final String[] RFC822DATES = { "EEE, dd MMM yyyy HH:mm:ss Z",
+ "dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yy HH:mm:ss Z",
+ "dd MMM yy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss z",
+ "dd MMM yyyy HH:mm:ss z", "EEE, dd MMM yy HH:mm:ss z",
+ "dd MMM yy HH:mm:ss z" };
/** RFC 3339 date format for UTC dates. */
public static final String RFC3339UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/** RFC 3339 date format for localtime dates with offset. */
public static final String RFC3339LOCAL = "yyyy-MM-dd'T'HH:mm:ssZ";
-
+
private static ThreadLocal<SimpleDateFormat> RFC822Formatter = new ThreadLocal<SimpleDateFormat>() {
@Override
- protected SimpleDateFormat initialValue() {
- return new SimpleDateFormat(RFC822DAY, Locale.US);
+ protected SimpleDateFormat initialValue() {
+ return new SimpleDateFormat(RFC822DATES[0], Locale.US);
}
-
+
};
-
+
private static ThreadLocal<SimpleDateFormat> RFC3339Formatter = new ThreadLocal<SimpleDateFormat>() {
@Override
- protected SimpleDateFormat initialValue() {
+ protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(RFC3339UTC, Locale.US);
}
-
+
};
- public static Date parseRFC822Date(final String date) {
+ public static Date parseRFC822Date(String date) {
Date result = null;
+ if (date.contains("PDT")) {
+ date = date.replace("PDT", "PST8PDT");
+ }
SimpleDateFormat format = RFC822Formatter.get();
- try {
- result = format.parse(date);
- } catch (ParseException e) {
- e.printStackTrace();
- format.applyPattern(RFC822);
+ for (int i = 0; i < RFC822DATES.length; i++) {
try {
- result = format.parse(date);
- } catch (ParseException e1) {
- e1.printStackTrace();
- Log.e(TAG, "Unable to parse feed date correctly");
- } finally {
- format.applyPattern(RFC822DAY); // apply old pattern again
+ result = format.parse(date);
+ break;
+ } catch (ParseException e) {
+ e.printStackTrace();
}
}
+ if (result == null) {
+ Log.e(TAG, "Unable to parse feed date correctly");
+ }
return result;
}
@@ -90,7 +93,11 @@ public class SyndDateUtils {
return result;
}
- /** Takes a string of the form [HH:]MM:SS[.mmm] and converts it to milliseconds. */
+
+ /**
+ * Takes a string of the form [HH:]MM:SS[.mmm] and converts it to
+ * milliseconds.
+ */
public static long parseTimeString(final String time) {
String[] parts = time.split(":");
long result = 0;
@@ -102,7 +109,7 @@ public class SyndDateUtils {
}
result += Integer.valueOf(parts[idx]) * 60000;
idx++;
- result += ( Float.valueOf(parts[idx])) * 1000;
+ result += (Float.valueOf(parts[idx])) * 1000;
return result;
}
}
diff --git a/src/de/danoeh/antennapod/util/DownloadError.java b/src/de/danoeh/antennapod/util/DownloadError.java
index b2f43a8dd..c3f44672f 100644
--- a/src/de/danoeh/antennapod/util/DownloadError.java
+++ b/src/de/danoeh/antennapod/util/DownloadError.java
@@ -1,27 +1,35 @@
package de.danoeh.antennapod.util;
-import de.danoeh.antennapod.R;
-import android.app.DownloadManager;
import android.content.Context;
+import de.danoeh.antennapod.R;
/** Utility class for Download Errors. */
public class DownloadError {
public static final int ERROR_PARSER_EXCEPTION = 1;
public static final int ERROR_UNSUPPORTED_TYPE = 2;
public static final int ERROR_CONNECTION_ERROR = 3;
-
+ public static final int ERROR_MALFORMED_URL = 4;
+ public static final int ERROR_IO_ERROR = 5;
+ public static final int ERROR_FILE_EXISTS = 6;
+ public static final int ERROR_DOWNLOAD_CANCELLED = 7;
+ public static final int ERROR_DEVICE_NOT_FOUND = 8;
+ public static final int ERROR_HTTP_DATA_ERROR = 9;
+ public static final int ERROR_NOT_ENOUGH_SPACE = 10;
/** Get a human-readable string for a specific error code. */
public static String getErrorString(Context context, int code) {
int resId;
switch(code) {
- case DownloadManager.ERROR_DEVICE_NOT_FOUND:
+ case ERROR_NOT_ENOUGH_SPACE:
resId = R.string.download_error_insufficient_space;
break;
- case DownloadManager.ERROR_FILE_ERROR:
- resId = R.string.download_error_file_error;
+ case ERROR_DEVICE_NOT_FOUND:
+ resId = R.string.download_error_device_not_found;
+ break;
+ case ERROR_IO_ERROR:
+ resId = R.string.download_error_io_error;
break;
- case DownloadManager.ERROR_HTTP_DATA_ERROR:
+ case ERROR_HTTP_DATA_ERROR:
resId = R.string.download_error_http_data_error;
break;
case ERROR_PARSER_EXCEPTION:
diff --git a/src/de/danoeh/antennapod/util/StorageUtils.java b/src/de/danoeh/antennapod/util/StorageUtils.java
index 942c333fb..eb3b5d3a4 100644
--- a/src/de/danoeh/antennapod/util/StorageUtils.java
+++ b/src/de/danoeh/antennapod/util/StorageUtils.java
@@ -4,6 +4,7 @@ import de.danoeh.antennapod.activity.StorageErrorActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Environment;
+import android.os.StatFs;
/** Utility functions for handling storage errors */
public class StorageUtils {
@@ -25,4 +26,14 @@ public class StorageUtils {
}
return storageAvailable;
}
+
+ /** Get the number of free bytes that are available on the external storage. */
+ public static int getFreeSpaceAvailable() {
+ StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ return stat.getAvailableBlocks() * stat.getBlockSize();
+ }
+
+ public static boolean externalStorageMounted() {
+ return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+ }
}
diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
index 24100d8c4..d88902c6f 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
@@ -81,8 +81,7 @@ public class FeedItemMenuHandler {
manager.deleteFeedMedia(context, selectedItem.getMedia());
break;
case R.id.cancel_download_item:
- requester.cancelDownload(context, selectedItem.getMedia()
- .getDownloadId());
+ requester.cancelDownload(context, selectedItem.getMedia());
break;
case R.id.mark_read_item:
manager.markItemRead(context, selectedItem, true);
diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java
index 38adc0b50..6bb1478d6 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/FeedMenuHandler.java
@@ -15,7 +15,7 @@ import de.danoeh.antennapod.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
-import de.danoeh.antennapod.service.DownloadService;
+import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.ShareUtils;
import de.danoeh.antennapod.AppConfig;