Merge branch 'downloadmanager' into develop
This commit is contained in:
commit
fdb9a296ad
@ -11,8 +11,6 @@
|
||||
<uses-sdk
|
||||
android:minSdkVersion="10"
|
||||
android:targetSdkVersion="14" />
|
||||
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<supports-screens
|
||||
@ -74,7 +72,7 @@
|
||||
android:theme="@style/Theme.MediaPlayer" android:screenOrientation="portrait"/>
|
||||
|
||||
<service
|
||||
android:name="de.danoeh.antennapod.service.DownloadService"
|
||||
android:name=".service.download.DownloadService"
|
||||
android:enabled="true" />
|
||||
<service
|
||||
android:name="de.danoeh.antennapod.service.PlaybackService"
|
||||
@ -200,4 +198,4 @@
|
||||
<activity android:name=".activity.VideoplayerActivity" android:configChanges="keyboardHidden|orientation" android:screenOrientation="landscape" android:theme="@style/VideoplayerTheme"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
@ -87,7 +87,7 @@
|
||||
<string name="pref_pauseOnHeadsetDisconnect_title">Headphones disconnect</string>
|
||||
<string name="pref_mobileUpdate_title">Mobile updates</string>
|
||||
<string name="pref_mobileUpdate_sum">Allow updates over the mobile data connection</string>
|
||||
<string name="download_report_title">All downloads completed</string>
|
||||
<string name="download_report_title">Downloads completed</string>
|
||||
<string name="refresh_label">Refresh</string>
|
||||
<string name="external_storage_error_msg">No external storage is available. Please make sure that external storage is mounted so that the app can work properly.</string>
|
||||
<string name="share_link_label">Share link</string>
|
||||
@ -176,6 +176,9 @@
|
||||
<string name="user_interface_label">User Interface</string>
|
||||
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded.</string>
|
||||
<string name="image_of_prefix">Image of:\u0020</string>
|
||||
<string name="download_error_malformed_url">Malformed URL</string>
|
||||
<string name="download_error_io_error">IO Error</string>
|
||||
<string name="download_error_device_not_found">External storage unavailable</string>
|
||||
|
||||
|
||||
</resources>
|
@ -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;
|
||||
|
@ -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 onReceive(Context context, Intent intent) {
|
||||
if (dla != null) {
|
||||
if (AppConfig.DEBUG)
|
||||
Log.d(TAG, "Refreshing content");
|
||||
dla.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate() {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
@ -26,6 +33,20 @@ public class DownloadLogActivity extends SherlockListActivity {
|
||||
setListAdapter(dla);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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() + "%");
|
||||
|
@ -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);
|
||||
|
@ -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> {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,104 +218,164 @@ 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
|
||||
@SuppressLint("NewApi")
|
||||
@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");
|
||||
}
|
||||
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));
|
||||
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();
|
||||
}
|
||||
c.close();
|
||||
return null;
|
||||
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 (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
|
||||
handler.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} else {
|
||||
handler.execute();
|
||||
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 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(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);
|
||||
}
|
||||
} else {
|
||||
if (!successful
|
||||
&& reason != DownloadError.ERROR_DOWNLOAD_CANCELLED) {
|
||||
Log.e(TAG, "Download failed");
|
||||
}
|
||||
saveDownloadStatus(status);
|
||||
sendDownloadHandledIntent(getDownloadType(download));
|
||||
|
||||
}
|
||||
}
|
||||
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
|
||||
* saves it in the database
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
59
src/de/danoeh/antennapod/service/download/Downloader.java
Normal file
59
src/de/danoeh/antennapod/service/download/Downloader.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
136
src/de/danoeh/antennapod/service/download/HttpDownloader.java
Normal file
136
src/de/danoeh/antennapod/service/download/HttpDownloader.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
} else {
|
||||
request.setVisibleInDownloadsUi(false);
|
||||
request.setShowRunningNotification(false);
|
||||
}
|
||||
|
||||
// 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));
|
||||
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 {
|
||||
Intent queueIntent = new Intent(
|
||||
DownloadService.ACTION_ENQUEUE_DOWNLOAD);
|
||||
queueIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
|
||||
context.sendBroadcast(queueIntent);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
return new SimpleDateFormat(RFC3339UTC, Locale.US);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
public static Date parseRFC822Date(final String date) {
|
||||
private static ThreadLocal<SimpleDateFormat> RFC3339Formatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(RFC3339UTC, Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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 DownloadManager.ERROR_HTTP_DATA_ERROR:
|
||||
case ERROR_IO_ERROR:
|
||||
resId = R.string.download_error_io_error;
|
||||
break;
|
||||
case ERROR_HTTP_DATA_ERROR:
|
||||
resId = R.string.download_error_http_data_error;
|
||||
break;
|
||||
case ERROR_PARSER_EXCEPTION:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user