Rewrite download request creation (#5530)
Android has a limit on the size of Intent parameters. When enqueuing a huge number of items, it just ignored the argument and did not call onNewIntent. We now load the list over in DownloadService.
This commit is contained in:
parent
8252eb2183
commit
849bf4ad85
|
@ -8,6 +8,7 @@ import androidx.core.util.Consumer;
|
|||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.test.antennapod.EspressoTestUtils;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.awaitility.core.ConditionTimeoutException;
|
||||
|
@ -33,7 +34,6 @@ import de.danoeh.antennapod.core.service.download.DownloaderFactory;
|
|||
import de.danoeh.antennapod.core.service.download.StubDownloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
import static de.test.antennapod.util.event.DownloadEventListener.withDownloadEventListener;
|
||||
import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
|
||||
|
@ -80,7 +80,7 @@ public class DownloadServiceTest {
|
|||
public void tearDown() throws Exception {
|
||||
DownloadService.setDownloaderFactory(origFactory);
|
||||
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
DownloadRequester.getInstance().cancelAllDownloads(context);
|
||||
DownloadService.cancelAll(context);
|
||||
context.stopService(new Intent(context, DownloadService.class));
|
||||
EspressoTestUtils.tryKillDownloadService();
|
||||
}
|
||||
|
@ -113,8 +113,8 @@ public class DownloadServiceTest {
|
|||
assertFalse("The media in test should not yet been downloaded",
|
||||
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
|
||||
|
||||
DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry
|
||||
.getInstrumentation().getTargetContext(), true, testMedia11.getItem());
|
||||
DownloadService.download(InstrumentationRegistry.getInstrumentation().getTargetContext(), false,
|
||||
DownloadRequestCreator.create(testMedia11).build());
|
||||
Awaitility.await()
|
||||
.atMost(5000, TimeUnit.MILLISECONDS)
|
||||
.until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
|
||||
|
@ -158,7 +158,8 @@ public class DownloadServiceTest {
|
|||
}
|
||||
|
||||
withFeedItemEventListener(feedItemEventListener -> {
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, true, testMedia11.getItem());
|
||||
DownloadService.download(InstrumentationRegistry.getInstrumentation().getTargetContext(), false,
|
||||
DownloadRequestCreator.create(testMedia11).build());
|
||||
withDownloadEventListener(downloadEventListener ->
|
||||
Awaitility.await("download is actually running")
|
||||
.atMost(5000, TimeUnit.MILLISECONDS)
|
||||
|
@ -174,7 +175,7 @@ public class DownloadServiceTest {
|
|||
.atMost(2000, TimeUnit.MILLISECONDS)
|
||||
.until(() -> feedItemEventListener.getEvents().size() >= 1);
|
||||
}
|
||||
DownloadRequester.getInstance().cancelDownload(context, testMedia11);
|
||||
DownloadService.cancel(context, testMedia11.getDownload_url());
|
||||
final int totalNumEventsExpected = itemAlreadyInQueue ? 1 : 3;
|
||||
Awaitility.await("item dequeue event + download termination event")
|
||||
.atMost(2000, TimeUnit.MILLISECONDS)
|
||||
|
|
|
@ -4,13 +4,13 @@ import android.os.Bundle;
|
|||
import android.text.TextUtils;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.model.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
@ -62,7 +62,7 @@ public class DownloadAuthenticationActivity extends AppCompatActivity {
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
|
||||
DownloadService.download(DownloadAuthenticationActivity.this, false, request);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,8 +31,9 @@ import com.google.android.material.snackbar.Snackbar;
|
|||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.feed.FeedUrlNotFoundException;
|
||||
import de.danoeh.antennapod.discovery.CombinedSearcher;
|
||||
import de.danoeh.antennapod.discovery.PodcastSearchResult;
|
||||
|
@ -49,8 +50,6 @@ import de.danoeh.antennapod.core.service.download.HttpDownloader;
|
|||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandler;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
|
||||
|
@ -467,12 +466,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
|
||||
f.setPreferences(feed.getPreferences());
|
||||
this.feed = f;
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadFeed(this, f);
|
||||
} catch (DownloadRequestException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage());
|
||||
}
|
||||
DownloadService.download(this, false, DownloadRequestCreator.create(f).build());
|
||||
didPressSubscribe = true;
|
||||
handleUpdatedFeedStatus(feed);
|
||||
}
|
||||
|
@ -553,7 +547,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
|
||||
private void handleUpdatedFeedStatus(Feed feed) {
|
||||
if (feed != null) {
|
||||
if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
|
||||
if (DownloadService.isDownloadingFile(feed.getDownload_url())) {
|
||||
viewBinding.subscribeButton.setEnabled(false);
|
||||
viewBinding.subscribeButton.setText(R.string.subscribing_label);
|
||||
} else if (feedInFeedlist(feed)) {
|
||||
|
|
|
@ -28,8 +28,8 @@ import de.danoeh.antennapod.core.export.opml.OpmlElement;
|
|||
import de.danoeh.antennapod.core.export.opml.OpmlReader;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.databinding.OpmlSelectionBinding;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import io.reactivex.Completable;
|
||||
|
@ -89,7 +89,6 @@ public class OpmlImportActivity extends AppCompatActivity {
|
|||
viewBinding.butConfirm.setOnClickListener(v -> {
|
||||
viewBinding.progressBar.setVisibility(View.VISIBLE);
|
||||
Completable.fromAction(() -> {
|
||||
DownloadRequester requester = DownloadRequester.getInstance();
|
||||
SparseBooleanArray checked = viewBinding.feedlist.getCheckedItemPositions();
|
||||
for (int i = 0; i < checked.size(); i++) {
|
||||
if (!checked.valueAt(i)) {
|
||||
|
@ -97,11 +96,7 @@ public class OpmlImportActivity extends AppCompatActivity {
|
|||
}
|
||||
OpmlElement element = readElements.get(checked.keyAt(i));
|
||||
Feed feed = new Feed(element.getXmlUrl(), null, element.getText());
|
||||
try {
|
||||
requester.downloadFeed(getApplicationContext(), feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DownloadService.download(this, false, DownloadRequestCreator.create(feed).build());
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
|
|
@ -13,14 +13,13 @@ import androidx.core.content.ContextCompat;
|
|||
import androidx.fragment.app.ListFragment;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
|
@ -131,11 +130,7 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
Log.e(TAG, "Could not find feed for feed id: " + status.getFeedfileId());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DBTasks.forceRefreshFeed(context, feed, true);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DBTasks.forceRefreshFeed(context, feed, true);
|
||||
});
|
||||
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
holder.secondaryActionButton.setOnClickListener(v -> {
|
||||
|
@ -145,14 +140,9 @@ public class DownloadLogAdapter extends BaseAdapter {
|
|||
Log.e(TAG, "Could not find feed media for feed id: " + status.getFeedfileId());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, media.getItem());
|
||||
((MainActivity) context).showSnackbarAbovePlayer(
|
||||
R.string.status_downloading_label, Toast.LENGTH_SHORT);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
|
||||
}
|
||||
DownloadService.download(context, true, DownloadRequestCreator.create(media).build());
|
||||
((MainActivity) context).showSnackbarAbovePlayer(
|
||||
R.string.status_downloading_label, Toast.LENGTH_SHORT);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ import androidx.annotation.DrawableRes;
|
|||
import androidx.annotation.StringRes;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
public class CancelDownloadActionButton extends ItemActionButton {
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class CancelDownloadActionButton extends ItemActionButton {
|
|||
@Override
|
||||
public void onClick(Context context) {
|
||||
FeedMedia media = item.getMedia();
|
||||
DownloadRequester.getInstance().cancelDownload(context, media);
|
||||
DownloadService.cancel(context, media.getDownload_url());
|
||||
if (UserPreferences.isEnableAutodownload()) {
|
||||
item.disableAutoDownload();
|
||||
DBWriter.setFeedItem(item);
|
||||
|
|
|
@ -9,21 +9,18 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.StringRes;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UsageStatistics;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
|
||||
public class DownloadActionButton extends ItemActionButton {
|
||||
private boolean isInQueue;
|
||||
|
||||
public DownloadActionButton(FeedItem item) {
|
||||
super(item);
|
||||
this.isInQueue = item.isTagged(FeedItem.TAG_QUEUE);;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,30 +50,17 @@ public class DownloadActionButton extends ItemActionButton {
|
|||
UsageStatistics.logAction(UsageStatistics.ACTION_DOWNLOAD);
|
||||
|
||||
if (NetworkUtils.isEpisodeDownloadAllowed() || MobileDownloadHelper.userAllowedMobileDownloads()) {
|
||||
downloadEpisode(context);
|
||||
} else if (MobileDownloadHelper.userChoseAddToQueue() && !isInQueue) {
|
||||
addEpisodeToQueue(context);
|
||||
DownloadService.download(context, false, DownloadRequestCreator.create(item.getMedia()).build());
|
||||
} else if (MobileDownloadHelper.userChoseAddToQueue() && !item.isTagged(FeedItem.TAG_QUEUE)) {
|
||||
DBWriter.addQueueItem(context, item);
|
||||
Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
MobileDownloadHelper.confirmMobileDownload(context, item);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldNotDownload(@NonNull FeedMedia media) {
|
||||
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
|
||||
boolean isDownloading = DownloadService.isDownloadingFile(media.getDownload_url());
|
||||
return isDownloading || media.isDownloaded();
|
||||
}
|
||||
|
||||
private void addEpisodeToQueue(Context context) {
|
||||
DBWriter.addQueueItem(context, item);
|
||||
Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void downloadEpisode(Context context) {
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, item);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.StringRes;
|
||||
import android.view.View;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
|
||||
public abstract class ItemActionButton {
|
||||
|
@ -39,7 +39,7 @@ public abstract class ItemActionButton {
|
|||
return new MarkAsPlayedActionButton(item);
|
||||
}
|
||||
|
||||
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
|
||||
final boolean isDownloadingMedia = DownloadService.isDownloadingFile(media.getDownload_url());
|
||||
if (FeedItemUtil.isCurrentlyPlaying(media)) {
|
||||
return new PauseActionButton(item);
|
||||
} else if (item.getFeed().isLocalFeed()) {
|
||||
|
|
|
@ -4,12 +4,11 @@ import android.content.Context;
|
|||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
class MobileDownloadHelper {
|
||||
private static long addToQueueTimestamp;
|
||||
|
@ -45,11 +44,6 @@ class MobileDownloadHelper {
|
|||
|
||||
private static void downloadFeedItems(Context context, FeedItem item) {
|
||||
allowMobileDownloadTimestamp = System.currentTimeMillis();
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(context, true, item);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
|
||||
}
|
||||
DownloadService.download(context, true, DownloadRequestCreator.create(item.getMedia()).build());
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@ import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
|
|||
import de.danoeh.antennapod.activity.OpmlImportActivity;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||
import de.danoeh.antennapod.databinding.AddfeedBinding;
|
||||
import de.danoeh.antennapod.databinding.EditTextDialogBinding;
|
||||
|
@ -204,7 +203,7 @@ public class AddFeedFragment extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private Feed addLocalFolder(Uri uri) throws DownloadRequestException {
|
||||
private Feed addLocalFolder(Uri uri) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler;
|
|||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
|
@ -162,7 +161,7 @@ public class CompletedDownloadsFragment extends Fragment implements
|
|||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||
|
|
|
@ -29,7 +29,6 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
|||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
|
@ -108,7 +107,7 @@ public class DownloadLogFragment extends ListFragment {
|
|||
Object item = adapter.getItem(position);
|
||||
if (item instanceof Downloader) {
|
||||
DownloadRequest downloadRequest = ((Downloader) item).getDownloadRequest();
|
||||
DownloadRequester.getInstance().cancelDownload(getActivity(), downloadRequest.getSource());
|
||||
DownloadService.cancel(getContext(), downloadRequest.getSource());
|
||||
|
||||
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
|
||||
|
@ -196,7 +195,7 @@ public class DownloadLogFragment extends ListFragment {
|
|||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
private void loadDownloadLog() {
|
||||
if (disposable != null) {
|
||||
|
|
|
@ -45,7 +45,6 @@ import de.danoeh.antennapod.event.FeedItemEvent;
|
|||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
|
@ -113,7 +112,7 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||
|
|
|
@ -36,12 +36,10 @@ import com.google.android.material.snackbar.Snackbar;
|
|||
import com.joanzapata.iconify.Iconify;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
|
||||
|
@ -280,13 +278,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
|
|||
R.string.please_wait_for_data, Toast.LENGTH_LONG);
|
||||
return false;
|
||||
}
|
||||
boolean handled = false;
|
||||
try {
|
||||
handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getContext(), e.getMessage());
|
||||
}
|
||||
boolean handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
|
||||
|
||||
if (item.getItemId() == R.id.reconnect_local_folder && Build.VERSION.SDK_INT >= 21) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
|
||||
|
|
|
@ -48,7 +48,6 @@ import java.util.Set;
|
|||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.event.FavoritesEvent;
|
||||
|
@ -65,8 +64,6 @@ import de.danoeh.antennapod.core.service.download.DownloadService;
|
|||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemPermutors;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
|
||||
|
@ -203,12 +200,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||
nextPageLoader = new MoreContentListFooterUtil(root.findViewById(R.id.more_content_list_footer));
|
||||
nextPageLoader.setClickListener(() -> {
|
||||
if (feed != null) {
|
||||
try {
|
||||
DBTasks.loadNextPageOfFeed(getActivity(), feed, false);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
|
||||
}
|
||||
DBTasks.loadNextPageOfFeed(getActivity(), feed, false);
|
||||
}
|
||||
});
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
|
@ -226,11 +218,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||
SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
|
||||
swipeRefreshLayout.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
|
||||
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
try {
|
||||
DBTasks.forceRefreshFeed(requireContext(), feed, true);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DBTasks.forceRefreshFeed(requireContext(), feed, true);
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false),
|
||||
getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
|
||||
});
|
||||
|
@ -285,12 +273,8 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
|
||||
@Override
|
||||
public boolean isRefreshing() {
|
||||
return feed != null && DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFile(feed);
|
||||
}
|
||||
};
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFile(feed.getDownload_url());
|
||||
|
||||
private void refreshToolbarState() {
|
||||
if (feed == null) {
|
||||
|
@ -318,14 +302,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||
R.string.please_wait_for_data, Toast.LENGTH_LONG);
|
||||
return true;
|
||||
}
|
||||
boolean feedMenuHandled;
|
||||
try {
|
||||
feedMenuHandled = FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
|
||||
return true;
|
||||
}
|
||||
boolean feedMenuHandled = FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed);
|
||||
if (feedMenuHandled) {
|
||||
return true;
|
||||
}
|
||||
|
@ -479,10 +456,10 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
|
|||
if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) {
|
||||
refreshToolbarState();
|
||||
}
|
||||
if (!DownloadRequester.getInstance().isDownloadingFeeds()) {
|
||||
if (!DownloadService.isDownloadingFeeds()) {
|
||||
nextPageLoader.getRoot().setVisibility(View.GONE);
|
||||
}
|
||||
nextPageLoader.setLoadingState(DownloadRequester.getInstance().isDownloadingFeeds());
|
||||
nextPageLoader.setLoadingState(DownloadService.isDownloadingFeeds());
|
||||
}
|
||||
|
||||
private void displayList() {
|
||||
|
|
|
@ -39,6 +39,7 @@ import de.danoeh.antennapod.adapter.actionbutton.StreamActionButton;
|
|||
import de.danoeh.antennapod.adapter.actionbutton.VisitWebsiteActionButton;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.event.PlayerStatusEvent;
|
||||
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
|
||||
|
@ -50,7 +51,6 @@ import de.danoeh.antennapod.core.preferences.UsageStatistics;
|
|||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.DateFormatter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
|
@ -336,7 +336,7 @@ public class ItemFragment extends Fragment {
|
|||
} else {
|
||||
actionButton1 = new StreamActionButton(item);
|
||||
}
|
||||
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
|
||||
if (DownloadService.isDownloadingFile(media.getDownload_url())) {
|
||||
actionButton2 = new CancelDownloadActionButton(item);
|
||||
} else if (!media.isDownloaded()) {
|
||||
actionButton2 = new DownloadActionButton(item);
|
||||
|
|
|
@ -48,7 +48,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||
|
@ -262,7 +261,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
|
|||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
private void refreshToolbarState() {
|
||||
boolean keepSorted = UserPreferences.isQueueKeepSorted();
|
||||
|
|
|
@ -50,7 +50,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.dialog.FeedSortDialog;
|
||||
|
@ -403,7 +402,7 @@ public class SubscriptionFragment extends Fragment
|
|||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
@Override
|
||||
public void onEndSelectMode() {
|
||||
|
|
|
@ -11,10 +11,10 @@ import java.util.List;
|
|||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
|
||||
|
@ -78,19 +78,14 @@ public class EpisodeMultiSelectActionHandler {
|
|||
|
||||
private void downloadChecked() {
|
||||
// download the check episodes in the same order as they are currently displayed
|
||||
List<FeedItem> toDownload = new ArrayList<>(selectedItems.size());
|
||||
List<DownloadRequest> requests = new ArrayList<>();
|
||||
for (FeedItem episode : selectedItems) {
|
||||
if (episode.hasMedia() && !episode.getFeed().isLocalFeed()) {
|
||||
toDownload.add(episode);
|
||||
requests.add(DownloadRequestCreator.create(episode.getMedia()).build());
|
||||
}
|
||||
}
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(activity, true, toDownload.toArray(new FeedItem[0]));
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(activity, e.getMessage());
|
||||
}
|
||||
showMessage(R.plurals.downloading_batch_label, toDownload.size());
|
||||
DownloadService.download(activity, true, requests.toArray(new DownloadRequest[0]));
|
||||
showMessage(R.plurals.downloading_batch_label, requests.size());
|
||||
}
|
||||
|
||||
private void deleteChecked() {
|
||||
|
|
|
@ -17,7 +17,6 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
|||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.ShareUtils;
|
||||
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||
|
@ -55,11 +54,8 @@ public class FeedMenuHandler {
|
|||
|
||||
/**
|
||||
* NOTE: This method does not handle clicks on the 'remove feed' - item.
|
||||
*
|
||||
* @throws DownloadRequestException
|
||||
*/
|
||||
public static boolean onOptionsItemClicked(final Context context, final MenuItem item,
|
||||
final Feed selectedFeed) throws DownloadRequestException {
|
||||
public static boolean onOptionsItemClicked(final Context context, final MenuItem item, final Feed selectedFeed) {
|
||||
final int itemId = item.getItemId();
|
||||
if (itemId == R.id.refresh_item) {
|
||||
DBTasks.forceRefreshFeed(context, selectedFeed, true);
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.net.ConnectivityManager;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import android.util.Log;
|
|||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
// modified from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
|
||||
// and ConnectivityActionReceiver.java
|
||||
|
@ -37,7 +37,7 @@ public class PowerConnectionReceiver extends BroadcastReceiver {
|
|||
// if we're not supposed to be auto-downloading when we're not charging, stop it
|
||||
if (!UserPreferences.isEnableAutodownloadOnBattery()) {
|
||||
Log.d(TAG, "not charging anymore, canceling auto-download");
|
||||
DownloadRequester.getInstance().cancelAllDownloads(context);
|
||||
DownloadService.cancelAll(context);
|
||||
} else {
|
||||
Log.d(TAG, "not charging anymore, but the user allows auto-download " +
|
||||
"when on battery so we'll keep going");
|
||||
|
|
|
@ -11,9 +11,9 @@ import java.util.Arrays;
|
|||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
/**
|
||||
* Receives intents from AntennaPod Single Purpose apps
|
||||
|
@ -44,12 +44,7 @@ public class SPAReceiver extends BroadcastReceiver{
|
|||
ClientConfig.initialize(context);
|
||||
for (String url : feedUrls) {
|
||||
Feed f = new Feed(url, null);
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadFeed(context, f);
|
||||
} catch (DownloadRequestException e) {
|
||||
Log.e(TAG, "Error while trying to add feed " + url);
|
||||
e.printStackTrace();
|
||||
}
|
||||
DownloadService.download(context, false, DownloadRequestCreator.create(f).build());
|
||||
}
|
||||
Toast.makeText(context, R.string.sp_apps_importing_feeds_msg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import de.danoeh.antennapod.R;
|
|||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.CoverLoader;
|
||||
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.util.DateFormatter;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
|
@ -28,9 +30,7 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
|
|||
import de.danoeh.antennapod.model.playback.MediaType;
|
||||
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
|
@ -145,8 +145,8 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||
itemView.setBackgroundResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.selectableItemBackground));
|
||||
}
|
||||
|
||||
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
|
||||
final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media);
|
||||
if (DownloadService.isDownloadingFile(media.getDownload_url())) {
|
||||
final DownloadRequest downloadRequest = DownloadService.findRequest(media.getDownload_url());
|
||||
float percent = 0.01f * downloadRequest.getProgressPercent();
|
||||
secondaryActionProgress.setPercentage(Math.max(percent, 0.01f), item);
|
||||
} else if (media.isDownloaded()) {
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
<Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD"/>
|
||||
<Class name="de.danoeh.antennapod.core.service.download.DownloadService"/>
|
||||
</Match>
|
||||
<Match>
|
||||
<Bug pattern="MS_CANNOT_BE_FINAL"/>
|
||||
<Class name="de.danoeh.antennapod.core.service.download.DownloadService"/>
|
||||
</Match>
|
||||
<Match>
|
||||
<Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD"/>
|
||||
<Class name="de.danoeh.antennapod.core.service.playback.PlaybackService"/>
|
||||
|
|
|
@ -8,6 +8,9 @@ import android.content.Context;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
|
@ -33,8 +36,6 @@ import de.danoeh.antennapod.core.export.opml.OpmlReader;
|
|||
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
||||
public class OpmlBackupAgent extends BackupAgentHelper {
|
||||
private static final String OPML_BACKUP_KEY = "opml";
|
||||
|
@ -146,16 +147,10 @@ public class OpmlBackupAgent extends BackupAgentHelper {
|
|||
try {
|
||||
ArrayList<OpmlElement> opmlElements = new OpmlReader().readDocument(reader);
|
||||
mChecksum = digester == null ? null : digester.digest();
|
||||
DownloadRequester downloader = DownloadRequester.getInstance();
|
||||
|
||||
for (OpmlElement opmlElem : opmlElements) {
|
||||
Feed feed = new Feed(opmlElem.getXmlUrl(), null, opmlElem.getText());
|
||||
|
||||
try {
|
||||
downloader.downloadFeed(mContext, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
Log.d(TAG, "Error while restoring/downloading feed", e);
|
||||
}
|
||||
DownloadRequest request = DownloadRequestCreator.create(feed).build();
|
||||
DownloadService.download(mContext, false, request);
|
||||
}
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.e(TAG, "Error while parsing the OPML file", e);
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package de.danoeh.antennapod.core.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import de.danoeh.antennapod.core.R;
|
||||
|
||||
/** Creates Alert Dialogs if a DownloadRequestException has happened. */
|
||||
public class DownloadRequestErrorDialogCreator {
|
||||
private DownloadRequestErrorDialogCreator() {
|
||||
}
|
||||
|
||||
public static void newRequestErrorDialog(Context context,
|
||||
String errorMessage) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setNeutralButton(android.R.string.ok,
|
||||
(dialog, which) -> dialog.dismiss())
|
||||
.setTitle(R.string.download_error_request_error)
|
||||
.setMessage(
|
||||
context.getString(R.string.download_request_error_dialog_message_prefix)
|
||||
+ errorMessage);
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ import de.danoeh.antennapod.model.feed.FeedFile;
|
|||
import de.danoeh.antennapod.core.util.URLChecker;
|
||||
|
||||
public class DownloadRequest implements Parcelable {
|
||||
public static final String REQUEST_ARG_PAGE_NR = "page";
|
||||
public static final String REQUEST_ARG_LOAD_ALL_PAGES = "loadAllPages";
|
||||
|
||||
private final String destination;
|
||||
private final String source;
|
||||
|
@ -128,7 +130,6 @@ public class DownloadRequest implements Parcelable {
|
|||
if (size != that.size) return false;
|
||||
if (soFar != that.soFar) return false;
|
||||
if (statusMsg != that.statusMsg) return false;
|
||||
if (!arguments.equals(that.arguments)) return false;
|
||||
if (!destination.equals(that.destination)) return false;
|
||||
if (password != null ? !password.equals(that.password) : that.password != null)
|
||||
return false;
|
||||
|
@ -269,18 +270,27 @@ public class DownloadRequest implements Parcelable {
|
|||
private boolean deleteOnFailure = false;
|
||||
private final long feedfileId;
|
||||
private final int feedfileType;
|
||||
private Bundle arguments;
|
||||
private boolean initiatedByUser;
|
||||
private Bundle arguments = new Bundle();
|
||||
private boolean initiatedByUser = true;
|
||||
|
||||
public Builder(@NonNull String destination, @NonNull FeedFile item, boolean initiatedByUser) {
|
||||
public Builder(@NonNull String destination, @NonNull FeedFile item) {
|
||||
this.destination = destination;
|
||||
this.source = URLChecker.prepareURL(item.getDownload_url());
|
||||
this.title = item.getHumanReadableIdentifier();
|
||||
this.feedfileId = item.getId();
|
||||
this.feedfileType = item.getTypeAsInt();
|
||||
}
|
||||
|
||||
public void setInitiatedByUser(boolean initiatedByUser) {
|
||||
this.initiatedByUser = initiatedByUser;
|
||||
}
|
||||
|
||||
public void setForce(boolean force) {
|
||||
if (force) {
|
||||
lastModified = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder deleteOnFailure(boolean deleteOnFailure) {
|
||||
this.deleteOnFailure = deleteOnFailure;
|
||||
return this;
|
||||
|
@ -297,14 +307,14 @@ public class DownloadRequest implements Parcelable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void loadAllPages(boolean loadAllPages) {
|
||||
if (loadAllPages) {
|
||||
arguments.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, true);
|
||||
}
|
||||
}
|
||||
|
||||
public DownloadRequest build() {
|
||||
return new DownloadRequest(this);
|
||||
}
|
||||
|
||||
public Builder withArguments(Bundle args) {
|
||||
this.arguments = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package de.danoeh.antennapod.core.service.download;
|
||||
|
||||
import android.util.Log;
|
||||
import android.webkit.URLUtil;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Creates download requests that can be sent to the DownloadService.
|
||||
*/
|
||||
public class DownloadRequestCreator {
|
||||
private static final String TAG = "DownloadRequester";
|
||||
private static final String FEED_DOWNLOADPATH = "cache/";
|
||||
private static final String MEDIA_DOWNLOADPATH = "media/";
|
||||
|
||||
public static DownloadRequest.Builder create(Feed feed) {
|
||||
File dest = new File(getFeedfilePath(), getFeedfileName(feed));
|
||||
if (!isFilenameAvailable(dest.toString())) {
|
||||
dest = findUnusedFile(dest);
|
||||
}
|
||||
Log.d(TAG, "Requesting download of url " + feed.getDownload_url());
|
||||
|
||||
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
|
||||
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
|
||||
|
||||
return new DownloadRequest.Builder(dest.toString(), feed)
|
||||
.withAuthentication(username, password)
|
||||
.deleteOnFailure(true)
|
||||
.lastModified(feed.getLastUpdate());
|
||||
}
|
||||
|
||||
public static DownloadRequest.Builder create(FeedMedia media) {
|
||||
final boolean partiallyDownloadedFileExists =
|
||||
media.getFile_url() != null && new File(media.getFile_url()).exists();
|
||||
File dest;
|
||||
if (media.getFile_url() != null && new File(media.getFile_url()).exists()) {
|
||||
dest = new File(media.getFile_url());
|
||||
} else {
|
||||
dest = new File(getMediafilePath(media), getMediafilename(media));
|
||||
}
|
||||
|
||||
if (!isFilenameAvailable(dest.toString()) || (!partiallyDownloadedFileExists && dest.exists())) {
|
||||
dest = findUnusedFile(dest);
|
||||
}
|
||||
Log.d(TAG, "Requesting download of url " + media.getDownload_url());
|
||||
|
||||
String username = (media.getItem().getFeed().getPreferences() != null)
|
||||
? media.getItem().getFeed().getPreferences().getUsername() : null;
|
||||
String password = (media.getItem().getFeed().getPreferences() != null)
|
||||
? media.getItem().getFeed().getPreferences().getPassword() : null;
|
||||
|
||||
return new DownloadRequest.Builder(dest.toString(), media)
|
||||
.deleteOnFailure(false)
|
||||
.withAuthentication(username, password);
|
||||
}
|
||||
|
||||
private static File findUnusedFile(File dest) {
|
||||
// find different name
|
||||
File newDest = null;
|
||||
for (int i = 1; i < Integer.MAX_VALUE; i++) {
|
||||
String newName = FilenameUtils.getBaseName(dest
|
||||
.getName())
|
||||
+ "-"
|
||||
+ i
|
||||
+ FilenameUtils.EXTENSION_SEPARATOR
|
||||
+ FilenameUtils.getExtension(dest.getName());
|
||||
Log.d(TAG, "Testing filename " + newName);
|
||||
newDest = new File(dest.getParent(), newName);
|
||||
if (!newDest.exists() && isFilenameAvailable(newDest.toString())) {
|
||||
Log.d(TAG, "File doesn't exist yet. Using " + newName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newDest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a filename is available and false if it has already been
|
||||
* taken by another requested download.
|
||||
*/
|
||||
private static boolean isFilenameAvailable(String path) {
|
||||
for (Downloader downloader : DownloadService.downloads) {
|
||||
if (downloader.request.getDestination().equals(path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String getFeedfilePath() {
|
||||
return UserPreferences.getDataFolder(FEED_DOWNLOADPATH).toString() + "/";
|
||||
}
|
||||
|
||||
private static String getFeedfileName(Feed feed) {
|
||||
String filename = feed.getDownload_url();
|
||||
if (feed.getTitle() != null && !feed.getTitle().isEmpty()) {
|
||||
filename = feed.getTitle();
|
||||
}
|
||||
return "feed-" + FileNameGenerator.generateFileName(filename);
|
||||
}
|
||||
|
||||
private static String getMediafilePath(FeedMedia media) {
|
||||
String mediaPath = MEDIA_DOWNLOADPATH
|
||||
+ FileNameGenerator.generateFileName(media.getItem().getFeed().getTitle());
|
||||
return UserPreferences.getDataFolder(mediaPath).toString() + "/";
|
||||
}
|
||||
|
||||
private static String getMediafilename(FeedMedia media) {
|
||||
String filename;
|
||||
String titleBaseFilename = "";
|
||||
|
||||
// Try to generate the filename by the item title
|
||||
if (media.getItem() != null && media.getItem().getTitle() != null) {
|
||||
String title = media.getItem().getTitle();
|
||||
titleBaseFilename = FileNameGenerator.generateFileName(title);
|
||||
}
|
||||
|
||||
String urlBaseFilename = URLUtil.guessFileName(media.getDownload_url(), null, media.getMime_type());
|
||||
|
||||
if (!titleBaseFilename.equals("")) {
|
||||
// Append extension
|
||||
final int filenameMaxLength = 220;
|
||||
if (titleBaseFilename.length() > filenameMaxLength) {
|
||||
titleBaseFilename = titleBaseFilename.substring(0, filenameMaxLength);
|
||||
}
|
||||
filename = titleBaseFilename + FilenameUtils.EXTENSION_SEPARATOR
|
||||
+ FilenameUtils.getExtension(urlBaseFilename);
|
||||
} else {
|
||||
// Fall back on URL file name
|
||||
filename = urlBaseFilename;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
}
|
|
@ -9,9 +9,7 @@ import android.content.Intent;
|
|||
import android.content.IntentFilter;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -20,6 +18,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.app.ServiceCompat;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
@ -29,15 +28,13 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.util.download.ConnectionStateMonitor;
|
||||
|
@ -53,7 +50,6 @@ import de.danoeh.antennapod.core.service.download.handler.PostDownloaderTask;
|
|||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
|
||||
/**
|
||||
|
@ -65,65 +61,34 @@ import de.danoeh.antennapod.core.util.DownloadError;
|
|||
*/
|
||||
public class DownloadService extends Service {
|
||||
private static final String TAG = "DownloadService";
|
||||
|
||||
/**
|
||||
* Cancels one download. The intent MUST have an EXTRA_DOWNLOAD_URL extra that contains the download URL of the
|
||||
* object whose download should be cancelled.
|
||||
*/
|
||||
private static final int SCHED_EX_POOL_SIZE = 1;
|
||||
public static final String ACTION_CANCEL_DOWNLOAD = "action.de.danoeh.antennapod.core.service.cancelDownload";
|
||||
|
||||
/**
|
||||
* Cancels all running downloads.
|
||||
*/
|
||||
public static final String ACTION_CANCEL_ALL_DOWNLOADS = "action.de.danoeh.antennapod.core.service.cancelAllDownloads";
|
||||
|
||||
/**
|
||||
* Extra for ACTION_CANCEL_DOWNLOAD
|
||||
*/
|
||||
public static final String ACTION_CANCEL_ALL_DOWNLOADS = "action.de.danoeh.antennapod.core.service.cancelAll";
|
||||
public static final String EXTRA_DOWNLOAD_URL = "downloadUrl";
|
||||
|
||||
/**
|
||||
* Extra for ACTION_ENQUEUE_DOWNLOAD intent.
|
||||
*/
|
||||
public static final String EXTRA_REQUESTS = "downloadRequests";
|
||||
|
||||
public static final String EXTRA_REFRESH_ALL = "refreshAll";
|
||||
public static final String EXTRA_INITIATED_BY_USER = "initiatedByUser";
|
||||
public static final String EXTRA_CLEANUP_MEDIA = "cleanupMedia";
|
||||
|
||||
/**
|
||||
* Contains all completed downloads that have not been included in the report yet.
|
||||
*/
|
||||
private final List<DownloadStatus> reportQueue;
|
||||
private final ExecutorService syncExecutor;
|
||||
private final CompletionService<Downloader> downloadExecutor;
|
||||
private final DownloadRequester requester;
|
||||
private DownloadServiceNotification notificationManager;
|
||||
private final NewEpisodesNotification newEpisodesNotification;
|
||||
|
||||
/**
|
||||
* Currently running downloads.
|
||||
*/
|
||||
private final List<Downloader> downloads;
|
||||
|
||||
/**
|
||||
* Number of running downloads.
|
||||
*/
|
||||
private AtomicInteger numberOfDownloads;
|
||||
|
||||
/**
|
||||
* True if service is running.
|
||||
*/
|
||||
public static boolean isRunning = false;
|
||||
|
||||
private Handler handler;
|
||||
// Can be modified from another thread while iterating. Both possible race conditions are not critical:
|
||||
// Remove while iterating: We think it is still downloading and don't start a new download with the same file.
|
||||
// Add while iterating: We think it is not downloading and might start a second download with the same file.
|
||||
static final List<Downloader> downloads = Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
||||
private final ExecutorService downloadHandleExecutor;
|
||||
private final ExecutorService downloadEnqueueExecutor;
|
||||
|
||||
private final List<DownloadStatus> reportQueue = new ArrayList<>();
|
||||
private DownloadServiceNotification notificationManager;
|
||||
private final NewEpisodesNotification newEpisodesNotification;
|
||||
private NotificationUpdater notificationUpdater;
|
||||
private ScheduledFuture<?> notificationUpdaterFuture;
|
||||
private ScheduledFuture<?> downloadPostFuture;
|
||||
private static final int SCHED_EX_POOL_SIZE = 1;
|
||||
private final ScheduledThreadPoolExecutor schedExecutor;
|
||||
private final ScheduledThreadPoolExecutor notificationUpdateExecutor;
|
||||
private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
|
||||
private final IBinder binder = new LocalBinder();
|
||||
private ConnectionStateMonitor connectionMonitor;
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
|
||||
private class LocalBinder extends Binder {
|
||||
public DownloadService getService() {
|
||||
|
@ -131,61 +96,42 @@ public class DownloadService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return binder;
|
||||
}
|
||||
|
||||
public DownloadService() {
|
||||
reportQueue = Collections.synchronizedList(new ArrayList<>());
|
||||
downloads = Collections.synchronizedList(new ArrayList<>());
|
||||
numberOfDownloads = new AtomicInteger(0);
|
||||
requester = DownloadRequester.getInstance();
|
||||
newEpisodesNotification = new NewEpisodesNotification();
|
||||
|
||||
syncExecutor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "SyncThread");
|
||||
downloadEnqueueExecutor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "EnqueueThread");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
});
|
||||
// Must be the first runnable in syncExecutor
|
||||
syncExecutor.execute(newEpisodesNotification::loadCountersBeforeRefresh);
|
||||
downloadEnqueueExecutor.execute(newEpisodesNotification::loadCountersBeforeRefresh);
|
||||
|
||||
Log.d(TAG, "parallel downloads: " + UserPreferences.getParallelDownloads());
|
||||
downloadExecutor = new ExecutorCompletionService<>(
|
||||
Executors.newFixedThreadPool(UserPreferences.getParallelDownloads(),
|
||||
r -> {
|
||||
Thread t = new Thread(r, "DownloadThread");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
}
|
||||
)
|
||||
);
|
||||
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE,
|
||||
downloadHandleExecutor = Executors.newFixedThreadPool(UserPreferences.getParallelDownloads(),
|
||||
r -> {
|
||||
Thread t = new Thread(r, "DownloadThread");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
});
|
||||
notificationUpdateExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE,
|
||||
r -> {
|
||||
Thread t = new Thread(r, "DownloadSchedExecutorThread");
|
||||
Thread t = new Thread(r, "NotificationUpdateExecutor");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
}, (r, executor) -> Log.w(TAG, "SchedEx rejected submission of new task")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && intent.getParcelableArrayListExtra(EXTRA_REQUESTS) != null) {
|
||||
Notification notification = notificationManager.updateNotifications(
|
||||
requester.getNumberOfDownloads(), downloads);
|
||||
startForeground(R.id.notification_downloading, notification);
|
||||
setupNotificationUpdaterIfNecessary();
|
||||
syncExecutor.execute(() -> onDownloadQueued(intent));
|
||||
} else if (numberOfDownloads.get() == 0) {
|
||||
shutdown();
|
||||
} else {
|
||||
Log.d(TAG, "onStartCommand: Unknown intent");
|
||||
}
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.d(TAG, "Service started");
|
||||
isRunning = true;
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
notificationManager = new DownloadServiceNotification(this);
|
||||
|
||||
IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
|
||||
|
@ -197,13 +143,106 @@ public class DownloadService extends Service {
|
|||
connectionMonitor = new ConnectionStateMonitor();
|
||||
connectionMonitor.enable(getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
downloadCompletionThread.start();
|
||||
public static void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
|
||||
if (requests.length > 100) {
|
||||
throw new IllegalArgumentException("Android silently drops intent payloads that are too large");
|
||||
}
|
||||
ArrayList<DownloadRequest> requestsToSend = new ArrayList<>();
|
||||
for (DownloadRequest request : requests) {
|
||||
if (!isDownloadingFile(request.getSource())) {
|
||||
requestsToSend.add(request);
|
||||
}
|
||||
}
|
||||
if (requestsToSend.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Intent launchIntent = new Intent(context, DownloadService.class);
|
||||
launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend);
|
||||
if (cleanupMedia) {
|
||||
launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true);
|
||||
}
|
||||
ContextCompat.startForegroundService(context, launchIntent);
|
||||
}
|
||||
|
||||
public static void refreshAllFeeds(Context context, boolean initiatedByUser) {
|
||||
Intent launchIntent = new Intent(context, DownloadService.class);
|
||||
launchIntent.putExtra(DownloadService.EXTRA_REFRESH_ALL, true);
|
||||
launchIntent.putExtra(DownloadService.EXTRA_INITIATED_BY_USER, initiatedByUser);
|
||||
ContextCompat.startForegroundService(context, launchIntent);
|
||||
}
|
||||
|
||||
public static void cancel(Context context, String url) {
|
||||
if (!isRunning) {
|
||||
return;
|
||||
}
|
||||
Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD);
|
||||
cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, url);
|
||||
cancelIntent.setPackage(context.getPackageName());
|
||||
context.sendBroadcast(cancelIntent);
|
||||
}
|
||||
|
||||
public static void cancelAll(Context context) {
|
||||
if (!isRunning) {
|
||||
return;
|
||||
}
|
||||
Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_ALL_DOWNLOADS);
|
||||
cancelIntent.setPackage(context.getPackageName());
|
||||
context.sendBroadcast(cancelIntent);
|
||||
}
|
||||
|
||||
public static boolean isDownloadingFeeds() {
|
||||
if (!isRunning) {
|
||||
return false;
|
||||
}
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.request.getFeedfileType() == Feed.FEEDFILETYPE_FEED && !downloader.cancelled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isDownloadingFile(String downloadUrl) {
|
||||
if (!isRunning) {
|
||||
return false;
|
||||
}
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.request.getSource().equals(downloadUrl) && !downloader.cancelled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DownloadRequest findRequest(String downloadUrl) {
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.request.getSource().equals(downloadUrl)) {
|
||||
return downloader.request;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && intent.hasExtra(EXTRA_REQUESTS)) {
|
||||
Notification notification = notificationManager.updateNotifications(downloads);
|
||||
startForeground(R.id.notification_downloading, notification);
|
||||
setupNotificationUpdaterIfNecessary();
|
||||
downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
|
||||
} else if (intent != null && intent.getBooleanExtra(EXTRA_REFRESH_ALL, false)) {
|
||||
Notification notification = notificationManager.updateNotifications(downloads);
|
||||
startForeground(R.id.notification_downloading, notification);
|
||||
setupNotificationUpdaterIfNecessary();
|
||||
downloadEnqueueExecutor.execute(() -> enqueueAll(intent));
|
||||
} else if (downloads.size() == 0) {
|
||||
shutdown();
|
||||
} else {
|
||||
Log.d(TAG, "onStartCommand: Unknown intent");
|
||||
}
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,19 +257,14 @@ public class DownloadService extends Service {
|
|||
}
|
||||
|
||||
EventBus.getDefault().postSticky(DownloadEvent.refresh(Collections.emptyList()));
|
||||
|
||||
downloadCompletionThread.interrupt();
|
||||
try {
|
||||
downloadCompletionThread.join(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
cancelNotificationUpdater();
|
||||
syncExecutor.shutdown();
|
||||
schedExecutor.shutdown();
|
||||
downloadHandleExecutor.shutdown();
|
||||
downloadEnqueueExecutor.shutdown();
|
||||
notificationUpdateExecutor.shutdown();
|
||||
if (downloadPostFuture != null) {
|
||||
downloadPostFuture.cancel(true);
|
||||
}
|
||||
downloads.clear();
|
||||
unregisterReceiver(cancelDownloadReceiver);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
connectionMonitor.disable(getApplicationContext());
|
||||
|
@ -240,41 +274,26 @@ public class DownloadService extends Service {
|
|||
DBTasks.autodownloadUndownloadedItems(getApplicationContext());
|
||||
}
|
||||
|
||||
private final Thread downloadCompletionThread = new Thread("DownloadCompletionThread") {
|
||||
private static final String TAG = "downloadCompletionThd";
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "downloadCompletionThread was started");
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
Downloader downloader = downloadExecutor.take().get();
|
||||
Log.d(TAG, "Received 'Download Complete' - message.");
|
||||
|
||||
if (downloader.getResult().isSuccessful()) {
|
||||
syncExecutor.execute(() -> {
|
||||
handleSuccessfulDownload(downloader);
|
||||
removeDownload(downloader);
|
||||
numberOfDownloads.decrementAndGet();
|
||||
stopServiceIfEverythingDoneAsync();
|
||||
});
|
||||
} else {
|
||||
handleFailedDownload(downloader);
|
||||
removeDownload(downloader);
|
||||
numberOfDownloads.decrementAndGet();
|
||||
stopServiceIfEverythingDoneAsync();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "DownloadCompletionThread was interrupted");
|
||||
return;
|
||||
} catch (ExecutionException e) {
|
||||
Log.e(TAG, "ExecutionException in DownloadCompletionThread: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "End of downloadCompletionThread");
|
||||
private void performDownload(Downloader downloader) {
|
||||
try {
|
||||
downloader.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
try {
|
||||
if (downloader.getResult().isSuccessful()) {
|
||||
handleSuccessfulDownload(downloader);
|
||||
} else {
|
||||
handleFailedDownload(downloader);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
downloadEnqueueExecutor.submit(() -> {
|
||||
downloads.remove(downloader);
|
||||
stopServiceIfEverythingDone();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSuccessfulDownload(Downloader downloader) {
|
||||
DownloadRequest request = downloader.getDownloadRequest();
|
||||
|
@ -287,12 +306,15 @@ public class DownloadService extends Service {
|
|||
boolean success = task.run();
|
||||
|
||||
if (success) {
|
||||
if (request.getFeedfileId() == 0) {
|
||||
return; // No download logs for new subscriptions
|
||||
}
|
||||
// we create a 'successful' download log if the feed's last refresh failed
|
||||
List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
|
||||
if (log.size() > 0 && !log.get(0).isSuccessful()) {
|
||||
saveDownloadStatus(task.getDownloadStatus());
|
||||
}
|
||||
if (request.getFeedfileId() != 0 && !request.isInitiatedByUser()) {
|
||||
if (!request.isInitiatedByUser()) {
|
||||
// Was stored in the database before and not initiated manually
|
||||
newEpisodesNotification.showIfNeeded(DownloadService.this, task.getSavedFeed());
|
||||
}
|
||||
|
@ -320,11 +342,11 @@ public class DownloadService extends Service {
|
|||
|
||||
Log.d(TAG, "Requested invalid range, restarting download from the beginning");
|
||||
FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
|
||||
DownloadRequester.getInstance().download(DownloadService.this, downloader.getDownloadRequest());
|
||||
download(this, false, downloader.getDownloadRequest());
|
||||
} else {
|
||||
Log.e(TAG, "Download failed");
|
||||
saveDownloadStatus(status);
|
||||
syncExecutor.execute(new FailedDownloadHandler(downloader.getDownloadRequest()));
|
||||
new FailedDownloadHandler(downloader.getDownloadRequest()).run();
|
||||
|
||||
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
FeedItem item = getFeedItemFromId(status.getFeedfileId());
|
||||
|
@ -350,94 +372,77 @@ public class DownloadService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private Downloader getDownloader(String downloadUrl) {
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.getDownloadRequest().getSource().equals(downloadUrl)) {
|
||||
return downloader;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final BroadcastReceiver cancelDownloadReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "cancelDownloadReceiver: " + intent.getAction());
|
||||
if (TextUtils.equals(intent.getAction(), ACTION_CANCEL_DOWNLOAD)) {
|
||||
String url = intent.getStringExtra(EXTRA_DOWNLOAD_URL);
|
||||
if (url == null) {
|
||||
throw new IllegalArgumentException("ACTION_CANCEL_DOWNLOAD intent needs download url extra");
|
||||
}
|
||||
|
||||
Log.d(TAG, "Cancelling download with url " + url);
|
||||
Downloader d = getDownloader(url);
|
||||
if (d != null) {
|
||||
d.cancel();
|
||||
DownloadRequest request = d.getDownloadRequest();
|
||||
DownloadRequester.getInstance().removeDownload(request);
|
||||
|
||||
FeedItem item = getFeedItemFromId(request.getFeedfileId());
|
||||
if (item != null) {
|
||||
// undo enqueue upon cancel
|
||||
if (request.isMediaEnqueued()) {
|
||||
Log.v(TAG, "Undoing enqueue upon cancelling download");
|
||||
try {
|
||||
DBWriter.removeQueueItem(getApplicationContext(), false, item).get();
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG, "Unexpected exception during undoing enqueue upon cancel", t);
|
||||
}
|
||||
}
|
||||
EventBus.getDefault().post(FeedItemEvent.updated(item));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Could not cancel download with url " + url);
|
||||
}
|
||||
postDownloaders();
|
||||
|
||||
downloadEnqueueExecutor.execute(() -> {
|
||||
doCancel(url);
|
||||
postDownloaders();
|
||||
stopServiceIfEverythingDone();
|
||||
});
|
||||
} else if (TextUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
|
||||
for (Downloader d : downloads) {
|
||||
d.cancel();
|
||||
downloadEnqueueExecutor.execute(() -> {
|
||||
for (Downloader d : downloads) {
|
||||
d.cancel();
|
||||
}
|
||||
Log.d(TAG, "Cancelled all downloads");
|
||||
}
|
||||
postDownloaders();
|
||||
postDownloaders();
|
||||
stopServiceIfEverythingDone();
|
||||
});
|
||||
}
|
||||
stopServiceIfEverythingDone();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private void doCancel(String url) {
|
||||
Log.d(TAG, "Cancelling download with url " + url);
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.cancelled || !downloader.getDownloadRequest().getSource().equals(url)) {
|
||||
continue;
|
||||
}
|
||||
downloader.cancel();
|
||||
DownloadRequest request = downloader.getDownloadRequest();
|
||||
FeedItem item = getFeedItemFromId(request.getFeedfileId());
|
||||
if (item != null) {
|
||||
EventBus.getDefault().post(FeedItemEvent.updated(item));
|
||||
// undo enqueue upon cancel
|
||||
if (request.isMediaEnqueued()) {
|
||||
Log.v(TAG, "Undoing enqueue upon cancelling download");
|
||||
DBWriter.removeQueueItem(getApplicationContext(), false, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onDownloadQueued(Intent intent) {
|
||||
List<DownloadRequest> requests = intent.getParcelableArrayListExtra(EXTRA_REQUESTS);
|
||||
if (requests == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
|
||||
throw new IllegalArgumentException("ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
|
||||
}
|
||||
boolean cleanupMedia = intent.getBooleanExtra(EXTRA_CLEANUP_MEDIA, false);
|
||||
Log.d(TAG, "Received enqueue request. #requests=" + requests.size()
|
||||
+ ", cleanupMedia=" + cleanupMedia);
|
||||
Log.d(TAG, "Received enqueue request. #requests=" + requests.size());
|
||||
|
||||
if (cleanupMedia) {
|
||||
UserPreferences.getEpisodeCleanupAlgorithm()
|
||||
.makeRoomForEpisodes(getApplicationContext(), requests.size());
|
||||
}
|
||||
|
||||
// #2448: First, add to-download items to the queue before actual download
|
||||
// so that the resulting queue order is the same as when download is clicked
|
||||
List<? extends FeedItem> itemsEnqueued;
|
||||
try {
|
||||
itemsEnqueued = enqueueFeedItems(requests);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unexpected exception during enqueue before downloads. Abort download", e);
|
||||
return;
|
||||
if (intent.getBooleanExtra(EXTRA_CLEANUP_MEDIA, false)) {
|
||||
UserPreferences.getEpisodeCleanupAlgorithm().makeRoomForEpisodes(getApplicationContext(), requests.size());
|
||||
}
|
||||
|
||||
for (DownloadRequest request : requests) {
|
||||
onDownloadQueued(request, itemsEnqueued);
|
||||
addNewRequest(request);
|
||||
}
|
||||
postDownloaders();
|
||||
stopServiceIfEverythingDone();
|
||||
|
||||
// Add to-download items to the queue before actual download completed
|
||||
// so that the resulting queue order is the same as when download is clicked
|
||||
enqueueFeedItems(requests);
|
||||
}
|
||||
|
||||
private List<? extends FeedItem> enqueueFeedItems(@NonNull List<? extends DownloadRequest> requests)
|
||||
throws Exception {
|
||||
private void enqueueFeedItems(@NonNull List<DownloadRequest> requests) {
|
||||
List<FeedItem> feedItems = new ArrayList<>();
|
||||
for (DownloadRequest request : requests) {
|
||||
if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
|
@ -450,42 +455,51 @@ public class DownloadService extends Service {
|
|||
feedItems.add(media.getItem());
|
||||
}
|
||||
}
|
||||
|
||||
return DBTasks.enqueueFeedItemsToDownload(getApplicationContext(), feedItems);
|
||||
}
|
||||
|
||||
private void onDownloadQueued(@NonNull DownloadRequest request,
|
||||
@NonNull List<? extends FeedItem> itemsEnqueued) {
|
||||
writeFileUrl(request);
|
||||
|
||||
Downloader downloader = downloaderFactory.create(request);
|
||||
if (downloader != null) {
|
||||
numberOfDownloads.incrementAndGet();
|
||||
|
||||
if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
|
||||
&& isEnqueued(request, itemsEnqueued)) {
|
||||
request.setMediaEnqueued(true);
|
||||
}
|
||||
handler.post(() -> {
|
||||
downloads.add(downloader);
|
||||
downloadExecutor.submit(downloader);
|
||||
postDownloaders();
|
||||
});
|
||||
List<FeedItem> actuallyEnqueued = Collections.emptyList();
|
||||
try {
|
||||
actuallyEnqueued = DBTasks.enqueueFeedItemsToDownload(getApplicationContext(), feedItems);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
handler.post(this::stopServiceIfEverythingDone);
|
||||
}
|
||||
|
||||
private static boolean isEnqueued(@NonNull DownloadRequest request,
|
||||
@NonNull List<? extends FeedItem> itemsEnqueued) {
|
||||
if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
for (DownloadRequest request : requests) {
|
||||
if (request.getFeedfileType() != FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
continue;
|
||||
}
|
||||
final long mediaId = request.getFeedfileId();
|
||||
for (FeedItem item : itemsEnqueued) {
|
||||
for (FeedItem item : actuallyEnqueued) {
|
||||
if (item.getMedia() != null && item.getMedia().getId() == mediaId) {
|
||||
return true;
|
||||
request.setMediaEnqueued(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void enqueueAll(Intent intent) {
|
||||
boolean initiatedByUser = intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false);
|
||||
List<Feed> feeds = DBReader.getFeedList();
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.getPreferences().getKeepUpdated() && !feed.isLocalFeed()) {
|
||||
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
|
||||
builder.setInitiatedByUser(initiatedByUser);
|
||||
addNewRequest(builder.build());
|
||||
}
|
||||
}
|
||||
postDownloaders();
|
||||
stopServiceIfEverythingDone();
|
||||
}
|
||||
|
||||
private void addNewRequest(@NonNull DownloadRequest request) {
|
||||
if (isDownloadingFile(request.getSource())) {
|
||||
Log.d(TAG, "Skipped enqueueing request. Already running.");
|
||||
return;
|
||||
}
|
||||
writeFileUrl(request);
|
||||
Downloader downloader = downloaderFactory.create(request);
|
||||
if (downloader != null) {
|
||||
downloads.add(downloader);
|
||||
downloadHandleExecutor.submit(() -> performDownload(downloader));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -500,20 +514,6 @@ public class DownloadService extends Service {
|
|||
DownloadService.downloaderFactory = downloaderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove download from the DownloadRequester list and from the
|
||||
* DownloadService list.
|
||||
*/
|
||||
private void removeDownload(final Downloader d) {
|
||||
handler.post(() -> {
|
||||
Log.d(TAG, "Removing downloader: " + d.getDownloadRequest().getSource());
|
||||
boolean rc = downloads.remove(d);
|
||||
Log.d(TAG, "Result of downloads.remove: " + rc);
|
||||
DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
|
||||
postDownloaders();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new DownloadStatus object to the list of completed downloads and
|
||||
* saves it in the database
|
||||
|
@ -525,21 +525,12 @@ public class DownloadService extends Service {
|
|||
DBWriter.addDownloadStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is
|
||||
* used from a thread other than the main thread.
|
||||
*/
|
||||
private void stopServiceIfEverythingDoneAsync() {
|
||||
handler.post(DownloadService.this::stopServiceIfEverythingDone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's something else to download, otherwise stop.
|
||||
*/
|
||||
private void stopServiceIfEverythingDone() {
|
||||
Log.d(TAG, numberOfDownloads.get() + " downloads left");
|
||||
|
||||
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
|
||||
Log.d(TAG, downloads.size() + " downloads left");
|
||||
if (downloads.size() <= 0) {
|
||||
Log.d(TAG, "Attempting shutdown");
|
||||
shutdown();
|
||||
}
|
||||
|
@ -598,7 +589,8 @@ public class DownloadService extends Service {
|
|||
if (notificationUpdater == null) {
|
||||
Log.d(TAG, "Setting up notification updater");
|
||||
notificationUpdater = new NotificationUpdater();
|
||||
notificationUpdaterFuture = schedExecutor.scheduleAtFixedRate(notificationUpdater, 1, 1, TimeUnit.SECONDS);
|
||||
notificationUpdaterFuture = notificationUpdateExecutor
|
||||
.scheduleAtFixedRate(notificationUpdater, 1, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,11 +606,10 @@ public class DownloadService extends Service {
|
|||
|
||||
private class NotificationUpdater implements Runnable {
|
||||
public void run() {
|
||||
Notification n = notificationManager.updateNotifications(requester.getNumberOfDownloads(), downloads);
|
||||
Notification n = notificationManager.updateNotifications(downloads);
|
||||
if (n != null) {
|
||||
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.notify(R.id.notification_downloading, n);
|
||||
Log.d(TAG, "Download progress notification was posted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -627,7 +618,7 @@ public class DownloadService extends Service {
|
|||
new PostDownloaderTask(downloads).run();
|
||||
|
||||
if (downloadPostFuture == null) {
|
||||
downloadPostFuture = schedExecutor.scheduleAtFixedRate(
|
||||
downloadPostFuture = notificationUpdateExecutor.scheduleAtFixedRate(
|
||||
new PostDownloaderTask(downloads), 1, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
@ -639,10 +630,9 @@ public class DownloadService extends Service {
|
|||
if (notificationUpdater != null) {
|
||||
notificationUpdater.run();
|
||||
}
|
||||
handler.post(() -> {
|
||||
cancelNotificationUpdater();
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE);
|
||||
stopSelf();
|
||||
});
|
||||
downloadEnqueueExecutor.shutdown(); // Do not accept new downloads
|
||||
cancelNotificationUpdater();
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,14 +45,14 @@ public class DownloadServiceNotification {
|
|||
* Updates the contents of the service's notifications. Should be called
|
||||
* after setupNotificationBuilders.
|
||||
*/
|
||||
public Notification updateNotifications(int numDownloads, List<Downloader> downloads) {
|
||||
public Notification updateNotifications(List<Downloader> downloads) {
|
||||
if (notificationCompatBuilder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String contentTitle = context.getString(R.string.download_notification_title);
|
||||
String downloadsLeft = (numDownloads > 0)
|
||||
? context.getResources().getQuantityString(R.plurals.downloads_left, numDownloads, numDownloads)
|
||||
String downloadsLeft = (downloads.size() > 0)
|
||||
? context.getResources().getQuantityString(R.plurals.downloads_left, downloads.size(), downloads.size())
|
||||
: context.getString(R.string.completing);
|
||||
String bigText = compileNotificationString(downloads);
|
||||
|
||||
|
|
|
@ -49,10 +49,6 @@ public abstract class Downloader implements Callable<Downloader> {
|
|||
wifiLock.release();
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"Downloader hasn't created DownloadStatus object");
|
||||
}
|
||||
finished = true;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import de.danoeh.antennapod.model.feed.FeedPreferences;
|
|||
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandler;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
|
||||
import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException;
|
||||
|
@ -38,7 +37,7 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
|
|||
feed.setDownloaded(true);
|
||||
feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
|
||||
VolumeAdaptionSetting.OFF, request.getUsername(), request.getPassword()));
|
||||
feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
|
||||
feed.setPageNr(request.getArguments().getInt(DownloadRequest.REQUEST_ARG_PAGE_NR, 0));
|
||||
|
||||
DownloadError reason = null;
|
||||
String reasonDetailed = null;
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package de.danoeh.antennapod.core.service.download.handler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
|
||||
|
||||
public class FeedSyncTask {
|
||||
|
@ -34,15 +31,11 @@ public class FeedSyncTask {
|
|||
|
||||
savedFeed = DBTasks.updateFeed(context, result.feed, false);
|
||||
// If loadAllPages=true, check if another page is available and queue it for download
|
||||
final boolean loadAllPages = request.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
|
||||
final boolean loadAllPages = request.getArguments().getBoolean(DownloadRequest.REQUEST_ARG_LOAD_ALL_PAGES);
|
||||
final Feed feed = result.feed;
|
||||
if (loadAllPages && feed.getNextPageLink() != null) {
|
||||
try {
|
||||
feed.setId(savedFeed.getId());
|
||||
DBTasks.loadNextPageOfFeed(context, feed, true);
|
||||
} catch (DownloadRequestException e) {
|
||||
Log.e(TAG, "Error trying to load next page", e);
|
||||
}
|
||||
feed.setId(savedFeed.getId());
|
||||
DBTasks.loadNextPageOfFeed(context, feed, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.service.download.handler;
|
|||
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -24,7 +23,6 @@ public class PostDownloaderTask implements Runnable {
|
|||
runningDownloads.add(downloader);
|
||||
}
|
||||
}
|
||||
DownloadRequester.getInstance().updateProgress(downloads);
|
||||
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
|
||||
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
|
@ -86,17 +89,17 @@ public class AutomaticDownloadAlgorithm {
|
|||
episodeSpaceLeft = episodeCacheSize - (downloadedEpisodes - deletedEpisodes);
|
||||
}
|
||||
|
||||
FeedItem[] itemsToDownload = candidates.subList(0, episodeSpaceLeft)
|
||||
.toArray(new FeedItem[episodeSpaceLeft]);
|
||||
List<FeedItem> itemsToDownload = candidates.subList(0, episodeSpaceLeft);
|
||||
if (itemsToDownload.size() > 0) {
|
||||
Log.d(TAG, "Enqueueing " + itemsToDownload.size() + " items for download");
|
||||
|
||||
if (itemsToDownload.length > 0) {
|
||||
Log.d(TAG, "Enqueueing " + itemsToDownload.length + " items for download");
|
||||
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadMedia(false, context, false, itemsToDownload);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
List<DownloadRequest> requests = new ArrayList<>();
|
||||
for (FeedItem episode : itemsToDownload) {
|
||||
DownloadRequest.Builder request = DownloadRequestCreator.create(episode.getMedia());
|
||||
request.setInitiatedByUser(false);
|
||||
requests.add(request.build());
|
||||
}
|
||||
DownloadService.download(context, false, requests.toArray(new DownloadRequest[0]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,12 +5,14 @@ import static android.content.Context.MODE_PRIVATE;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -18,14 +20,12 @@ import java.util.Collections;
|
|||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
|
@ -104,8 +104,6 @@ public final class DBTasks {
|
|||
}
|
||||
}
|
||||
|
||||
private static final AtomicBoolean isRefreshing = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Refreshes all feeds.
|
||||
* It must not be from the main thread.
|
||||
|
@ -116,28 +114,15 @@ public final class DBTasks {
|
|||
* @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
|
||||
*/
|
||||
public static void refreshAllFeeds(final Context context, boolean initiatedByUser) {
|
||||
if (!isRefreshing.compareAndSet(false, true)) {
|
||||
Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
|
||||
}
|
||||
|
||||
List<Feed> feeds = DBReader.getFeedList();
|
||||
ListIterator<Feed> iterator = feeds.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (!iterator.next().getPreferences().getKeepUpdated()) {
|
||||
iterator.remove();
|
||||
new Thread(() -> {
|
||||
List<Feed> feeds = DBReader.getFeedList();
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.isLocalFeed() && feed.getPreferences().getKeepUpdated()) {
|
||||
LocalFeedUpdater.updateFeed(feed, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
refreshFeeds(context, feeds, false, false, false);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
isRefreshing.set(false);
|
||||
}).start();
|
||||
DownloadService.refreshAllFeeds(context, initiatedByUser);
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
|
||||
|
@ -149,27 +134,7 @@ public final class DBTasks {
|
|||
// See Issue #2577 for the details of the rationale
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads all pages of the given feed even if feed has not been modified since last refresh
|
||||
*
|
||||
* @param context Used for requesting the download.
|
||||
* @param feed The Feed object.
|
||||
*/
|
||||
public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
|
||||
try {
|
||||
refreshFeeds(context, Collections.singletonList(feed), true, true, false);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DBWriter.addDownloadStatus(
|
||||
new DownloadStatus(feed,
|
||||
feed.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR,
|
||||
false,
|
||||
e.getMessage(),
|
||||
false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queues the next page of this Feed for download. The given Feed has to be a paged
|
||||
|
@ -179,53 +144,39 @@ public final class DBTasks {
|
|||
* @param feed The feed whose next page should be loaded.
|
||||
* @param loadAllPages True if any subsequent pages should also be loaded, false otherwise.
|
||||
*/
|
||||
public static void loadNextPageOfFeed(final Context context, Feed feed, boolean loadAllPages) throws DownloadRequestException {
|
||||
public static void loadNextPageOfFeed(final Context context, Feed feed, boolean loadAllPages) {
|
||||
if (feed.isPaged() && feed.getNextPageLink() != null) {
|
||||
int pageNr = feed.getPageNr() + 1;
|
||||
Feed nextFeed = new Feed(feed.getNextPageLink(), null, feed.getTitle() + "(" + pageNr + ")");
|
||||
nextFeed.setPageNr(pageNr);
|
||||
nextFeed.setPaged(true);
|
||||
nextFeed.setId(feed.getId());
|
||||
DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false, true);
|
||||
|
||||
DownloadRequest.Builder builder = DownloadRequestCreator.create(nextFeed);
|
||||
builder.loadAllPages(loadAllPages);
|
||||
DownloadService.download(context, false, builder.build());
|
||||
} else {
|
||||
Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a specific feed even if feed has not been modified since last refresh
|
||||
*
|
||||
* @param context Used for requesting the download.
|
||||
* @param feed The Feed object.
|
||||
*/
|
||||
public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() + ")");
|
||||
refreshFeeds(context, Collections.singletonList(feed), false, true, initiatedByUser);
|
||||
public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser) {
|
||||
forceRefreshFeed(context, feed, false, initiatedByUser);
|
||||
}
|
||||
|
||||
private static void refreshFeeds(Context context, List<Feed> feeds, boolean loadAllPages,
|
||||
boolean force, boolean initiatedByUser) throws DownloadRequestException {
|
||||
List<Feed> localFeeds = new ArrayList<>();
|
||||
List<Feed> normalFeeds = new ArrayList<>();
|
||||
public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
|
||||
forceRefreshFeed(context, feed, true, true);
|
||||
}
|
||||
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.isLocalFeed()) {
|
||||
localFeeds.add(feed);
|
||||
} else {
|
||||
normalFeeds.add(feed);
|
||||
}
|
||||
}
|
||||
|
||||
if (!localFeeds.isEmpty()) {
|
||||
new Thread(() -> {
|
||||
for (Feed feed : localFeeds) {
|
||||
LocalFeedUpdater.updateFeed(feed, context);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
if (!normalFeeds.isEmpty()) {
|
||||
DownloadRequester.getInstance().downloadFeeds(context, normalFeeds, loadAllPages, force, initiatedByUser);
|
||||
private static void forceRefreshFeed(Context context, Feed feed, boolean loadAllPages, boolean initiatedByUser) {
|
||||
if (feed.isLocalFeed()) {
|
||||
new Thread(() -> LocalFeedUpdater.updateFeed(feed, context)).start();
|
||||
} else {
|
||||
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
|
||||
builder.setInitiatedByUser(initiatedByUser);
|
||||
builder.setForce(true);
|
||||
builder.loadAllPages(loadAllPages);
|
||||
DownloadService.download(context, false, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,8 +193,8 @@ public final class DBTasks {
|
|||
EventBus.getDefault().post(new MessageEvent(context.getString(R.string.error_file_not_found)));
|
||||
}
|
||||
|
||||
public static List<? extends FeedItem> enqueueFeedItemsToDownload(final Context context,
|
||||
List<? extends FeedItem> items) throws InterruptedException, ExecutionException {
|
||||
public static List<FeedItem> enqueueFeedItemsToDownload(final Context context,
|
||||
List<FeedItem> items) throws InterruptedException, ExecutionException {
|
||||
List<FeedItem> itemsToEnqueue = new ArrayList<>();
|
||||
if (UserPreferences.enqueueDownloadedEpisodes()) {
|
||||
LongList queueIDList = DBReader.getQueueIDList();
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -191,7 +192,6 @@ public class DBWriter {
|
|||
* Deleting media also removes the download log entries.
|
||||
*/
|
||||
private static void deleteFeedItemsSynchronous(@NonNull Context context, @NonNull List<FeedItem> items) {
|
||||
DownloadRequester requester = DownloadRequester.getInstance();
|
||||
List<FeedItem> queue = DBReader.getQueue();
|
||||
List<FeedItem> removedFromQueue = new ArrayList<>();
|
||||
for (FeedItem item : items) {
|
||||
|
@ -206,9 +206,8 @@ public class DBWriter {
|
|||
}
|
||||
if (item.getMedia().isDownloaded()) {
|
||||
deleteFeedMediaSynchronous(context, item.getMedia());
|
||||
} else if (requester.isDownloadingFile(item.getMedia())) {
|
||||
requester.cancelDownload(context, item.getMedia());
|
||||
}
|
||||
DownloadService.cancel(context, item.getMedia().getDownload_url());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package de.danoeh.antennapod.core.storage;
|
||||
|
||||
/**
|
||||
* Thrown by the DownloadRequester if a download request contains invalid data
|
||||
* or something went wrong while processing the request.
|
||||
*/
|
||||
public class DownloadRequestException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DownloadRequestException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DownloadRequestException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DownloadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DownloadRequestException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -1,474 +0,0 @@
|
|||
package de.danoeh.antennapod.core.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedFile;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.FileNameGenerator;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.URLChecker;
|
||||
|
||||
|
||||
/**
|
||||
* Sends download requests to the DownloadService. This class should always be used for starting downloads,
|
||||
* otherwise they won't work correctly.
|
||||
*/
|
||||
public class DownloadRequester implements DownloadStateProvider {
|
||||
private static final String TAG = "DownloadRequester";
|
||||
|
||||
private static final String FEED_DOWNLOADPATH = "cache/";
|
||||
private static final String MEDIA_DOWNLOADPATH = "media/";
|
||||
|
||||
/**
|
||||
* Denotes the page of the feed that is contained in the DownloadRequest sent by the DownloadRequester.
|
||||
*/
|
||||
public static final String REQUEST_ARG_PAGE_NR = "page";
|
||||
|
||||
/**
|
||||
* True if all pages after the feed that is contained in this DownloadRequest should be downloaded.
|
||||
*/
|
||||
public static final String REQUEST_ARG_LOAD_ALL_PAGES = "loadAllPages";
|
||||
|
||||
private static DownloadRequester downloader;
|
||||
|
||||
private final Map<String, DownloadRequest> downloads;
|
||||
|
||||
private DownloadRequester() {
|
||||
downloads = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static synchronized DownloadRequester getInstance() {
|
||||
if (downloader == null) {
|
||||
downloader = new DownloadRequester();
|
||||
}
|
||||
return downloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new download with the given a list of DownloadRequest. This method should only
|
||||
* be used from outside classes if the DownloadRequest was created by the DownloadService to
|
||||
* ensure that the data is valid. Use downloadFeed(), downloadImage() or downloadMedia() instead.
|
||||
*
|
||||
* @param context Context object for starting the DownloadService
|
||||
* @param requests The list of DownloadRequest objects. If another DownloadRequest
|
||||
* with the same source URL is already stored, this one will be skipped.
|
||||
* @return True if any of the download request was accepted, false otherwise.
|
||||
*/
|
||||
public synchronized boolean download(@NonNull Context context, DownloadRequest... requests) {
|
||||
return download(context, false, requests);
|
||||
}
|
||||
|
||||
private boolean download(@NonNull Context context, boolean cleanupMedia, DownloadRequest... requests) {
|
||||
if (requests.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
boolean result = false;
|
||||
|
||||
ArrayList<DownloadRequest> requestsToSend = new ArrayList<>(requests.length);
|
||||
for (DownloadRequest request : requests) {
|
||||
if (downloads.containsKey(request.getSource())) {
|
||||
if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
|
||||
continue;
|
||||
}
|
||||
downloads.put(request.getSource(), request);
|
||||
|
||||
requestsToSend.add(request);
|
||||
result = true;
|
||||
}
|
||||
Intent launchIntent = new Intent(context, DownloadService.class);
|
||||
launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend);
|
||||
if (cleanupMedia) {
|
||||
launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, cleanupMedia);
|
||||
}
|
||||
ContextCompat.startForegroundService(context, launchIntent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest, boolean overwriteIfExists,
|
||||
String username, String password, String lastModified,
|
||||
boolean deleteOnFailure, Bundle arguments, boolean initiatedByUser) {
|
||||
final boolean partiallyDownloadedFileExists = item.getFile_url() != null && new File(item.getFile_url()).exists();
|
||||
|
||||
Log.d(TAG, "partiallyDownloadedFileExists: " + partiallyDownloadedFileExists);
|
||||
if (isDownloadingFile(item)) {
|
||||
Log.e(TAG, "URL " + item.getDownload_url()
|
||||
+ " is already being downloaded");
|
||||
return null;
|
||||
}
|
||||
if (!isFilenameAvailable(dest.toString()) || (!partiallyDownloadedFileExists && dest.exists())) {
|
||||
Log.d(TAG, "Filename already used.");
|
||||
if (isFilenameAvailable(dest.toString()) && overwriteIfExists) {
|
||||
boolean result = dest.delete();
|
||||
Log.d(TAG, "Deleting file. Result: " + result);
|
||||
} else {
|
||||
// find different name
|
||||
File newDest = null;
|
||||
for (int i = 1; i < Integer.MAX_VALUE; i++) {
|
||||
String newName = FilenameUtils.getBaseName(dest
|
||||
.getName())
|
||||
+ "-"
|
||||
+ i
|
||||
+ FilenameUtils.EXTENSION_SEPARATOR
|
||||
+ FilenameUtils.getExtension(dest.getName());
|
||||
Log.d(TAG, "Testing filename " + newName);
|
||||
newDest = new File(dest.getParent(), newName);
|
||||
if (!newDest.exists()
|
||||
&& isFilenameAvailable(newDest.toString())) {
|
||||
Log.d(TAG, "File doesn't exist yet. Using " + newName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newDest != null) {
|
||||
dest = newDest;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Requesting download of url " + item.getDownload_url());
|
||||
String baseUrl = (container != null) ? container.getDownload_url() : null;
|
||||
item.setDownload_url(URLChecker.prepareURL(item.getDownload_url(), baseUrl));
|
||||
|
||||
DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item, initiatedByUser)
|
||||
.withAuthentication(username, password)
|
||||
.lastModified(lastModified)
|
||||
.deleteOnFailure(deleteOnFailure)
|
||||
.withArguments(arguments);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a filename is available and false if it has already been
|
||||
* taken by another requested download.
|
||||
*/
|
||||
private boolean isFilenameAvailable(String path) {
|
||||
for (String key : downloads.keySet()) {
|
||||
DownloadRequest r = downloads.get(key);
|
||||
if (TextUtils.equals(r.getDestination(), path)) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, path
|
||||
+ " is already used by another requested download");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, path + " is available as a download destination");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a feed.
|
||||
*
|
||||
* @param context The application's environment.
|
||||
* @param feed Feeds to download
|
||||
* @param loadAllPages Set to true to download all pages
|
||||
*/
|
||||
public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages,
|
||||
boolean force, boolean initiatedByUser) throws DownloadRequestException {
|
||||
downloadFeeds(context, Collections.singletonList(feed), loadAllPages, force, initiatedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a list of feeds.
|
||||
*
|
||||
* @param context The application's environment.
|
||||
* @param feeds Feeds to download
|
||||
* @param loadAllPages Set to true to download all pages
|
||||
*/
|
||||
public synchronized void downloadFeeds(Context context, List<Feed> feeds, boolean loadAllPages,
|
||||
boolean force, boolean initiatedByUser) throws DownloadRequestException {
|
||||
List<DownloadRequest> requests = new ArrayList<>();
|
||||
for (Feed feed : feeds) {
|
||||
if (!feedFileValid(feed)) {
|
||||
continue;
|
||||
}
|
||||
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
|
||||
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
|
||||
String lastModified = feed.isPaged() || force ? null : feed.getLastUpdate();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
|
||||
args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
|
||||
|
||||
DownloadRequest request = createRequest(feed, null, getDownloadPathForFeed(feed),
|
||||
true, username, password, lastModified, true, args, initiatedByUser
|
||||
);
|
||||
if (request != null) {
|
||||
requests.add(request);
|
||||
}
|
||||
}
|
||||
if (!requests.isEmpty()) {
|
||||
download(context, requests.toArray(new DownloadRequest[0]));
|
||||
}
|
||||
}
|
||||
|
||||
public File getDownloadPathForFeed(Feed feed) throws DownloadRequestException {
|
||||
return new File(getFeedfilePath(), getFeedfileName(feed));
|
||||
}
|
||||
|
||||
public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {
|
||||
downloadFeed(context, feed, false, false, true);
|
||||
}
|
||||
|
||||
public synchronized void downloadMedia(@NonNull Context context, boolean initiatedByUser, FeedItem... feedItems)
|
||||
throws DownloadRequestException {
|
||||
downloadMedia(true, context, initiatedByUser, feedItems);
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public synchronized void downloadMedia(boolean performAutoCleanup, @NonNull Context context,
|
||||
boolean initiatedByUser, FeedItem... items)
|
||||
throws DownloadRequestException {
|
||||
Log.d(TAG, "downloadMedia() called with: performAutoCleanup = [" + performAutoCleanup
|
||||
+ "], #items = [" + items.length + "]");
|
||||
|
||||
List<DownloadRequest> requests = new ArrayList<>(items.length);
|
||||
for (FeedItem item : items) {
|
||||
try {
|
||||
DownloadRequest request = createRequest(item.getMedia(), initiatedByUser);
|
||||
if (request != null) {
|
||||
requests.add(request);
|
||||
}
|
||||
} catch (DownloadRequestException e) {
|
||||
if (items.length < 2) {
|
||||
// single download, typically initiated from users
|
||||
throw e;
|
||||
} else {
|
||||
// batch download, typically initiated by auto-download in the background
|
||||
e.printStackTrace();
|
||||
DBWriter.addDownloadStatus(
|
||||
new DownloadStatus(item.getMedia(), item
|
||||
.getMedia()
|
||||
.getHumanReadableIdentifier(),
|
||||
DownloadError.ERROR_REQUEST_ERROR,
|
||||
false, e.getMessage(), initiatedByUser
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
download(context, performAutoCleanup, requests.toArray(new DownloadRequest[0]));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DownloadRequest createRequest(@Nullable FeedMedia feedmedia, boolean initiatedByUser)
|
||||
throws DownloadRequestException {
|
||||
if (!feedFileValid(feedmedia)) {
|
||||
return null;
|
||||
}
|
||||
Feed feed = feedmedia.getItem().getFeed();
|
||||
String username;
|
||||
String password;
|
||||
if (feed != null && feed.getPreferences() != null) {
|
||||
username = feed.getPreferences().getUsername();
|
||||
password = feed.getPreferences().getPassword();
|
||||
} else {
|
||||
username = null;
|
||||
password = null;
|
||||
}
|
||||
|
||||
File dest;
|
||||
if (feedmedia.getFile_url() != null && new File(feedmedia.getFile_url()).exists()) {
|
||||
dest = new File(feedmedia.getFile_url());
|
||||
} else {
|
||||
dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia));
|
||||
}
|
||||
return createRequest(feedmedia, feed, dest, false, username, password, null, false, null, initiatedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a DownloadRequestException if the feedfile or the download url of
|
||||
* the feedfile is null.
|
||||
*
|
||||
* @throws DownloadRequestException
|
||||
*/
|
||||
private boolean feedFileValid(FeedFile f) throws DownloadRequestException {
|
||||
if (f == null) {
|
||||
throw new DownloadRequestException("Feedfile was null");
|
||||
} else if (f.getDownload_url() == null) {
|
||||
throw new DownloadRequestException("File has no download URL");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a running download.
|
||||
*/
|
||||
public synchronized void cancelDownload(final Context context, final FeedFile f) {
|
||||
cancelDownload(context, f.getDownload_url());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a running download.
|
||||
*/
|
||||
public synchronized void cancelDownload(final Context context, final String downloadUrl) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Cancelling download with url " + downloadUrl);
|
||||
Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD);
|
||||
cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, downloadUrl);
|
||||
cancelIntent.setPackage(context.getPackageName());
|
||||
context.sendBroadcast(cancelIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all running downloads
|
||||
*/
|
||||
public synchronized void cancelAllDownloads(Context context) {
|
||||
Log.d(TAG, "Cancelling all running downloads");
|
||||
IntentUtils.sendLocalBroadcast(context, DownloadService.ACTION_CANCEL_ALL_DOWNLOADS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one Feed in the downloads queue.
|
||||
*/
|
||||
public synchronized boolean isDownloadingFeeds() {
|
||||
for (DownloadRequest r : downloads.values()) {
|
||||
if (r.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if feedfile is in the downloads list
|
||||
*/
|
||||
public synchronized boolean isDownloadingFile(@NonNull FeedFile item) {
|
||||
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the downloader for this item.
|
||||
*/
|
||||
public synchronized DownloadRequest getRequestFor(FeedFile item) {
|
||||
if (isDownloadingFile(item)) {
|
||||
return downloads.get(item.getDownload_url());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if feedfile with the given download url is in the downloads list
|
||||
*/
|
||||
public synchronized boolean isDownloadingFile(String downloadUrl) {
|
||||
return downloads.get(downloadUrl) != null;
|
||||
}
|
||||
|
||||
public synchronized boolean hasNoDownloads() {
|
||||
return downloads.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from the downloads-list of the requester.
|
||||
*/
|
||||
public synchronized void removeDownload(DownloadRequest r) {
|
||||
if (downloads.remove(r.getSource()) == null) {
|
||||
Log.e(TAG,
|
||||
"Could not remove object with url " + r.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of uncompleted Downloads
|
||||
*/
|
||||
public synchronized int getNumberOfDownloads() {
|
||||
return downloads.size();
|
||||
}
|
||||
|
||||
private synchronized String getFeedfilePath() throws DownloadRequestException {
|
||||
return getExternalFilesDirOrThrowException(FEED_DOWNLOADPATH).toString() + "/";
|
||||
}
|
||||
|
||||
private synchronized String getFeedfileName(Feed feed) {
|
||||
String filename = feed.getDownload_url();
|
||||
if (feed.getTitle() != null && !feed.getTitle().isEmpty()) {
|
||||
filename = feed.getTitle();
|
||||
}
|
||||
return "feed-" + FileNameGenerator.generateFileName(filename);
|
||||
}
|
||||
|
||||
private synchronized String getMediafilePath(FeedMedia media) throws DownloadRequestException {
|
||||
File externalStorage = getExternalFilesDirOrThrowException(
|
||||
MEDIA_DOWNLOADPATH
|
||||
+ FileNameGenerator.generateFileName(media.getItem()
|
||||
.getFeed().getTitle()) + "/"
|
||||
);
|
||||
return externalStorage.toString();
|
||||
}
|
||||
|
||||
private File getExternalFilesDirOrThrowException(String type) throws DownloadRequestException {
|
||||
File result = UserPreferences.getDataFolder(type);
|
||||
if (result == null) {
|
||||
throw new DownloadRequestException(
|
||||
"Failed to access external storage");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getMediafilename(FeedMedia media) {
|
||||
String filename;
|
||||
String titleBaseFilename = "";
|
||||
|
||||
// Try to generate the filename by the item title
|
||||
if (media.getItem() != null && media.getItem().getTitle() != null) {
|
||||
String title = media.getItem().getTitle();
|
||||
titleBaseFilename = FileNameGenerator.generateFileName(title);
|
||||
}
|
||||
|
||||
String URLBaseFilename = URLUtil.guessFileName(media.getDownload_url(),
|
||||
null, media.getMime_type());
|
||||
|
||||
if (!titleBaseFilename.equals("")) {
|
||||
// Append extension
|
||||
final int FILENAME_MAX_LENGTH = 220;
|
||||
if (titleBaseFilename.length() > FILENAME_MAX_LENGTH) {
|
||||
titleBaseFilename = titleBaseFilename.substring(0, FILENAME_MAX_LENGTH);
|
||||
}
|
||||
filename = titleBaseFilename + FilenameUtils.EXTENSION_SEPARATOR +
|
||||
FilenameUtils.getExtension(URLBaseFilename);
|
||||
} else {
|
||||
// Fall back on URL file name
|
||||
filename = URLBaseFilename;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
public void updateProgress(List<Downloader> newDownloads) {
|
||||
for (Downloader downloader : newDownloads) {
|
||||
DownloadRequest request = downloader.getDownloadRequest();
|
||||
if (downloads.containsKey(request.getSource())) {
|
||||
downloads.put(request.getSource(), request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package de.danoeh.antennapod.core.storage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.FeedFile;
|
||||
|
||||
/**
|
||||
* Allow callers to query the states of downloads, but not affect them.
|
||||
*/
|
||||
public interface DownloadStateProvider {
|
||||
/**
|
||||
* @return {@code true} if the named feedfile is in the downloads list
|
||||
*/
|
||||
boolean isDownloadingFile(@NonNull FeedFile item);
|
||||
|
||||
}
|
|
@ -4,10 +4,10 @@ import android.content.Context;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
|
||||
|
@ -22,9 +22,6 @@ class ItemEnqueuePositionCalculator {
|
|||
@NonNull
|
||||
private final EnqueueLocation enqueueLocation;
|
||||
|
||||
@VisibleForTesting
|
||||
DownloadStateProvider downloadStateProvider = DownloadRequester.getInstance();
|
||||
|
||||
public ItemEnqueuePositionCalculator(@NonNull EnqueueLocation enqueueLocation) {
|
||||
this.enqueueLocation = enqueueLocation;
|
||||
}
|
||||
|
@ -71,14 +68,9 @@ class ItemEnqueuePositionCalculator {
|
|||
} catch (IndexOutOfBoundsException e) {
|
||||
curItem = null;
|
||||
}
|
||||
|
||||
if (curItem != null
|
||||
return curItem != null
|
||||
&& curItem.getMedia() != null
|
||||
&& downloadStateProvider.isDownloadingFile(curItem.getMedia())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
&& DownloadService.isDownloadingFile(curItem.getMedia().getDownload_url());
|
||||
}
|
||||
|
||||
private static int getCurrentlyPlayingPosition(@NonNull List<FeedItem> curQueue,
|
||||
|
|
|
@ -20,6 +20,9 @@ import androidx.work.WorkManager;
|
|||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
|
@ -35,8 +38,6 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
|||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueStorage;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
|
@ -146,11 +147,8 @@ public class SyncService extends Worker {
|
|||
for (String downloadUrl : subscriptionChanges.getAdded()) {
|
||||
if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) {
|
||||
Feed feed = new Feed(downloadUrl, null);
|
||||
try {
|
||||
DownloadRequester.getInstance().downloadFeed(getApplicationContext(), feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
|
||||
DownloadService.download(getApplicationContext(), false, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +186,7 @@ public class SyncService extends Worker {
|
|||
private void waitForDownloadServiceCompleted() {
|
||||
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_wait_for_downloads));
|
||||
try {
|
||||
while (DownloadRequester.getInstance().isDownloadingFeeds()) {
|
||||
while (DownloadService.isRunning) {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ import java.util.List;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
|
@ -238,7 +238,7 @@ public class NetworkUtils {
|
|||
// otherwise cancel all downloads
|
||||
if (NetworkUtils.isNetworkRestricted()) {
|
||||
Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
|
||||
DownloadRequester.getInstance().cancelAllDownloads(context);
|
||||
DownloadService.cancelAll(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -280,7 +280,6 @@
|
|||
<string name="download_log_title_unknown">Unknown Title</string>
|
||||
<string name="download_type_feed">Feed</string>
|
||||
<string name="download_type_media">Media file</string>
|
||||
<string name="download_request_error_dialog_message_prefix">An error occurred when trying to download the file:\u0020</string>
|
||||
<string name="null_value_podcast_error">No podcast was provided that could be shown.</string>
|
||||
<string name="no_feed_url_podcast_found_by_search">The suggested podcast did not have an RSS link, AntennaPod found a podcast that could match</string>
|
||||
<string name="authentication_notification_title">Authentication required</string>
|
||||
|
|
|
@ -41,24 +41,19 @@ public class DownloadRequestTest {
|
|||
String username = "testUser";
|
||||
String password = "testPassword";
|
||||
FeedFile item = createFeedItem(1);
|
||||
Bundle arg = new Bundle();
|
||||
arg.putString("arg1", "value1");
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item, true)
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item)
|
||||
.deleteOnFailure(true)
|
||||
.withAuthentication(username, password)
|
||||
.withArguments(arg)
|
||||
.build();
|
||||
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item, true)
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item)
|
||||
.deleteOnFailure(true)
|
||||
.withAuthentication(username, password)
|
||||
.withArguments(arg)
|
||||
.build();
|
||||
|
||||
DownloadRequest request3 = new DownloadRequest.Builder(destStr, item, true)
|
||||
DownloadRequest request3 = new DownloadRequest.Builder(destStr, item)
|
||||
.deleteOnFailure(true)
|
||||
.withAuthentication("diffUsername", "diffPassword")
|
||||
.withArguments(arg)
|
||||
.build();
|
||||
|
||||
assertEquals(request1, request2);
|
||||
|
@ -74,15 +69,12 @@ public class DownloadRequestTest {
|
|||
{ // test DownloadRequests to parcel
|
||||
String destStr = "file://location/media.mp3";
|
||||
FeedFile item1 = createFeedItem(1);
|
||||
Bundle arg1 = new Bundle();
|
||||
arg1.putString("arg1", "value1");
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1, false)
|
||||
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1)
|
||||
.withAuthentication(username1, password1)
|
||||
.withArguments(arg1)
|
||||
.build();
|
||||
|
||||
FeedFile item2 = createFeedItem(2);
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2, true)
|
||||
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2)
|
||||
.withAuthentication(username2, password2)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package de.danoeh.antennapod.core.storage;
|
||||
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.model.playback.RemoteMedia;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -20,6 +21,8 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
|
|||
import de.danoeh.antennapod.core.feed.FeedMother;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.AFTER_CURRENTLY_PLAYING;
|
||||
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.BACK;
|
||||
|
@ -29,9 +32,6 @@ import static de.danoeh.antennapod.core.util.CollectionTestUtil.list;
|
|||
import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ItemEnqueuePositionCalculatorTest {
|
||||
|
||||
|
@ -84,7 +84,9 @@ public class ItemEnqueuePositionCalculatorTest {
|
|||
idsExpected);
|
||||
}
|
||||
|
||||
Playable getCurrentlyPlaying() { return null; }
|
||||
Playable getCurrentlyPlaying() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
|
@ -127,12 +129,11 @@ public class ItemEnqueuePositionCalculatorTest {
|
|||
}
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public static class ItemEnqueuePositionCalculatorPreserveDownloadOrderTest {
|
||||
|
||||
public static class PreserveDownloadOrderTest {
|
||||
/**
|
||||
* The test covers the use case that when user initiates multiple downloads in succession,
|
||||
* resulting in multiple addQueueItem() calls in succession.
|
||||
* the items in the queue will be in the same order as the the order user taps to download
|
||||
* the items in the queue will be in the same order as the order user taps to download
|
||||
*/
|
||||
@Parameters(name = "{index}: case<{0}>")
|
||||
public static Iterable<Object[]> data() {
|
||||
|
@ -183,61 +184,39 @@ public class ItemEnqueuePositionCalculatorTest {
|
|||
|
||||
@Test
|
||||
public void testQueueOrderWhenDownloading2Items() {
|
||||
|
||||
// Setup class under test
|
||||
//
|
||||
ItemEnqueuePositionCalculator calculator = new ItemEnqueuePositionCalculator(options);
|
||||
DownloadStateProvider stubDownloadStateProvider = mock(DownloadStateProvider.class);
|
||||
when(stubDownloadStateProvider.isDownloadingFile(any(FeedMedia.class))).thenReturn(false);
|
||||
calculator.downloadStateProvider = stubDownloadStateProvider;
|
||||
|
||||
// Setup initial data
|
||||
// A shallow copy, as the test code will manipulate the queue
|
||||
List<FeedItem> queue = new ArrayList<>(queueInitial);
|
||||
|
||||
// Test body
|
||||
Playable currentlyPlaying = getCurrentlyPlaying(idCurrentlyPlaying);
|
||||
// User clicks download on feed item 101
|
||||
FeedItem tFI101 = setAsDownloading(101, stubDownloadStateProvider, true);
|
||||
doAddToQueueAndAssertResult(message + " (1st download)",
|
||||
calculator, tFI101, queue, currentlyPlaying,
|
||||
idsExpectedAfter101);
|
||||
// Then user clicks download on feed item 102
|
||||
FeedItem tFI102 = setAsDownloading(102, stubDownloadStateProvider, true);
|
||||
doAddToQueueAndAssertResult(message + " (2nd download, it should preserve order of download)",
|
||||
calculator, tFI102, queue, currentlyPlaying,
|
||||
idsExpectedAfter102);
|
||||
// simulate download failure case for 102
|
||||
setAsDownloading(tFI102, stubDownloadStateProvider, false);
|
||||
// Then user clicks download on feed item 103
|
||||
FeedItem tFI103 = setAsDownloading(103, stubDownloadStateProvider, true);
|
||||
doAddToQueueAndAssertResult(message
|
||||
+ " (3rd download, with 2nd download failed; "
|
||||
+ "it should be behind 1st download (unless enqueueLocation is BACK)",
|
||||
calculator, tFI103, queue, currentlyPlaying,
|
||||
idsExpectedAfter103);
|
||||
try (MockedStatic<DownloadService> downloadServiceMock = Mockito.mockStatic(DownloadService.class)) {
|
||||
List<FeedItem> queue = new ArrayList<>(queueInitial);
|
||||
|
||||
// Test body
|
||||
Playable currentlyPlaying = getCurrentlyPlaying(idCurrentlyPlaying);
|
||||
// User clicks download on feed item 101
|
||||
FeedItem feedItem101 = createFeedItem(101);
|
||||
downloadServiceMock.when(() ->
|
||||
DownloadService.isDownloadingFile(feedItem101.getMedia().getDownload_url())).thenReturn(true);
|
||||
doAddToQueueAndAssertResult(message + " (1st download)",
|
||||
calculator, feedItem101, queue, currentlyPlaying, idsExpectedAfter101);
|
||||
// Then user clicks download on feed item 102
|
||||
FeedItem feedItem102 = createFeedItem(102);
|
||||
downloadServiceMock.when(() ->
|
||||
DownloadService.isDownloadingFile(feedItem102.getMedia().getDownload_url())).thenReturn(true);
|
||||
doAddToQueueAndAssertResult(message + " (2nd download, it should preserve order of download)",
|
||||
calculator, feedItem102, queue, currentlyPlaying, idsExpectedAfter102);
|
||||
// simulate download failure case for 102
|
||||
downloadServiceMock.when(() ->
|
||||
DownloadService.isDownloadingFile(feedItem102.getMedia().getDownload_url())).thenReturn(false);
|
||||
// Then user clicks download on feed item 103
|
||||
FeedItem feedItem103 = createFeedItem(103);
|
||||
downloadServiceMock.when(() ->
|
||||
DownloadService.isDownloadingFile(feedItem103.getMedia().getDownload_url())).thenReturn(true);
|
||||
doAddToQueueAndAssertResult(message
|
||||
+ " (3rd download, with 2nd download failed; "
|
||||
+ "it should be behind 1st download (unless enqueueLocation is BACK)",
|
||||
calculator, feedItem103, queue, currentlyPlaying, idsExpectedAfter103);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static FeedItem setAsDownloading(int id, DownloadStateProvider stubDownloadStateProvider,
|
||||
boolean isDownloading) {
|
||||
FeedItem item = createFeedItem(id);
|
||||
FeedMedia media = new FeedMedia(item, "http://download.url.net/" + id, 100000 + id, "audio/mp3");
|
||||
media.setId(item.getId());
|
||||
item.setMedia(media);
|
||||
return setAsDownloading(item, stubDownloadStateProvider, isDownloading);
|
||||
}
|
||||
|
||||
private static FeedItem setAsDownloading(FeedItem item, DownloadStateProvider stubDownloadStateProvider,
|
||||
boolean isDownloading) {
|
||||
when(stubDownloadStateProvider.isDownloadingFile(item.getMedia())).thenReturn(isDownloading);
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void doAddToQueueAndAssertResult(String message,
|
||||
ItemEnqueuePositionCalculator calculator,
|
||||
FeedItem itemToAdd,
|
||||
|
@ -279,7 +258,7 @@ public class ItemEnqueuePositionCalculatorTest {
|
|||
static FeedItem createFeedItem(long id) {
|
||||
FeedItem item = new FeedItem(id, "Item" + id, "ItemId" + id, "url",
|
||||
new Date(), FeedItem.PLAYED, FeedMother.anyFeed());
|
||||
FeedMedia media = new FeedMedia(item, "download_url", 1234567, "audio/mpeg");
|
||||
FeedMedia media = new FeedMedia(item, "http://download.url.net/" + id, 1234567, "audio/mpeg");
|
||||
media.setId(item.getId());
|
||||
item.setMedia(media);
|
||||
return item;
|
||||
|
|
Loading…
Reference in New Issue