summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <ByteHamster@users.noreply.github.com>2023-05-05 23:09:03 +0200
committerGitHub <noreply@github.com>2023-05-05 23:09:03 +0200
commit6d7bfef8a5fe8180f13904739996bb2b8de8a0d4 (patch)
tree84f246b74fe7254678788e9f206d81d1a30ffa5e
parent4c286931cd2dbd9038022f808f9d8a73ccbb6759 (diff)
downloadAntennaPod-6d7bfef8a5fe8180f13904739996bb2b8de8a0d4.zip
Download Service Rewrite (#6420)
-rw-r--r--app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java16
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java217
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java38
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/event/DownloadEventListener.java45
-rw-r--r--app/src/main/AndroidManifest.xml11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java47
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java49
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/DownloadLogDetailsDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java42
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java108
-rw-r--r--app/src/main/java/de/danoeh/antennapod/ui/home/sections/EpisodesSurpriseSection.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/ui/home/sections/InboxSection.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/ui/home/sections/QueueSection.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/HorizontalItemViewHolder.java13
-rw-r--r--app/src/main/res/layout/numberpicker.xml16
-rw-r--r--app/src/main/res/xml/preferences_downloads.xml7
-rw-r--r--core/src/main/AndroidManifest.xml4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java58
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java536
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java102
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java306
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java265
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/LocalFeedStubDownloader.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadResultComparator.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java15
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java59
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java95
-rw-r--r--event/build.gradle1
-rw-r--r--event/src/main/java/de/danoeh/antennapod/event/EpisodeDownloadEvent.java18
-rw-r--r--event/src/main/java/de/danoeh/antennapod/event/MessageEvent.java14
-rw-r--r--model/src/main/java/de/danoeh/antennapod/model/download/DownloadResult.java128
-rw-r--r--model/src/main/java/de/danoeh/antennapod/model/download/DownloadStatus.java156
-rw-r--r--net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterface.java40
-rw-r--r--net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterfaceStub.java14
-rw-r--r--storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java17
-rw-r--r--storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadResultCursorMapper.java (renamed from storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadStatusCursorMapper.java)16
-rw-r--r--storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java5
-rw-r--r--ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/DownloadAuthenticationActivityStarter.java39
-rw-r--r--ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java9
-rw-r--r--ui/common/src/main/java/de/danoeh/antennapod/ui/common/CircularProgressBar.java11
-rw-r--r--ui/i18n/src/main/res/values/strings.xml18
-rw-r--r--ui/png-icons/src/main/res/drawable/ic_notification_cancel.xml5
-rw-r--r--ui/png-icons/src/main/res/drawable/ic_notification_key.xml6
78 files changed, 879 insertions, 2477 deletions
diff --git a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
index 302301c85..835af1f95 100644
--- a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
+++ b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.service.download;
import androidx.annotation.NonNull;
import androidx.core.util.Consumer;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
public class StubDownloader extends Downloader {
@@ -10,9 +10,10 @@ public class StubDownloader extends Downloader {
private final long downloadTime;
@NonNull
- private final Consumer<DownloadStatus> onDownloadComplete;
+ private final Consumer<DownloadResult> onDownloadComplete;
- public StubDownloader(@NonNull DownloadRequest request, long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
+ public StubDownloader(@NonNull DownloadRequest request, long downloadTime,
+ @NonNull Consumer<DownloadResult> onDownloadComplete) {
super(request);
this.downloadTime = downloadTime;
this.onDownloadComplete = onDownloadComplete;
@@ -36,7 +37,7 @@ public class StubDownloader extends Downloader {
@NonNull
@Override
- public DownloadStatus getResult() {
+ public DownloadResult getResult() {
return super.getResult();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index d65289638..a7575862b 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -24,7 +24,6 @@ import junit.framework.AssertionFailedError;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.NavDrawerFragment;
@@ -224,21 +223,6 @@ public class EspressoTestUtils {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
- public static void tryKillDownloadService() {
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- context.stopService(new Intent(context, DownloadService.class));
- try {
- // Android has no reliable way to stop a service instantly.
- // Calling stopSelf marks allows the system to destroy the service but the actual call
- // to onDestroy takes until the next GC of the system, which we can not influence.
- // Try to wait for the service at least a bit.
- Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !DownloadService.isRunning);
- } catch (ConditionTimeoutException e) {
- e.printStackTrace();
- }
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
public static Matcher<View> actionBarOverflow() {
return allOf(isDisplayed(), withContentDescription("More options"));
}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
deleted file mode 100644
index 97499b24a..000000000
--- a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
+++ /dev/null
@@ -1,217 +0,0 @@
-package de.test.antennapod.service.download;
-
-import android.content.Context;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-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.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.test.antennapod.EspressoTestUtils;
-import org.awaitility.Awaitility;
-import org.awaitility.core.ConditionTimeoutException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
-import de.danoeh.antennapod.model.download.DownloadStatus;
-import de.danoeh.antennapod.core.service.download.Downloader;
-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 static de.test.antennapod.util.event.DownloadEventListener.withDownloadEventListener;
-import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * @see HttpDownloaderTest for the test of actual download (and saving the file).
- */
-@RunWith(AndroidJUnit4.class)
-public class DownloadServiceTest {
- private FeedMedia testMedia11 = null;
-
- private DownloaderFactory origFactory = null;
-
- @Before
- public void setUp() throws Exception {
- EspressoTestUtils.clearDatabase();
- EspressoTestUtils.clearPreferences();
- origFactory = DownloadService.getDownloaderFactory();
- Feed testFeed = setUpTestFeeds();
- testMedia11 = testFeed.getItemAtIndex(0).getMedia();
- }
-
- private Feed setUpTestFeeds() throws Exception {
- // To avoid complication in case of test failures, leaving behind orphaned
- // media files: add a timestamp so that each test run will have its own directory for media files.
- Feed feed = new Feed("url", null, "Test Feed title 1 " + System.currentTimeMillis());
- List<FeedItem> items = new ArrayList<>();
- feed.setItems(items);
- FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
- items.add(item1);
- FeedMedia media1 = new FeedMedia(0, item1, 123, 1, 1, "audio/mp3", null, "http://example.com/episode.mp3", false, null, 0, 0);
- item1.setMedia(media1);
-
- DBWriter.setFeedItem(item1).get();
- return feed;
- }
-
-
- @After
- public void tearDown() throws Exception {
- DownloadService.setDownloaderFactory(origFactory);
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- DownloadServiceInterface.get().cancelAll(context);
- context.stopService(new Intent(context, DownloadService.class));
- EspressoTestUtils.tryKillDownloadService();
- }
-
- @Test
- public void testEventsGeneratedCaseMediaDownloadSuccess_noEnqueue() throws Exception {
- doTestEventsGeneratedCaseMediaDownloadSuccess(false, 1);
- }
-
- @Test
- public void testEventsGeneratedCaseMediaDownloadSuccess_withEnqueue() throws Exception {
- // enqueue itself generates additional FeedItem event
- doTestEventsGeneratedCaseMediaDownloadSuccess(true, 2);
- }
-
- private void doTestEventsGeneratedCaseMediaDownloadSuccess(boolean enqueueDownloaded,
- int numEventsExpected)
- throws Exception {
- // create a stub download that returns successful
- //
- // OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
- // are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
- // to do so
- DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, DownloadStatus::setSuccessful));
-
- UserPreferences.setEnqueueDownloadedEpisodes(enqueueDownloaded);
- withFeedItemEventListener(feedItemEventListener -> {
- try {
- assertEquals(0, feedItemEventListener.getEvents().size());
- assertFalse("The media in test should not yet been downloaded",
- DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
-
- DownloadServiceInterface.get()
- .download(InstrumentationRegistry.getInstrumentation().getTargetContext(), false,
- DownloadRequestCreator.create(testMedia11).build());
- Awaitility.await()
- .atMost(5000, TimeUnit.MILLISECONDS)
- .until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
- assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
- DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
- assertEquals("The FeedItem should have been " + (enqueueDownloaded ? "" : "not ") + "enqueued",
- enqueueDownloaded,
- DBReader.getQueueIDList().contains(testMedia11.getItem().getId()));
- } catch (ConditionTimeoutException cte) {
- fail("The expected FeedItemEvent (for media download complete) has not been posted. "
- + cte.getMessage());
- }
- });
- }
-
- @Test
- public void testCancelDownload_UndoEnqueue_Normal() throws Exception {
- doTestCancelDownload_UndoEnqueue(false);
- }
-
- @Test
- public void testCancelDownload_UndoEnqueue_AlreadyInQueue() throws Exception {
- doTestCancelDownload_UndoEnqueue(true);
- }
-
- private void doTestCancelDownload_UndoEnqueue(boolean itemAlreadyInQueue) throws Exception {
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- // let download take longer to ensure the test can cancel the download in time
- DownloadService.setDownloaderFactory(
- new StubDownloaderFactory(30000, DownloadStatus::setSuccessful));
- UserPreferences.setEnqueueDownloadedEpisodes(true);
- UserPreferences.setEnableAutodownload(false);
-
- final long item1Id = testMedia11.getItem().getId();
- if (itemAlreadyInQueue) {
- // simulate item already in queue condition
- DBWriter.addQueueItem(context, false, item1Id).get();
- assertTrue(DBReader.getQueueIDList().contains(item1Id));
- } else {
- assertFalse(DBReader.getQueueIDList().contains(item1Id));
- }
-
- withFeedItemEventListener(feedItemEventListener -> {
- DownloadServiceInterface.get()
- .download(InstrumentationRegistry.getInstrumentation().getTargetContext(), false,
- DownloadRequestCreator.create(testMedia11).build());
- withDownloadEventListener(downloadEventListener ->
- Awaitility.await("download is actually running")
- .atMost(5000, TimeUnit.MILLISECONDS)
- .until(() -> downloadEventListener.getLatestEvent() != null
- && downloadEventListener.getLatestEvent().update.mediaIds.length > 0
- && downloadEventListener.getLatestEvent().update.mediaIds[0] == testMedia11.getId()));
-
- if (itemAlreadyInQueue) {
- assertEquals("download service receives the request - no event is expected before cancel is issued",
- 0, feedItemEventListener.getEvents().size());
- } else {
- Awaitility.await("item enqueue event")
- .atMost(2000, TimeUnit.MILLISECONDS)
- .until(() -> feedItemEventListener.getEvents().size() >= 1);
- }
- DownloadServiceInterface.get().cancel(context, testMedia11.getDownload_url());
- final int totalNumEventsExpected = itemAlreadyInQueue ? 1 : 3;
- Awaitility.await("item dequeue event + download termination event")
- .atMost(2000, TimeUnit.MILLISECONDS)
- .until(() -> feedItemEventListener.getEvents().size() >= totalNumEventsExpected);
- assertFalse("The download should have been canceled",
- DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
- if (itemAlreadyInQueue) {
- assertTrue("The FeedItem should still be in the queue after the download is cancelled."
- + " It's there before download.",
- DBReader.getQueueIDList().contains(item1Id));
- } else {
- assertFalse("The FeedItem should not be in the queue after the download is cancelled.",
- DBReader.getQueueIDList().contains(item1Id));
- }
- });
- }
-
- private static class StubDownloaderFactory implements DownloaderFactory {
- private final long downloadTime;
-
- @NonNull
- private final Consumer<DownloadStatus> onDownloadComplete;
-
- StubDownloaderFactory(long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
- this.downloadTime = downloadTime;
- this.onDownloadComplete = onDownloadComplete;
- }
-
- @Nullable
- @Override
- public Downloader create(@NonNull DownloadRequest request) {
- return new StubDownloader(request, downloadTime, onDownloadComplete);
- }
- }
-
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index f276434f6..76cba4706 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -10,7 +10,7 @@ import java.io.IOException;
import de.danoeh.antennapod.model.feed.FeedFile;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.model.download.DownloadError;
@@ -80,10 +80,9 @@ public class HttpDownloaderTest {
DownloadRequest request = new DownloadRequest(feedFile.getFile_url(), url, title, 0, feedFile.getTypeAsInt(), username, password, deleteOnFail, null, false);
Downloader downloader = new HttpDownloader(request);
downloader.call();
- DownloadStatus status = downloader.getResult();
+ DownloadResult status = downloader.getResult();
assertNotNull(status);
assertEquals(expectedResult, status.isSuccessful());
- assertTrue(status.isDone());
// the file should not exist if the download has failed and deleteExisting was true
assertTrue(!deleteExisting || new File(feedFile.getFile_url()).exists() == expectedResult);
return downloader;
@@ -127,10 +126,8 @@ public class HttpDownloaderTest {
} catch (InterruptedException e) {
e.printStackTrace();
}
- DownloadStatus result = downloader.getResult();
- assertTrue(result.isDone());
+ DownloadResult result = downloader.getResult();
assertFalse(result.isSuccessful());
- assertTrue(result.isCancelled());
assertFalse(new File(feedFile.getFile_url()).exists());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index 5940d511b..1f387b24b 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -27,8 +27,6 @@ import java.util.Arrays;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
-import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.action.ViewActions.swipeDown;
import static androidx.test.espresso.action.ViewActions.swipeUp;
@@ -37,13 +35,11 @@ import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
-import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertTrue;
@@ -224,40 +220,6 @@ public class PreferencesTest {
}
@Test
- public void testSetSequentialDownload() {
- clickPreference(R.string.downloads_pref);
- clickPreference(R.string.pref_parallel_downloads_title);
- onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
- onView(withClassName(endsWith("EditText"))).perform(replaceText("1"));
- onView(withText(android.R.string.ok)).perform(click());
- Awaitility.await().atMost(1000, MILLISECONDS)
- .until(() -> UserPreferences.getParallelDownloads() == 1);
- }
-
- @Test
- public void testSetParallelDownloads() {
- clickPreference(R.string.downloads_pref);
- clickPreference(R.string.pref_parallel_downloads_title);
- onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
- onView(withClassName(endsWith("EditText"))).perform(replaceText("10"));
- onView(withClassName(endsWith("EditText"))).perform(closeSoftKeyboard());
- onView(withText(android.R.string.ok)).perform(click());
- Awaitility.await().atMost(1000, MILLISECONDS)
- .until(() -> UserPreferences.getParallelDownloads() == 10);
- }
-
- @Test
- public void testSetParallelDownloadsInvalidInput() {
- clickPreference(R.string.downloads_pref);
- clickPreference(R.string.pref_parallel_downloads_title);
- onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
- onView(withClassName(endsWith("EditText"))).perform(replaceText("0"));
- onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
- onView(withClassName(endsWith("EditText"))).perform(replaceText("100"));
- onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
- }
-
- @Test
public void testSetEpisodeCache() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
diff --git a/app/src/androidTest/java/de/test/antennapod/util/event/DownloadEventListener.java b/app/src/androidTest/java/de/test/antennapod/util/event/DownloadEventListener.java
deleted file mode 100644
index d322c1cbf..000000000
--- a/app/src/androidTest/java/de/test/antennapod/util/event/DownloadEventListener.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package de.test.antennapod.util.event;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import io.reactivex.functions.Consumer;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Test helper to listen to {@link DownloadEvent} and handle them accordingly.
- */
-public class DownloadEventListener {
- private final List<DownloadEvent> events = new ArrayList<>();
-
- /**
- * Provides an listener subscribing to {@link DownloadEvent} that the callers can use.
- * Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
- */
- public static void withDownloadEventListener(@NonNull Consumer<DownloadEventListener> consumer) throws Exception {
- DownloadEventListener feedItemEventListener = new DownloadEventListener();
- try {
- EventBus.getDefault().register(feedItemEventListener);
- consumer.accept(feedItemEventListener);
- } finally {
- EventBus.getDefault().unregister(feedItemEventListener);
- }
- }
-
- @Subscribe
- public void onEvent(DownloadEvent event) {
- events.add(event);
- }
-
- @Nullable
- public DownloadEvent getLatestEvent() {
- if (events.size() == 0) {
- return null;
- }
- return events.get(events.size() - 1);
- }
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8c779db26..95b0c1614 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -130,17 +130,6 @@
</activity>
<activity
- android:name=".activity.DownloadAuthenticationActivity"
- android:theme="@style/Theme.AntennaPod.Dark.Translucent"
- android:exported="false"
- android:launchMode="singleInstance">
- <intent-filter>
- <action android:name="de.danoeh.antennapod.intents.DOWNLOAD_AUTH_ACTIVITY" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
- <activity
android:name=".activity.PreferenceActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false"
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
deleted file mode 100644
index 176c3c990..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.os.Bundle;
-import android.text.TextUtils;
-import androidx.appcompat.app.AppCompatActivity;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.ThemeSwitcher;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.dialog.AuthenticationDialog;
-import de.danoeh.antennapod.ui.appstartintent.DownloadAuthenticationActivityStarter;
-import io.reactivex.Completable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.schedulers.Schedulers;
-
-
-/**
- * Shows a username and a password text field.
- * The activity MUST be started with the ARG_DOWNlOAD_REQUEST argument set to a non-null value.
- */
-public class DownloadAuthenticationActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(ThemeSwitcher.getTranslucentTheme(this));
- super.onCreate(savedInstanceState);
-
- DownloadRequest request = getIntent().getParcelableExtra(
- DownloadAuthenticationActivityStarter.EXTRA_DOWNLOAD_REQUEST);
-
- new AuthenticationDialog(this, R.string.authentication_label, true, "", "") {
- @Override
- protected void onConfirmed(String username, String password) {
- Completable.fromAction(
- () -> {
- request.setUsername(username);
- request.setPassword(password);
-
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- long mediaId = request.getFeedfileId();
- FeedMedia media = DBReader.getFeedMedia(mediaId);
- if (media != null) {
- FeedPreferences preferences = media.getItem().getFeed().getPreferences();
- if (TextUtils.isEmpty(preferences.getPassword())
- || TextUtils.isEmpty(preferences.getUsername())) {
- preferences.setUsername(username);
- preferences.setPassword(password);
- DBWriter.setFeedPreferences(preferences);
- }
- }
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(() -> {
- DownloadServiceInterface.get()
- .download(DownloadAuthenticationActivity.this, false, request);
- finish();
- });
- }
-
- @Override
- protected void onCancelled() {
- finish();
- }
- }.show();
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index bd467076a..a55bfade3 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -39,12 +39,14 @@ import de.danoeh.antennapod.core.preferences.ThemeSwitcher;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.dialog.RatingDialog;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
import de.danoeh.antennapod.fragment.AudioPlayerFragment;
import de.danoeh.antennapod.fragment.CompletedDownloadsFragment;
+import de.danoeh.antennapod.fragment.DownloadLogFragment;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.fragment.InboxFragment;
import de.danoeh.antennapod.fragment.NavDrawerFragment;
@@ -53,6 +55,8 @@ import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.fragment.TransitionEffect;
+import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
@@ -66,6 +70,9 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* The activity that is shown when the user launches the app.
*/
@@ -173,6 +180,39 @@ public class MainActivity extends CastEnabledActivity {
}
EventBus.getDefault().postSticky(new FeedUpdateRunningEvent(isRefreshingFeeds));
});
+ WorkManager.getInstance(this)
+ .getWorkInfosByTagLiveData(DownloadServiceInterface.WORK_TAG)
+ .observe(this, workInfos -> {
+ Map<String, DownloadStatus> updatedEpisodes = new HashMap<>();
+ for (WorkInfo workInfo : workInfos) {
+ String downloadUrl = null;
+ for (String tag : workInfo.getTags()) {
+ if (tag.startsWith(DownloadServiceInterface.WORK_TAG_EPISODE_URL)) {
+ downloadUrl = tag.substring(DownloadServiceInterface.WORK_TAG_EPISODE_URL.length());
+ }
+ }
+ if (downloadUrl == null) {
+ continue;
+ }
+ int status;
+ if (workInfo.getState() == WorkInfo.State.RUNNING) {
+ status = DownloadStatus.STATE_RUNNING;
+ } else if (workInfo.getState() == WorkInfo.State.ENQUEUED
+ || workInfo.getState() == WorkInfo.State.BLOCKED) {
+ status = DownloadStatus.STATE_QUEUED;
+ } else {
+ status = DownloadStatus.STATE_COMPLETED;
+ }
+ int progress = workInfo.getProgress().getInt(DownloadServiceInterface.WORK_DATA_PROGRESS, -1);
+ if (progress == -1 && status != DownloadStatus.STATE_COMPLETED) {
+ status = DownloadStatus.STATE_QUEUED;
+ progress = 0;
+ }
+ updatedEpisodes.put(downloadUrl, new DownloadStatus(status, progress));
+ }
+ DownloadServiceInterface.get().setCurrentDownloads(updatedEpisodes);
+ EventBus.getDefault().postSticky(new EpisodeDownloadEvent(updatedEpisodes));
+ });
}
@Override
@@ -531,9 +571,9 @@ public class MainActivity extends CastEnabledActivity {
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
- Snackbar snackbar = showSnackbarAbovePlayer(event.message, Snackbar.LENGTH_SHORT);
+ Snackbar snackbar = showSnackbarAbovePlayer(event.message, Snackbar.LENGTH_LONG);
if (event.action != null) {
- snackbar.setAction(getString(R.string.undo), v -> event.action.run());
+ snackbar.setAction(event.actionText, v -> event.action.accept(this));
}
}
@@ -570,6 +610,9 @@ public class MainActivity extends CastEnabledActivity {
if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_DRAWER, false) && drawerLayout != null) {
drawerLayout.open();
}
+ if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_DOWNLOAD_LOGS, false)) {
+ new DownloadLogFragment().show(getSupportFragmentManager(), null);
+ }
if (intent.getBooleanExtra(EXTRA_REFRESH_ON_START, false)) {
FeedUpdateManager.runOnceOrAsk(this);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index f5f3d28f6..3812339bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -31,20 +31,20 @@ import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.preferences.ThemeSwitcher;
-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.core.storage.DBTasks;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface;
import de.danoeh.antennapod.core.util.DownloadErrorLabel;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -307,10 +307,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- private void checkDownloadResult(@NonNull DownloadStatus status, String destination) {
- if (status.isCancelled()) {
- return;
- }
+ private void checkDownloadResult(@NonNull DownloadResult status, String destination) {
if (status.isSuccessful()) {
parseFeed(destination);
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
@@ -341,9 +338,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
);
}
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(EpisodeDownloadEvent event) {
handleUpdatedFeedStatus();
}
@@ -535,7 +531,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
private void handleUpdatedFeedStatus() {
- if (DownloadService.isDownloadingFile(selectedDownloadUrl)) {
+ if (DownloadServiceInterface.get().isDownloadingEpisode(selectedDownloadUrl)) {
viewBinding.subscribeButton.setEnabled(false);
viewBinding.subscribeButton.setText(R.string.subscribing_label);
} else if (feedInFeedlist()) {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 8df3e3968..0fb6a7e7a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter;
import android.app.Activity;
import android.text.format.DateUtils;
-import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -10,18 +9,13 @@ import android.widget.BaseAdapter;
import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.adapter.actionbutton.DownloadActionButton;
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.util.DownloadErrorLabel;
import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.view.viewholder.DownloadLogItemViewHolder;
@@ -36,24 +30,18 @@ public class DownloadLogAdapter extends BaseAdapter {
private static final String TAG = "DownloadLogAdapter";
private final Activity context;
- private List<DownloadStatus> downloadLog = new ArrayList<>();
- private List<Downloader> runningDownloads = new ArrayList<>();
+ private List<DownloadResult> downloadLog = new ArrayList<>();
public DownloadLogAdapter(Activity context) {
super();
this.context = context;
}
- public void setDownloadLog(List<DownloadStatus> downloadLog) {
+ public void setDownloadLog(List<DownloadResult> downloadLog) {
this.downloadLog = downloadLog;
notifyDataSetChanged();
}
- public void setRunningDownloads(List<Downloader> runningDownloads) {
- this.runningDownloads = runningDownloads;
- notifyDataSetChanged();
- }
-
@Override
public View getView(int position, View convertView, ViewGroup parent) {
DownloadLogItemViewHolder holder;
@@ -63,17 +51,11 @@ public class DownloadLogAdapter extends BaseAdapter {
} else {
holder = (DownloadLogItemViewHolder) convertView.getTag();
}
-
- Object item = getItem(position);
- if (item instanceof DownloadStatus) {
- bind(holder, (DownloadStatus) item, position);
- } else if (item instanceof Downloader) {
- bind(holder, (Downloader) item, position);
- }
+ bind(holder, getItem(position), position);
return holder.itemView;
}
- private void bind(DownloadLogItemViewHolder holder, DownloadStatus status, int position) {
+ private void bind(DownloadLogItemViewHolder holder, DownloadResult status, int position) {
String statusText = "";
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
statusText += context.getString(R.string.download_type_feed);
@@ -111,8 +93,7 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.reason.setVisibility(View.VISIBLE);
holder.tapForDetails.setVisibility(View.VISIBLE);
- if (newerWasSuccessful(position - runningDownloads.size(),
- status.getFeedfileType(), status.getFeedfileId())) {
+ if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
holder.secondaryActionButton.setOnClickListener(null);
holder.secondaryActionButton.setTag(null);
@@ -138,8 +119,7 @@ public class DownloadLogAdapter extends BaseAdapter {
Log.e(TAG, "Could not find feed media for feed id: " + status.getFeedfileId());
return;
}
- DownloadServiceInterface.get()
- .download(context, true, DownloadRequestCreator.create(media).build());
+ new DownloadActionButton(media.getItem()).onClick(context);
((MainActivity) context).showSnackbarAbovePlayer(
R.string.status_downloading_label, Toast.LENGTH_SHORT);
});
@@ -148,56 +128,9 @@ public class DownloadLogAdapter extends BaseAdapter {
}
}
- private void bind(DownloadLogItemViewHolder holder, Downloader downloader, int position) {
- DownloadRequest request = downloader.getDownloadRequest();
- holder.title.setText(request.getTitle());
- holder.secondaryActionIcon.setImageResource(R.drawable.ic_cancel);
- holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
- holder.secondaryActionButton.setVisibility(View.VISIBLE);
- holder.secondaryActionButton.setTag(downloader);
- holder.secondaryActionButton.setOnClickListener(v -> {
- DownloadServiceInterface.get().cancel(context, request.getSource());
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
- FeedItem feedItem = media.getItem();
- feedItem.disableAutoDownload();
- DBWriter.setFeedItem(feedItem);
- }
- });
- holder.reason.setVisibility(View.GONE);
- holder.tapForDetails.setVisibility(View.GONE);
- holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.colorPrimary));
- holder.icon.setText("{fa-arrow-circle-down}");
- holder.icon.setContentDescription(context.getString(R.string.status_downloading_label));
-
- boolean percentageWasSet = false;
- String status = "";
- if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- status += context.getString(R.string.download_type_feed);
- } else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- status += context.getString(R.string.download_type_media);
- }
- status += " · ";
- if (request.getSoFar() <= 0) {
- status += context.getString(R.string.download_pending);
- } else {
- status += Formatter.formatShortFileSize(context, request.getSoFar());
- if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) {
- status += " / " + Formatter.formatShortFileSize(context, request.getSize());
- holder.secondaryActionProgress.setPercentage(
- 0.01f * Math.max(1, request.getProgressPercent()), request);
- percentageWasSet = true;
- }
- }
- if (!percentageWasSet) {
- holder.secondaryActionProgress.setPercentage(0, request);
- }
- holder.status.setText(status);
- }
-
private boolean newerWasSuccessful(int downloadStatusIndex, int feedTypeId, long id) {
for (int i = 0; i < downloadStatusIndex; i++) {
- DownloadStatus status = downloadLog.get(i);
+ DownloadResult status = downloadLog.get(i);
if (status.getFeedfileType() == feedTypeId && status.getFeedfileId() == id && status.isSuccessful()) {
return true;
}
@@ -207,15 +140,13 @@ public class DownloadLogAdapter extends BaseAdapter {
@Override
public int getCount() {
- return downloadLog.size() + runningDownloads.size();
+ return downloadLog.size();
}
@Override
- public Object getItem(int position) {
- if (position < runningDownloads.size()) {
- return runningDownloads.get(position);
- } else if (position - runningDownloads.size() < downloadLog.size()) {
- return downloadLog.get(position - runningDownloads.size());
+ public DownloadResult getItem(int position) {
+ if (position < downloadLog.size()) {
+ return downloadLog.get(position);
}
return null;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
index 3b54efc03..86a8047a9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -2,20 +2,17 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.view.View;
-import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
-import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
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.util.NetworkUtils;
public class DownloadActionButton extends ItemActionButton {
@@ -50,19 +47,23 @@ public class DownloadActionButton extends ItemActionButton {
UsageStatistics.logAction(UsageStatistics.ACTION_DOWNLOAD);
- if (NetworkUtils.isEpisodeDownloadAllowed() || MobileDownloadHelper.userAllowedMobileDownloads()) {
- DownloadServiceInterface.get()
- .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();
+ if (NetworkUtils.isEpisodeDownloadAllowed()) {
+ DownloadServiceInterface.get().downloadNow(context, item, false);
} else {
- MobileDownloadHelper.confirmMobileDownload(context, item);
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
+ .setTitle(R.string.confirm_mobile_download_dialog_title)
+ .setMessage(R.string.confirm_mobile_download_dialog_message)
+ .setPositiveButton(R.string.confirm_mobile_download_dialog_download_later,
+ (d, w) -> DownloadServiceInterface.get().downloadNow(context, item, false))
+ .setNeutralButton(R.string.confirm_mobile_download_dialog_allow_this_time,
+ (d, w) -> DownloadServiceInterface.get().downloadNow(context, item, true))
+ .setNegativeButton(R.string.cancel_label, null);
+ builder.show();
}
}
private boolean shouldNotDownload(@NonNull FeedMedia media) {
- boolean isDownloading = DownloadService.isDownloadingFile(media.getDownload_url());
+ boolean isDownloading = DownloadServiceInterface.get().isDownloadingEpisode(media.getDownload_url());
return isDownloading || media.isDownloaded();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index e7a95c404..613dd32f0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -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.core.util.PlaybackStatus;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
public abstract class ItemActionButton {
@@ -39,7 +39,7 @@ public abstract class ItemActionButton {
return new MarkAsPlayedActionButton(item);
}
- final boolean isDownloadingMedia = DownloadService.isDownloadingFile(media.getDownload_url());
+ final boolean isDownloadingMedia = DownloadServiceInterface.get().isDownloadingEpisode(media.getDownload_url());
if (PlaybackStatus.isCurrentlyPlaying(media)) {
return new PauseActionButton(item);
} else if (item.getFeed().isLocalFeed()) {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
deleted file mode 100644
index 015f46318..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package de.danoeh.antennapod.adapter.actionbutton;
-
-import android.content.Context;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-
-class MobileDownloadHelper {
- private static long addToQueueTimestamp;
- private static long allowMobileDownloadTimestamp;
- private static final int TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000;
-
- static boolean userChoseAddToQueue() {
- return System.currentTimeMillis() - addToQueueTimestamp < TEN_MINUTES_IN_MILLIS;
- }
-
- static boolean userAllowedMobileDownloads() {
- return System.currentTimeMillis() - allowMobileDownloadTimestamp < TEN_MINUTES_IN_MILLIS;
- }
-
- static void confirmMobileDownload(final Context context, final FeedItem item) {
- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
- .setTitle(R.string.confirm_mobile_download_dialog_title)
- .setMessage(R.string.confirm_mobile_download_dialog_message)
- .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily),
- (dialog, which) -> downloadFeedItems(context, item));
- if (!DBReader.getQueueIDList().contains(item.getId())) {
- builder.setMessage(R.string.confirm_mobile_download_dialog_message_not_in_queue)
- .setNeutralButton(R.string.confirm_mobile_download_dialog_only_add_to_queue,
- (dialog, which) -> addToQueue(context, item));
- }
- builder.show();
- }
-
- private static void addToQueue(Context context, FeedItem item) {
- addToQueueTimestamp = System.currentTimeMillis();
- DBWriter.addQueueItem(context, item);
- }
-
- private static void downloadFeedItems(Context context, FeedItem item) {
- allowMobileDownloadTimestamp = System.currentTimeMillis();
- DownloadServiceInterface.get().download(context, true, DownloadRequestCreator.create(item.getMedia()).build());
- }
-} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/DownloadLogDetailsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/DownloadLogDetailsDialog.java
index 5e28639dd..00936a38a 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/DownloadLogDetailsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/DownloadLogDetailsDialog.java
@@ -9,7 +9,7 @@ import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.DownloadErrorLabel;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.model.feed.Feed;
@@ -18,7 +18,7 @@ import org.greenrobot.eventbus.EventBus;
public class DownloadLogDetailsDialog extends MaterialAlertDialogBuilder {
- public DownloadLogDetailsDialog(@NonNull Context context, DownloadStatus status) {
+ public DownloadLogDetailsDialog(@NonNull Context context, DownloadResult status) {
super(context);
String url = "unknown";
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 737975389..ba328adba 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -20,12 +20,12 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.actionbutton.DeleteActionButton;
-import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
@@ -36,6 +36,7 @@ import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.view.EmptyViewHandler;
import de.danoeh.antennapod.view.EpisodeItemListRecyclerView;
@@ -50,9 +51,10 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Displays all completed downloads and provides a button to delete them.
@@ -63,7 +65,7 @@ public class CompletedDownloadsFragment extends Fragment
public static final String ARG_SHOW_LOGS = "show_logs";
private static final String KEY_UP_ARROW = "up_arrow";
- private long[] runningDownloads = new long[0];
+ private Set<String> runningDownloads = new HashSet<>();
private List<FeedItem> items = new ArrayList<>();
private CompletedDownloadsListAdapter adapter;
private EpisodeItemListRecyclerView recyclerView;
@@ -217,19 +219,22 @@ public class CompletedDownloadsFragment extends Fragment
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if (!Arrays.equals(event.update.mediaIds, runningDownloads)) {
- runningDownloads = event.update.mediaIds;
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ Set<String> newRunningDownloads = new HashSet<>();
+ for (String url : event.getUrls()) {
+ if (DownloadServiceInterface.get().isDownloadingEpisode(url)) {
+ newRunningDownloads.add(url);
+ }
+ }
+ if (!newRunningDownloads.equals(runningDownloads)) {
+ runningDownloads = newRunningDownloads;
loadItems();
return; // Refreshed anyway
}
- if (event.update.mediaIds.length > 0) {
- for (long mediaId : event.update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(items, mediaId);
- if (pos >= 0) {
- adapter.notifyItemChangedCompat(pos);
- }
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(items, downloadUrl);
+ if (pos >= 0) {
+ adapter.notifyItemChangedCompat(pos);
}
}
}
@@ -318,17 +323,17 @@ public class CompletedDownloadsFragment extends Fragment
List<FeedItem> downloadedItems = DBReader.getEpisodes(0, Integer.MAX_VALUE,
new FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder);
- List<Long> mediaIds = new ArrayList<>();
+ List<String> mediaUrls = new ArrayList<>();
if (runningDownloads == null) {
return downloadedItems;
}
- for (long id : runningDownloads) {
- if (FeedItemUtil.indexOfItemWithMediaId(downloadedItems, id) != -1) {
+ for (String url : runningDownloads) {
+ if (FeedItemUtil.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) {
continue; // Already in list
}
- mediaIds.add(id);
+ mediaUrls.add(url);
}
- List<FeedItem> currentDownloads = DBReader.getFeedItemsWithMedia(mediaIds.toArray(new Long[0]));
+ List<FeedItem> currentDownloads = DBReader.getFeedItemsWithUrl(mediaUrls);
currentDownloads.addAll(downloadedItems);
return currentDownloads;
})
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index c27d8c058..03913ff12 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -14,15 +14,12 @@ import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
-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.databinding.DownloadLogFragmentBinding;
import de.danoeh.antennapod.dialog.DownloadLogDetailsDialog;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -30,7 +27,6 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.List;
@@ -42,8 +38,7 @@ public class DownloadLogFragment extends BottomSheetDialogFragment
implements AdapterView.OnItemClickListener, MaterialToolbar.OnMenuItemClickListener {
private static final String TAG = "DownloadLogFragment";
- private List<DownloadStatus> downloadLog = new ArrayList<>();
- private List<Downloader> runningDownloads = new ArrayList<>();
+ private List<DownloadResult> downloadLog = new ArrayList<>();
private DownloadLogAdapter adapter;
private Disposable disposable;
private DownloadLogFragmentBinding viewBinding;
@@ -93,8 +88,8 @@ public class DownloadLogFragment extends BottomSheetDialogFragment
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Object item = adapter.getItem(position);
- if (item instanceof DownloadStatus) {
- new DownloadLogDetailsDialog(getContext(), (DownloadStatus) item).show();
+ if (item instanceof DownloadResult) {
+ new DownloadLogDetailsDialog(getContext(), (DownloadResult) item).show();
}
}
@@ -119,14 +114,6 @@ public class DownloadLogFragment extends BottomSheetDialogFragment
return false;
}
- @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEvent(DownloadEvent event) {
- Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- runningDownloads = update.downloaders;
- adapter.setRunningDownloads(runningDownloads);
- }
-
private void loadDownloadLog() {
if (disposable != null) {
disposable.dispose();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index ef784e9ee..00d671d36 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -27,11 +27,10 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
@@ -387,16 +386,11 @@ public abstract class EpisodesListFragment extends Fragment
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- updateToolbar();
- if (update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
- if (pos >= 0) {
- listAdapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(episodes, downloadUrl);
+ if (pos >= 0) {
+ listAdapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 61883afe7..f59d5dbd4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -29,8 +29,6 @@ import com.leinardi.android.speeddial.SpeedDialView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -45,6 +43,7 @@ import de.danoeh.antennapod.dialog.DownloadLogDetailsDialog;
import de.danoeh.antennapod.dialog.FeedItemFilterDialog;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
import de.danoeh.antennapod.dialog.RenameItemDialog;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FavoritesEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
@@ -57,7 +56,7 @@ import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler;
import de.danoeh.antennapod.fragment.swipeactions.SwipeActions;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
@@ -330,16 +329,14 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- updateToolbar();
- if (update.mediaIds.length > 0 && feed != null) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId);
- if (pos >= 0) {
- adapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ if (feed == null) {
+ return;
+ }
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(feed.getItems(), downloadUrl);
+ if (pos >= 0) {
+ adapter.notifyItemChangedCompat(pos);
}
}
}
@@ -479,7 +476,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
private void showErrorDetails() {
Maybe.fromCallable(
() -> {
- List<DownloadStatus> feedDownloadLog = DBReader.getFeedDownloadLog(feedID);
+ List<DownloadResult> feedDownloadLog = DBReader.getFeedDownloadLog(feedID);
if (feedDownloadLog.size() == 0 || feedDownloadLog.get(0).isSuccessful()) {
return null;
}
@@ -490,9 +487,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
.subscribe(
downloadStatus -> new DownloadLogDetailsDialog(getContext(), downloadStatus).show(),
error -> error.printStackTrace(),
- () -> {
- ((MainActivity) getActivity()).loadChildFragment(new DownloadLogFragment());
- });
+ () -> new DownloadLogFragment().show(getChildFragmentManager(), null));
}
private void showFeedInfo() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index df48c9a98..94877811e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -37,10 +37,7 @@ import de.danoeh.antennapod.adapter.actionbutton.PlayActionButton;
import de.danoeh.antennapod.adapter.actionbutton.PlayLocalActionButton;
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.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.core.util.PlaybackStatus;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
@@ -49,8 +46,8 @@ import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.preferences.UsageStatistics;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateFormatter;
@@ -63,12 +60,10 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.apache.commons.lang3.ArrayUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -98,7 +93,6 @@ public class ItemFragment extends Fragment {
private long itemId;
private FeedItem item;
private String webviewData;
- private List<Downloader> downloaderList;
private ViewGroup root;
private ShownotesWebView webvDescription;
@@ -309,14 +303,13 @@ public class ItemFragment extends Fragment {
private void updateButtons() {
progbarDownload.setVisibility(View.GONE);
- if (item.hasMedia() && downloaderList != null) {
- for (Downloader downloader : downloaderList) {
- DownloadRequest request = downloader.getDownloadRequest();
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && request.getFeedfileId() == item.getMedia().getId()) {
- progbarDownload.setVisibility(View.VISIBLE);
- progbarDownload.setPercentage(0.01f * Math.max(1, request.getProgressPercent()), request);
- }
+ if (item.hasMedia()) {
+ if (DownloadServiceInterface.get().isDownloadingEpisode(item.getMedia().getDownload_url())) {
+ progbarDownload.setVisibility(View.VISIBLE);
+ progbarDownload.setPercentage(0.01f * Math.max(1,
+ DownloadServiceInterface.get().getProgress(item.getMedia().getDownload_url())), item);
+ progbarDownload.setIndeterminate(
+ DownloadServiceInterface.get().isEpisodeQueued(item.getMedia().getDownload_url()));
}
}
@@ -341,7 +334,7 @@ public class ItemFragment extends Fragment {
} else {
actionButton1 = new StreamActionButton(item);
}
- if (DownloadService.isDownloadingFile(media.getDownload_url())) {
+ if (DownloadServiceInterface.get().isDownloadingEpisode(media.getDownload_url())) {
actionButton2 = new CancelDownloadActionButton(item);
} else if (!media.isDownloaded()) {
actionButton2 = new DownloadActionButton(item);
@@ -386,18 +379,15 @@ public class ItemFragment extends Fragment {
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- downloaderList = update.downloaders;
+ public void onEventMainThread(EpisodeDownloadEvent event) {
if (item == null || item.getMedia() == null) {
return;
}
- long mediaId = item.getMedia().getId();
- if (ArrayUtils.contains(update.mediaIds, mediaId)) {
- if (itemsLoaded && getActivity() != null) {
- updateButtons();
- }
+ if (!event.getUrls().contains(item.getMedia().getDownload_url())) {
+ return;
+ }
+ if (itemsLoaded && getActivity() != null) {
+ updateButtons();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index 6681df4c1..a1aa7226d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -32,8 +32,6 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -41,6 +39,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
@@ -186,16 +185,14 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with DownloadEvent");
- DownloaderUpdate update = event.update;
- refreshToolbarState();
- if (recyclerAdapter != null && update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(queue, mediaId);
- if (pos >= 0) {
- recyclerAdapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ if (queue == null) {
+ return;
+ }
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(queue, downloadUrl);
+ if (pos >= 0) {
+ recyclerAdapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index d2aa35549..b85e34e7d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -28,9 +28,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.HorizontalFeedListAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
@@ -295,15 +294,14 @@ public class SearchFragment extends Fragment {
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- if (adapter != null && update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(results, mediaId);
- if (pos >= 0) {
- adapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ if (results == null) {
+ return;
+ }
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(results, downloadUrl);
+ if (pos >= 0) {
+ adapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 490c79d47..a6daec2e7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -36,7 +36,6 @@ import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsRecyclerAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.NavDrawerData;
@@ -362,12 +361,6 @@ public class SubscriptionFragment extends Fragment
loadSubscriptions();
}
- @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- refreshToolbarState();
- }
-
@Override
public void onEndSelectMode() {
speedDialView.close();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
index e076d35b6..a14bfcd16 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
@@ -6,13 +6,10 @@ import androidx.annotation.PluralsRes;
import com.google.android.material.snackbar.Snackbar;
-import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.LongList;
@@ -93,14 +90,12 @@ public class EpisodeMultiSelectActionHandler {
private void downloadChecked(List<FeedItem> items) {
// download the check episodes in the same order as they are currently displayed
- List<DownloadRequest> requests = new ArrayList<>();
for (FeedItem episode : items) {
if (episode.hasMedia() && !episode.getFeed().isLocalFeed()) {
- requests.add(DownloadRequestCreator.create(episode.getMedia()).build());
+ DownloadServiceInterface.get().download(activity, episode);
}
}
- DownloadServiceInterface.get().download(activity, true, requests.toArray(new DownloadRequest[0]));
- showMessage(R.plurals.downloading_batch_label, requests.size());
+ showMessage(R.plurals.downloading_batch_label, items.size());
}
private void deleteChecked(List<FeedItem> items) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
index c486089fc..7b0c3efdf 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.SharedPreferences;
-import android.content.res.Resources;
import android.os.Bundle;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
@@ -43,7 +42,6 @@ public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
@Override
public void onResume() {
super.onResume();
- setParallelDownloadsText(UserPreferences.getParallelDownloads());
setDataFolderText();
}
@@ -52,12 +50,6 @@ public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_autodownload);
return true;
});
- findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setOnPreferenceChangeListener((preference, o) -> {
- if (o instanceof Integer) {
- setParallelDownloadsText((Integer) o);
- }
- return true;
- });
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
ProxyDialog dialog = new ProxyDialog(getActivity());
@@ -73,12 +65,6 @@ public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
});
}
- private void setParallelDownloadsText(int downloads) {
- final Resources res = getActivity().getResources();
- String s = res.getString(R.string.parallel_downloads, downloads);
- findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
- }
-
private void setDataFolderText() {
File f = UserPreferences.getDataFolder(null);
if (f != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
deleted file mode 100644
index a15c6d6b2..000000000
--- a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.content.Context;
-import androidx.appcompat.app.AlertDialog;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import androidx.preference.Preference;
-import android.text.InputFilter;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.EditText;
-
-import de.danoeh.antennapod.R;
-
-public class NumberPickerPreference extends Preference {
- private Context context;
- private int defaultValue = 0;
- private int minValue = 0;
- private int maxValue = Integer.MAX_VALUE;
-
- public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init(context, attrs);
- }
-
- public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context, attrs);
- }
-
- public NumberPickerPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- public NumberPickerPreference(Context context) {
- super(context);
- this.context = context;
- }
-
- private void init(Context context, AttributeSet attrs) {
- this.context = context;
-
- for (int i = 0; i < attrs.getAttributeCount(); i++) {
- String name = attrs.getAttributeName(i);
- String value = attrs.getAttributeValue(i);
- switch (name) {
- case "defaultValue":
- defaultValue = Integer.parseInt(value);
- break;
- case "minValue":
- minValue = Integer.parseInt(value);
- break;
- case "maxValue":
- maxValue = Integer.parseInt(value);
- break;
- }
- }
- }
-
- @Override
- protected void onClick() {
- super.onClick();
-
- View view = View.inflate(context, R.layout.numberpicker, null);
- EditText number = view.findViewById(R.id.number);
- number.setText(getSharedPreferences().getString(getKey(), ""+defaultValue));
- number.setFilters(new InputFilter[]{(source, start, end, dest, dstart, dend) -> {
- try {
- String newVal = dest.toString().substring(0, dstart) + dest.toString().substring(dend);
- newVal = newVal.substring(0, dstart) + source.toString() + newVal.substring(dstart);
- int input = Integer.parseInt(newVal);
- if (input >= minValue && input <= maxValue) {
- return null;
- }
- } catch (NumberFormatException nfe) {
- nfe.printStackTrace();
- }
- return "";
- }});
-
- AlertDialog dialog = new MaterialAlertDialogBuilder(context)
- .setTitle(getTitle())
- .setView(view)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
- try {
- String numberString = number.getText().toString();
- int value = Integer.parseInt(numberString);
-
- if (value < minValue || value > maxValue) {
- return;
- }
-
- getSharedPreferences().edit().putString(getKey(), "" + value).apply();
-
- if (getOnPreferenceChangeListener() != null) {
- getOnPreferenceChangeListener().onPreferenceChange(this, value);
- }
- } catch (NumberFormatException e) {
- // Do not set value
- }
- })
- .create();
- dialog.show();
- dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EpisodesSurpriseSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EpisodesSurpriseSection.java
index 7e2c855e9..298bd05c5 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EpisodesSurpriseSection.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EpisodesSurpriseSection.java
@@ -13,11 +13,10 @@ import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.HorizontalItemListAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
@@ -32,6 +31,7 @@ import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -41,7 +41,7 @@ public class EpisodesSurpriseSection extends HomeSection {
private static int seed = 0;
private HorizontalItemListAdapter listAdapter;
private Disposable disposable;
- private List<FeedItem> episodes;
+ private List<FeedItem> episodes = new ArrayList<>();
@Nullable
@Override
@@ -103,9 +103,6 @@ public class EpisodesSurpriseSection extends HomeSection {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if (episodes == null) {
- return;
- }
for (int i = 0, size = event.items.size(); i < size; i++) {
FeedItem item = event.items.get(i);
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
@@ -118,15 +115,11 @@ public class EpisodesSurpriseSection extends HomeSection {
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with DownloadEvent");
- DownloaderUpdate update = event.update;
- if (listAdapter != null && update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
- if (pos >= 0) {
- listAdapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(episodes, downloadUrl);
+ if (pos >= 0) {
+ listAdapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/InboxSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/InboxSection.java
index 8d343a16a..fe9d15dc1 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/InboxSection.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/InboxSection.java
@@ -14,11 +14,10 @@ import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
@@ -35,6 +34,7 @@ import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -42,7 +42,7 @@ public class InboxSection extends HomeSection {
public static final String TAG = "InboxSection";
private static final int NUM_EPISODES = 2;
private EpisodeItemListAdapter adapter;
- private List<FeedItem> items;
+ private List<FeedItem> items = new ArrayList<>();
private Disposable disposable;
@Nullable
@@ -97,15 +97,11 @@ public class InboxSection extends HomeSection {
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with DownloadEvent");
- DownloaderUpdate update = event.update;
- if (adapter != null && update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(items, mediaId);
- if (pos >= 0) {
- adapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(items, downloadUrl);
+ if (pos >= 0) {
+ adapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/QueueSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/QueueSection.java
index 070d56ed6..33335f2bb 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/QueueSection.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/QueueSection.java
@@ -13,11 +13,10 @@ import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.HorizontalItemListAdapter;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.event.QueueEvent;
@@ -33,6 +32,7 @@ import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import java.util.ArrayList;
import java.util.List;
public class QueueSection extends HomeSection {
@@ -40,7 +40,7 @@ public class QueueSection extends HomeSection {
private static final int NUM_EPISODES = 8;
private HorizontalItemListAdapter listAdapter;
private Disposable disposable;
- private List<FeedItem> queue;
+ private List<FeedItem> queue = new ArrayList<>();
@Nullable
@Override
@@ -102,15 +102,11 @@ public class QueueSection extends HomeSection {
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with DownloadEvent");
- DownloaderUpdate update = event.update;
- if (listAdapter != null && update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(queue, mediaId);
- if (pos >= 0) {
- listAdapter.notifyItemChangedCompat(pos);
- }
+ public void onEventMainThread(EpisodeDownloadEvent event) {
+ for (String downloadUrl : event.getUrls()) {
+ int pos = FeedItemUtil.indexOfItemWithDownloadUrl(queue, downloadUrl);
+ if (pos >= 0) {
+ listAdapter.notifyItemChangedCompat(pos);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
index fd3ec9299..03df844b1 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
@@ -21,8 +21,6 @@ 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.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.util.PlaybackStatus;
import de.danoeh.antennapod.core.util.download.MediaSizeLoader;
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
@@ -31,6 +29,7 @@ import de.danoeh.antennapod.model.feed.FeedItem;
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.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.NetworkUtils;
@@ -117,6 +116,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
bind(item.getMedia());
} else {
secondaryActionProgress.setPercentage(0, item);
+ secondaryActionProgress.setIndeterminate(false);
isVideo.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
duration.setVisibility(View.GONE);
@@ -145,14 +145,17 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
itemView.setBackgroundResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.selectableItemBackground));
}
- if (DownloadService.isDownloadingFile(media.getDownload_url())) {
- final DownloadRequest downloadRequest = DownloadService.findRequest(media.getDownload_url());
- float percent = 0.01f * downloadRequest.getProgressPercent();
+ if (DownloadServiceInterface.get().isDownloadingEpisode(media.getDownload_url())) {
+ float percent = 0.01f * DownloadServiceInterface.get().getProgress(media.getDownload_url());
secondaryActionProgress.setPercentage(Math.max(percent, 0.01f), item);
+ secondaryActionProgress.setIndeterminate(
+ DownloadServiceInterface.get().isEpisodeQueued(media.getDownload_url()));
} else if (media.isDownloaded()) {
secondaryActionProgress.setPercentage(1, item); // Do not animate 100% -> 0%
+ secondaryActionProgress.setIndeterminate(false);
} else {
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
+ secondaryActionProgress.setIndeterminate(false);
}
duration.setText(Converter.getDurationStringLong(media.getDuration()));
@@ -210,6 +213,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
pubDate.setText("████");
duration.setText("████");
secondaryActionProgress.setPercentage(0, null);
+ secondaryActionProgress.setIndeterminate(false);
progressBar.setVisibility(View.GONE);
position.setVisibility(View.GONE);
dragHandle.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/HorizontalItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/HorizontalItemViewHolder.java
index 05240b371..f809de175 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/HorizontalItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/HorizontalItemViewHolder.java
@@ -14,13 +14,12 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.util.DateFormatter;
import de.danoeh.antennapod.core.util.PlaybackStatus;
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.ui.common.CircularProgressBar;
import de.danoeh.antennapod.ui.common.SquareImageView;
import de.danoeh.antennapod.ui.common.ThemeUtils;
@@ -85,14 +84,17 @@ public class HorizontalItemViewHolder extends RecyclerView.ViewHolder {
setProgressBar(false, 0);
}
- if (DownloadService.isDownloadingFile(media.getDownload_url())) {
- final DownloadRequest downloadRequest = DownloadService.findRequest(media.getDownload_url());
- float percent = 0.01f * downloadRequest.getProgressPercent();
+ if (DownloadServiceInterface.get().isDownloadingEpisode(media.getDownload_url())) {
+ float percent = 0.01f * DownloadServiceInterface.get().getProgress(media.getDownload_url());
circularProgressBar.setPercentage(Math.max(percent, 0.01f), item);
+ circularProgressBar.setIndeterminate(
+ DownloadServiceInterface.get().isEpisodeQueued(media.getDownload_url()));
} else if (media.isDownloaded()) {
circularProgressBar.setPercentage(1, item); // Do not animate 100% -> 0%
+ circularProgressBar.setIndeterminate(false);
} else {
circularProgressBar.setPercentage(0, item); // Animate X% -> 0%
+ circularProgressBar.setIndeterminate(false);
}
}
}
@@ -107,6 +109,7 @@ public class HorizontalItemViewHolder extends RecyclerView.ViewHolder {
date.setText("███");
secondaryActionIcon.setImageDrawable(null);
circularProgressBar.setPercentage(0, null);
+ circularProgressBar.setIndeterminate(false);
setProgressBar(true, 50);
}
diff --git a/app/src/main/res/layout/numberpicker.xml b/app/src/main/res/layout/numberpicker.xml
deleted file mode 100644
index d493f2e6c..000000000
--- a/app/src/main/res/layout/numberpicker.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="16dp">
-
- <EditText
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:ems="10"
- android:selectAllOnFocus="true"
- android:id="@+id/number" />
-
-</LinearLayout>
diff --git a/app/src/main/res/xml/preferences_downloads.xml b/app/src/main/res/xml/preferences_downloads.xml
index 865748d20..18fc7df11 100644
--- a/app/src/main/res/xml/preferences_downloads.xml
+++ b/app/src/main/res/xml/preferences_downloads.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:numberpicker="http://schemas.android.com/apk/de.danoeh.antennapod"
xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch">
<Preference
@@ -56,12 +55,6 @@
android:key="prefMobileUpdateTypes"
android:summary="@string/pref_mobileUpdate_sum"
android:title="@string/pref_mobileUpdate_title"/>
- <de.danoeh.antennapod.preferences.NumberPickerPreference
- android:defaultValue="4"
- numberpicker:minValue="1"
- numberpicker:maxValue="50"
- android:key="prefParallelDownloads"
- android:title="@string/pref_parallel_downloads_title"/>
<Preference
android:key="prefProxy"
android:summary="@string/pref_proxy_sum"
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index 6f5508f27..e186a856f 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -16,10 +16,6 @@
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true">
- <service
- android:name=".service.download.DownloadService"
- android:enabled="true" />
-
<service android:name=".service.playback.PlaybackService"
android:label="@string/app_name"
android:enabled="true"
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
deleted file mode 100644
index efd53ab9d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import androidx.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.danoeh.antennapod.core.service.download.Downloader;
-
-public class DownloadEvent {
-
- public final DownloaderUpdate update;
-
- private DownloadEvent(DownloaderUpdate downloader) {
- this.update = downloader;
- }
-
- public static DownloadEvent refresh(List<Downloader> list) {
- list = new ArrayList<>(list);
- DownloaderUpdate update = new DownloaderUpdate(list);
- return new DownloadEvent(update);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "DownloadEvent{" +
- "update=" + update +
- '}';
- }
-
- public boolean hasChangedFeedUpdateStatus(boolean oldStatus) {
- return oldStatus != update.feedIds.length > 0;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
deleted file mode 100644
index 1cab7e0f0..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import androidx.annotation.NonNull;
-
-import java.util.Arrays;
-import java.util.List;
-
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.core.util.LongList;
-
-public class DownloaderUpdate {
-
- /* Downloaders that are currently running */
- @NonNull
- public final List<Downloader> downloaders;
-
- /**
- * IDs of feeds that are currently being downloaded
- * Often used to show some progress wheel in the action bar
- */
- public final long[] feedIds;
-
- /**
- * IDs of feed media that are currently being downloaded
- * Can be used to show and update download progress bars
- */
- public final long[] mediaIds;
-
- DownloaderUpdate(@NonNull List<Downloader> downloaders) {
- this.downloaders = downloaders;
- LongList feedIds1 = new LongList();
- LongList mediaIds1 = new LongList();
- for(Downloader d1 : downloaders) {
- int type = d1.getDownloadRequest().getFeedfileType();
- long id = d1.getDownloadRequest().getFeedfileId();
- if(type == Feed.FEEDFILETYPE_FEED) {
- feedIds1.add(id);
- } else if(type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- mediaIds1.add(id);
- }
- }
-
- this.feedIds = feedIds1.toArray();
- this.mediaIds = mediaIds1.toArray();
- }
-
- @NonNull
- @Override
- public String toString() {
- return "DownloaderUpdate{" +
- "downloaders=" + downloaders +
- ", feedIds=" + Arrays.toString(feedIds) +
- ", mediaIds=" + Arrays.toString(mediaIds) +
- '}';
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
index d4d948b2a..03881ee4f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -28,7 +28,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.documentfile.provider.DocumentFile;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.util.FastDocumentFile;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -246,8 +246,8 @@ public class LocalFeedUpdater {
}
private static void reportError(Feed feed, String reasonDetailed) {
- DownloadStatus status = new DownloadStatus(feed, feed.getTitle(),
- DownloadError.ERROR_IO_ERROR, false, reasonDetailed, true);
+ DownloadResult status = new DownloadResult(feed, feed.getTitle(),
+ DownloadError.ERROR_IO_ERROR, false, reasonDetailed);
DBWriter.addDownloadStatus(status);
DBWriter.setFeedLastUpdateFailed(feed.getId(), true);
}
@@ -256,8 +256,7 @@ public class LocalFeedUpdater {
* Reports a successful download status.
*/
private static void reportSuccess(Feed feed) {
- DownloadStatus status = new DownloadStatus(feed, feed.getTitle(),
- DownloadError.SUCCESS, true, null, true);
+ DownloadResult status = new DownloadResult(feed, feed.getTitle(), DownloadError.SUCCESS, true, null);
DBWriter.addDownloadStatus(status);
DBWriter.setFeedLastUpdateFailed(feed.getId(), false);
}
@@ -266,21 +265,21 @@ public class LocalFeedUpdater {
* Answers if reporting success is needed for the given feed.
*/
private static boolean mustReportDownloadSuccessful(Feed feed) {
- List<DownloadStatus> downloadStatuses = DBReader.getFeedDownloadLog(feed.getId());
+ List<DownloadResult> downloadResults = DBReader.getFeedDownloadLog(feed.getId());
- if (downloadStatuses.isEmpty()) {
+ if (downloadResults.isEmpty()) {
// report success if never reported before
return true;
}
- Collections.sort(downloadStatuses, (downloadStatus1, downloadStatus2) ->
+ Collections.sort(downloadResults, (downloadStatus1, downloadStatus2) ->
downloadStatus1.getCompletionDate().compareTo(downloadStatus2.getCompletionDate()));
- DownloadStatus lastDownloadStatus = downloadStatuses.get(downloadStatuses.size() - 1);
+ DownloadResult lastDownloadResult = downloadResults.get(downloadResults.size() - 1);
// report success if the last update was not successful
// (avoid logging success again if the last update was ok)
- return !lastDownloadStatus.isSuccessful();
+ return !lastDownloadResult.isSuccessful();
}
@FunctionalInterface
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
index 8d9f046e2..5f59f0c41 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
@@ -20,12 +20,13 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.NewEpisodesNotification;
import de.danoeh.antennapod.core.service.download.handler.FeedSyncTask;
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.util.NetworkUtils;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
@@ -80,6 +81,7 @@ public class FeedUpdateWorker extends Worker {
refreshFeeds(toUpdate, true);
}
notificationManager.cancel(R.id.notification_updating_feeds);
+ DBTasks.autodownloadUndownloadedItems(getApplicationContext());
return Result.success();
}
@@ -115,8 +117,8 @@ public class FeedUpdateWorker extends Worker {
}
} catch (Exception e) {
DBWriter.setFeedLastUpdateFailed(feed.getId(), true);
- DownloadStatus status = new DownloadStatus(feed, feed.getTitle(),
- DownloadError.ERROR_IO_ERROR, false, e.getMessage(), true);
+ DownloadResult status = new DownloadResult(feed, feed.getTitle(),
+ DownloadError.ERROR_IO_ERROR, false, e.getMessage());
DBWriter.addDownloadStatus(status);
}
toUpdate.remove(0);
@@ -144,7 +146,7 @@ public class FeedUpdateWorker extends Worker {
downloader.call();
if (!downloader.getResult().isSuccessful()) {
- if (downloader.getResult().isCancelled()) {
+ if (downloader.cancelled) {
return;
}
DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
@@ -165,7 +167,7 @@ public class FeedUpdateWorker extends Worker {
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());
+ List<DownloadResult> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
if (log.size() > 0 && !log.get(0).isSuccessful()) {
DBWriter.addDownloadStatus(feedSyncTask.getDownloadStatus());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java
index d6a4b8378..5ca904ff6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java
@@ -21,9 +21,6 @@ public class DownloadRequestCreator {
public static DownloadRequest.Builder create(Feed feed) {
File dest = new File(getFeedfilePath(), getFeedfileName(feed));
- if (!isFilenameAvailable(dest.toString()) && !feed.isLocalFeed()) {
- dest = findUnusedFile(dest);
- }
Log.d(TAG, "Requesting download of url " + feed.getDownload_url());
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
@@ -45,7 +42,7 @@ public class DownloadRequestCreator {
dest = new File(getMediafilePath(media), getMediafilename(media));
}
- if (!isFilenameAvailable(dest.toString()) || (!partiallyDownloadedFileExists && dest.exists())) {
+ if (dest.exists() && !partiallyDownloadedFileExists) {
dest = findUnusedFile(dest);
}
Log.d(TAG, "Requesting download of url " + media.getDownload_url());
@@ -72,7 +69,7 @@ public class DownloadRequestCreator {
+ FilenameUtils.getExtension(dest.getName());
Log.d(TAG, "Testing filename " + newName);
newDest = new File(dest.getParent(), newName);
- if (!newDest.exists() && isFilenameAvailable(newDest.toString())) {
+ if (!newDest.exists()) {
Log.d(TAG, "File doesn't exist yet. Using " + newName);
break;
}
@@ -80,19 +77,6 @@ public class DownloadRequestCreator {
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() + "/";
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
deleted file mode 100644
index 9c238137e..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ /dev/null
@@ -1,536 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.IBinder;
-import android.text.TextUtils;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.app.ServiceCompat;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.service.download.handler.FailedDownloadHandler;
-import de.danoeh.antennapod.core.service.download.handler.MediaDownloadedHandler;
-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.EpisodeCleanupAlgorithmFactory;
-import de.danoeh.antennapod.core.util.download.ConnectionStateMonitor;
-import de.danoeh.antennapod.event.FeedItemEvent;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.download.DownloadStatus;
-import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import org.apache.commons.io.FileUtils;
-import org.greenrobot.eventbus.EventBus;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutionException;
-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;
-
-/**
- * Manages the download of feedfiles in the app. Downloads can be enqueued via the startService intent.
- * The argument of the intent is an instance of DownloadRequest in the EXTRA_REQUESTS field of
- * the intent.
- * After the downloads have finished, the downloaded object will be passed on to a specific handler, depending on the
- * type of the feedfile.
- */
-public class DownloadService extends Service {
- private static final String TAG = "DownloadService";
- private static final int SCHED_EX_POOL_SIZE = 1;
- public static final String ACTION_CANCEL_DOWNLOAD = "action.de.danoeh.antennapod.core.service.cancelDownload";
- public static final String ACTION_CANCEL_ALL_DOWNLOADS = "action.de.danoeh.antennapod.core.service.cancelAll";
- public static final String EXTRA_DOWNLOAD_URL = "downloadUrl";
- public static final String EXTRA_REQUESTS = "downloadRequests";
- public static final String EXTRA_INITIATED_BY_USER = "initiatedByUser";
- public static final String EXTRA_CLEANUP_MEDIA = "cleanupMedia";
-
- public static boolean isRunning = false;
-
- // 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 final List<DownloadRequest> failedRequestsForReport = new ArrayList<>();
- private DownloadServiceNotification notificationManager;
- private NotificationUpdater notificationUpdater;
- private ScheduledFuture<?> notificationUpdaterFuture;
- private ScheduledFuture<?> downloadPostFuture;
- private final ScheduledThreadPoolExecutor notificationUpdateExecutor;
- private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
- private ConnectionStateMonitor connectionMonitor;
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- public DownloadService() {
-
- downloadEnqueueExecutor = Executors.newSingleThreadExecutor(r -> {
- Thread t = new Thread(r, "EnqueueThread");
- t.setPriority(Thread.MIN_PRIORITY);
- return t;
- });
- Log.d(TAG, "parallel downloads: " + UserPreferences.getParallelDownloads());
- 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, "NotificationUpdateExecutor");
- t.setPriority(Thread.MIN_PRIORITY);
- return t;
- }, (r, executor) -> Log.w(TAG, "SchedEx rejected submission of new task")
- );
- }
-
- @Override
- public void onCreate() {
- Log.d(TAG, "Service started");
- isRunning = true;
- notificationManager = new DownloadServiceNotification(this);
-
- IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
- cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_ALL_DOWNLOADS);
- cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_DOWNLOAD);
- registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter);
-
- connectionMonitor = new ConnectionStateMonitor();
- connectionMonitor.enable(getApplicationContext());
- }
-
- 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 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);
- NotificationManagerCompat.from(this).cancel(R.id.notification_download_report);
- NotificationManagerCompat.from(this).cancel(R.id.notification_auto_download_report);
- setupNotificationUpdaterIfNecessary();
- downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
- } else if (downloads.size() == 0) {
- shutdown();
- } else {
- Log.d(TAG, "onStartCommand: Unknown intent");
- }
- return Service.START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- Log.d(TAG, "Service shutting down");
- isRunning = false;
-
- boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
- if (UserPreferences.showDownloadReport() || showAutoDownloadReport) {
- notificationManager.updateReport(reportQueue, showAutoDownloadReport, failedRequestsForReport);
- reportQueue.clear();
- failedRequestsForReport.clear();
- }
-
- unregisterReceiver(cancelDownloadReceiver);
- connectionMonitor.disable(getApplicationContext());
-
- EventBus.getDefault().postSticky(DownloadEvent.refresh(Collections.emptyList()));
- cancelNotificationUpdater();
- downloadEnqueueExecutor.shutdownNow();
- downloadHandleExecutor.shutdownNow();
- notificationUpdateExecutor.shutdownNow();
- if (downloadPostFuture != null) {
- downloadPostFuture.cancel(true);
- }
- downloads.clear();
-
- // start auto download in case anything new has shown up
- DBTasks.autodownloadUndownloadedItems(getApplicationContext());
- }
-
- /**
- * This method MUST NOT, in any case, throw an exception.
- * Otherwise, it hangs up the refresh thread pool.
- */
- 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();
- DownloadStatus status = downloader.getResult();
- final int type = status.getFeedfileType();
-
- if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- Log.d(TAG, "Handling completed FeedMedia Download");
- MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
- handler.run();
- saveDownloadStatus(handler.getUpdatedStatus(), downloader.getDownloadRequest());
- }
- }
-
- private void handleFailedDownload(Downloader downloader) {
- DownloadStatus status = downloader.getResult();
- final int type = status.getFeedfileType();
-
- if (!status.isCancelled()) {
- if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
- notificationManager.postAuthenticationNotification(downloader.getDownloadRequest());
- } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && Integer.parseInt(status.getReasonDetailed()) == 416) {
-
- Log.d(TAG, "Requested invalid range, restarting download from the beginning");
- FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
- DownloadServiceInterface.get().download(this, false, downloader.getDownloadRequest());
- } else {
- Log.e(TAG, "Download failed");
- saveDownloadStatus(status, downloader.getDownloadRequest());
- new FailedDownloadHandler(downloader.getDownloadRequest()).run();
-
- if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedItem item = getFeedItemFromId(status.getFeedfileId());
- if (item == null) {
- return;
- }
- item.increaseFailedAutoDownloadAttempts(System.currentTimeMillis());
- DBWriter.setFeedItem(item);
- // to make lists reload the failed item, we fake an item update
- EventBus.getDefault().post(FeedItemEvent.updated(item));
- }
- }
- } else {
- // if FeedMedia download has been canceled, fake FeedItem update
- // so that lists reload that it
- if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedItem item = getFeedItemFromId(status.getFeedfileId());
- if (item == null) {
- return;
- }
- EventBus.getDefault().post(FeedItemEvent.updated(item));
- }
- }
- }
-
- private final BroadcastReceiver cancelDownloadReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "cancelDownloadReceiver: " + intent.getAction());
- if (!isRunning) {
- return;
- }
- 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");
- }
- downloadEnqueueExecutor.execute(() -> {
- doCancel(url);
- postDownloaders();
- stopServiceIfEverythingDone();
- });
- } else if (TextUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
- downloadEnqueueExecutor.execute(() -> {
- for (Downloader d : downloads) {
- d.cancel();
- }
- Log.d(TAG, "Cancelled all downloads");
- postDownloaders();
- 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");
- }
- Log.d(TAG, "Received enqueue request. #requests=" + requests.size());
-
- if (intent.getBooleanExtra(EXTRA_CLEANUP_MEDIA, false)) {
- EpisodeCleanupAlgorithmFactory.build().makeRoomForEpisodes(getApplicationContext(), requests.size());
- }
-
- for (DownloadRequest request : requests) {
- 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 void enqueueFeedItems(@NonNull List<DownloadRequest> requests) {
- List<FeedItem> feedItems = new ArrayList<>();
- for (DownloadRequest request : requests) {
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- long mediaId = request.getFeedfileId();
- FeedMedia media = DBReader.getFeedMedia(mediaId);
- if (media == null) {
- Log.w(TAG, "enqueueFeedItems() : FeedFile Id " + mediaId + " is not found. ignore it.");
- continue;
- }
- feedItems.add(media.getItem());
- }
- }
- List<FeedItem> actuallyEnqueued = Collections.emptyList();
- try {
- actuallyEnqueued = DBTasks.enqueueFeedItemsToDownload(getApplicationContext(), feedItems);
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
-
- for (DownloadRequest request : requests) {
- if (request.getFeedfileType() != FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- continue;
- }
- final long mediaId = request.getFeedfileId();
- for (FeedItem item : actuallyEnqueued) {
- if (item.getMedia() != null && item.getMedia().getId() == mediaId) {
- request.setMediaEnqueued(true);
- }
- }
- }
- }
-
- private void addNewRequest(@NonNull DownloadRequest request) {
- if (isDownloadingFile(request.getSource())) {
- Log.d(TAG, "Skipped enqueueing request. Already running.");
- return;
- } else if (downloadHandleExecutor.isShutdown()) {
- Log.d(TAG, "Skipped enqueueing request. Service is already shutting down.");
- return;
- }
- Log.d(TAG, "Add new request: " + request.getSource());
- writeFileUrl(request);
- Downloader downloader = downloaderFactory.create(request);
- if (downloader != null) {
- downloads.add(downloader);
- downloadHandleExecutor.submit(() -> performDownload(downloader));
- }
- }
-
- @VisibleForTesting
- public static DownloaderFactory getDownloaderFactory() {
- return downloaderFactory;
- }
-
- // public scope rather than package private,
- // because androidTest put classes in the non-standard de.test.antennapod hierarchy
- @VisibleForTesting
- public static void setDownloaderFactory(DownloaderFactory downloaderFactory) {
- DownloadService.downloaderFactory = downloaderFactory;
- }
-
- /**
- * Adds a new DownloadStatus object to the list of completed downloads and
- * saves it in the database
- *
- * @param status the download that is going to be saved
- */
- private void saveDownloadStatus(@NonNull DownloadStatus status, @NonNull DownloadRequest request) {
- reportQueue.add(status);
- if (!status.isSuccessful() && !status.isCancelled()) {
- failedRequestsForReport.add(request);
- }
- DBWriter.addDownloadStatus(status);
- }
-
- /**
- * Check if there's something else to download, otherwise stop.
- */
- private void stopServiceIfEverythingDone() {
- Log.d(TAG, downloads.size() + " downloads left");
- if (downloads.size() <= 0) {
- Log.d(TAG, "Attempting shutdown");
- shutdown();
- }
- }
-
- @Nullable
- private FeedItem getFeedItemFromId(long id) {
- FeedMedia media = DBReader.getFeedMedia(id);
- if (media != null) {
- return media.getItem();
- } else {
- return null;
- }
- }
-
- /**
- * Creates the destination file and writes FeedMedia File_url directly after starting download
- * to make it possible to resume download after the service was killed by the system.
- */
- private void writeFileUrl(DownloadRequest request) {
- if (request.getFeedfileType() != FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- return;
- }
-
- File dest = new File(request.getDestination());
- if (!dest.exists()) {
- try {
- dest.createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Unable to create file");
- }
- }
-
- if (dest.exists()) {
- Log.d(TAG, "Writing file url");
- FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
- if (media == null) {
- Log.d(TAG, "No media");
- return;
- }
- media.setFile_url(request.getDestination());
- try {
- DBWriter.setFeedMedia(media).get();
- } catch (InterruptedException e) {
- Log.e(TAG, "writeFileUrl was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in writeFileUrl: " + e.getMessage());
- }
- }
- }
-
- /**
- * Schedules the notification updater task if it hasn't been scheduled yet.
- */
- private void setupNotificationUpdaterIfNecessary() {
- if (notificationUpdater == null) {
- Log.d(TAG, "Setting up notification updater");
- notificationUpdater = new NotificationUpdater();
- notificationUpdaterFuture = notificationUpdateExecutor
- .scheduleAtFixedRate(notificationUpdater, 1, 1, TimeUnit.SECONDS);
- }
- }
-
- private void cancelNotificationUpdater() {
- boolean result = false;
- if (notificationUpdaterFuture != null) {
- result = notificationUpdaterFuture.cancel(true);
- }
- notificationUpdater = null;
- notificationUpdaterFuture = null;
- Log.d(TAG, "NotificationUpdater cancelled. Result: " + result);
- }
-
- private class NotificationUpdater implements Runnable {
- public void run() {
- Notification n = notificationManager.updateNotifications(downloads);
- if (n != null) {
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(R.id.notification_downloading, n);
- }
- }
- }
-
- private void postDownloaders() {
- new PostDownloaderTask(downloads).run();
-
- if (downloadPostFuture == null) {
- downloadPostFuture = notificationUpdateExecutor.scheduleAtFixedRate(
- new PostDownloaderTask(downloads), 1, 1, TimeUnit.SECONDS);
- }
- }
-
- private void shutdown() {
- // If the service was run for a very short time, the system may delay closing
- // the notification. Set the notification text now so that a misleading message
- // is not left on the notification.
- if (notificationUpdater != null) {
- notificationUpdater.run();
- }
- cancelNotificationUpdater();
- ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE);
- stopSelf();
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java
index 976d8255f..87cbeda84 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java
@@ -1,74 +1,72 @@
package de.danoeh.antennapod.core.service.download;
import android.content.Context;
-import android.content.Intent;
-import androidx.core.content.ContextCompat;
-import com.google.android.exoplayer2.util.Log;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
+import androidx.work.Constraints;
+import androidx.work.Data;
+import androidx.work.ExistingWorkPolicy;
+import androidx.work.NetworkType;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.OutOfQuotaPolicy;
+import androidx.work.WorkManager;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import java.util.ArrayList;
-
-import static de.danoeh.antennapod.core.service.download.DownloadService.isDownloadingFile;
+import java.util.concurrent.TimeUnit;
public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
- private static final String TAG = "DownloadServiceInterface";
-
- public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
- Intent intent = makeDownloadIntent(context, cleanupMedia, requests);
- if (intent != null) {
- ContextCompat.startForegroundService(context, intent);
+ public void downloadNow(Context context, FeedItem item, boolean ignoreConstraints) {
+ OneTimeWorkRequest.Builder workRequest = getRequest(context, item);
+ workRequest.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
+ if (ignoreConstraints) {
+ workRequest.setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build());
+ } else {
+ workRequest.setConstraints(getConstraints());
}
+ WorkManager.getInstance(context).enqueueUniqueWork(item.getMedia().getDownload_url(),
+ ExistingWorkPolicy.KEEP, workRequest.build());
}
- public Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests) {
- ArrayList<DownloadRequest> requestsToSend = new ArrayList<>();
- for (DownloadRequest request : requests) {
- if (!isDownloadingFile(request.getSource())) {
- requestsToSend.add(request);
- }
- }
- if (requestsToSend.isEmpty()) {
- return null;
- } else if (requestsToSend.size() > 100) {
- if (BuildConfig.DEBUG) {
- throw new IllegalArgumentException("Android silently drops intent payloads that are too large");
- } else {
- Log.d(TAG, "Too many download requests. Dropping some to avoid Android dropping all.");
- requestsToSend = new ArrayList<>(requestsToSend.subList(0, 100));
- }
- }
+ public void download(Context context, FeedItem item) {
+ OneTimeWorkRequest.Builder workRequest = getRequest(context, item);
+ workRequest.setConstraints(getConstraints());
+ WorkManager.getInstance(context).enqueueUniqueWork(item.getMedia().getDownload_url(),
+ ExistingWorkPolicy.KEEP, workRequest.build());
+ }
- Intent launchIntent = new Intent(context, DownloadService.class);
- launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend);
- if (cleanupMedia) {
- launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true);
+ private static OneTimeWorkRequest.Builder getRequest(Context context, FeedItem item) {
+ OneTimeWorkRequest.Builder workRequest = new OneTimeWorkRequest.Builder(EpisodeDownloadWorker.class)
+ .setInitialDelay(0L, TimeUnit.MILLISECONDS)
+ .addTag(DownloadServiceInterface.WORK_TAG)
+ .addTag(DownloadServiceInterface.WORK_TAG_EPISODE_URL + item.getMedia().getDownload_url());
+ Data.Builder builder = new Data.Builder();
+ builder.putLong(WORK_DATA_MEDIA_ID, item.getMedia().getId());
+ if (!item.isTagged(FeedItem.TAG_QUEUE) && UserPreferences.enqueueDownloadedEpisodes()) {
+ DBWriter.addQueueItem(context, false, item.getId());
+ builder.putBoolean(WORK_DATA_WAS_QUEUED, true);
}
- return launchIntent;
+ workRequest.setInputData(builder.build());
+ return workRequest;
}
- public void refreshAllFeeds(Context context, boolean initiatedByUser) {
- FeedUpdateManager.runOnce(context);
+ private static Constraints getConstraints() {
+ Constraints.Builder constraints = new Constraints.Builder();
+ if (UserPreferences.isAllowMobileEpisodeDownload()) {
+ constraints.setRequiredNetworkType(NetworkType.CONNECTED);
+ } else {
+ constraints.setRequiredNetworkType(NetworkType.UNMETERED);
+ }
+ return constraints.build();
}
+ @Override
public void cancel(Context context, String url) {
- if (!DownloadService.isRunning) {
- return;
- }
- Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD);
- cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, url);
- cancelIntent.setPackage(context.getPackageName());
- context.sendBroadcast(cancelIntent);
+ WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG_EPISODE_URL + url);
}
+ @Override
public void cancelAll(Context context) {
- if (!DownloadService.isRunning) {
- return;
- }
- Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_ALL_DOWNLOADS);
- cancelIntent.setPackage(context.getPackageName());
- context.sendBroadcast(cancelIntent);
+ WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
deleted file mode 100644
index b9846c06c..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
+++ /dev/null
@@ -1,306 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.util.Log;
-import androidx.core.app.NotificationCompat;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.util.DownloadErrorLabel;
-import de.danoeh.antennapod.model.download.DownloadStatus;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.core.util.gui.NotificationUtils;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.ui.appstartintent.DownloadAuthenticationActivityStarter;
-import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
-
-import java.util.List;
-
-public class DownloadServiceNotification {
- private static final String TAG = "DownloadSvcNotification";
-
- private final Context context;
- private NotificationCompat.Builder notificationCompatBuilder;
-
- public DownloadServiceNotification(Context context) {
- this.context = context;
- setupNotificationBuilders();
- }
-
- private void setupNotificationBuilders() {
- notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
- .setOngoing(false)
- .setWhen(0)
- .setOnlyAlertOnce(true)
- .setShowWhen(false)
- .setContentIntent(getNotificationContentIntent(context))
- .setSmallIcon(R.drawable.ic_notification_sync)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- Log.d(TAG, "Notification set up");
- }
-
- /**
- * Updates the contents of the service's notifications. Should be called
- * after setupNotificationBuilders.
- */
- public Notification updateNotifications(List<Downloader> downloads) {
- if (notificationCompatBuilder == null) {
- return null;
- }
-
- String contentTitle;
- if (typeIsOnly(downloads, Feed.FEEDFILETYPE_FEED)) {
- contentTitle = context.getString(R.string.download_notification_title_feeds);
- } else if (typeIsOnly(downloads, FeedMedia.FEEDFILETYPE_FEEDMEDIA)) {
- contentTitle = context.getString(R.string.download_notification_title_episodes);
- } else {
- contentTitle = context.getString(R.string.download_notification_title);
- }
-
- int numDownloads = getNumberOfRunningDownloads(downloads);
- String contentText = context.getString(R.string.completing);
- String bigText = context.getString(R.string.completing);
- notificationCompatBuilder.clearActions();
- if (numDownloads > 0) {
- bigText = compileNotificationString(downloads);
- if (numDownloads == 1) {
- contentText = bigText;
- } else {
- contentText = context.getResources().getQuantityString(R.plurals.downloads_left,
- numDownloads, numDownloads);
- }
-
- Intent cancelDownloadsIntent = new Intent(DownloadService.ACTION_CANCEL_ALL_DOWNLOADS);
- cancelDownloadsIntent.setPackage(context.getPackageName());
- PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(context,
- R.id.pending_intent_download_cancel_all, cancelDownloadsIntent, PendingIntent.FLAG_UPDATE_CURRENT
- | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- notificationCompatBuilder.addAction(new NotificationCompat.Action(
- R.drawable.ic_notification_cancel, context.getString(R.string.cancel_label), cancelPendingIntent));
- }
-
- notificationCompatBuilder.setContentTitle(contentTitle);
- notificationCompatBuilder.setContentText(contentText);
- notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
- return notificationCompatBuilder.build();
- }
-
- private int getNumberOfRunningDownloads(List<Downloader> downloads) {
- int running = 0;
- for (Downloader downloader : downloads) {
- if (!downloader.cancelled && !downloader.isFinished()) {
- running++;
- }
- }
- return running;
- }
-
- private boolean typeIsOnly(List<Downloader> downloads, int feedFileType) {
- for (Downloader downloader : downloads) {
- if (downloader.cancelled) {
- continue;
- }
- DownloadRequest request = downloader.getDownloadRequest();
- if (request.getFeedfileType() != feedFileType) {
- return false;
- }
- }
- return true;
- }
-
- private static String compileNotificationString(List<Downloader> downloads) {
- StringBuilder stringBuilder = new StringBuilder();
- for (int i = 0; i < downloads.size(); i++) {
- Downloader downloader = downloads.get(i);
- if (downloader.cancelled) {
- continue;
- }
- stringBuilder.append("• ");
- DownloadRequest request = downloader.getDownloadRequest();
- if (request.getTitle() != null) {
- stringBuilder.append(request.getTitle());
- } else {
- stringBuilder.append(request.getSource());
- }
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- stringBuilder.append(" (").append(request.getProgressPercent()).append("%)");
- } else if (request.getSource().startsWith(Feed.PREFIX_LOCAL_FOLDER)) {
- stringBuilder.append(" (").append(request.getSoFar())
- .append("/").append(request.getSize()).append(")");
- }
- if (i != downloads.size() - 1) {
- stringBuilder.append("\n");
- }
- }
- return stringBuilder.toString();
- }
-
- private static String createAutoDownloadNotificationContent(List<DownloadStatus> statuses) {
- int length = statuses.size();
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < length; i++) {
- sb.append("• ").append(statuses.get(i).getTitle());
- if (i != length - 1) {
- sb.append("\n");
- }
- }
-
- return sb.toString();
- }
-
- private String createFailedDownloadNotificationContent(List<DownloadStatus> statuses) {
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < statuses.size(); i++) {
- if (statuses.get(i) == null || statuses.get(i).isSuccessful()) {
- continue;
- }
- sb.append("• ").append(statuses.get(i).getTitle());
- if (statuses.get(i).getReason() != null) {
- sb.append(": ").append(context.getString(DownloadErrorLabel.from(statuses.get(i).getReason())));
- }
- if (i != statuses.size() - 1) {
- sb.append("\n");
- }
- }
-
- return sb.toString();
- }
-
- /**
- * Creates a notification at the end of the service lifecycle to notify the
- * user about the number of completed downloads. A report will only be
- * created if there is at least one failed download excluding images
- */
- public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport,
- List<DownloadRequest> failedRequests) {
- // check if report should be created
- boolean createReport = false;
- int failedDownloads = 0;
-
- // a download report is created if at least one download has failed
- // (excluding failed image downloads)
- for (DownloadStatus status : reportQueue) {
- if (status == null || status.isCancelled()) {
- continue;
- }
- if (status.isSuccessful()) {
- createReport |= showAutoDownloadReport && !status.isInitiatedByUser()
- && status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA;
- } else {
- failedDownloads++;
- createReport = true;
- }
- }
-
- if (!createReport) {
- Log.d(TAG, "No report is created");
- return;
- }
- Log.d(TAG, "Creating report");
- if (failedDownloads == 0) {
- createAutoDownloadReportNotification(reportQueue);
- } else {
- createDownloadFailedNotification(reportQueue, failedRequests);
- }
- Log.d(TAG, "Download report notification was posted");
- }
-
- private void createAutoDownloadReportNotification(List<DownloadStatus> reportQueue) {
- PendingIntent intent = getAutoDownloadReportNotificationContentIntent(context);
- String content = createAutoDownloadNotificationContent(reportQueue);
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
- NotificationUtils.CHANNEL_ID_AUTO_DOWNLOAD);
- builder.setTicker(context.getString(R.string.auto_download_report_title))
- .setContentTitle(context.getString(R.string.auto_download_report_title))
- .setContentText(content)
- .setStyle(new NotificationCompat.BigTextStyle().bigText(content))
- .setSmallIcon(R.drawable.ic_notification_new)
- .setContentIntent(intent)
- .setAutoCancel(true)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(R.id.notification_auto_download_report, builder.build());
- }
-
- private void createDownloadFailedNotification(List<DownloadStatus> reportQueue,
- List<DownloadRequest> failedRequests) {
- Intent retryIntent = DownloadServiceInterface.get().makeDownloadIntent(context,
- false, failedRequests.toArray(new DownloadRequest[0]));
- PendingIntent retryPendingIntent = null;
- if (retryIntent != null && Build.VERSION.SDK_INT >= 26) {
- retryPendingIntent = PendingIntent.getForegroundService(context, R.id.pending_intent_download_service_retry,
- retryIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- } else if (retryIntent != null) {
- retryPendingIntent = PendingIntent.getService(context,
- R.id.pending_intent_download_service_retry, retryIntent,
- PendingIntent.FLAG_UPDATE_CURRENT
- | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- }
- PendingIntent intent = getReportNotificationContentIntent(context);
- String content = createFailedDownloadNotificationContent(reportQueue);
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
- NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR);
- builder.setTicker(context.getString(R.string.download_report_title))
- .setContentTitle(context.getString(R.string.download_report_title))
- .setContentText(content)
- .setStyle(new NotificationCompat.BigTextStyle().bigText(content))
- .setSmallIcon(R.drawable.ic_notification_sync_error)
- .setContentIntent(intent)
- .setAutoCancel(true);
- if (retryPendingIntent != null) {
- builder.addAction(new NotificationCompat.Action(
- R.drawable.ic_notification_sync, context.getString(R.string.retry_label), retryPendingIntent));
- }
- builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(R.id.notification_download_report, builder.build());
- }
-
- public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
- final String resourceTitle = (downloadRequest.getTitle() != null) ?
- downloadRequest.getTitle() : downloadRequest.getSource();
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_USER_ACTION);
- builder.setTicker(context.getText(R.string.authentication_notification_title))
- .setContentTitle(context.getText(R.string.authentication_notification_title))
- .setContentText(context.getText(R.string.authentication_notification_msg))
- .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getText(R.string.authentication_notification_msg)
- + ": " + resourceTitle))
- .setSmallIcon(R.drawable.ic_notification_key)
- .setAutoCancel(true)
- .setContentIntent(new DownloadAuthenticationActivityStarter(
- context, downloadRequest.getFeedfileId(), downloadRequest).getPendingIntent());
- builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(downloadRequest.getSource().hashCode(), builder.build());
- }
-
- public PendingIntent getReportNotificationContentIntent(Context context) {
- Intent intent = new MainActivityStarter(context)
- .withFragmentLoaded("DownloadsFragment")
- .withFragmentArgs("show_logs", true)
- .getIntent();
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- }
-
- public PendingIntent getAutoDownloadReportNotificationContentIntent(Context context) {
- Intent intent = new MainActivityStarter(context).withFragmentLoaded("QueueFragment").getIntent();
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- }
-
- public PendingIntent getNotificationContentIntent(Context context) {
- Intent intent = new MainActivityStarter(context).withFragmentLoaded("DownloadsFragment").getIntent();
- return PendingIntent.getActivity(context,
- R.id.pending_intent_download_service_notification, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
index f7f5e8e9c..35247509d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
@@ -9,7 +9,7 @@ import java.util.concurrent.Callable;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
/**
@@ -25,15 +25,15 @@ public abstract class Downloader implements Callable<Downloader> {
@NonNull
final DownloadRequest request;
@NonNull
- final DownloadStatus result;
+ final DownloadResult result;
Downloader(@NonNull DownloadRequest request) {
super();
this.request = request;
this.request.setStatusMsg(R.string.download_pending);
this.cancelled = false;
- this.result = new DownloadStatus(0, request.getTitle(), request.getFeedfileId(), request.getFeedfileType(),
- false, cancelled, false, null, new Date(), null, request.isInitiatedByUser());
+ this.result = new DownloadResult(0, request.getTitle(), request.getFeedfileId(), request.getFeedfileType(),
+ false, null, new Date(), null);
}
protected abstract void download();
@@ -63,7 +63,7 @@ public abstract class Downloader implements Callable<Downloader> {
}
@NonNull
- public DownloadStatus getResult() {
+ public DownloadResult getResult() {
return result;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java
new file mode 100644
index 000000000..c428bc861
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java
@@ -0,0 +1,265 @@
+package de.danoeh.antennapod.core.service.download;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.work.Data;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+import de.danoeh.antennapod.core.ClientConfigurator;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.service.download.handler.MediaDownloadedHandler;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+import de.danoeh.antennapod.event.MessageEvent;
+import de.danoeh.antennapod.model.download.DownloadError;
+import de.danoeh.antennapod.model.download.DownloadResult;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
+import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
+import org.apache.commons.io.FileUtils;
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+public class EpisodeDownloadWorker extends Worker {
+ private static final String TAG = "EpisodeDownloadWorker";
+ private static final Map<String, Integer> notificationProgress = new HashMap<>();
+
+ private Downloader downloader = null;
+
+ public EpisodeDownloadWorker(@NonNull Context context, @NonNull WorkerParameters params) {
+ super(context, params);
+ }
+
+ @Override
+ @NonNull
+ public Result doWork() {
+ ClientConfigurator.initialize(getApplicationContext());
+ long mediaId = getInputData().getLong(DownloadServiceInterface.WORK_DATA_MEDIA_ID, 0);
+ FeedMedia media = DBReader.getFeedMedia(mediaId);
+ if (media == null) {
+ return Result.failure();
+ }
+
+ DownloadRequest request = DownloadRequestCreator.create(media).build();
+ Thread progressUpdaterThread = new Thread() {
+ @Override
+ public void run() {
+ while (!isInterrupted()) {
+ try {
+ Thread.sleep(1000);
+ notificationProgress.put(media.getEpisodeTitle(), request.getProgressPercent());
+ setProgressAsync(
+ new Data.Builder()
+ .putInt(DownloadServiceInterface.WORK_DATA_PROGRESS, request.getProgressPercent())
+ .build())
+ .get();
+ sendProgressNotification();
+ } catch (InterruptedException | ExecutionException e) {
+ return;
+ }
+ }
+ }
+ };
+ progressUpdaterThread.start();
+ final Result result = performDownload(media, request);
+ progressUpdaterThread.interrupt();
+ try {
+ progressUpdaterThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ notificationProgress.remove(media.getEpisodeTitle());
+ if (notificationProgress.isEmpty()) {
+ NotificationManager nm = (NotificationManager) getApplicationContext()
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.cancel(R.id.notification_downloading);
+ }
+ Log.d(TAG, "Worker for " + media.getDownload_url() + " returned.");
+ return result;
+ }
+
+ @Override
+ public void onStopped() {
+ super.onStopped();
+ if (downloader != null) {
+ downloader.cancel();
+ }
+ }
+
+ private Result performDownload(FeedMedia media, DownloadRequest request) {
+ File dest = new File(request.getDestination());
+ if (!dest.exists()) {
+ try {
+ dest.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to create file");
+ }
+ }
+
+ if (dest.exists()) {
+ media.setFile_url(request.getDestination());
+ try {
+ DBWriter.setFeedMedia(media).get();
+ } catch (Exception e) {
+ Log.e(TAG, "ExecutionException in writeFileUrl: " + e.getMessage());
+ }
+ }
+
+ downloader = new DefaultDownloaderFactory().create(request);
+ if (downloader == null) {
+ Log.d(TAG, "Unable to create downloader");
+ return Result.failure();
+ }
+
+ try {
+ downloader.call();
+ } catch (Exception e) {
+ DBWriter.addDownloadStatus(downloader.getResult());
+ if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) {
+ sendMessage(request.getTitle(), false);
+ } else {
+ sendErrorNotification();
+ }
+ return Result.failure();
+ }
+
+ if (downloader.cancelled) {
+ if (getInputData().getBoolean(DownloadServiceInterface.WORK_DATA_WAS_QUEUED, false)) {
+ try {
+ DBWriter.removeQueueItem(getApplicationContext(), false, media.getItem()).get();
+ } catch (ExecutionException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ return Result.success();
+ }
+
+ DownloadResult status = downloader.getResult();
+ if (status.isSuccessful()) {
+ MediaDownloadedHandler handler = new MediaDownloadedHandler(
+ getApplicationContext(), downloader.getResult(), request);
+ handler.run();
+ DBWriter.addDownloadStatus(handler.getUpdatedStatus());
+ return Result.success();
+ }
+
+ if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
+ && Integer.parseInt(status.getReasonDetailed()) == 416) {
+ Log.d(TAG, "Requested invalid range, restarting download from the beginning");
+ FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
+ sendMessage(request.getTitle(), true);
+ return retry3times();
+ }
+
+ Log.e(TAG, "Download failed");
+ DBWriter.addDownloadStatus(status);
+ if (status.getReason() == DownloadError.ERROR_FORBIDDEN
+ || status.getReason() == DownloadError.ERROR_NOT_FOUND
+ || status.getReason() == DownloadError.ERROR_UNAUTHORIZED
+ || status.getReason() == DownloadError.ERROR_IO_BLOCKED) {
+ // Fail fast, these are probably unrecoverable
+ if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) {
+ sendMessage(request.getTitle(), false);
+ } else {
+ sendErrorNotification();
+ }
+ return Result.failure();
+ }
+ sendMessage(request.getTitle(), true);
+ return retry3times();
+ }
+
+ private Result retry3times() {
+ if (getRunAttemptCount() < 2) {
+ return Result.retry();
+ } else {
+ sendErrorNotification();
+ return Result.failure();
+ }
+ }
+
+ private void sendMessage(String episodeTitle, boolean retrying) {
+ if (episodeTitle.length() > 20) {
+ episodeTitle = episodeTitle.substring(0, 19) + "…";
+ }
+ EventBus.getDefault().post(new MessageEvent(
+ getApplicationContext().getString(
+ retrying ? R.string.download_error_retrying : R.string.download_error_not_retrying,
+ episodeTitle), (ctx) -> new MainActivityStarter(ctx).withDownloadLogsOpen().start(),
+ getApplicationContext().getString(R.string.download_error_details)));
+ }
+
+ private PendingIntent getDownloadLogsIntent(Context context) {
+ Intent intent = new MainActivityStarter(context).withDownloadLogsOpen().getIntent();
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
+ }
+
+ private PendingIntent getDownloadsIntent(Context context) {
+ Intent intent = new MainActivityStarter(context).withFragmentLoaded("DownloadsFragment").getIntent();
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_notification, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
+ }
+
+ private void sendErrorNotification() {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),
+ NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR);
+ builder.setTicker(getApplicationContext().getString(R.string.download_report_title))
+ .setContentTitle(getApplicationContext().getString(R.string.download_report_title))
+ .setContentText(getApplicationContext().getString(R.string.download_error_tap_for_details))
+ .setSmallIcon(R.drawable.ic_notification_sync_error)
+ .setContentIntent(getDownloadLogsIntent(getApplicationContext()))
+ .setAutoCancel(true);
+ builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ NotificationManager nm = (NotificationManager) getApplicationContext()
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(R.id.notification_download_report, builder.build());
+ }
+
+ private void sendProgressNotification() {
+ StringBuilder bigTextB = new StringBuilder();
+ Map<String, Integer> progressCopy = new HashMap<>(notificationProgress);
+ for (Map.Entry<String, Integer> entry : progressCopy.entrySet()) {
+ bigTextB.append(String.format(Locale.getDefault(), "%s (%d%%)\n", entry.getKey(), entry.getValue()));
+ }
+ String bigText = bigTextB.toString().trim();
+ String contentText;
+ if (notificationProgress.size() == 1) {
+ contentText = bigText;
+ } else {
+ contentText = getApplicationContext().getResources().getQuantityString(R.plurals.downloads_left,
+ notificationProgress.size(), notificationProgress.size());
+ }
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),
+ NotificationUtils.CHANNEL_ID_DOWNLOADING);
+ builder.setTicker(getApplicationContext().getString(R.string.download_notification_title_episodes))
+ .setContentTitle(getApplicationContext().getString(R.string.download_notification_title_episodes))
+ .setContentText(contentText)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText))
+ .setContentIntent(getDownloadsIntent(getApplicationContext()))
+ .setAutoCancel(false)
+ .setOngoing(true)
+ .setWhen(0)
+ .setOnlyAlertOnce(true)
+ .setShowWhen(false)
+ .setSmallIcon(R.drawable.ic_notification_sync)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ NotificationManager nm = (NotificationManager) getApplicationContext()
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(R.id.notification_downloading, builder.build());
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
index a0a0615cb..949f9966b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
@@ -5,7 +5,7 @@ import android.text.TextUtils;
import android.util.Log;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
import okhttp3.CacheControl;
import okhttp3.internal.http.StatusLine;
@@ -149,12 +149,12 @@ public class HttpDownloader extends Downloader {
request.setSize(responseBody.contentLength() + request.getSoFar());
Log.d(TAG, "Size is " + request.getSize());
if (request.getSize() < 0) {
- request.setSize(DownloadStatus.SIZE_UNKNOWN);
+ request.setSize(DownloadResult.SIZE_UNKNOWN);
}
long freeSpace = StorageUtils.getFreeSpaceAvailable();
Log.d(TAG, "Free space is " + freeSpace);
- if (request.getSize() != DownloadStatus.SIZE_UNKNOWN && request.getSize() > freeSpace) {
+ if (request.getSize() != DownloadResult.SIZE_UNKNOWN && request.getSize() > freeSpace) {
onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
return;
}
@@ -175,7 +175,7 @@ public class HttpDownloader extends Downloader {
} else {
// check if size specified in the response header is the same as the size of the
// written file. This check cannot be made if compression was used
- if (!isGzip && request.getSize() != DownloadStatus.SIZE_UNKNOWN
+ if (!isGzip && request.getSize() != DownloadResult.SIZE_UNKNOWN
&& request.getSoFar() != request.getSize()) {
onFail(DownloadError.ERROR_IO_WRONG_SIZE, "Download completed but size: "
+ request.getSoFar() + " does not equal expected size " + request.getSize());
@@ -267,7 +267,8 @@ public class HttpDownloader extends Downloader {
} else if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
error = DownloadError.ERROR_FORBIDDEN;
details = String.valueOf(response.code());
- } else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) {
+ } else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND
+ || response.code() == HttpURLConnection.HTTP_GONE) {
error = DownloadError.ERROR_NOT_FOUND;
details = String.valueOf(response.code());
} else {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/LocalFeedStubDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/LocalFeedStubDownloader.java
deleted file mode 100644
index 750255958..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/LocalFeedStubDownloader.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import androidx.annotation.NonNull;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-
-/**
- * This does not actually download, but it keeps track of a local feed's refresh state.
- */
-public class LocalFeedStubDownloader extends Downloader {
-
- public LocalFeedStubDownloader(@NonNull DownloadRequest request) {
- super(request);
- }
-
- @Override
- protected void download() {
- }
-} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
deleted file mode 100644
index 937f051ec..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package de.danoeh.antennapod.core.service.download.handler;
-
-import android.util.Log;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.storage.DBWriter;
-
-/**
- * Handles failed downloads.
- * <p/>
- * If the file has been partially downloaded, this handler will set the file_url of the FeedFile to the location
- * of the downloaded file.
- * <p/>
- * Currently, this handler only handles FeedMedia objects, because Feeds and FeedImages are deleted if the download fails.
- */
-public class FailedDownloadHandler implements Runnable {
- private static final String TAG = "FailedDownloadHandler";
- private final DownloadRequest request;
-
- public FailedDownloadHandler(DownloadRequest request) {
- this.request = request;
- }
-
- @Override
- public void run() {
- if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
- } else if (request.isDeleteOnFailure()) {
- Log.d(TAG, "Ignoring failed download, deleteOnFailure=true");
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
index 1118b93cd..5da250e15 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
@@ -8,7 +8,7 @@ import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.parser.feed.FeedHandler;
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException;
@@ -25,15 +25,15 @@ import java.util.concurrent.Callable;
public class FeedParserTask implements Callable<FeedHandlerResult> {
private static final String TAG = "FeedParserTask";
private final DownloadRequest request;
- private DownloadStatus downloadStatus;
+ private DownloadResult downloadResult;
private boolean successful = true;
public FeedParserTask(DownloadRequest request) {
this.request = request;
- downloadStatus = new DownloadStatus(
+ downloadResult = new DownloadResult(
0, request.getTitle(), 0, request.getFeedfileType(), false,
- false, true, DownloadError.ERROR_REQUEST_ERROR, new Date(),
- "Unknown error: Status not set", request.isInitiatedByUser());
+ DownloadError.ERROR_REQUEST_ERROR, new Date(),
+ "Unknown error: Status not set");
}
@Override
@@ -87,12 +87,12 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
}
if (successful) {
- downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(), DownloadError.SUCCESS,
- successful, reasonDetailed, request.isInitiatedByUser());
+ downloadResult = new DownloadResult(feed, feed.getHumanReadableIdentifier(), DownloadError.SUCCESS,
+ successful, reasonDetailed);
return result;
} else {
- downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(), reason,
- successful, reasonDetailed, request.isInitiatedByUser());
+ downloadResult = new DownloadResult(feed, feed.getHumanReadableIdentifier(), reason,
+ successful, reasonDetailed);
return null;
}
}
@@ -120,7 +120,7 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
}
@NonNull
- public DownloadStatus getDownloadStatus() {
- return downloadStatus;
+ public DownloadResult getDownloadStatus() {
+ return downloadResult;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
index 9cb1166b4..3b72ed164 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.service.download.handler;
import android.content.Context;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
@@ -30,7 +30,7 @@ public class FeedSyncTask {
}
@NonNull
- public DownloadStatus getDownloadStatus() {
+ public DownloadResult getDownloadStatus() {
return task.getDownloadStatus();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
index c632bf1e0..a46b4c6d0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
@@ -13,7 +13,7 @@ import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
@@ -30,9 +30,9 @@ public class MediaDownloadedHandler implements Runnable {
private static final String TAG = "MediaDownloadedHandler";
private final DownloadRequest request;
private final Context context;
- private DownloadStatus updatedStatus;
+ private DownloadResult updatedStatus;
- public MediaDownloadedHandler(@NonNull Context context, @NonNull DownloadStatus status,
+ public MediaDownloadedHandler(@NonNull Context context, @NonNull DownloadResult status,
@NonNull DownloadRequest request) {
this.request = request;
this.context = context;
@@ -94,8 +94,8 @@ public class MediaDownloadedHandler implements Runnable {
Log.e(TAG, "MediaHandlerThread was interrupted");
} catch (ExecutionException e) {
Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
- updatedStatus = new DownloadStatus(media, media.getEpisodeTitle(),
- DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage(), request.isInitiatedByUser());
+ updatedStatus = new DownloadResult(media, media.getEpisodeTitle(),
+ DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage());
}
if (item != null) {
@@ -107,7 +107,7 @@ public class MediaDownloadedHandler implements Runnable {
}
@NonNull
- public DownloadStatus getUpdatedStatus() {
+ public DownloadResult getUpdatedStatus() {
return updatedStatus;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
deleted file mode 100644
index 5d2c48679..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.danoeh.antennapod.core.service.download.handler;
-
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.service.download.Downloader;
-import org.greenrobot.eventbus.EventBus;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class PostDownloaderTask implements Runnable {
- private List<Downloader> downloads;
-
- public PostDownloaderTask(List<Downloader> downloads) {
- this.downloads = downloads;
- }
-
- @Override
- public void run() {
- List<Downloader> runningDownloads = new ArrayList<>();
- for (Downloader downloader : downloads) {
- if (!downloader.cancelled) {
- runningDownloads.add(downloader);
- }
- }
- List<Downloader> list = Collections.unmodifiableList(runningDownloads);
- EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 42631296b..6fc9035ca 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -824,7 +824,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
&& SleepTimerPreferences.autoEnable() && autoEnableByTime && !sleepTimerActive()) {
setSleepTimer(SleepTimerPreferences.timerMillis());
EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
- PlaybackService.this::disableSleepTimer));
+ (ctx) -> disableSleepTimer(), getString(R.string.undo)));
}
loadQueueForMediaSession();
break;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
index 0f3121551..dbbfba379 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
@@ -9,12 +9,10 @@ import java.util.List;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.core.util.PlaybackStatus;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.PowerUtils;
@@ -97,13 +95,9 @@ public class AutomaticDownloadAlgorithm {
if (itemsToDownload.size() > 0) {
Log.d(TAG, "Enqueueing " + itemsToDownload.size() + " items for download");
- List<DownloadRequest> requests = new ArrayList<>();
for (FeedItem episode : itemsToDownload) {
- DownloadRequest.Builder request = DownloadRequestCreator.create(episode.getMedia());
- request.withInitiatedByUser(false);
- requests.add(request.build());
+ DownloadServiceInterface.get().download(context, episode);
}
- DownloadServiceInterface.get().download(context, false, requests.toArray(new DownloadRequest[0]));
}
}
};
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index 94a7334f3..d83557b0c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -16,10 +16,9 @@ import java.util.List;
import java.util.Map;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
+import de.danoeh.antennapod.core.util.comparator.DownloadResultComparator;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparator;
-import de.danoeh.antennapod.model.download.DownloadStatus;
import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
@@ -28,14 +27,15 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
import de.danoeh.antennapod.storage.database.mapper.ChapterCursorMapper;
-import de.danoeh.antennapod.storage.database.mapper.DownloadStatusCursorMapper;
+import de.danoeh.antennapod.storage.database.mapper.DownloadResultCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedItemCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedMediaCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedPreferencesCursorMapper;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
/**
* Provides methods for reading data from the AntennaPod database.
@@ -394,17 +394,17 @@ public final class DBReader {
* @return A list with DownloadStatus objects that represent the download log.
* The size of the returned list is limited by {@link #DOWNLOAD_LOG_SIZE}.
*/
- public static List<DownloadStatus> getDownloadLog() {
+ public static List<DownloadResult> getDownloadLog() {
Log.d(TAG, "getDownloadLog() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
try (Cursor cursor = adapter.getDownloadLogCursor(DOWNLOAD_LOG_SIZE)) {
- List<DownloadStatus> downloadLog = new ArrayList<>(cursor.getCount());
+ List<DownloadResult> downloadLog = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
- downloadLog.add(DownloadStatusCursorMapper.convert(cursor));
+ downloadLog.add(DownloadResultCursorMapper.convert(cursor));
}
- Collections.sort(downloadLog, new DownloadStatusComparator());
+ Collections.sort(downloadLog, new DownloadResultComparator());
return downloadLog;
} finally {
adapter.close();
@@ -418,17 +418,17 @@ public final class DBReader {
* @return A list with DownloadStatus objects that represent the feed's download log,
* newest events first.
*/
- public static List<DownloadStatus> getFeedDownloadLog(long feedId) {
+ public static List<DownloadResult> getFeedDownloadLog(long feedId) {
Log.d(TAG, "getFeedDownloadLog() called with: " + "feed = [" + feedId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
try (Cursor cursor = adapter.getDownloadLog(Feed.FEEDFILETYPE_FEED, feedId)) {
- List<DownloadStatus> downloadLog = new ArrayList<>(cursor.getCount());
+ List<DownloadResult> downloadLog = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
- downloadLog.add(DownloadStatusCursorMapper.convert(cursor));
+ downloadLog.add(DownloadResultCursorMapper.convert(cursor));
}
- Collections.sort(downloadLog, new DownloadStatusComparator());
+ Collections.sort(downloadLog, new DownloadResultComparator());
return downloadLog;
} finally {
adapter.close();
@@ -717,10 +717,10 @@ public final class DBReader {
}
}
- public static List<FeedItem> getFeedItemsWithMedia(Long[] mediaIds) {
+ public static List<FeedItem> getFeedItemsWithUrl(List<String> urls) {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- try (Cursor itemCursor = adapter.getFeedItemCursorByMediaIds(mediaIds)) {
+ try (Cursor itemCursor = adapter.getFeedItemCursorByUrl(urls)) {
List<FeedItem> items = extractItemlistFromCursor(adapter, itemCursor);
loadAdditionalFeedItemListData(items);
Collections.sort(items, new PlaybackCompletionDateComparator());
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 8b79d594c..e3ac7a7e1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -7,14 +7,13 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
-import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
@@ -114,21 +113,6 @@ public final class DBTasks {
EventBus.getDefault().post(new MessageEvent(context.getString(R.string.error_file_not_found)));
}
- 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();
- for (FeedItem item : items) {
- if (!queueIDList.contains(item.getId())) {
- itemsToEnqueue.add(item);
- }
- }
- DBWriter.addQueueItem(context, false, itemsToEnqueue.toArray(new FeedItem[0])).get();
- }
- return itemsToEnqueue;
- }
-
/**
* Looks for non-downloaded episodes in the queue or list of unread items and request a download if
* 1. Network is available
@@ -267,13 +251,13 @@ public final class DBTasks {
FeedItem possibleDuplicate = searchFeedItemGuessDuplicate(newFeed.getItems(), item);
if (!newFeed.isLocalFeed() && possibleDuplicate != null && item != possibleDuplicate) {
// Canonical episode is the first one returned (usually oldest)
- DBWriter.addDownloadStatus(new DownloadStatus(savedFeed,
+ DBWriter.addDownloadStatus(new DownloadResult(savedFeed,
item.getTitle(), DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
"The podcast host appears to have added the same episode twice. "
+ "AntennaPod still refreshed the feed and attempted to repair it."
+ "\n\nOriginal episode:\n" + duplicateEpisodeDetails(item)
+ "\n\nSecond episode that is also in the feed:\n"
- + duplicateEpisodeDetails(possibleDuplicate), false));
+ + duplicateEpisodeDetails(possibleDuplicate)));
continue;
}
@@ -282,13 +266,13 @@ public final class DBTasks {
oldItem = searchFeedItemGuessDuplicate(savedFeed.getItems(), item);
if (oldItem != null) {
Log.d(TAG, "Repaired duplicate: " + oldItem + ", " + item);
- DBWriter.addDownloadStatus(new DownloadStatus(savedFeed,
+ DBWriter.addDownloadStatus(new DownloadResult(savedFeed,
item.getTitle(), DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
"The podcast host changed the ID of an existing episode instead of just "
+ "updating the episode itself. AntennaPod still refreshed the feed and "
+ "attempted to repair it."
+ "\n\nOriginal episode:\n" + duplicateEpisodeDetails(oldItem)
- + "\n\nNow the feed contains:\n" + duplicateEpisodeDetails(item), false));
+ + "\n\nNow the feed contains:\n" + duplicateEpisodeDetails(item)));
oldItem.setItemIdentifier(item.getItemIdentifier());
if (oldItem.isPlayed() && oldItem.getMedia() != null) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index dcee8a45a..4815737f4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -36,7 +36,7 @@ import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -299,7 +299,7 @@ public class DBWriter {
*
* @param status The DownloadStatus object.
*/
- public static Future<?> addDownloadStatus(final DownloadStatus status) {
+ public static Future<?> addDownloadStatus(final DownloadResult status) {
return dbExec.submit(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
index b81f281e8..4ed17c43f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
@@ -8,9 +8,9 @@ import androidx.annotation.Nullable;
import java.util.List;
import java.util.Random;
-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.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.model.playback.Playable;
@@ -74,7 +74,7 @@ class ItemEnqueuePositionCalculator {
}
return curItem != null
&& curItem.getMedia() != null
- && DownloadService.isDownloadingFile(curItem.getMedia().getDownload_url());
+ && DownloadServiceInterface.get().isDownloadingEpisode(curItem.getMedia().getDownload_url());
}
private static int getCurrentlyPlayingPosition(@NonNull List<FeedItem> curQueue,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
index 50b4d411f..d848b5a30 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
@@ -24,10 +24,10 @@ public class FeedItemUtil {
return -1;
}
- public static int indexOfItemWithMediaId(List<FeedItem> items, long mediaId) {
- for(int i=0; i < items.size(); i++) {
+ public static int indexOfItemWithDownloadUrl(List<FeedItem> items, String downloadUrl) {
+ for (int i = 0; i < items.size(); i++) {
FeedItem item = items.get(i);
- if(item != null && item.getMedia() != null && item.getMedia().getId() == mediaId) {
+ if (item != null && item.getMedia() != null && item.getMedia().getDownload_url().equals(downloadUrl)) {
return i;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadResultComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadResultComparator.java
new file mode 100644
index 000000000..d1d50fc8a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadResultComparator.java
@@ -0,0 +1,14 @@
+package de.danoeh.antennapod.core.util.comparator;
+
+import java.util.Comparator;
+
+import de.danoeh.antennapod.model.download.DownloadResult;
+
+/** Compares the completion date of two DownloadResult objects. */
+public class DownloadResultComparator implements Comparator<DownloadResult> {
+
+ @Override
+ public int compare(DownloadResult lhs, DownloadResult rhs) {
+ return rhs.getCompletionDate().compareTo(lhs.getCompletionDate());
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java
deleted file mode 100644
index 68b38ec7f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.danoeh.antennapod.core.util.comparator;
-
-import java.util.Comparator;
-
-import de.danoeh.antennapod.model.download.DownloadStatus;
-
-/** Compares the completion date of two Downloadstatus objects. */
-public class DownloadStatusComparator implements Comparator<DownloadStatus> {
-
- @Override
- public int compare(DownloadStatus lhs, DownloadStatus rhs) {
- return rhs.getCompletionDate().compareTo(lhs.getCompletionDate());
- }
-
-}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
index d66bd2360..6a7e51bac 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
@@ -13,7 +13,6 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -26,7 +25,6 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -255,63 +253,6 @@ public class DbTasksTest {
}
}
- @Test
- public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception {
- // Setup test data / environment
- UserPreferences.setEnqueueDownloadedEpisodes(true);
- UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
-
- List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
- List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
-
- DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
- // the first item fis1.get(0) is already in the queue
- FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
-
- // Expectations:
- List<FeedItem> expectedEnqueued = Arrays.asList(fis1.get(1), fis2.get(2), fis2.get(1));
- List<FeedItem> expectedQueue = new ArrayList<>();
- expectedQueue.addAll(DBReader.getQueue());
- expectedQueue.addAll(expectedEnqueued);
-
- // Run actual test and assert results
- List<? extends FeedItem> actualEnqueued =
- DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
-
- assertEqualsByIds("Only items not in the queue are enqueued", expectedEnqueued, actualEnqueued);
- assertEqualsByIds("Queue has new items appended", expectedQueue, DBReader.getQueue());
- }
-
- @Test
- public void testAddQueueItemsInDownload_EnqueueDisabled() throws Exception {
- // Setup test data / environment
- UserPreferences.setEnqueueDownloadedEpisodes(false);
-
- List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
- List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
-
- DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
- FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
-
- // Expectations:
- List<FeedItem> expectedEnqueued = Collections.emptyList();
- List<FeedItem> expectedQueue = DBReader.getQueue();
-
- // Run actual test and assert results
- List<? extends FeedItem> actualEnqueued =
- DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
-
- assertEqualsByIds("No item is enqueued", expectedEnqueued, actualEnqueued);
- assertEqualsByIds("Queue is unchanged", expectedQueue, DBReader.getQueue());
- }
-
- private void assertEqualsByIds(String msg, List<? extends FeedItem> expected, List<? extends FeedItem> actual) {
- // assert only the IDs, so that any differences are easily to spot.
- List<Long> expectedIds = getIdList(expected);
- List<Long> actualIds = getIdList(actual);
- assertEquals(msg, expectedIds, actualIds);
- }
-
private Feed createSavedFeed(String title, int numFeedItems) {
final Feed feed = new Feed("url", null, title);
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
index 376e0e65c..2594fabf6 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
@@ -1,7 +1,8 @@
package de.danoeh.antennapod.core.storage;
-import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.model.playback.RemoteMedia;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterfaceStub;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -21,8 +22,6 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedMother;
import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.model.playback.Playable;
-import org.mockito.MockedStatic;
-import org.mockito.Mockito;
import static de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation.AFTER_CURRENTLY_PLAYING;
import static de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation.BACK;
@@ -74,6 +73,7 @@ public class ItemEnqueuePositionCalculatorTest {
*/
@Test
public void test() {
+ DownloadServiceInterface.setImpl(new DownloadServiceInterfaceStub());
ItemEnqueuePositionCalculator calculator = new ItemEnqueuePositionCalculator(options);
// shallow copy to which the test will add items
@@ -128,95 +128,6 @@ public class ItemEnqueuePositionCalculatorTest {
}
- @RunWith(Parameterized.class)
- 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 order user taps to download
- */
- @Parameters(name = "{index}: case<{0}>")
- public static Iterable<Object[]> data() {
- // Attempts to make test more readable by showing the expected list of ids
- // (rather than the expected positions)
- return Arrays.asList(new Object[][] {
- {"download order test, enqueue default",
- concat(QUEUE_DEFAULT_IDS, 101L),
- concat(QUEUE_DEFAULT_IDS, list(101L, 102L)),
- concat(QUEUE_DEFAULT_IDS, list(101L, 102L, 103L)),
- BACK, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NULL},
- {"download order test, enqueue at front (currently playing has no effect)",
- concat(101L, QUEUE_DEFAULT_IDS),
- concat(list(101L, 102L), QUEUE_DEFAULT_IDS),
- concat(list(101L, 103L, 102L), QUEUE_DEFAULT_IDS),
- // ^ 103 is put ahead of 102, after 102 failed.
- // It is a limitation as the logic can't tell 102 download has failed
- // (as opposed to simply being enqueued)
- FRONT, QUEUE_DEFAULT, 11L}, // 11 is at the front, currently playing
- {"download order test, enqueue after currently playing",
- list(11L, 101L, 12L, 13L, 14L),
- list(11L, 101L, 102L, 12L, 13L, 14L),
- list(11L, 101L, 103L, 102L, 12L, 13L, 14L),
- AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, 11L} // 11 is at the front, currently playing
- });
- }
-
- @Parameter
- public String message;
-
- @Parameter(1)
- public List<Long> idsExpectedAfter101;
-
- @Parameter(2)
- public List<Long> idsExpectedAfter102;
-
- @Parameter(3)
- public List<Long> idsExpectedAfter103;
-
- @Parameter(4)
- public EnqueueLocation options;
-
- @Parameter(5)
- public List<FeedItem> queueInitial;
-
- @Parameter(6)
- public long idCurrentlyPlaying;
-
- @Test
- public void testQueueOrderWhenDownloading2Items() {
- ItemEnqueuePositionCalculator calculator = new ItemEnqueuePositionCalculator(options);
- 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);
- }
- }
- }
-
static void doAddToQueueAndAssertResult(String message,
ItemEnqueuePositionCalculator calculator,
FeedItem itemToAdd,
diff --git a/event/build.gradle b/event/build.gradle
index 033fc5a3c..b1efac85d 100644
--- a/event/build.gradle
+++ b/event/build.gradle
@@ -5,6 +5,7 @@ apply from: "../common.gradle"
dependencies {
implementation project(':model')
+ implementation "androidx.core:core:$coreVersion"
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
}
diff --git a/event/src/main/java/de/danoeh/antennapod/event/EpisodeDownloadEvent.java b/event/src/main/java/de/danoeh/antennapod/event/EpisodeDownloadEvent.java
new file mode 100644
index 000000000..c9790d18a
--- /dev/null
+++ b/event/src/main/java/de/danoeh/antennapod/event/EpisodeDownloadEvent.java
@@ -0,0 +1,18 @@
+package de.danoeh.antennapod.event;
+
+import de.danoeh.antennapod.model.download.DownloadStatus;
+
+import java.util.Map;
+import java.util.Set;
+
+public class EpisodeDownloadEvent {
+ private final Map<String, DownloadStatus> map;
+
+ public EpisodeDownloadEvent(Map<String, DownloadStatus> map) {
+ this.map = map;
+ }
+
+ public Set<String> getUrls() {
+ return map.keySet();
+ }
+}
diff --git a/event/src/main/java/de/danoeh/antennapod/event/MessageEvent.java b/event/src/main/java/de/danoeh/antennapod/event/MessageEvent.java
index 3f6b2db32..7f1cb20e2 100644
--- a/event/src/main/java/de/danoeh/antennapod/event/MessageEvent.java
+++ b/event/src/main/java/de/danoeh/antennapod/event/MessageEvent.java
@@ -1,21 +1,27 @@
package de.danoeh.antennapod.event;
+import android.content.Context;
import androidx.annotation.Nullable;
+import androidx.core.util.Consumer;
+
public class MessageEvent {
public final String message;
@Nullable
- public final Runnable action;
+ public final Consumer<Context> action;
+
+ @Nullable
+ public final String actionText;
public MessageEvent(String message) {
- this(message, null);
+ this(message, null, null);
}
- public MessageEvent(String message, Runnable action) {
+ public MessageEvent(String message, Consumer<Context> action, String actionText) {
this.message = message;
this.action = action;
+ this.actionText = actionText;
}
-
}
diff --git a/model/src/main/java/de/danoeh/antennapod/model/download/DownloadResult.java b/model/src/main/java/de/danoeh/antennapod/model/download/DownloadResult.java
new file mode 100644
index 000000000..66901cf65
--- /dev/null
+++ b/model/src/main/java/de/danoeh/antennapod/model/download/DownloadResult.java
@@ -0,0 +1,128 @@
+package de.danoeh.antennapod.model.download;
+
+import androidx.annotation.NonNull;
+
+import java.util.Date;
+
+import de.danoeh.antennapod.model.feed.FeedFile;
+
+/**
+ * Contains status attributes for one download
+ */
+public class DownloadResult {
+ /**
+ * Downloaders should use this constant for the size attribute if necessary
+ * so that the listadapters etc. can react properly.
+ */
+ public static final int SIZE_UNKNOWN = -1;
+
+ /**
+ * A human-readable string which is shown to the user so that he can
+ * identify the download. Should be the title of the item/feed/media or the
+ * URL if the download has no other title.
+ */
+ private final String title;
+ private final long feedfileId;
+ /**
+ * Is used to determine the type of the feedfile even if the feedfile does
+ * not exist anymore. The value should be FEEDFILETYPE_FEED,
+ * FEEDFILETYPE_FEEDIMAGE or FEEDFILETYPE_FEEDMEDIA
+ */
+ private final int feedfileType;
+ /**
+ * Unique id for storing the object in database.
+ */
+ private long id;
+ private DownloadError reason;
+ /**
+ * A message which can be presented to the user to give more information.
+ * Should be null if Download was successful.
+ */
+ private String reasonDetailed;
+ private boolean successful;
+ private final Date completionDate;
+
+ /**
+ * Constructor for creating new completed downloads.
+ */
+ public DownloadResult(@NonNull FeedFile feedfile, String title, DownloadError reason, boolean successful,
+ String reasonDetailed) {
+ this(0, title, feedfile.getId(), feedfile.getTypeAsInt(), successful, reason, new Date(),
+ reasonDetailed);
+ }
+
+ public DownloadResult(long id, String title, long feedfileId, int feedfileType, boolean successful,
+ DownloadError reason, Date completionDate,
+ String reasonDetailed) {
+ this.id = id;
+ this.title = title;
+ this.feedfileId = feedfileId;
+ this.reason = reason;
+ this.successful = successful;
+ this.completionDate = (Date) completionDate.clone();
+ this.reasonDetailed = reasonDetailed;
+ this.feedfileType = feedfileType;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "DownloadStatus [id=" + id + ", title=" + title + ", reason="
+ + reason + ", reasonDetailed=" + reasonDetailed
+ + ", successful=" + successful + ", completionDate="
+ + completionDate + ", feedfileId=" + feedfileId
+ + ", feedfileType=" + feedfileType + "]";
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public DownloadError getReason() {
+ return reason;
+ }
+
+ public String getReasonDetailed() {
+ return reasonDetailed;
+ }
+
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ public Date getCompletionDate() {
+ return (Date) completionDate.clone();
+ }
+
+ public long getFeedfileId() {
+ return feedfileId;
+ }
+
+ public int getFeedfileType() {
+ return feedfileType;
+ }
+
+ public void setSuccessful() {
+ this.successful = true;
+ this.reason = DownloadError.SUCCESS;
+ }
+
+ public void setFailed(DownloadError reason, String reasonDetailed) {
+ this.successful = false;
+ this.reason = reason;
+ this.reasonDetailed = reasonDetailed;
+ }
+
+ public void setCancelled() {
+ this.successful = false;
+ this.reason = DownloadError.ERROR_DOWNLOAD_CANCELLED;
+ }
+} \ No newline at end of file
diff --git a/model/src/main/java/de/danoeh/antennapod/model/download/DownloadStatus.java b/model/src/main/java/de/danoeh/antennapod/model/download/DownloadStatus.java
index 0a18973df..823c183d2 100644
--- a/model/src/main/java/de/danoeh/antennapod/model/download/DownloadStatus.java
+++ b/model/src/main/java/de/danoeh/antennapod/model/download/DownloadStatus.java
@@ -1,153 +1,23 @@
package de.danoeh.antennapod.model.download;
-import androidx.annotation.NonNull;
-
-import java.util.Date;
-
-import de.danoeh.antennapod.model.feed.FeedFile;
-
-/**
- * Contains status attributes for one download
- */
public class DownloadStatus {
- /**
- * Downloaders should use this constant for the size attribute if necessary
- * so that the listadapters etc. can react properly.
- */
- public static final int SIZE_UNKNOWN = -1;
+ public static final int STATE_QUEUED = 0;
+ public static final int STATE_COMPLETED = 1; // Both successful and not successful
+ public static final int STATE_RUNNING = 2;
- // ----------------------------------- ATTRIBUTES STORED IN DB
- /**
- * A human-readable string which is shown to the user so that he can
- * identify the download. Should be the title of the item/feed/media or the
- * URL if the download has no other title.
- */
- private final String title;
- private final long feedfileId;
- /**
- * Is used to determine the type of the feedfile even if the feedfile does
- * not exist anymore. The value should be FEEDFILETYPE_FEED,
- * FEEDFILETYPE_FEEDIMAGE or FEEDFILETYPE_FEEDMEDIA
- */
- private final int feedfileType;
- private final boolean initiatedByUser;
- /**
- * Unique id for storing the object in database.
- */
- private long id;
- private DownloadError reason;
- /**
- * A message which can be presented to the user to give more information.
- * Should be null if Download was successful.
- */
- private String reasonDetailed;
- private boolean successful;
- private final Date completionDate;
- // ------------------------------------ NOT STORED IN DB
- private boolean done;
- private boolean cancelled;
-
- /**
- * Constructor for creating new completed downloads.
- */
- public DownloadStatus(@NonNull FeedFile feedfile, String title, DownloadError reason, boolean successful,
- String reasonDetailed, boolean initiatedByUser) {
- this(0, title, feedfile.getId(), feedfile.getTypeAsInt(), successful, false, true, reason, new Date(),
- reasonDetailed, initiatedByUser);
- }
-
- public DownloadStatus(long id, String title, long feedfileId, int feedfileType, boolean successful,
- boolean cancelled, boolean done, DownloadError reason, Date completionDate,
- String reasonDetailed, boolean initiatedByUser) {
- this.id = id;
- this.title = title;
- this.feedfileId = feedfileId;
- this.reason = reason;
- this.successful = successful;
- this.cancelled = cancelled;
- this.done = done;
- this.completionDate = (Date) completionDate.clone();
- this.reasonDetailed = reasonDetailed;
- this.feedfileType = feedfileType;
- this.initiatedByUser = initiatedByUser;
- }
-
- @Override
- @NonNull
- public String toString() {
- return "DownloadStatus [id=" + id + ", title=" + title + ", reason="
- + reason + ", reasonDetailed=" + reasonDetailed
- + ", successful=" + successful + ", completionDate="
- + completionDate + ", feedfileId=" + feedfileId
- + ", feedfileType=" + feedfileType + ", done=" + done
- + ", cancelled=" + cancelled + "]";
- }
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public DownloadError getReason() {
- return reason;
- }
-
- public String getReasonDetailed() {
- return reasonDetailed;
- }
-
- public boolean isSuccessful() {
- return successful;
- }
-
- public Date getCompletionDate() {
- return (Date) completionDate.clone();
- }
-
- public long getFeedfileId() {
- return feedfileId;
- }
-
- public int getFeedfileType() {
- return feedfileType;
- }
-
- public boolean isInitiatedByUser() {
- return initiatedByUser;
- }
-
- public boolean isDone() {
- return done;
- }
-
- public boolean isCancelled() {
- return cancelled;
- }
+ private final int state;
+ private final int progress;
- public void setSuccessful() {
- this.successful = true;
- this.reason = DownloadError.SUCCESS;
- this.done = true;
+ public DownloadStatus(int state, int progress) {
+ this.state = state;
+ this.progress = progress;
}
- public void setFailed(DownloadError reason, String reasonDetailed) {
- this.successful = false;
- this.reason = reason;
- this.reasonDetailed = reasonDetailed;
- this.done = true;
+ public int getState() {
+ return state;
}
- public void setCancelled() {
- this.successful = false;
- this.reason = DownloadError.ERROR_DOWNLOAD_CANCELLED;
- this.done = true;
- this.cancelled = true;
+ public int getProgress() {
+ return progress;
}
-} \ No newline at end of file
+}
diff --git a/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterface.java b/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterface.java
index b5d0cd991..5fac79ce8 100644
--- a/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterface.java
+++ b/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterface.java
@@ -1,10 +1,20 @@
package de.danoeh.antennapod.net.download.serviceinterface;
import android.content.Context;
-import android.content.Intent;
+import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.feed.FeedItem;
+
+import java.util.HashMap;
+import java.util.Map;
public abstract class DownloadServiceInterface {
+ public static final String WORK_TAG = "episodeDownload";
+ public static final String WORK_TAG_EPISODE_URL = "episodeUrl:";
+ public static final String WORK_DATA_PROGRESS = "progress";
+ public static final String WORK_DATA_MEDIA_ID = "media_id";
+ public static final String WORK_DATA_WAS_QUEUED = "was_queued";
private static DownloadServiceInterface impl;
+ private Map<String, DownloadStatus> currentDownloads = new HashMap<>();
public static DownloadServiceInterface get() {
return impl;
@@ -14,13 +24,35 @@ public abstract class DownloadServiceInterface {
DownloadServiceInterface.impl = impl;
}
- public abstract void download(Context context, boolean cleanupMedia, DownloadRequest... requests);
+ public void setCurrentDownloads(Map<String, DownloadStatus> currentDownloads) {
+ this.currentDownloads = currentDownloads;
+ }
- public abstract Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests);
+ /**
+ * Download immediately after user action.
+ */
+ public abstract void downloadNow(Context context, FeedItem item, boolean ignoreConstraints);
- public abstract void refreshAllFeeds(Context context, boolean initiatedByUser);
+ /**
+ * Download when device seems fit.
+ */
+ public abstract void download(Context context, FeedItem item);
public abstract void cancel(Context context, String url);
public abstract void cancelAll(Context context);
+
+ public boolean isDownloadingEpisode(String url) {
+ return currentDownloads.containsKey(url)
+ && currentDownloads.get(url).getState() != DownloadStatus.STATE_COMPLETED;
+ }
+
+ public boolean isEpisodeQueued(String url) {
+ return currentDownloads.containsKey(url)
+ && currentDownloads.get(url).getState() == DownloadStatus.STATE_QUEUED;
+ }
+
+ public int getProgress(String url) {
+ return isDownloadingEpisode(url) ? currentDownloads.get(url).getProgress() : -1;
+ }
}
diff --git a/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterfaceStub.java b/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterfaceStub.java
index 947746485..716d68d14 100644
--- a/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterfaceStub.java
+++ b/net/download/service-interface/src/main/java/de/danoeh/antennapod/net/download/serviceinterface/DownloadServiceInterfaceStub.java
@@ -1,23 +1,23 @@
package de.danoeh.antennapod.net.download.serviceinterface;
import android.content.Context;
-import android.content.Intent;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class DownloadServiceInterfaceStub extends DownloadServiceInterface {
- public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
+ @Override
+ public void downloadNow(Context context, FeedItem item, boolean ignoreConstraints) {
}
- public Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests) {
- return null;
- }
-
- public void refreshAllFeeds(Context context, boolean initiatedByUser) {
+ @Override
+ public void download(Context context, FeedItem item) {
}
+ @Override
public void cancel(Context context, String url) {
}
+ @Override
public void cancelAll(Context context) {
}
}
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
index 40933e8d1..21f12e223 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
@@ -35,7 +35,7 @@ import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.storage.database.mapper.FeedItemFilterQuery;
import de.danoeh.antennapod.storage.database.mapper.FeedItemSortQuery;
@@ -757,7 +757,7 @@ public class PodDBAdapter {
/**
* Inserts or updates a download status.
*/
- public long setDownloadStatus(DownloadStatus status) {
+ public long setDownloadStatus(DownloadResult status) {
ContentValues values = new ContentValues();
values.put(KEY_FEEDFILE, status.getFeedfileId());
values.put(KEY_FEEDFILETYPE, status.getFeedfileType());
@@ -1142,12 +1142,19 @@ public class PodDBAdapter {
return db.rawQuery(query, null);
}
- public final Cursor getFeedItemCursorByMediaIds(final Long[] ids) {
- if (ids.length > IN_OPERATOR_MAXIMUM) {
+ public final Cursor getFeedItemCursorByUrl(List<String> urls) {
+ if (urls.size() > IN_OPERATOR_MAXIMUM) {
throw new IllegalArgumentException("number of IDs must not be larger than " + IN_OPERATOR_MAXIMUM);
}
+ StringBuilder urlsString = new StringBuilder();
+ for (int i = 0; i < urls.size(); i++) {
+ if (i != 0) {
+ urlsString.append(",");
+ }
+ urlsString.append(DatabaseUtils.sqlEscapeString(urls.get(i)));
+ }
final String query = SELECT_FEED_ITEMS_AND_MEDIA
- + " WHERE " + SELECT_KEY_MEDIA_ID + " IN (" + TextUtils.join(",", ids) + ")";
+ + " WHERE " + KEY_DOWNLOAD_URL + " IN (" + urlsString + ")";
return db.rawQuery(query, null);
}
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadStatusCursorMapper.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadResultCursorMapper.java
index 1b8f3c726..d8f40d6a3 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadStatusCursorMapper.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/DownloadResultCursorMapper.java
@@ -2,21 +2,21 @@ package de.danoeh.antennapod.storage.database.mapper;
import android.database.Cursor;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.model.download.DownloadStatus;
+import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.download.DownloadError;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
import java.util.Date;
/**
- * Converts a {@link Cursor} to a {@link DownloadStatus} object.
+ * Converts a {@link Cursor} to a {@link DownloadResult} object.
*/
-public abstract class DownloadStatusCursorMapper {
+public abstract class DownloadResultCursorMapper {
/**
- * Create a {@link DownloadStatus} instance from a database row (cursor).
+ * Create a {@link DownloadResult} instance from a database row (cursor).
*/
@NonNull
- public static DownloadStatus convert(@NonNull Cursor cursor) {
+ public static DownloadResult convert(@NonNull Cursor cursor) {
int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_ID);
int indexTitle = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE);
int indexFeedFile = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FEEDFILE);
@@ -26,10 +26,10 @@ public abstract class DownloadStatusCursorMapper {
int indexCompletionDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_COMPLETION_DATE);
int indexReasonDetailed = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_REASON_DETAILED);
- return new DownloadStatus(cursor.getLong(indexId), cursor.getString(indexTitle), cursor.getLong(indexFeedFile),
- cursor.getInt(indexFileFileType), cursor.getInt(indexSuccessful) > 0, false, true,
+ return new DownloadResult(cursor.getLong(indexId), cursor.getString(indexTitle), cursor.getLong(indexFeedFile),
+ cursor.getInt(indexFileFileType), cursor.getInt(indexSuccessful) > 0,
DownloadError.fromCode(cursor.getInt(indexReason)),
new Date(cursor.getLong(indexCompletionDate)),
- cursor.getString(indexReasonDetailed), false);
+ cursor.getString(indexReasonDetailed));
}
}
diff --git a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
index 8a19300bb..f2b122fcc 100644
--- a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
+++ b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
@@ -90,7 +90,6 @@ public class UserPreferences {
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
private static final String PREF_MOBILE_UPDATE = "prefMobileUpdateTypes";
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
- public static final String PREF_PARALLEL_DOWNLOADS = "prefParallelDownloads";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
public static final String PREF_ENABLE_AUTODL_ON_BATTERY = "prefEnableAutoDownloadOnBattery";
@@ -525,10 +524,6 @@ public class UserPreferences {
setAllowMobileFor("sync", allow);
}
- public static int getParallelDownloads() {
- return Integer.parseInt(prefs.getString(PREF_PARALLEL_DOWNLOADS, "4"));
- }
-
/**
* Returns the capacity of the episode cache. This method will return the
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
diff --git a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/DownloadAuthenticationActivityStarter.java b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/DownloadAuthenticationActivityStarter.java
deleted file mode 100644
index 03c5e915e..000000000
--- a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/DownloadAuthenticationActivityStarter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package de.danoeh.antennapod.ui.appstartintent;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Parcelable;
-
-/**
- * Launches the download authentication activity of the app with specific arguments.
- * Does not require a dependency on the actual implementation of the activity.
- */
-public class DownloadAuthenticationActivityStarter {
- public static final String INTENT = "de.danoeh.antennapod.intents.DOWNLOAD_AUTH_ACTIVITY";
- public static final String EXTRA_DOWNLOAD_REQUEST = "download_request";
-
- private final Intent intent;
- private final Context context;
- private final long feedFileId;
-
- public DownloadAuthenticationActivityStarter(Context context, long feedFileId, Parcelable downloadRequest) {
- this.context = context;
- this.feedFileId = feedFileId;
- intent = new Intent(INTENT);
- intent.setAction("request" + feedFileId);
- intent.putExtra(EXTRA_DOWNLOAD_REQUEST, downloadRequest);
- intent.setPackage(context.getPackageName());
- }
-
- public Intent getIntent() {
- return intent;
- }
-
- public PendingIntent getPendingIntent() {
- return PendingIntent.getActivity(context.getApplicationContext(),
- ("downloadAuth" + feedFileId).hashCode(), getIntent(),
- PendingIntent.FLAG_ONE_SHOT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- }
-}
diff --git a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
index 1463978ee..c635ff0cc 100644
--- a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
+++ b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
@@ -17,6 +17,7 @@ public class MainActivityStarter {
public static final String EXTRA_ADD_TO_BACK_STACK = "add_to_back_stack";
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_OPEN_DRAWER = "open_drawer";
+ public static final String EXTRA_OPEN_DOWNLOAD_LOGS = "open_download_logs";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
private final Intent intent;
@@ -30,6 +31,9 @@ public class MainActivityStarter {
}
public Intent getIntent() {
+ if (fragmentArgs != null) {
+ intent.putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
+ }
return intent;
}
@@ -67,6 +71,11 @@ public class MainActivityStarter {
return this;
}
+ public MainActivityStarter withDownloadLogsOpen() {
+ intent.putExtra(EXTRA_OPEN_DOWNLOAD_LOGS, true);
+ return this;
+ }
+
public MainActivityStarter withFragmentArgs(String name, boolean value) {
if (fragmentArgs == null) {
fragmentArgs = new Bundle();
diff --git a/ui/common/src/main/java/de/danoeh/antennapod/ui/common/CircularProgressBar.java b/ui/common/src/main/java/de/danoeh/antennapod/ui/common/CircularProgressBar.java
index a693c28b0..42f63bd9d 100644
--- a/ui/common/src/main/java/de/danoeh/antennapod/ui/common/CircularProgressBar.java
+++ b/ui/common/src/main/java/de/danoeh/antennapod/ui/common/CircularProgressBar.java
@@ -4,7 +4,9 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.DashPathEffect;
import android.graphics.Paint;
+import android.graphics.PathEffect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
@@ -13,11 +15,13 @@ import androidx.annotation.Nullable;
public class CircularProgressBar extends View {
public static final float MINIMUM_PERCENTAGE = 0.005f;
public static final float MAXIMUM_PERCENTAGE = 1 - MINIMUM_PERCENTAGE;
+ private static final PathEffect DASHED = new DashPathEffect(new float[] {5f, 5f}, 0f);
private final Paint paintBackground = new Paint();
private final Paint paintProgress = new Paint();
private float percentage = 0;
private float targetPercentage = 0;
+ private boolean isIndeterminate = false;
private Object tag = null;
private final RectF bounds = new RectF();
@@ -73,9 +77,10 @@ public class CircularProgressBar extends View {
float padding = getHeight() * 0.07f;
paintBackground.setStrokeWidth(getHeight() * 0.02f);
+ paintBackground.setPathEffect(isIndeterminate ? DASHED : null);
paintProgress.setStrokeWidth(padding);
bounds.set(padding, padding, getWidth() - padding, getHeight() - padding);
- canvas.drawArc(bounds, 0, 360, false, paintBackground);
+ canvas.drawArc(bounds, -90, 360, false, paintBackground);
if (MINIMUM_PERCENTAGE <= percentage && percentage <= MAXIMUM_PERCENTAGE) {
canvas.drawArc(bounds, -90, percentage * 360, false, paintProgress);
@@ -92,4 +97,8 @@ public class CircularProgressBar extends View {
invalidate();
}
}
+
+ public void setIndeterminate(boolean indeterminate) {
+ isIndeterminate = indeterminate;
+ }
}
diff --git a/ui/i18n/src/main/res/values/strings.xml b/ui/i18n/src/main/res/values/strings.xml
index 59fc8b4e5..1959a2252 100644
--- a/ui/i18n/src/main/res/values/strings.xml
+++ b/ui/i18n/src/main/res/values/strings.xml
@@ -130,7 +130,6 @@
<string name="feed_volume_reduction_off">Off</string>
<string name="feed_volume_reduction_light">Light</string>
<string name="feed_volume_reduction_heavy">Heavy</string>
- <string name="parallel_downloads">%1$d parallel downloads</string>
<string name="feed_auto_download_always">Always</string>
<string name="feed_auto_download_never">Never</string>
<string name="feed_new_episodes_action_add_to_inbox">Add to Inbox</string>
@@ -239,7 +238,6 @@
<item quantity="other">%d episodes marked as unplayed.</item>
</plurals>
<string name="add_to_queue_label">Add to Queue</string>
- <string name="added_to_queue_label">Added to Queue</string>
<plurals name="added_to_queue_batch_label">
<item quantity="one">%d episode added to queue.</item>
<item quantity="other">%d episodes added to queue.</item>
@@ -266,6 +264,8 @@
<string name="download_running">Download running</string>
<string name="download_error_details">Details</string>
<string name="download_log_details_message">%1$s \n\nTechnical reason: \n%2$s \n\nFile URL:\n%3$s</string>
+ <string name="download_error_retrying">Download of \"%1$s\" failed. Will be retried later.</string>
+ <string name="download_error_not_retrying">Download of \"%1$s\" failed.</string>
<string name="download_error_tap_for_details">Tap to view details.</string>
<string name="download_error_device_not_found">Storage Device not found</string>
<string name="download_error_insufficient_space">There is not enough space left on your device.</string>
@@ -277,7 +277,7 @@
<string name="download_error_not_found">The podcast host\'s server does not know where to find the file. It may have been deleted.</string>
<string name="download_error_connection_error">Connection Error</string>
<string name="download_error_unknown_host">Cannot find the server. Check if the address is typed correctly and if you have a working network connection.</string>
- <string name="download_error_unauthorized">Authentication Error</string>
+ <string name="download_error_unauthorized">Authentication Error. Make sure that username and password are correct.</string>
<string name="download_error_file_type_type">File Type Error</string>
<string name="download_error_forbidden">The podcast host\'s server refuses to respond.</string>
<string name="download_canceled_msg">Download canceled</string>
@@ -285,7 +285,6 @@
<string name="download_error_blocked">The download was blocked by another app on your device (like a VPN or ad blocker).</string>
<string name="download_error_certificate">Unable to establish a secure connection. This can mean that another app on your device (like a VPN or an ad blocker) blocked the download, or that something is wrong with the server certificates.</string>
<string name="download_report_title">Downloads completed with error(s)</string>
- <string name="auto_download_report_title">Auto-downloads completed</string>
<string name="download_error_io_error">IO Error</string>
<string name="download_error_request_error">Request Error</string>
<string name="download_error_db_access">Database Access Error</string>
@@ -293,8 +292,6 @@
<item quantity="one">%d download left</item>
<item quantity="other">%d downloads left</item>
</plurals>
- <string name="completing">Completing…</string>
- <string name="download_notification_title">Downloading podcast data</string>
<string name="download_notification_title_feeds">Refreshing podcasts</string>
<string name="download_notification_title_episodes">Downloading episodes</string>
<string name="download_log_title_unknown">Unknown Title</string>
@@ -303,16 +300,14 @@
<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>
- <string name="authentication_notification_msg">The resource you requested requires a username and a password</string>
<string name="confirm_mobile_download_dialog_title">Confirm Mobile Download</string>
- <string name="confirm_mobile_download_dialog_message_not_in_queue">Downloading over mobile data connection is disabled in the settings.\n\nYou can choose to either only add the episode to the queue or you can allow downloading temporarily.\n\n<small>Your choice will be remembered for 10 minutes.</small></string>
- <string name="confirm_mobile_download_dialog_message">Downloading over mobile data connection is disabled in the settings.\n\nDo you want to allow downloading temporarily?\n\n<small>Your choice will be remembered for 10 minutes.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Downloading over mobile data connection is disabled in the settings. AntennaPod can download the episode later automatically when Wi-Fi is available.</string>
+ <string name="confirm_mobile_download_dialog_download_later">Download later</string>
+ <string name="confirm_mobile_download_dialog_allow_this_time">Download anyway</string>
<string name="confirm_mobile_streaming_notification_title">Confirm Mobile streaming</string>
<string name="confirm_mobile_streaming_notification_message">Streaming over mobile data connection is disabled in the settings. Tap to stream anyway.</string>
<string name="confirm_mobile_streaming_button_always">Always</string>
<string name="confirm_mobile_streaming_button_once">Once</string>
- <string name="confirm_mobile_download_dialog_only_add_to_queue">Enqueue</string>
- <string name="confirm_mobile_download_dialog_enable_temporarily">Allow temporarily</string>
<!-- Mediaplayer messages -->
<string name="playback_error_generic"><![CDATA[The media file could not be played.\n\n- Try deleting and re-downloading the episode.\n- Check your network connection, and make sure no VPN or login page is blocking access.\n- Try long-pressing and sharing the \"Media address\" to your web browser to see if it can be played there. If not, contact the podcast creators.]]></string>
@@ -453,7 +448,6 @@
<string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string>
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
- <string name="pref_parallel_downloads_title">Parallel Downloads</string>
<string name="pref_episode_cache_title">Episode Cache</string>
<string name="pref_episode_cache_summary">Total number of downloaded episodes cached on the device. Automatic download will be suspended if this number is reached.</string>
<string name="pref_episode_cover_title">Use Episode Cover</string>
diff --git a/ui/png-icons/src/main/res/drawable/ic_notification_cancel.xml b/ui/png-icons/src/main/res/drawable/ic_notification_cancel.xml
deleted file mode 100644
index a5480c71f..000000000
--- a/ui/png-icons/src/main/res/drawable/ic_notification_cancel.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
-</vector>
diff --git a/ui/png-icons/src/main/res/drawable/ic_notification_key.xml b/ui/png-icons/src/main/res/drawable/ic_notification_key.xml
deleted file mode 100644
index c8a817eeb..000000000
--- a/ui/png-icons/src/main/res/drawable/ic_notification_key.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
- <path android:fillColor="#FFFFFFFF"
- android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
-</vector>