summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java166
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java139
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java101
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java167
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManager.java112
-rw-r--r--core/src/main/res/values/arrays.xml22
-rw-r--r--core/src/main/res/values/ids.xml1
13 files changed, 351 insertions, 422 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index 9046b7165..79c6dd6bc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -8,9 +8,8 @@ import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
-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.DBTasks;
+import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -30,6 +29,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import de.danoeh.antennapod.core.export.opml.OpmlElement;
import de.danoeh.antennapod.core.export.opml.OpmlReader;
@@ -144,9 +144,10 @@ public class OpmlBackupAgent extends BackupAgentHelper {
mChecksum = digester == null ? null : digester.digest();
for (OpmlElement opmlElem : opmlElements) {
Feed feed = new Feed(opmlElem.getXmlUrl(), null, opmlElem.getText());
- DownloadRequest request = DownloadRequestCreator.create(feed).build();
- DownloadServiceInterface.get().download(mContext, false, request);
+ feed.setItems(Collections.emptyList());
+ DBTasks.updateFeed(mContext, feed, false);
}
+ FeedUpdateManager.runOnce(mContext);
} catch (XmlPullParserException e) {
Log.e(TAG, "Error while parsing the OPML file", e);
} catch (IOException e) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index 9ce89ebe2..e30b49280 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -6,7 +6,7 @@ import android.content.Intent;
import android.util.Log;
import de.danoeh.antennapod.core.ClientConfigurator;
-import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
+import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
/**
* Refreshes all feeds when it receives an intent
@@ -20,7 +20,7 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
Log.d(TAG, "Received intent");
ClientConfigurator.initialize(context);
- AutoUpdateManager.runOnce(context);
+ FeedUpdateManager.runOnce(context);
}
}
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 49c5211b0..bbf0dc357 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
@@ -1,47 +1,177 @@
package de.danoeh.antennapod.core.service;
+import android.app.Notification;
import android.content.Context;
-import androidx.annotation.NonNull;
import android.util.Log;
-
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.work.ForegroundInfo;
+import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
-
+import com.annimon.stream.Collectors;
+import com.annimon.stream.Stream;
import de.danoeh.antennapod.core.ClientConfigurator;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
+import de.danoeh.antennapod.core.service.download.DefaultDownloaderFactory;
+import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
+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.DBWriter;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
+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.feed.Feed;
+import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-public class FeedUpdateWorker extends Worker {
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+public class FeedUpdateWorker extends Worker {
private static final String TAG = "FeedUpdateWorker";
- public static final String PARAM_RUN_ONCE = "runOnce";
+ private final NewEpisodesNotification newEpisodesNotification;
public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
+ newEpisodesNotification = new NewEpisodesNotification();
}
@Override
@NonNull
public Result doWork() {
- final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false);
- Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce);
ClientConfigurator.initialize(getApplicationContext());
+ newEpisodesNotification.loadCountersBeforeRefresh();
- if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) {
- DBTasks.refreshAllFeeds(getApplicationContext(), false);
- } else {
+ if (!NetworkUtils.networkAvailable() || !NetworkUtils.isFeedRefreshAllowed()) {
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
+ return Result.retry();
}
- if (!isRunOnce && UserPreferences.isAutoUpdateTimeOfDay()) {
- // WorkManager does not allow to set specific time for repeated tasks.
- // We repeatedly schedule a OneTimeWorkRequest instead.
- AutoUpdateManager.restartUpdateAlarm(getApplicationContext());
+ List<Feed> toUpdate;
+ long feedId = getInputData().getLong(FeedUpdateManager.EXTRA_FEED_ID, -1);
+ if (feedId == -1) { // Update all
+ toUpdate = DBReader.getFeedList();
+ Iterator<Feed> itr = toUpdate.iterator();
+ while (itr.hasNext()) {
+ Feed feed = itr.next();
+ if (!feed.getPreferences().getKeepUpdated()) {
+ itr.remove();
+ }
+ }
+ Collections.shuffle(toUpdate); // If the worker gets cancelled early, every feed has a chance to be updated
+ refreshFeeds(toUpdate, false);
+ } else {
+ toUpdate = new ArrayList<>();
+ Feed feed = DBReader.getFeed(feedId);
+ if (feed == null) {
+ return Result.success();
+ }
+ toUpdate.add(feed);
+ refreshFeeds(toUpdate, true);
}
-
return Result.success();
}
+
+ @NonNull
+ private ForegroundInfo createForegroundInfo(List<Feed> toUpdate) {
+ Context context = getApplicationContext();
+ String contentText = context.getResources().getQuantityString(R.plurals.downloads_left,
+ toUpdate.size(), toUpdate.size());
+ String bigText = Stream.of(toUpdate).map(feed -> "• " + feed.getTitle()).collect(Collectors.joining("\n"));
+ Notification notification = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
+ .setContentTitle(context.getString(R.string.download_notification_title_feeds))
+ .setContentText(contentText)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText))
+ .setSmallIcon(R.drawable.ic_notification_sync)
+ .setOngoing(true)
+ .addAction(R.drawable.ic_cancel, context.getString(R.string.cancel_label),
+ WorkManager.getInstance(context).createCancelPendingIntent(getId()))
+ .build();
+ return new ForegroundInfo(R.id.notification_updating_feeds, notification);
+ }
+
+ private void refreshFeeds(List<Feed> toUpdate, boolean force) {
+ while (!toUpdate.isEmpty()) {
+ if (isStopped()) {
+ return;
+ }
+ setForegroundAsync(createForegroundInfo(toUpdate));
+ Feed feed = toUpdate.get(0);
+ try {
+ if (feed.isLocalFeed()) {
+ LocalFeedUpdater.updateFeed(feed, getApplicationContext(), null);
+ } else {
+ refreshFeed(feed, force);
+ }
+ } catch (Exception e) {
+ DBWriter.setFeedLastUpdateFailed(feed.getId(), true);
+ DownloadStatus status = new DownloadStatus(feed, feed.getTitle(),
+ DownloadError.ERROR_IO_ERROR, false, e.getMessage(), true);
+ DBWriter.addDownloadStatus(status);
+ }
+ toUpdate.remove(0);
+ }
+ }
+
+ void refreshFeed(Feed feed, boolean force) throws Exception {
+ boolean nextPage = getInputData().getBoolean(FeedUpdateManager.EXTRA_NEXT_PAGE, false)
+ && feed.getNextPageLink() != null;
+ if (nextPage) {
+ feed.setPageNr(feed.getPageNr() + 1);
+ }
+ DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
+ builder.setForce(force || feed.hasLastUpdateFailed());
+ if (nextPage) {
+ builder.setSource(feed.getNextPageLink());
+ }
+ DownloadRequest request = builder.build();
+
+ Downloader downloader = new DefaultDownloaderFactory().create(request);
+ if (downloader == null) {
+ throw new Exception("Unable to create downloader");
+ }
+
+ downloader.call();
+
+ if (!downloader.getResult().isSuccessful()) {
+ if (downloader.getResult().isCancelled()) {
+ return;
+ }
+ DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
+ DBWriter.addDownloadStatus(downloader.getResult());
+ return;
+ }
+
+ FeedSyncTask feedSyncTask = new FeedSyncTask(getApplicationContext(), request);
+ boolean success = feedSyncTask.run();
+
+ if (!success) {
+ DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
+ DBWriter.addDownloadStatus(feedSyncTask.getDownloadStatus());
+ return;
+ }
+
+ if (request.getFeedfileId() == 0) {
+ return; // No download logs for new subscriptions
+ }
+ // we create a 'successful' download log if the feed's last refresh failed
+ List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
+ if (log.size() > 0 && !log.get(0).isSuccessful()) {
+ DBWriter.addDownloadStatus(feedSyncTask.getDownloadStatus());
+ }
+ newEpisodesNotification.showIfNeeded(getApplicationContext(), feedSyncTask.getSavedFeed());
+ if (downloader.permanentRedirectUrl != null) {
+ DBWriter.updateFeedDownloadURL(request.getSource(), downloader.permanentRedirectUrl);
+ } else if (feedSyncTask.getRedirectUrl() != null) {
+ DBWriter.updateFeedDownloadURL(request.getSource(), feedSyncTask.getRedirectUrl());
+ }
+ }
}
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
index 0e55e9a36..9c238137e 100644
--- 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
@@ -10,19 +10,29 @@ 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.feed.LocalFeedUpdater;
+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;
@@ -39,22 +49,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.util.download.ConnectionStateMonitor;
-import de.danoeh.antennapod.event.FeedItemEvent;
-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.core.service.download.handler.FailedDownloadHandler;
-import de.danoeh.antennapod.core.service.download.handler.FeedSyncTask;
-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.model.download.DownloadError;
-
/**
* 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
@@ -69,7 +63,6 @@ public class DownloadService extends Service {
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_REFRESH_ALL = "refreshAll";
public static final String EXTRA_INITIATED_BY_USER = "initiatedByUser";
public static final String EXTRA_CLEANUP_MEDIA = "cleanupMedia";
@@ -85,7 +78,6 @@ public class DownloadService extends Service {
private final List<DownloadStatus> reportQueue = new ArrayList<>();
private final List<DownloadRequest> failedRequestsForReport = new ArrayList<>();
private DownloadServiceNotification notificationManager;
- private final NewEpisodesNotification newEpisodesNotification;
private NotificationUpdater notificationUpdater;
private ScheduledFuture<?> notificationUpdaterFuture;
private ScheduledFuture<?> downloadPostFuture;
@@ -99,16 +91,12 @@ public class DownloadService extends Service {
}
public DownloadService() {
- newEpisodesNotification = new NewEpisodesNotification();
downloadEnqueueExecutor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "EnqueueThread");
t.setPriority(Thread.MIN_PRIORITY);
return t;
});
- // Must be the first runnable in syncExecutor
- downloadEnqueueExecutor.execute(newEpisodesNotification::loadCountersBeforeRefresh);
-
Log.d(TAG, "parallel downloads: " + UserPreferences.getParallelDownloads());
downloadHandleExecutor = Executors.newFixedThreadPool(UserPreferences.getParallelDownloads(),
r -> {
@@ -140,18 +128,6 @@ public class DownloadService extends Service {
connectionMonitor.enable(getApplicationContext());
}
- public static boolean isDownloadingFeeds() {
- if (!isRunning) {
- return false;
- }
- for (Downloader downloader : downloads) {
- if (downloader.request.getFeedfileType() == Feed.FEEDFILETYPE_FEED && !downloader.cancelled) {
- return true;
- }
- }
- return false;
- }
-
public static boolean isDownloadingFile(String downloadUrl) {
if (!isRunning) {
return false;
@@ -182,13 +158,6 @@ public class DownloadService extends Service {
NotificationManagerCompat.from(this).cancel(R.id.notification_auto_download_report);
setupNotificationUpdaterIfNecessary();
downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
- } else if (intent != null && intent.getBooleanExtra(EXTRA_REFRESH_ALL, false)) {
- Notification notification = notificationManager.updateNotifications(downloads);
- startForeground(R.id.notification_downloading, notification);
- NotificationManagerCompat.from(this).cancel(R.id.notification_download_report);
- NotificationManagerCompat.from(this).cancel(R.id.notification_auto_download_report);
- setupNotificationUpdaterIfNecessary();
- downloadEnqueueExecutor.execute(() -> enqueueAll(intent));
} else if (downloads.size() == 0) {
shutdown();
} else {
@@ -251,61 +220,12 @@ public class DownloadService extends Service {
});
}
- /**
- * This method MUST NOT, in any case, throw an exception.
- * Otherwise, it hangs up the refresh thread pool.
- */
- private void performLocalFeedRefresh(Downloader downloader, DownloadRequest request) {
- try {
- Feed feed = DBReader.getFeed(request.getFeedfileId());
- LocalFeedUpdater.updateFeed(feed, DownloadService.this, (scanned, totalFiles) -> {
- request.setSize(totalFiles);
- request.setSoFar(scanned);
- request.setProgressPercent((int) (100.0 * scanned / totalFiles));
- });
- } 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 == Feed.FEEDFILETYPE_FEED) {
- Log.d(TAG, "Handling completed Feed Download");
- FeedSyncTask feedSyncTask = new FeedSyncTask(DownloadService.this, request);
- boolean success = feedSyncTask.run();
-
- if (success) {
- if (request.getFeedfileId() == 0) {
- return; // No download logs for new subscriptions
- }
- // we create a 'successful' download log if the feed's last refresh failed
- List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
- if (log.size() > 0 && !log.get(0).isSuccessful()) {
- saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
- }
- if (!request.isInitiatedByUser()) {
- // Was stored in the database before and not initiated manually
- newEpisodesNotification.showIfNeeded(DownloadService.this, feedSyncTask.getSavedFeed());
- }
- if (downloader.permanentRedirectUrl != null) {
- DBWriter.updateFeedDownloadURL(request.getSource(), downloader.permanentRedirectUrl);
- } else if (feedSyncTask.getRedirectUrl() != null) {
- DBWriter.updateFeedDownloadURL(request.getSource(), feedSyncTask.getRedirectUrl());
- }
- } else {
- DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
- saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
- }
- } else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
Log.d(TAG, "Handling completed FeedMedia Download");
MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
handler.run();
@@ -461,23 +381,6 @@ public class DownloadService extends Service {
}
}
- private void enqueueAll(Intent intent) {
- boolean initiatedByUser = intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false);
- List<Feed> feeds = DBReader.getFeedList();
- for (Feed feed : feeds) {
- if (feed.getPreferences().getKeepUpdated()) {
- DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
- builder.withInitiatedByUser(initiatedByUser);
- if (feed.hasLastUpdateFailed()) {
- builder.setForce(true);
- }
- addNewRequest(builder.build());
- }
- }
- postDownloaders();
- stopServiceIfEverythingDone();
- }
-
private void addNewRequest(@NonNull DownloadRequest request) {
if (isDownloadingFile(request.getSource())) {
Log.d(TAG, "Skipped enqueueing request. Already running.");
@@ -487,17 +390,11 @@ public class DownloadService extends Service {
return;
}
Log.d(TAG, "Add new request: " + request.getSource());
- if (request.getSource().startsWith(Feed.PREFIX_LOCAL_FOLDER)) {
- Downloader downloader = new LocalFeedStubDownloader(request);
+ writeFileUrl(request);
+ Downloader downloader = downloaderFactory.create(request);
+ if (downloader != null) {
downloads.add(downloader);
- downloadHandleExecutor.submit(() -> performLocalFeedRefresh(downloader, request));
- } else {
- writeFileUrl(request);
- Downloader downloader = downloaderFactory.create(request);
- if (downloader != null) {
- downloads.add(downloader);
- downloadHandleExecutor.submit(() -> performDownload(downloader));
- }
+ downloadHandleExecutor.submit(() -> performDownload(downloader));
}
}
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 7b7e52e0e..976d8255f 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
@@ -5,6 +5,7 @@ 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 de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
@@ -49,10 +50,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
}
public void refreshAllFeeds(Context context, boolean initiatedByUser) {
- Intent launchIntent = new Intent(context, DownloadService.class);
- launchIntent.putExtra(DownloadService.EXTRA_REFRESH_ALL, true);
- launchIntent.putExtra(DownloadService.EXTRA_INITIATED_BY_USER, initiatedByUser);
- ContextCompat.startForegroundService(context, launchIntent);
+ FeedUpdateManager.runOnce(context);
}
public void cancel(Context context, String url) {
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 e3010fe24..9cb1166b4 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
@@ -1,23 +1,20 @@
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.feed.Feed;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadStatus;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
public class FeedSyncTask {
- private final DownloadRequest request;
private final Context context;
private Feed savedFeed;
private final FeedParserTask task;
private FeedHandlerResult feedHandlerResult;
public FeedSyncTask(Context context, DownloadRequest request) {
- this.request = request;
this.context = context;
this.task = new FeedParserTask(request);
}
@@ -29,13 +26,6 @@ public class FeedSyncTask {
}
savedFeed = DBTasks.updateFeed(context, feedHandlerResult.feed, false);
- // If loadAllPages=true, check if another page is available and queue it for download
- final boolean loadAllPages = request.getArguments().getBoolean(DownloadRequest.REQUEST_ARG_LOAD_ALL_PAGES);
- final Feed feed = feedHandlerResult.feed;
- if (loadAllPages && feed.getNextPageLink() != null) {
- feed.setId(savedFeed.getId());
- DBTasks.loadNextPageOfFeed(context, feed, true);
- }
return true;
}
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 18f157908..8b79d594c 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
@@ -1,20 +1,28 @@
package de.danoeh.antennapod.core.storage;
-import static android.content.Context.MODE_PRIVATE;
-
import android.content.Context;
-import android.content.SharedPreferences;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
-
import androidx.annotation.VisibleForTesting;
-
-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.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.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.net.sync.model.EpisodeAction;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
@@ -29,31 +37,12 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.event.FeedItemEvent;
-import de.danoeh.antennapod.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.event.MessageEvent;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.model.download.DownloadStatus;
-import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
-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.model.feed.FeedPreferences;
-import de.danoeh.antennapod.net.sync.model.EpisodeAction;
-
/**
* Provides methods for doing common tasks that use DBReader and DBWriter.
*/
public final class DBTasks {
private static final String TAG = "DBTasks";
- private static final String PREF_NAME = "dbtasks";
- private static final String PREF_LAST_REFRESH = "last_refresh";
-
/**
* Executor service used by the autodownloadUndownloadedEpisodes method.
*/
@@ -104,68 +93,12 @@ public final class DBTasks {
}
}
- /**
- * Refreshes all feeds.
- * It must not be from the main thread.
- * This method might ignore subsequent calls if it is still
- * enqueuing Feeds for download from a previous call
- *
- * @param context Might be used for accessing the database
- * @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
- */
- public static void refreshAllFeeds(final Context context, boolean initiatedByUser) {
- DownloadServiceInterface.get().refreshAllFeeds(context, initiatedByUser);
-
- SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
-
- SynchronizationQueueSink.syncNow();
- // Note: automatic download of episodes will be done but not here.
- // Instead it is done after all feeds have been refreshed (asynchronously),
- // in DownloadService.onDestroy()
- // See Issue #2577 for the details of the rationale
- }
-
-
-
- /**
- * Queues the next page of this Feed for download. The given Feed has to be a paged
- * Feed (isPaged()=true) and must contain a nextPageLink.
- *
- * @param context Used for requesting the download.
- * @param feed The feed whose next page should be loaded.
- * @param loadAllPages True if any subsequent pages should also be loaded, false otherwise.
- */
- public static void loadNextPageOfFeed(final Context context, Feed feed, boolean loadAllPages) {
- if (feed.isPaged() && feed.getNextPageLink() != null) {
- int pageNr = feed.getPageNr() + 1;
- Feed nextFeed = new Feed(feed.getNextPageLink(), null, feed.getTitle() + "(" + pageNr + ")");
- nextFeed.setPageNr(pageNr);
- nextFeed.setPaged(true);
- nextFeed.setId(feed.getId());
-
- DownloadRequest.Builder builder = DownloadRequestCreator.create(nextFeed);
- builder.loadAllPages(loadAllPages);
- DownloadServiceInterface.get().download(context, false, builder.build());
- } else {
- Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink");
- }
- }
-
public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser) {
forceRefreshFeed(context, feed, false, initiatedByUser);
}
- public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
- forceRefreshFeed(context, feed, true, true);
- }
-
private static void forceRefreshFeed(Context context, Feed feed, boolean loadAllPages, boolean initiatedByUser) {
- DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
- builder.withInitiatedByUser(initiatedByUser);
- builder.setForce(true);
- builder.loadAllPages(loadAllPages);
- DownloadServiceInterface.get().download(context, false, builder.build());
+ FeedUpdateManager.runOnce(context, feed);
}
/**
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 9b4146f15..dcee8a45a 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
@@ -665,6 +665,15 @@ public class DBWriter {
adapter.close();
}
+ public static Future<?> resetPagedFeedPage(Feed feed) {
+ return dbExec.submit(() -> {
+ final PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.resetPagedFeedPage(feed);
+ adapter.close();
+ });
+ }
+
/*
* Sets the 'read'-attribute of all specified FeedItems
*
@@ -698,7 +707,6 @@ public class DBWriter {
});
}
-
/**
* Sets the 'read'-attribute of a FeedItem to the specified value.
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
index 73f467154..2fd492cbd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
@@ -20,16 +20,15 @@ import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
+import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
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.DownloadService;
-import de.danoeh.antennapod.core.service.download.DownloadRequestCreator;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -153,9 +152,10 @@ public class SyncService extends Worker {
continue;
}
if (!UrlChecker.containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) {
- Feed feed = new Feed(downloadUrl, null);
- DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
- DownloadServiceInterface.get().download(getApplicationContext(), false, builder.build());
+ Feed feed = new Feed(downloadUrl, null, "Unknown podcast");
+ feed.setItems(Collections.emptyList());
+ Feed newFeed = DBTasks.updateFeed(getApplicationContext(), feed, false);
+ FeedUpdateManager.runOnce(getApplicationContext(), newFeed);
}
}
@@ -193,9 +193,13 @@ public class SyncService extends Worker {
private void waitForDownloadServiceCompleted() {
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_wait_for_downloads));
try {
- while (DownloadService.isRunning) {
+ while (true) {
//noinspection BusyWait
Thread.sleep(1000);
+ FeedUpdateRunningEvent event = EventBus.getDefault().getStickyEvent(FeedUpdateRunningEvent.class);
+ if (event == null || !event.isFeedUpdateRunning) {
+ return;
+ }
}
} catch (InterruptedException e) {
e.printStackTrace();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
deleted file mode 100644
index 0602fc4fe..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package de.danoeh.antennapod.core.util.download;
-
-import android.content.Context;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import androidx.work.Constraints;
-import androidx.work.Data;
-import androidx.work.ExistingPeriodicWorkPolicy;
-import androidx.work.ExistingWorkPolicy;
-import androidx.work.NetworkType;
-import androidx.work.OneTimeWorkRequest;
-import androidx.work.PeriodicWorkRequest;
-import androidx.work.WorkManager;
-
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.FeedUpdateWorker;
-import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-
-public class AutoUpdateManager {
- private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker";
- private static final String WORK_ID_FEED_UPDATE_ONCE = WORK_ID_FEED_UPDATE + "Once";
- private static final String TAG = "AutoUpdateManager";
-
- private AutoUpdateManager() {
-
- }
-
- /**
- * Start / restart periodic auto feed refresh
- * @param context Context
- */
- public static void restartUpdateAlarm(Context context) {
- if (UserPreferences.isAutoUpdateDisabled()) {
- disableAutoUpdate(context);
- } else if (UserPreferences.isAutoUpdateTimeOfDay()) {
- int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
- Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
- restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1], context);
- } else {
- long milliseconds = UserPreferences.getUpdateInterval();
- restartUpdateIntervalAlarm(milliseconds, context);
- }
- }
-
- /**
- * Sets the interval in which the feeds are refreshed automatically
- */
- private static void restartUpdateIntervalAlarm(long intervalMillis, Context context) {
- Log.d(TAG, "Restarting update alarm.");
-
- PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class,
- intervalMillis, TimeUnit.MILLISECONDS)
- .setConstraints(getConstraints())
- .build();
-
- WorkManager.getInstance(context).enqueueUniquePeriodicWork(
- WORK_ID_FEED_UPDATE, ExistingPeriodicWorkPolicy.REPLACE, workRequest);
- }
-
- /**
- * Sets time of day the feeds are refreshed automatically
- */
- private static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute, Context context) {
- Log.d(TAG, "Restarting update alarm.");
-
- Calendar now = Calendar.getInstance();
- Calendar alarm = (Calendar) now.clone();
- alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay);
- alarm.set(Calendar.MINUTE, minute);
- if (alarm.before(now) || alarm.equals(now)) {
- alarm.add(Calendar.DATE, 1);
- }
- long triggerAtMillis = alarm.getTimeInMillis() - now.getTimeInMillis();
-
- OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
- .setConstraints(getConstraints())
- .setInitialDelay(triggerAtMillis, TimeUnit.MILLISECONDS)
- .build();
-
- WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_FEED_UPDATE,
- ExistingWorkPolicy.REPLACE, workRequest);
- }
-
- /**
- * Run auto feed refresh once in background, as soon as what OS scheduling allows.
- *
- * Callers from UI should use {@link #runImmediate(Context)}, as it will guarantee
- * the refresh be run immediately.
- * @param context Context
- */
- public static void runOnce(Context context) {
- Log.d(TAG, "Run auto update once, as soon as OS allows.");
-
- OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
- .setConstraints(getConstraints())
- .setInitialDelay(0L, TimeUnit.MILLISECONDS)
- .setInputData(new Data.Builder()
- .putBoolean(FeedUpdateWorker.PARAM_RUN_ONCE, true)
- .build()
- )
- .build();
-
- WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_FEED_UPDATE_ONCE,
- ExistingWorkPolicy.REPLACE, workRequest);
-
- }
-
- /**
- /**
- * Run auto feed refresh once in background immediately, using its own thread.
- *
- * Callers where the additional threads is not suitable should use {@link #runOnce(Context)}
- */
- public static void runImmediate(@NonNull Context context) {
- Log.d(TAG, "Run auto update immediately in background.");
- if (!NetworkUtils.networkAvailable()) {
- Log.d(TAG, "Ignoring: No network connection.");
- } else if (NetworkUtils.isFeedRefreshAllowed()) {
- startRefreshAllFeeds(context);
- } else {
- confirmMobileAllFeedsRefresh(context);
- }
- }
-
- private static void confirmMobileAllFeedsRefresh(final Context context) {
- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
- .setTitle(R.string.feed_refresh_title)
- .setMessage(R.string.confirm_mobile_feed_refresh_dialog_message)
- .setPositiveButton(R.string.confirm_mobile_streaming_button_once,
- (dialog, which) -> startRefreshAllFeeds(context))
- .setNeutralButton(R.string.confirm_mobile_streaming_button_always, (dialog, which) -> {
- UserPreferences.setAllowMobileFeedRefresh(true);
- startRefreshAllFeeds(context);
- })
- .setNegativeButton(R.string.no, null);
- builder.show();
- }
-
- private static void startRefreshAllFeeds(final Context context) {
- new Thread(() -> DBTasks.refreshAllFeeds(
- context.getApplicationContext(), true), "ManualRefreshAllFeeds").start();
- }
-
- public static void disableAutoUpdate(Context context) {
- WorkManager.getInstance(context).cancelUniqueWork(WORK_ID_FEED_UPDATE);
- }
-
- private static Constraints getConstraints() {
- Constraints.Builder constraints = new Constraints.Builder();
-
- if (UserPreferences.isAllowMobileFeedRefresh()) {
- constraints.setRequiredNetworkType(NetworkType.CONNECTED);
- } else {
- constraints.setRequiredNetworkType(NetworkType.UNMETERED);
- }
- return constraints.build();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManager.java
new file mode 100644
index 000000000..d1a273d4e
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManager.java
@@ -0,0 +1,112 @@
+package de.danoeh.antennapod.core.util.download;
+
+import android.content.Context;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.work.Constraints;
+import androidx.work.Data;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.ExistingWorkPolicy;
+import androidx.work.NetworkType;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.OutOfQuotaPolicy;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.service.FeedUpdateWorker;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+
+import java.util.concurrent.TimeUnit;
+
+public class FeedUpdateManager {
+ public static final String WORK_TAG_FEED_UPDATE = "feedUpdate";
+ private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker";
+ private static final String WORK_ID_FEED_UPDATE_MANUAL = "feedUpdateManual";
+ public static final String EXTRA_FEED_ID = "feed_id";
+ public static final String EXTRA_NEXT_PAGE = "next_page";
+ private static final String TAG = "AutoUpdateManager";
+
+ private FeedUpdateManager() {
+
+ }
+
+ /**
+ * Start / restart periodic auto feed refresh
+ * @param context Context
+ */
+ public static void restartUpdateAlarm(Context context, boolean replace) {
+ if (UserPreferences.isAutoUpdateDisabled()) {
+ WorkManager.getInstance(context).cancelUniqueWork(WORK_ID_FEED_UPDATE);
+ } else {
+ PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
+ FeedUpdateWorker.class, UserPreferences.getUpdateInterval(), TimeUnit.HOURS)
+ .setConstraints(getConstraints())
+ .build();
+ WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_ID_FEED_UPDATE,
+ replace ? ExistingPeriodicWorkPolicy.REPLACE : ExistingPeriodicWorkPolicy.KEEP, workRequest);
+ }
+ }
+
+ public static void runOnce(Context context) {
+ runOnce(context, null, false);
+ }
+
+ public static void runOnce(Context context, Feed feed) {
+ runOnce(context, feed, false);
+ }
+
+ public static void runOnce(Context context, Feed feed, boolean nextPage) {
+ OneTimeWorkRequest.Builder workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
+ .setInitialDelay(0L, TimeUnit.MILLISECONDS)
+ .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+ .addTag(WORK_TAG_FEED_UPDATE);
+ if (feed != null) {
+ Data.Builder builder = new Data.Builder();
+ builder.putLong(EXTRA_FEED_ID, feed.getId());
+ builder.putBoolean(EXTRA_NEXT_PAGE, nextPage);
+ workRequest.setInputData(builder.build());
+ }
+ WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_FEED_UPDATE_MANUAL,
+ ExistingWorkPolicy.REPLACE, workRequest.build());
+ }
+
+ public static void runOnceOrAsk(@NonNull Context context) {
+ Log.d(TAG, "Run auto update immediately in background.");
+ if (!NetworkUtils.networkAvailable()) {
+ Log.d(TAG, "Ignoring: No network connection.");
+ } else if (NetworkUtils.isFeedRefreshAllowed()) {
+ runOnce(context);
+ } else {
+ confirmMobileAllFeedsRefresh(context);
+ }
+ }
+
+ private static void confirmMobileAllFeedsRefresh(final Context context) {
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
+ .setTitle(R.string.feed_refresh_title)
+ .setMessage(R.string.confirm_mobile_feed_refresh_dialog_message)
+ .setPositiveButton(R.string.confirm_mobile_streaming_button_once,
+ (dialog, which) -> runOnce(context))
+ .setNeutralButton(R.string.confirm_mobile_streaming_button_always, (dialog, which) -> {
+ UserPreferences.setAllowMobileFeedRefresh(true);
+ runOnce(context);
+ })
+ .setNegativeButton(R.string.no, null);
+ builder.show();
+ }
+
+ private static Constraints getConstraints() {
+ Constraints.Builder constraints = new Constraints.Builder();
+
+ if (UserPreferences.isAllowMobileFeedRefresh()) {
+ constraints.setRequiredNetworkType(NetworkType.CONNECTED);
+ } else {
+ constraints.setRequiredNetworkType(NetworkType.UNMETERED);
+ }
+ return constraints.build();
+ }
+
+}
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 39f62a5d7..f3c0a0a3c 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -25,6 +25,28 @@
<item>heavy</item>
</string-array>
+ <string-array name="feed_refresh_interval_entries">
+ <item>@string/feed_refresh_never</item>
+ <item>@string/feed_every_hour</item>
+ <item>@string/feed_every_2_hours</item>
+ <item>@string/feed_every_4_hours</item>
+ <item>@string/feed_every_8_hours</item>
+ <item>@string/feed_every_12_hours</item>
+ <item>@string/feed_every_24_hours</item>
+ <item>@string/feed_every_72_hours</item>
+ </string-array>
+
+ <string-array name="feed_refresh_interval_values">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>4</item>
+ <item>8</item>
+ <item>12</item>
+ <item>24</item>
+ <item>72</item>
+ </string-array>
+
<string-array name="globalNewEpisodesActionItems">
<item>@string/feed_new_episodes_action_add_to_inbox</item>
<item>@string/feed_new_episodes_action_nothing</item>
diff --git a/core/src/main/res/values/ids.xml b/core/src/main/res/values/ids.xml
index 87046cc0f..90d143d38 100644
--- a/core/src/main/res/values/ids.xml
+++ b/core/src/main/res/values/ids.xml
@@ -19,6 +19,7 @@
<item name="notification_gpodnet_sync_error" type="id"/>
<item name="notification_gpodnet_sync_autherror" type="id"/>
<item name="notification_downloading" type="id"/>
+ <item name="notification_updating_feeds" type="id"/>
<item name="notification_download_report" type="id"/>
<item name="notification_auto_download_report" type="id"/>
<item name="notification_playing" type="id"/>