summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
authorByteHamster <ByteHamster@users.noreply.github.com>2024-03-29 19:27:53 +0100
committerGitHub <noreply@github.com>2024-03-29 19:27:53 +0100
commit2fd73b148d012fba7308c86494689103b8aaace4 (patch)
tree75782c565600eadd67d0fca46acf637370fcf4a3 /core/src/main
parent6f3a9b16764a57e43994ccbeeada5224dee93f44 (diff)
downloadAntennaPod-2fd73b148d012fba7308c86494689103b8aaace4.zip
Move download service to module (#7041)
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java62
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java188
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java286
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java221
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java123
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java96
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java62
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java311
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java314
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java147
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java125
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java119
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FastDocumentFile.java72
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java76
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/InvalidFeedException.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManagerImpl.java118
-rw-r--r--core/src/main/res/values/ids.xml4
21 files changed, 0 insertions, 2373 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java
deleted file mode 100644
index 8b5f9f286..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package de.danoeh.antennapod.core;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import de.danoeh.antennapod.core.storage.AutoDownloadManagerImpl;
-import de.danoeh.antennapod.core.util.download.FeedUpdateManagerImpl;
-import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager;
-import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
-import de.danoeh.antennapod.net.sync.service.SyncService;
-import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
-import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
-import de.danoeh.antennapod.storage.preferences.SynchronizationCredentials;
-import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.storage.preferences.SleepTimerPreferences;
-import de.danoeh.antennapod.storage.preferences.UsageStatistics;
-import de.danoeh.antennapod.net.common.UserAgentInterceptor;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.net.common.AntennapodHttpClient;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.core.service.download.DownloadServiceInterfaceImpl;
-import de.danoeh.antennapod.net.common.NetworkUtils;
-import de.danoeh.antennapod.core.util.download.NetworkConnectionChangeHandler;
-import de.danoeh.antennapod.net.ssl.SslProviderInstaller;
-import de.danoeh.antennapod.storage.database.PodDBAdapter;
-
-import de.danoeh.antennapod.ui.notifications.NotificationUtils;
-import java.io.File;
-
-public class ClientConfigurator {
- private static boolean initialized = false;
-
- public static synchronized void initialize(Context context) {
- if (initialized) {
- return;
- }
- try {
- PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- UserAgentInterceptor.USER_AGENT = "AntennaPod/" + packageInfo.versionName;
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- }
- PodDBAdapter.init(context);
- UserPreferences.init(context);
- SynchronizationCredentials.init(context);
- SynchronizationSettings.init(context);
- UsageStatistics.init(context);
- PlaybackPreferences.init(context);
- SslProviderInstaller.install(context);
- NetworkUtils.init(context);
- NetworkConnectionChangeHandler.init(context);
- DownloadServiceInterface.setImpl(new DownloadServiceInterfaceImpl());
- FeedUpdateManager.setInstance(new FeedUpdateManagerImpl());
- AutoDownloadManager.setInstance(new AutoDownloadManagerImpl());
- SynchronizationQueueSink.setServiceStarterImpl(() -> SyncService.sync(context));
- AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp"));
- AntennapodHttpClient.setProxyConfig(UserPreferences.getProxyConfig());
- SleepTimerPreferences.init(context);
- NotificationUtils.createChannels(context);
- initialized = true;
- }
-}
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
deleted file mode 100644
index b30f657a1..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ /dev/null
@@ -1,188 +0,0 @@
-package de.danoeh.antennapod.core.backup;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInputStream;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupHelper;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
-import de.danoeh.antennapod.storage.database.FeedDatabaseWriter;
-import de.danoeh.antennapod.storage.importexport.OpmlElement;
-import de.danoeh.antennapod.storage.importexport.OpmlReader;
-import de.danoeh.antennapod.storage.importexport.OpmlWriter;
-import org.apache.commons.io.IOUtils;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.math.BigInteger;
-import java.nio.charset.Charset;
-import java.security.DigestInputStream;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.storage.database.DBReader;
-
-public class OpmlBackupAgent extends BackupAgentHelper {
- private static final String OPML_BACKUP_KEY = "opml";
-
- @Override
- public void onCreate() {
- addHelper(OPML_BACKUP_KEY, new OpmlBackupHelper(this));
- }
-
- /**
- * Class for backing up and restoring the OPML file.
- */
- private static class OpmlBackupHelper implements BackupHelper {
- private static final String TAG = "OpmlBackupHelper";
-
- private static final String OPML_ENTITY_KEY = "antennapod-feeds.opml";
-
- private final Context mContext;
-
- /**
- * Checksum of restored OPML file
- */
- private byte[] mChecksum;
-
- public OpmlBackupHelper(Context context) {
- mContext = context;
- }
-
- @Override
- public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
- Log.d(TAG, "Performing backup");
- ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
- MessageDigest digester = null;
- Writer writer;
-
- try {
- digester = MessageDigest.getInstance("MD5");
- writer = new OutputStreamWriter(new DigestOutputStream(byteStream, digester),
- Charset.forName("UTF-8"));
- } catch (NoSuchAlgorithmException e) {
- writer = new OutputStreamWriter(byteStream, Charset.forName("UTF-8"));
- }
-
- try {
- // Write OPML
- OpmlWriter.writeDocument(DBReader.getFeedList(), writer);
-
- // Compare checksum of new and old file to see if we need to perform a backup at all
- if (digester != null) {
- byte[] newChecksum = digester.digest();
- Log.d(TAG, "New checksum: " + new BigInteger(1, newChecksum).toString(16));
-
- // Get the old checksum
- if (oldState != null) {
- try (final FileInputStream inState = new FileInputStream(oldState.getFileDescriptor())) {
- int len = inState.read();
-
- if (len != -1) {
- byte[] oldChecksum = new byte[len];
- IOUtils.read(inState, oldChecksum, 0, len);
- Log.d(TAG, "Old checksum: " + new BigInteger(1, oldChecksum).toString(16));
-
- if (Arrays.equals(oldChecksum, newChecksum)) {
- Log.d(TAG, "Checksums are the same; won't backup");
- return;
- }
- }
- }
- }
-
- writeNewStateDescription(newState, newChecksum);
- }
-
- Log.d(TAG, "Backing up OPML");
- byte[] bytes = byteStream.toByteArray();
- data.writeEntityHeader(OPML_ENTITY_KEY, bytes.length);
- data.writeEntityData(bytes, bytes.length);
- } catch (IOException e) {
- Log.e(TAG, "Error during backup", e);
- } finally {
- IOUtils.closeQuietly(writer);
- }
- }
-
- @Override
- public void restoreEntity(BackupDataInputStream data) {
- Log.d(TAG, "Backup restore");
-
- if (!OPML_ENTITY_KEY.equals(data.getKey())) {
- Log.d(TAG, "Unknown entity key: " + data.getKey());
- return;
- }
-
- MessageDigest digester = null;
- Reader reader;
-
- try {
- digester = MessageDigest.getInstance("MD5");
- reader = new InputStreamReader(new DigestInputStream(data, digester),
- Charset.forName("UTF-8"));
- } catch (NoSuchAlgorithmException e) {
- reader = new InputStreamReader(data, Charset.forName("UTF-8"));
- }
-
- try {
- ArrayList<OpmlElement> opmlElements = new OpmlReader().readDocument(reader);
- mChecksum = digester == null ? null : digester.digest();
- for (OpmlElement opmlElem : opmlElements) {
- Feed feed = new Feed(opmlElem.getXmlUrl(), null, opmlElem.getText());
- feed.setItems(Collections.emptyList());
- FeedDatabaseWriter.updateFeed(mContext, feed, false);
- }
- FeedUpdateManager.getInstance().runOnce(mContext);
- } catch (XmlPullParserException e) {
- Log.e(TAG, "Error while parsing the OPML file", e);
- } catch (IOException e) {
- Log.e(TAG, "Failed to restore OPML backup", e);
- } finally {
- IOUtils.closeQuietly(reader);
- }
- }
-
- @Override
- public void writeNewStateDescription(ParcelFileDescriptor newState) {
- writeNewStateDescription(newState, mChecksum);
- }
-
- /**
- * Writes the new state description, which is the checksum of the OPML file.
- *
- * @param newState
- * @param checksum
- */
- private void writeNewStateDescription(ParcelFileDescriptor newState, byte[] checksum) {
- if (checksum == null) {
- return;
- }
-
- try {
- FileOutputStream outState = new FileOutputStream(newState.getFileDescriptor());
- outState.write(checksum.length);
- outState.write(checksum);
- outState.flush();
- outState.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to write new state description", e);
- }
- }
- }
-}
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
deleted file mode 100644
index 8230924f9..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ /dev/null
@@ -1,286 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.content.Context;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.UUID;
-
-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.MediaMetadataRetrieverCompat;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.FeedDatabaseWriter;
-import de.danoeh.antennapod.storage.database.DBWriter;
-import de.danoeh.antennapod.parser.feed.util.DateUtils;
-import de.danoeh.antennapod.model.download.DownloadError;
-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.playback.MediaType;
-import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils;
-import de.danoeh.antennapod.parser.media.id3.ID3ReaderException;
-import de.danoeh.antennapod.parser.media.id3.Id3MetadataReader;
-import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentMetadataReader;
-import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentReaderException;
-import org.apache.commons.io.input.CountingInputStream;
-
-public class LocalFeedUpdater {
- private static final String TAG = "LocalFeedUpdater";
-
- static final String[] PREFERRED_FEED_IMAGE_FILENAMES = {"folder.jpg", "Folder.jpg", "folder.png", "Folder.png"};
-
- public static void updateFeed(Feed feed, Context context,
- @Nullable UpdaterProgressListener updaterProgressListener) {
- try {
- String uriString = feed.getDownloadUrl().replace(Feed.PREFIX_LOCAL_FOLDER, "");
- DocumentFile documentFolder = DocumentFile.fromTreeUri(context, Uri.parse(uriString));
- if (documentFolder == null) {
- throw new IOException("Unable to retrieve document tree. "
- + "Try re-connecting the folder on the podcast info page.");
- }
- if (!documentFolder.exists() || !documentFolder.canRead()) {
- throw new IOException("Cannot read local directory. "
- + "Try re-connecting the folder on the podcast info page.");
- }
- tryUpdateFeed(feed, context, documentFolder.getUri(), updaterProgressListener);
-
- if (mustReportDownloadSuccessful(feed)) {
- reportSuccess(feed);
- }
- } catch (Exception e) {
- e.printStackTrace();
- reportError(feed, e.getMessage());
- }
- }
-
- @VisibleForTesting
- static void tryUpdateFeed(Feed feed, Context context, Uri folderUri,
- UpdaterProgressListener updaterProgressListener) throws IOException {
- if (feed.getItems() == null) {
- feed.setItems(new ArrayList<>());
- }
- //make sure it is the latest 'version' of this feed from the db (all items etc)
- feed = FeedDatabaseWriter.updateFeed(context, feed, false);
-
- // list files in feed folder
- List<FastDocumentFile> allFiles = FastDocumentFile.list(context, folderUri);
- List<FastDocumentFile> mediaFiles = new ArrayList<>();
- Set<String> mediaFileNames = new HashSet<>();
- for (FastDocumentFile file : allFiles) {
- String mimeType = MimeTypeUtils.getMimeType(file.getType(), file.getUri().toString());
- MediaType mediaType = MediaType.fromMimeType(mimeType);
- if (mediaType == MediaType.AUDIO || mediaType == MediaType.VIDEO) {
- mediaFiles.add(file);
- mediaFileNames.add(file.getName());
- }
- }
-
- // add new files to feed and update item data
- List<FeedItem> newItems = feed.getItems();
- for (int i = 0; i < mediaFiles.size(); i++) {
- FeedItem oldItem = feedContainsFile(feed, mediaFiles.get(i).getName());
- FeedItem newItem = createFeedItem(feed, mediaFiles.get(i), context);
- if (oldItem == null) {
- newItems.add(newItem);
- } else {
- oldItem.updateFromOther(newItem);
- }
- if (updaterProgressListener != null) {
- updaterProgressListener.onLocalFileScanned(i, mediaFiles.size());
- }
- }
-
- // remove feed items without corresponding file
- Iterator<FeedItem> it = newItems.iterator();
- while (it.hasNext()) {
- FeedItem feedItem = it.next();
- if (!mediaFileNames.contains(feedItem.getLink())) {
- it.remove();
- }
- }
-
- feed.setImageUrl(getImageUrl(allFiles, folderUri));
-
- feed.getPreferences().setAutoDownload(false);
- feed.setDescription(context.getString(R.string.local_feed_description));
- feed.setAuthor(context.getString(R.string.local_folder));
-
- FeedDatabaseWriter.updateFeed(context, feed, true);
- }
-
- /**
- * Returns the image URL for the local feed.
- */
- @NonNull
- static String getImageUrl(List<FastDocumentFile> files, Uri folderUri) {
- // look for special file names
- for (String iconLocation : PREFERRED_FEED_IMAGE_FILENAMES) {
- for (FastDocumentFile file : files) {
- if (iconLocation.equals(file.getName())) {
- return file.getUri().toString();
- }
- }
- }
-
- // use the first image in the folder if existing
- for (FastDocumentFile file : files) {
- String mime = file.getType();
- if (mime != null && (mime.startsWith("image/jpeg") || mime.startsWith("image/png"))) {
- return file.getUri().toString();
- }
- }
-
- // use default icon as fallback
- return Feed.PREFIX_GENERATIVE_COVER + folderUri;
- }
-
- private static FeedItem feedContainsFile(Feed feed, String filename) {
- List<FeedItem> items = feed.getItems();
- for (FeedItem i : items) {
- if (i.getMedia() != null && i.getLink().equals(filename)) {
- return i;
- }
- }
- return null;
- }
-
- private static FeedItem createFeedItem(Feed feed, FastDocumentFile file, Context context) {
- FeedItem item = new FeedItem(0, file.getName(), UUID.randomUUID().toString(),
- file.getName(), new Date(file.getLastModified()), FeedItem.UNPLAYED, feed);
- item.disableAutoDownload();
-
- long size = file.getLength();
- FeedMedia media = new FeedMedia(0, item, 0, 0, size, file.getType(),
- file.getUri().toString(), file.getUri().toString(), false, null, 0, 0);
- item.setMedia(media);
-
- for (FeedItem existingItem : feed.getItems()) {
- if (existingItem.getMedia() != null
- && existingItem.getMedia().getDownloadUrl().equals(file.getUri().toString())
- && file.getLength() == existingItem.getMedia().getSize()) {
- // We found an old file that we already scanned. Re-use metadata.
- item.updateFromOther(existingItem);
- return item;
- }
- }
-
- // Did not find existing item. Scan metadata.
- try {
- loadMetadata(item, file, context);
- } catch (Exception e) {
- item.setDescriptionIfLonger(e.getMessage());
- }
- return item;
- }
-
- private static void loadMetadata(FeedItem item, FastDocumentFile file, Context context) {
- try (MediaMetadataRetrieverCompat mediaMetadataRetriever = new MediaMetadataRetrieverCompat()) {
- mediaMetadataRetriever.setDataSource(context, file.getUri());
-
- String dateStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
- if (!TextUtils.isEmpty(dateStr) && !"19040101T000000.000Z".equals(dateStr)) {
- try {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.getDefault());
- item.setPubDate(simpleDateFormat.parse(dateStr));
- } catch (ParseException parseException) {
- Date date = DateUtils.parse(dateStr);
- if (date != null) {
- item.setPubDate(date);
- }
- }
- }
-
- String title = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
- if (!TextUtils.isEmpty(title)) {
- item.setTitle(title);
- }
-
- String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- item.getMedia().setDuration((int) Long.parseLong(durationStr));
-
- item.getMedia().setHasEmbeddedPicture(mediaMetadataRetriever.getEmbeddedPicture() != null);
-
- try (InputStream inputStream = context.getContentResolver().openInputStream(file.getUri())) {
- Id3MetadataReader reader = new Id3MetadataReader(
- new CountingInputStream(new BufferedInputStream(inputStream)));
- reader.readInputStream();
- item.setDescriptionIfLonger(reader.getComment());
- } catch (IOException | ID3ReaderException e) {
- Log.d(TAG, "Unable to parse ID3 of " + file.getUri() + ": " + e.getMessage());
-
- try (InputStream inputStream = context.getContentResolver().openInputStream(file.getUri())) {
- VorbisCommentMetadataReader reader = new VorbisCommentMetadataReader(inputStream);
- reader.readInputStream();
- item.setDescriptionIfLonger(reader.getDescription());
- } catch (IOException | VorbisCommentReaderException e2) {
- Log.d(TAG, "Unable to parse vorbis comments of " + file.getUri() + ": " + e2.getMessage());
- }
- }
- }
- }
-
- private static void reportError(Feed feed, String reasonDetailed) {
- DownloadResult status = new DownloadResult(feed.getTitle(), feed.getId(),
- Feed.FEEDFILETYPE_FEED, false, DownloadError.ERROR_IO_ERROR, reasonDetailed);
- DBWriter.addDownloadStatus(status);
- DBWriter.setFeedLastUpdateFailed(feed.getId(), true);
- }
-
- /**
- * Reports a successful download status.
- */
- private static void reportSuccess(Feed feed) {
- DownloadResult status = new DownloadResult(feed.getTitle(), feed.getId(),
- Feed.FEEDFILETYPE_FEED, true, DownloadError.SUCCESS, null);
- DBWriter.addDownloadStatus(status);
- DBWriter.setFeedLastUpdateFailed(feed.getId(), false);
- }
-
- /**
- * Answers if reporting success is needed for the given feed.
- */
- private static boolean mustReportDownloadSuccessful(Feed feed) {
- List<DownloadResult> downloadResults = DBReader.getFeedDownloadLog(feed.getId());
-
- if (downloadResults.isEmpty()) {
- // report success if never reported before
- return true;
- }
-
- Collections.sort(downloadResults, (downloadStatus1, downloadStatus2) ->
- downloadStatus1.getCompletionDate().compareTo(downloadStatus2.getCompletionDate()));
-
- 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 !lastDownloadResult.isSuccessful();
- }
-
- @FunctionalInterface
- public interface UpdaterProgressListener {
- void onLocalFileScanned(int scanned, int totalFiles);
- }
-}
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 098c9bfa4..6b9644c41 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
@@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
-import de.danoeh.antennapod.core.ClientConfigurator;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
/**
@@ -18,8 +17,6 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
- ClientConfigurator.initialize(context);
-
FeedUpdateManager.getInstance().runOnce(context);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
index 8721ebb35..20621fd45 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
@@ -7,8 +7,6 @@ import androidx.core.content.ContextCompat;
import android.util.Log;
import android.view.KeyEvent;
-import de.danoeh.antennapod.core.ClientConfigurator;
-
/**
* Receives media button events.
*/
@@ -30,7 +28,6 @@ public class MediaButtonReceiver extends BroadcastReceiver {
}
KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
- ClientConfigurator.initialize(context);
Intent serviceIntent = new Intent(PLAYBACK_SERVICE_INTENT);
serviceIntent.setPackage(context.getPackageName());
serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode());
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
deleted file mode 100644
index e5828ac6e..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package de.danoeh.antennapod.core.service;
-
-import android.Manifest;
-import android.app.Notification;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.content.ContextCompat;
-import androidx.work.ForegroundInfo;
-import androidx.work.WorkManager;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import de.danoeh.antennapod.core.ClientConfigurator;
-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.FeedParserTask;
-import de.danoeh.antennapod.core.util.download.FeedUpdateManagerImpl;
-import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager;
-import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.FeedDatabaseWriter;
-import de.danoeh.antennapod.storage.database.DBWriter;
-import de.danoeh.antennapod.net.common.NetworkUtils;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestBuilder;
-import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
-import de.danoeh.antennapod.ui.notifications.NotificationUtils;
-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";
-
- private final NewEpisodesNotification newEpisodesNotification;
- private final NotificationManagerCompat notificationManager;
-
- public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
- super(context, params);
- newEpisodesNotification = new NewEpisodesNotification();
- notificationManager = NotificationManagerCompat.from(context);
- }
-
- @Override
- @NonNull
- public Result doWork() {
- ClientConfigurator.initialize(getApplicationContext());
- newEpisodesNotification.loadCountersBeforeRefresh();
-
- List<Feed> toUpdate;
- long feedId = getInputData().getLong(FeedUpdateManagerImpl.EXTRA_FEED_ID, -1);
- boolean allAreLocal = true;
- boolean force = false;
- 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();
- }
- if (!feed.isLocalFeed()) {
- allAreLocal = false;
- }
- }
- Collections.shuffle(toUpdate); // If the worker gets cancelled early, every feed has a chance to be updated
- } else {
- Feed feed = DBReader.getFeed(feedId);
- if (feed == null) {
- return Result.success();
- }
- if (!feed.isLocalFeed()) {
- allAreLocal = false;
- }
- toUpdate = new ArrayList<>();
- toUpdate.add(feed); // Needs to be updatable, so no singletonList
- force = true;
- }
-
- if (!getInputData().getBoolean(FeedUpdateManagerImpl.EXTRA_EVEN_ON_MOBILE, false) && !allAreLocal) {
- if (!NetworkUtils.networkAvailable() || !NetworkUtils.isFeedRefreshAllowed()) {
- Log.d(TAG, "Blocking automatic update");
- return Result.retry();
- }
- }
- refreshFeeds(toUpdate, force);
-
- notificationManager.cancel(R.id.notification_updating_feeds);
- AutoDownloadManager.getInstance().autodownloadUndownloadedItems(getApplicationContext());
- return Result.success();
- }
-
- @NonNull
- private Notification createNotification(@Nullable List<Feed> toUpdate) {
- Context context = getApplicationContext();
- String contentText = "";
- StringBuilder bigText = new StringBuilder();
- if (toUpdate != null) {
- contentText = context.getResources().getQuantityString(R.plurals.downloads_left,
- toUpdate.size(), toUpdate.size());
- for (int i = 0; i < toUpdate.size(); i++) {
- bigText.append("• ").append(toUpdate.get(i).getTitle());
- if (i != toUpdate.size() - 1) {
- bigText.append("\n");
- }
- }
- }
- return 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();
- }
-
- @NonNull
- @Override
- public ListenableFuture<ForegroundInfo> getForegroundInfoAsync() {
- return Futures.immediateFuture(new ForegroundInfo(R.id.notification_updating_feeds, createNotification(null)));
- }
-
- private void refreshFeeds(List<Feed> toUpdate, boolean force) {
- while (!toUpdate.isEmpty()) {
- if (isStopped()) {
- return;
- }
- if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.POST_NOTIFICATIONS)
- == PackageManager.PERMISSION_GRANTED) {
- notificationManager.notify(R.id.notification_updating_feeds, createNotification(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);
- DownloadResult status = new DownloadResult(feed.getTitle(),
- feed.getId(), Feed.FEEDFILETYPE_FEED, false,
- DownloadError.ERROR_IO_ERROR, e.getMessage());
- DBWriter.addDownloadStatus(status);
- }
- toUpdate.remove(0);
- }
- }
-
- void refreshFeed(Feed feed, boolean force) throws Exception {
- boolean nextPage = getInputData().getBoolean(FeedUpdateManagerImpl.EXTRA_NEXT_PAGE, false)
- && feed.getNextPageLink() != null;
- if (nextPage) {
- feed.setPageNr(feed.getPageNr() + 1);
- }
- DownloadRequestBuilder 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.cancelled || downloader.getResult().getReason() == DownloadError.ERROR_DOWNLOAD_CANCELLED) {
- return;
- }
- DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
- DBWriter.addDownloadStatus(downloader.getResult());
- return;
- }
-
- FeedParserTask parserTask = new FeedParserTask(request);
- FeedHandlerResult feedHandlerResult = parserTask.call();
- if (!parserTask.isSuccessful()) {
- DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
- DBWriter.addDownloadStatus(parserTask.getDownloadStatus());
- return;
- }
- feedHandlerResult.feed.setLastRefreshAttempt(System.currentTimeMillis());
- Feed savedFeed = FeedDatabaseWriter.updateFeed(getApplicationContext(), feedHandlerResult.feed, false);
-
- 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<DownloadResult> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
- if (!log.isEmpty() && !log.get(0).isSuccessful()) {
- DBWriter.addDownloadStatus(parserTask.getDownloadStatus());
- }
- newEpisodesNotification.showIfNeeded(getApplicationContext(), savedFeed);
- if (downloader.permanentRedirectUrl != null) {
- DBWriter.updateFeedDownloadURL(request.getSource(), downloader.permanentRedirectUrl);
- } else if (feedHandlerResult.redirectUrl != null
- && !feedHandlerResult.redirectUrl.equals(request.getSource())) {
- DBWriter.updateFeedDownloadURL(request.getSource(), feedHandlerResult.redirectUrl);
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java
deleted file mode 100644
index a1cc9bf6d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.util.Log;
-import android.webkit.URLUtil;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-
-public class DefaultDownloaderFactory implements DownloaderFactory {
- private static final String TAG = "DefaultDwnldrFactory";
-
- @Nullable
- @Override
- public Downloader create(@NonNull DownloadRequest request) {
- if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
- Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
- return null;
- }
- return new HttpDownloader(request);
- }
-} \ No newline at end of file
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
deleted file mode 100644
index 3e94e9f6b..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequestCreator.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.util.Log;
-import android.webkit.URLUtil;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestBuilder;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.FileNameGenerator;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import org.apache.commons.io.FilenameUtils;
-
-import java.io.File;
-
-/**
- * Creates download requests that can be sent to the DownloadService.
- */
-public class DownloadRequestCreator {
- private static final String TAG = "DownloadRequestCreat";
- private static final String FEED_DOWNLOADPATH = "cache/";
- private static final String MEDIA_DOWNLOADPATH = "media/";
-
- public static DownloadRequestBuilder create(Feed feed) {
- File dest = new File(getFeedfilePath(), getFeedfileName(feed));
- if (dest.exists()) {
- dest.delete();
- }
- Log.d(TAG, "Requesting download of url " + feed.getDownloadUrl());
-
- String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
- String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
-
- return new DownloadRequestBuilder(dest.toString(), feed)
- .withAuthentication(username, password)
- .lastModified(feed.getLastModified());
- }
-
- public static DownloadRequestBuilder create(FeedMedia media) {
- final boolean partiallyDownloadedFileExists =
- media.getLocalFileUrl() != null && new File(media.getLocalFileUrl()).exists();
- File dest;
- if (partiallyDownloadedFileExists) {
- dest = new File(media.getLocalFileUrl());
- } else {
- dest = new File(getMediafilePath(media), getMediafilename(media));
- }
-
- if (dest.exists() && !partiallyDownloadedFileExists) {
- dest = findUnusedFile(dest);
- }
- Log.d(TAG, "Requesting download of url " + media.getDownloadUrl());
-
- String username = (media.getItem().getFeed().getPreferences() != null)
- ? media.getItem().getFeed().getPreferences().getUsername() : null;
- String password = (media.getItem().getFeed().getPreferences() != null)
- ? media.getItem().getFeed().getPreferences().getPassword() : null;
-
- return new DownloadRequestBuilder(dest.toString(), media)
- .withAuthentication(username, password);
- }
-
- private static File findUnusedFile(File dest) {
- // find different name
- File newDest = null;
- for (int i = 1; i < Integer.MAX_VALUE; i++) {
- String newName = FilenameUtils.getBaseName(dest
- .getName())
- + "-"
- + i
- + FilenameUtils.EXTENSION_SEPARATOR
- + FilenameUtils.getExtension(dest.getName());
- Log.d(TAG, "Testing filename " + newName);
- newDest = new File(dest.getParent(), newName);
- if (!newDest.exists()) {
- Log.d(TAG, "File doesn't exist yet. Using " + newName);
- break;
- }
- }
- return newDest;
- }
-
- private static String getFeedfilePath() {
- return UserPreferences.getDataFolder(FEED_DOWNLOADPATH).toString() + "/";
- }
-
- private static String getFeedfileName(Feed feed) {
- String filename = feed.getDownloadUrl();
- if (feed.getTitle() != null && !feed.getTitle().isEmpty()) {
- filename = feed.getTitle();
- }
- return "feed-" + FileNameGenerator.generateFileName(filename) + feed.getId();
- }
-
- private static String getMediafilePath(FeedMedia media) {
- String mediaPath = MEDIA_DOWNLOADPATH
- + FileNameGenerator.generateFileName(media.getItem().getFeed().getTitle());
- return UserPreferences.getDataFolder(mediaPath).toString() + "/";
- }
-
- private static String getMediafilename(FeedMedia media) {
- String titleBaseFilename = "";
-
- // Try to generate the filename by the item title
- if (media.getItem() != null && media.getItem().getTitle() != null) {
- String title = media.getItem().getTitle();
- titleBaseFilename = FileNameGenerator.generateFileName(title);
- }
-
- String urlBaseFilename = URLUtil.guessFileName(media.getDownloadUrl(), null, media.getMimeType());
-
- String baseFilename;
- if (!titleBaseFilename.equals("")) {
- baseFilename = titleBaseFilename;
- } else {
- baseFilename = urlBaseFilename;
- }
- final int filenameMaxLength = 220;
- if (baseFilename.length() > filenameMaxLength) {
- baseFilename = baseFilename.substring(0, filenameMaxLength);
- }
- return baseFilename + FilenameUtils.EXTENSION_SEPARATOR + media.getId()
- + FilenameUtils.EXTENSION_SEPARATOR + FilenameUtils.getExtension(urlBaseFilename);
- }
-}
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
deleted file mode 100644
index e2489b493..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.content.Context;
-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.WorkInfo;
-import androidx.work.WorkManager;
-import de.danoeh.antennapod.storage.database.DBWriter;
-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;
-import io.reactivex.Observable;
-import io.reactivex.schedulers.Schedulers;
-
-import java.util.List;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
- 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().getDownloadUrl(),
- ExistingWorkPolicy.KEEP, workRequest.build());
- }
-
- public void download(Context context, FeedItem item) {
- OneTimeWorkRequest.Builder workRequest = getRequest(context, item);
- workRequest.setConstraints(getConstraints());
- WorkManager.getInstance(context).enqueueUniqueWork(item.getMedia().getDownloadUrl(),
- ExistingWorkPolicy.KEEP, workRequest.build());
- }
-
- 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().getDownloadUrl());
- if (!item.isTagged(FeedItem.TAG_QUEUE) && UserPreferences.enqueueDownloadedEpisodes()) {
- DBWriter.addQueueItem(context, false, item.getId());
- workRequest.addTag(DownloadServiceInterface.WORK_DATA_WAS_QUEUED);
- }
- workRequest.setInputData(new Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.getMedia().getId()).build());
- return workRequest;
- }
-
- 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, FeedMedia media) {
- // This needs to be done here, not in the worker. Reason: The worker might or might not be running.
- if (media.fileExists()) {
- DBWriter.deleteFeedMediaOfItem(context, media); // Remove partially downloaded file
- }
- String tag = WORK_TAG_EPISODE_URL + media.getDownloadUrl();
- Future<List<WorkInfo>> future = WorkManager.getInstance(context).getWorkInfosByTag(tag);
- Observable.fromFuture(future)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.io())
- .subscribe(
- workInfos -> {
- for (WorkInfo info : workInfos) {
- if (info.getTags().contains(DownloadServiceInterface.WORK_DATA_WAS_QUEUED)) {
- DBWriter.removeQueueItem(context, false, media.getItem());
- }
- }
- WorkManager.getInstance(context).cancelAllWorkByTag(tag);
- }, exception -> {
- WorkManager.getInstance(context).cancelAllWorkByTag(tag);
- exception.printStackTrace();
- });
- }
-
- @Override
- public void cancelAll(Context context) {
- WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG);
- }
-}
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
deleted file mode 100644
index 7010d61ba..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import androidx.annotation.NonNull;
-
-import java.util.Date;
-import java.util.concurrent.Callable;
-
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-
-/**
- * Downloads files
- */
-public abstract class Downloader implements Callable<Downloader> {
- private static final String TAG = "Downloader";
-
- private volatile boolean finished;
- public volatile boolean cancelled;
- public String permanentRedirectUrl = null;
-
- @NonNull
- final DownloadRequest request;
- @NonNull
- final DownloadResult result;
-
- Downloader(@NonNull DownloadRequest request) {
- super();
- this.request = request;
- this.request.setStatusMsg(R.string.download_pending);
- this.cancelled = false;
- this.result = new DownloadResult(0, request.getTitle(), request.getFeedfileId(), request.getFeedfileType(),
- false, null, new Date(), null);
- }
-
- protected abstract void download();
-
- public final Downloader call() {
- download();
- finished = true;
- return this;
- }
-
- @NonNull
- public DownloadRequest getDownloadRequest() {
- return request;
- }
-
- @NonNull
- public DownloadResult getResult() {
- return result;
- }
-
- public boolean isFinished() {
- return finished;
- }
-
- public void cancel() {
- cancelled = true;
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java
deleted file mode 100644
index 45ad45381..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-
-public interface DownloaderFactory {
- @Nullable
- Downloader create(@NonNull DownloadRequest request);
-} \ No newline at end of file
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
deleted file mode 100644
index a2b4ed100..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java
+++ /dev/null
@@ -1,311 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.wifi.WifiManager;
-import android.os.Build;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.core.app.NotificationCompat;
-import androidx.core.content.ContextCompat;
-import androidx.work.Data;
-import androidx.work.ForegroundInfo;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-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.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.DBWriter;
-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.model.download.DownloadRequest;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
-import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
-import de.danoeh.antennapod.ui.notifications.NotificationUtils;
-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 (true) {
- try {
- synchronized (notificationProgress) {
- if (isInterrupted()) {
- return;
- }
- notificationProgress.put(media.getEpisodeTitle(), request.getProgressPercent());
- }
- setProgressAsync(
- new Data.Builder()
- .putInt(DownloadServiceInterface.WORK_DATA_PROGRESS, request.getProgressPercent())
- .build())
- .get();
- NotificationManager nm = (NotificationManager) getApplicationContext()
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (ContextCompat.checkSelfPermission(getApplicationContext(),
- Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
- nm.notify(R.id.notification_downloading, generateProgressNotification());
- }
- Thread.sleep(1000);
- } catch (InterruptedException | ExecutionException e) {
- return;
- }
- }
- }
- };
- progressUpdaterThread.start();
- Result result;
- try {
- result = performDownload(media, request);
- } catch (Exception e) {
- e.printStackTrace();
- result = Result.failure();
- }
- if (result.equals(Result.failure()) && downloader != null) {
- FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
- }
- progressUpdaterThread.interrupt();
- try {
- progressUpdaterThread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (notificationProgress) {
- 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.getDownloadUrl() + " returned.");
- return result;
- }
-
- @Override
- public void onStopped() {
- super.onStopped();
- if (downloader != null) {
- downloader.cancel();
- }
- }
-
- @NonNull
- @Override
- public ListenableFuture<ForegroundInfo> getForegroundInfoAsync() {
- return Futures.immediateFuture(
- new ForegroundInfo(R.id.notification_downloading, generateProgressNotification()));
- }
-
- 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.setLocalFileUrl(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();
- }
-
- WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
- WifiManager.WifiLock wifiLock = null;
- if (wifiManager != null) {
- wifiLock = wifiManager.createWifiLock(TAG);
- wifiLock.acquire();
- }
- try {
- downloader.call();
- } catch (Exception e) {
- DBWriter.addDownloadStatus(downloader.getResult());
- sendErrorNotification(request.getTitle());
- return Result.failure();
- } finally {
- if (wifiLock != null) {
- wifiLock.release();
- }
- }
-
- if (downloader.cancelled) {
- // This also happens when the worker was preempted, not just when the user cancelled it
- 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(), false);
- 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
- sendErrorNotification(request.getTitle());
- return Result.failure();
- }
- sendMessage(request.getTitle(), false);
- return retry3times();
- }
-
- private Result retry3times() {
- if (isLastRunAttempt()) {
- sendErrorNotification(downloader.getDownloadRequest().getTitle());
- return Result.failure();
- } else {
- return Result.retry();
- }
- }
-
- private boolean isLastRunAttempt() {
- return getRunAttemptCount() >= 2;
- }
-
- private void sendMessage(String episodeTitle, boolean isImmediateFail) {
- boolean retrying = !isLastRunAttempt() && !isImmediateFail;
- 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(String title) {
- if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent.class)) {
- sendMessage(title, false);
- return;
- }
-
- 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);
- if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.POST_NOTIFICATIONS)
- == PackageManager.PERMISSION_GRANTED) {
- nm.notify(R.id.notification_download_report, builder.build());
- }
- }
-
- private Notification generateProgressNotification() {
- StringBuilder bigTextB = new StringBuilder();
- Map<String, Integer> progressCopy;
- synchronized (notificationProgress) {
- 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 (progressCopy.size() == 1) {
- contentText = bigText;
- } else {
- contentText = getApplicationContext().getResources().getQuantityString(R.plurals.downloads_left,
- progressCopy.size(), progressCopy.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);
- return 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
deleted file mode 100644
index 5e2a82f33..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ /dev/null
@@ -1,314 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import androidx.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.Log;
-
-import de.danoeh.antennapod.net.common.NetworkUtils;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-import de.danoeh.antennapod.net.common.AntennapodHttpClient;
-import okhttp3.CacheControl;
-import okhttp3.internal.http.StatusLine;
-import org.apache.commons.io.IOUtils;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.net.HttpURLConnection;
-import java.net.SocketTimeoutException;
-import java.net.URI;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.parser.feed.util.DateUtils;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.net.common.UriUtil;
-import okhttp3.OkHttpClient;
-import okhttp3.Protocol;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-
-public class HttpDownloader extends Downloader {
- private static final String TAG = "HttpDownloader";
- private static final int BUFFER_SIZE = 8 * 1024;
-
- public HttpDownloader(@NonNull DownloadRequest request) {
- super(request);
- }
-
- @Override
- protected void download() {
- File destination = new File(request.getDestination());
- final boolean fileExists = destination.exists();
-
- RandomAccessFile out = null;
- InputStream connection;
- ResponseBody responseBody = null;
-
- try {
- final URI uri = UriUtil.getURIFromRequestUrl(request.getSource());
- Request.Builder httpReq = new Request.Builder().url(uri.toURL());
- httpReq.tag(request);
- httpReq.cacheControl(new CacheControl.Builder().noStore().build());
-
- if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- // set header explicitly so that okhttp doesn't do transparent gzip
- Log.d(TAG, "addHeader(\"Accept-Encoding\", \"identity\")");
- httpReq.addHeader("Accept-Encoding", "identity");
- httpReq.cacheControl(new CacheControl.Builder().noCache().build()); // noStore breaks CDNs
- }
-
- if (uri.getScheme().equals("http")) {
- httpReq.addHeader("Upgrade-Insecure-Requests", "1");
- }
-
- if (!TextUtils.isEmpty(request.getLastModified())) {
- String lastModified = request.getLastModified();
- Date lastModifiedDate = DateUtils.parse(lastModified);
- if (lastModifiedDate != null) {
- long threeDaysAgo = System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 3;
- if (lastModifiedDate.getTime() > threeDaysAgo) {
- Log.d(TAG, "addHeader(\"If-Modified-Since\", \"" + lastModified + "\")");
- httpReq.addHeader("If-Modified-Since", lastModified);
- }
- } else {
- Log.d(TAG, "addHeader(\"If-None-Match\", \"" + lastModified + "\")");
- httpReq.addHeader("If-None-Match", lastModified);
- }
- }
-
- // add range header if necessary
- if (fileExists && destination.length() > 0) {
- request.setSoFar(destination.length());
- httpReq.addHeader("Range", "bytes=" + request.getSoFar() + "-");
- Log.d(TAG, "Adding range header: " + request.getSoFar());
- }
-
- Response response = newCall(httpReq);
- responseBody = response.body();
- String contentEncodingHeader = response.header("Content-Encoding");
- boolean isGzip = false;
- if (!TextUtils.isEmpty(contentEncodingHeader)) {
- isGzip = TextUtils.equals(contentEncodingHeader.toLowerCase(), "gzip");
- }
-
- Log.d(TAG, "Response code is " + response.code());
- if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_NOT_MODIFIED) {
- Log.d(TAG, "Feed '" + request.getSource() + "' not modified since last update, Download canceled");
- onCancelled();
- return;
- } else if (!response.isSuccessful() || response.body() == null) {
- callOnFailByResponseCode(response);
- return;
- } else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && isContentTypeTextAndSmallerThan100kb(response)) {
- onFail(DownloadError.ERROR_FILE_TYPE, null);
- return;
- }
- checkIfRedirect(response);
-
- connection = new BufferedInputStream(responseBody.byteStream());
-
- String contentRangeHeader = (fileExists) ? response.header("Content-Range") : null;
- if (fileExists && response.code() == HttpURLConnection.HTTP_PARTIAL
- && !TextUtils.isEmpty(contentRangeHeader)) {
- String start = contentRangeHeader.substring("bytes ".length(),
- contentRangeHeader.indexOf("-"));
- request.setSoFar(Long.parseLong(start));
- Log.d(TAG, "Starting download at position " + request.getSoFar());
-
- out = new RandomAccessFile(destination, "rw");
- out.seek(request.getSoFar());
- } else {
- boolean success = destination.delete();
- success |= destination.createNewFile();
- if (!success) {
- throw new IOException("Unable to recreate partially downloaded file");
- }
- out = new RandomAccessFile(destination, "rw");
- }
-
- byte[] buffer = new byte[BUFFER_SIZE];
- int count;
- request.setStatusMsg(R.string.download_running);
- Log.d(TAG, "Getting size of download");
- request.setSize(responseBody.contentLength() + request.getSoFar());
- Log.d(TAG, "Size is " + request.getSize());
- if (request.getSize() < 0) {
- request.setSize(DownloadResult.SIZE_UNKNOWN);
- }
-
- long freeSpace = StorageUtils.getFreeSpaceAvailable();
- Log.d(TAG, "Free space is " + freeSpace);
- if (request.getSize() != DownloadResult.SIZE_UNKNOWN && request.getSize() > freeSpace) {
- onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
- return;
- }
-
- Log.d(TAG, "Starting download");
- try {
- while (!cancelled && (count = connection.read(buffer)) != -1) {
- out.write(buffer, 0, count);
- request.setSoFar(request.getSoFar() + count);
- int progressPercent = (int) (100.0 * request.getSoFar() / request.getSize());
- request.setProgressPercent(progressPercent);
- }
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- }
- if (cancelled) {
- onCancelled();
- } 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() != 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());
- return;
- } else if (request.getSize() > 0 && request.getSoFar() == 0) {
- onFail(DownloadError.ERROR_IO_ERROR, "Download completed, but nothing was read");
- return;
- }
- String lastModified = response.header("Last-Modified");
- if (lastModified != null) {
- request.setLastModified(lastModified);
- } else {
- request.setLastModified(response.header("ETag"));
- }
- onSuccess();
- }
-
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_MALFORMED_URL, e.getMessage());
- } catch (SocketTimeoutException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_CONNECTION_ERROR, e.getMessage());
- } catch (UnknownHostException e) {
- e.printStackTrace();
- onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
- } catch (IOException e) {
- e.printStackTrace();
- if (NetworkUtils.wasDownloadBlocked(e)) {
- onFail(DownloadError.ERROR_IO_BLOCKED, e.getMessage());
- return;
- }
- String message = e.getMessage();
- if (message != null && message.contains("Trust anchor for certification path not found")) {
- onFail(DownloadError.ERROR_CERTIFICATE, e.getMessage());
- return;
- }
- onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
- } catch (NullPointerException e) {
- // might be thrown by connection.getInputStream()
- e.printStackTrace();
- onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
- } finally {
- IOUtils.closeQuietly(out);
- IOUtils.closeQuietly(responseBody);
- }
- }
-
- private Response newCall(Request.Builder httpReq) throws IOException {
- OkHttpClient httpClient = AntennapodHttpClient.getHttpClient();
- try {
- return httpClient.newCall(httpReq.build()).execute();
- } catch (IOException e) {
- Log.e(TAG, e.toString());
- if (e.getMessage() != null && e.getMessage().contains("PROTOCOL_ERROR")) {
- // Apparently some servers announce they support SPDY but then actually don't.
- httpClient = httpClient.newBuilder()
- .protocols(Collections.singletonList(Protocol.HTTP_1_1))
- .build();
- return httpClient.newCall(httpReq.build()).execute();
- } else {
- throw e;
- }
- }
- }
-
- private boolean isContentTypeTextAndSmallerThan100kb(Response response) {
- int contentLength = -1;
- String contentLen = response.header("Content-Length");
- if (contentLen != null) {
- try {
- contentLength = Integer.parseInt(contentLen);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- }
- Log.d(TAG, "content length: " + contentLength);
- String contentType = response.header("Content-Type");
- Log.d(TAG, "content type: " + contentType);
- return contentType != null && contentType.startsWith("text/") && contentLength < 100 * 1024;
- }
-
- private void callOnFailByResponseCode(Response response) {
- final DownloadError error;
- final String details;
- if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
- error = DownloadError.ERROR_UNAUTHORIZED;
- details = String.valueOf(response.code());
- } else if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
- error = DownloadError.ERROR_FORBIDDEN;
- details = String.valueOf(response.code());
- } else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND
- || response.code() == HttpURLConnection.HTTP_GONE) {
- error = DownloadError.ERROR_NOT_FOUND;
- details = String.valueOf(response.code());
- } else {
- error = DownloadError.ERROR_HTTP_DATA_ERROR;
- details = String.valueOf(response.code());
- }
- onFail(error, details);
- }
-
- private void checkIfRedirect(Response response) {
- // detect 301 Moved permanently and 308 Permanent Redirect
- ArrayList<Response> responses = new ArrayList<>();
- while (response != null) {
- responses.add(response);
- response = response.priorResponse();
- }
- if (responses.size() < 2) {
- return;
- }
- Collections.reverse(responses);
- int firstCode = responses.get(0).code();
- String firstUrl = responses.get(0).request().url().toString();
- String secondUrl = responses.get(1).request().url().toString();
- if (firstCode == HttpURLConnection.HTTP_MOVED_PERM || firstCode == StatusLine.HTTP_PERM_REDIRECT) {
- Log.d(TAG, "Detected permanent redirect from " + request.getSource() + " to " + secondUrl);
- permanentRedirectUrl = secondUrl;
- } else if (secondUrl.equals(firstUrl.replace("http://", "https://"))) {
- Log.d(TAG, "Treating http->https non-permanent redirect as permanent: " + firstUrl);
- permanentRedirectUrl = secondUrl;
- }
- }
-
- private void onSuccess() {
- Log.d(TAG, "Download was successful");
- result.setSuccessful();
- }
-
- private void onFail(DownloadError reason, String reasonDetailed) {
- Log.d(TAG, "onFail() called with: " + "reason = [" + reason + "], reasonDetailed = [" + reasonDetailed + "]");
- result.setFailed(reason, reasonDetailed);
- }
-
- private void onCancelled() {
- Log.d(TAG, "Download was cancelled");
- result.setCancelled();
- cancelled = true;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
deleted file mode 100644
index eff5816be..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
+++ /dev/null
@@ -1,147 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.util.Log;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.content.ContextCompat;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedCounter;
-import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.storage.database.PodDBAdapter;
-
-import de.danoeh.antennapod.ui.notifications.NotificationUtils;
-import java.util.Map;
-
-public class NewEpisodesNotification {
- private static final String TAG = "NewEpisodesNotification";
- private static final String GROUP_KEY = "de.danoeh.antennapod.EPISODES";
-
- private Map<Long, Integer> countersBefore;
-
- public NewEpisodesNotification() {
- }
-
- public void loadCountersBeforeRefresh() {
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- countersBefore = adapter.getFeedCounters(FeedCounter.SHOW_NEW);
- adapter.close();
- }
-
- public void showIfNeeded(Context context, Feed feed) {
- FeedPreferences prefs = feed.getPreferences();
- if (!prefs.getKeepUpdated() || !prefs.getShowEpisodeNotification()) {
- return;
- }
-
- int newEpisodesBefore = countersBefore.containsKey(feed.getId()) ? countersBefore.get(feed.getId()) : 0;
- int newEpisodesAfter = getNewEpisodeCount(feed.getId());
-
- Log.d(TAG, "New episodes before: " + newEpisodesBefore + ", after: " + newEpisodesAfter);
- if (newEpisodesAfter > newEpisodesBefore) {
- NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
- showNotification(newEpisodesAfter, feed, context, notificationManager);
- }
- }
-
- private static void showNotification(int newEpisodes, Feed feed, Context context,
- NotificationManagerCompat notificationManager) {
- Resources res = context.getResources();
- String text = res.getQuantityString(
- R.plurals.new_episode_notification_message, newEpisodes, newEpisodes, feed.getTitle()
- );
- String title = res.getQuantityString(R.plurals.new_episode_notification_title, newEpisodes);
-
- Intent intent = new Intent();
- intent.setAction("NewEpisodes" + feed.getId());
- intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.putExtra("fragment_feed_id", feed.getId());
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
- (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
-
- Notification notification = new NotificationCompat.Builder(
- context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
- .setSmallIcon(R.drawable.ic_notification_new)
- .setContentTitle(title)
- .setLargeIcon(loadIcon(context, feed))
- .setContentText(text)
- .setContentIntent(pendingIntent)
- .setGroup(GROUP_KEY)
- .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
- .setOnlyAlertOnce(true)
- .setAutoCancel(true)
- .build();
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)
- == PackageManager.PERMISSION_GRANTED) {
- notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS,
- feed.hashCode(), notification);
- }
- showGroupSummaryNotification(context, notificationManager);
- }
-
- private static void showGroupSummaryNotification(Context context, NotificationManagerCompat notificationManager) {
- Intent intent = new Intent();
- intent.setAction("NewEpisodes");
- intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.putExtra("fragment_tag", "NewEpisodesFragment");
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
- (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
-
- Notification notificationGroupSummary = new NotificationCompat.Builder(
- context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
- .setSmallIcon(R.drawable.ic_notification_new)
- .setContentTitle(context.getString(R.string.new_episode_notification_group_text))
- .setContentIntent(pendingIntent)
- .setGroup(GROUP_KEY)
- .setGroupSummary(true)
- .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
- .setOnlyAlertOnce(true)
- .setAutoCancel(true)
- .build();
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)
- == PackageManager.PERMISSION_GRANTED) {
- notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS,
- 0, notificationGroupSummary);
- }
- }
-
- private static Bitmap loadIcon(Context context, Feed feed) {
- int iconSize = (int) (128 * context.getResources().getDisplayMetrics().density);
- try {
- return Glide.with(context)
- .asBitmap()
- .load(feed.getImageUrl())
- .apply(new RequestOptions().centerCrop())
- .submit(iconSize, iconSize)
- .get();
- } catch (Throwable tr) {
- return null;
- }
- }
-
- private static int getNewEpisodeCount(long feedId) {
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- Map<Long, Integer> counters = adapter.getFeedCounters(FeedCounter.SHOW_NEW, feedId);
- int episodeCount = counters.containsKey(feedId) ? counters.get(feedId) : 0;
- adapter.close();
- return episodeCount;
- }
-}
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
deleted file mode 100644
index 37775ab94..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package de.danoeh.antennapod.core.service.download.handler;
-
-import android.text.TextUtils;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import de.danoeh.antennapod.model.feed.Feed;
-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.model.download.DownloadRequest;
-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;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.core.util.InvalidFeedException;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.concurrent.Callable;
-
-public class FeedParserTask implements Callable<FeedHandlerResult> {
- private static final String TAG = "FeedParserTask";
- private final DownloadRequest request;
- private DownloadResult downloadResult;
- private boolean successful = true;
-
- public FeedParserTask(DownloadRequest request) {
- this.request = request;
- downloadResult = new DownloadResult(
- 0, request.getTitle(), 0, request.getFeedfileType(), false,
- DownloadError.ERROR_REQUEST_ERROR, new Date(),
- "Unknown error: Status not set");
- }
-
- @Override
- public FeedHandlerResult call() {
- Feed feed = new Feed(request.getSource(), request.getLastModified());
- feed.setLocalFileUrl(request.getDestination());
- feed.setId(request.getFeedfileId());
- feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
- VolumeAdaptionSetting.OFF, FeedPreferences.NewEpisodesAction.GLOBAL, request.getUsername(),
- request.getPassword()));
- feed.setPageNr(request.getArguments().getInt(DownloadRequest.REQUEST_ARG_PAGE_NR, 0));
-
- DownloadError reason = null;
- String reasonDetailed = null;
- FeedHandler feedHandler = new FeedHandler();
-
- FeedHandlerResult result = null;
- try {
- result = feedHandler.parseFeed(feed);
- Log.d(TAG, feed.getTitle() + " parsed");
- checkFeedData(feed);
- if (TextUtils.isEmpty(feed.getImageUrl())) {
- feed.setImageUrl(Feed.PREFIX_GENERATIVE_COVER + feed.getDownloadUrl());
- }
- } catch (SAXException | IOException | ParserConfigurationException e) {
- successful = false;
- e.printStackTrace();
- reason = DownloadError.ERROR_PARSER_EXCEPTION;
- reasonDetailed = e.getMessage();
- } catch (UnsupportedFeedtypeException e) {
- e.printStackTrace();
- successful = false;
- reason = DownloadError.ERROR_UNSUPPORTED_TYPE;
- if ("html".equalsIgnoreCase(e.getRootElement())) {
- reason = DownloadError.ERROR_UNSUPPORTED_TYPE_HTML;
- }
- reasonDetailed = e.getMessage();
- } catch (InvalidFeedException e) {
- e.printStackTrace();
- successful = false;
- reason = DownloadError.ERROR_PARSER_EXCEPTION;
- reasonDetailed = e.getMessage();
- } finally {
- File feedFile = new File(request.getDestination());
- if (feedFile.exists()) {
- boolean deleted = feedFile.delete();
- Log.d(TAG, "Deletion of file '" + feedFile.getAbsolutePath() + "' "
- + (deleted ? "successful" : "FAILED"));
- }
- }
-
- if (successful) {
- downloadResult = new DownloadResult(feed.getHumanReadableIdentifier(), feed.getId(),
- Feed.FEEDFILETYPE_FEED, true, DownloadError.SUCCESS, reasonDetailed);
- return result;
- } else {
- downloadResult = new DownloadResult(feed.getHumanReadableIdentifier(), feed.getId(),
- Feed.FEEDFILETYPE_FEED, false, reason, reasonDetailed);
- return null;
- }
- }
-
- public boolean isSuccessful() {
- return successful;
- }
-
- /**
- * Checks if the feed was parsed correctly.
- */
- private void checkFeedData(Feed feed) throws InvalidFeedException {
- if (feed.getTitle() == null) {
- throw new InvalidFeedException("Feed has no title");
- }
- checkFeedItems(feed);
- }
-
- private void checkFeedItems(Feed feed) throws InvalidFeedException {
- for (FeedItem item : feed.getItems()) {
- if (item.getTitle() == null) {
- throw new InvalidFeedException("Item has no title: " + item);
- }
- }
- }
-
- @NonNull
- public DownloadResult getDownloadStatus() {
- return downloadResult;
- }
-}
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
deleted file mode 100644
index 24b157c88..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package de.danoeh.antennapod.core.service.download.handler;
-
-import android.content.Context;
-import android.media.MediaMetadataRetriever;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat;
-import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
-import org.greenrobot.eventbus.EventBus;
-
-import java.io.File;
-import java.io.InterruptedIOException;
-import java.util.concurrent.ExecutionException;
-
-import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.model.download.DownloadRequest;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.DBWriter;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.model.download.DownloadError;
-import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.net.sync.model.EpisodeAction;
-
-/**
- * Handles a completed media download.
- */
-public class MediaDownloadedHandler implements Runnable {
- private static final String TAG = "MediaDownloadedHandler";
- private final DownloadRequest request;
- private final Context context;
- private DownloadResult updatedStatus;
-
- public MediaDownloadedHandler(@NonNull Context context, @NonNull DownloadResult status,
- @NonNull DownloadRequest request) {
- this.request = request;
- this.context = context;
- this.updatedStatus = status;
- }
-
- @Override
- public void run() {
- FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
- if (media == null) {
- Log.e(TAG, "Could not find downloaded media object in database");
- return;
- }
- // media.setDownloaded modifies played state
- boolean broadcastUnreadStateUpdate = media.getItem() != null && media.getItem().isNew();
- media.setDownloaded(true);
- media.setLocalFileUrl(request.getDestination());
- media.setSize(new File(request.getDestination()).length());
- media.checkEmbeddedPicture(); // enforce check
-
- try {
- // Cache chapters if file has them
- if (media.getItem() != null && !media.getItem().hasChapters()) {
- media.setChapters(ChapterUtils.loadChaptersFromMediaFile(media, context));
- }
- if (media.getItem() != null && media.getItem().getPodcastIndexChapterUrl() != null) {
- ChapterUtils.loadChaptersFromUrl(media.getItem().getPodcastIndexChapterUrl(), false);
- }
- } catch (InterruptedIOException ignore) {
- // Ignore
- }
-
- // Get duration
- String durationStr = null;
- try (MediaMetadataRetrieverCompat mmr = new MediaMetadataRetrieverCompat()) {
- mmr.setDataSource(media.getLocalFileUrl());
- durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- media.setDuration(Integer.parseInt(durationStr));
- Log.d(TAG, "Duration of file is " + media.getDuration());
- } catch (NumberFormatException e) {
- Log.d(TAG, "Invalid file duration: " + durationStr);
- } catch (Exception e) {
- Log.e(TAG, "Get duration failed", e);
- }
-
- final FeedItem item = media.getItem();
-
- try {
- DBWriter.setFeedMedia(media).get();
-
- // we've received the media, we don't want to autodownload it again
- if (item != null) {
- item.disableAutoDownload();
- // setFeedItem() signals (via EventBus) that the item has been updated,
- // so we do it after the enclosing media has been updated above,
- // to ensure subscribers will get the updated FeedMedia as well
- DBWriter.setFeedItem(item).get();
- if (broadcastUnreadStateUpdate) {
- EventBus.getDefault().post(new UnreadItemsUpdateEvent());
- }
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "MediaHandlerThread was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
- updatedStatus = new DownloadResult(media.getEpisodeTitle(), media.getId(),
- FeedMedia.FEEDFILETYPE_FEEDMEDIA, false, DownloadError.ERROR_DB_ACCESS_ERROR, e.getMessage());
- }
-
- if (item != null) {
- EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD)
- .currentTimestamp()
- .build();
- SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
- }
- }
-
- @NonNull
- public DownloadResult getUpdatedStatus() {
- return updatedStatus;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FastDocumentFile.java b/core/src/main/java/de/danoeh/antennapod/core/util/FastDocumentFile.java
deleted file mode 100644
index a86bf0bcf..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FastDocumentFile.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Android's DocumentFile is slow because every single method call queries the ContentResolver.
- * This queries the ContentResolver a single time with all the information.
- */
-public class FastDocumentFile {
- private final String name;
- private final String type;
- private final Uri uri;
- private final long length;
- private final long lastModified;
-
- public static List<FastDocumentFile> list(Context context, Uri folderUri) {
- Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(folderUri,
- DocumentsContract.getDocumentId(folderUri));
- Cursor cursor = context.getContentResolver().query(childrenUri, new String[] {
- DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- DocumentsContract.Document.COLUMN_DISPLAY_NAME,
- DocumentsContract.Document.COLUMN_SIZE,
- DocumentsContract.Document.COLUMN_LAST_MODIFIED,
- DocumentsContract.Document.COLUMN_MIME_TYPE}, null, null, null);
- ArrayList<FastDocumentFile> list = new ArrayList<>();
- while (cursor.moveToNext()) {
- String id = cursor.getString(0);
- Uri uri = DocumentsContract.buildDocumentUriUsingTree(folderUri, id);
- String name = cursor.getString(1);
- long size = cursor.getLong(2);
- long lastModified = cursor.getLong(3);
- String mimeType = cursor.getString(4);
- list.add(new FastDocumentFile(name, mimeType, uri, size, lastModified));
- }
- cursor.close();
- return list;
- }
-
- public FastDocumentFile(String name, String type, Uri uri, long length, long lastModified) {
- this.name = name;
- this.type = type;
- this.uri = uri;
- this.length = length;
- this.lastModified = lastModified;
- }
-
- public String getName() {
- return name;
- }
-
- public String getType() {
- return type;
- }
-
- public Uri getUri() {
- return uri;
- }
-
- public long getLength() {
- return length;
- }
-
- public long getLastModified() {
- return lastModified;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
deleted file mode 100644
index 69c23efc2..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.text.TextUtils;
-
-import androidx.annotation.VisibleForTesting;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-
-
-/** Generates valid filenames for a given string. */
-public class FileNameGenerator {
- @VisibleForTesting
- public static final int MAX_FILENAME_LENGTH = 242; // limited by CircleCI
- private static final int MD5_HEX_LENGTH = 32;
-
- private static final char[] validChars =
- ("abcdefghijklmnopqrstuvwxyz"
- + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- + "0123456789"
- + " _-").toCharArray();
-
- private FileNameGenerator() {
- }
-
- /**
- * This method will return a new string that doesn't contain any illegal
- * characters of the given string.
- */
- public static String generateFileName(String string) {
- string = StringUtils.stripAccents(string);
- StringBuilder buf = new StringBuilder();
- for (int i = 0; i < string.length(); i++) {
- char c = string.charAt(i);
- if (Character.isSpaceChar(c)
- && (buf.length() == 0 || Character.isSpaceChar(buf.charAt(buf.length() - 1)))) {
- continue;
- }
- if (ArrayUtils.contains(validChars, c)) {
- buf.append(c);
- }
- }
- String filename = buf.toString().trim();
- if (TextUtils.isEmpty(filename)) {
- return randomString(8);
- } else if (filename.length() >= MAX_FILENAME_LENGTH) {
- return filename.substring(0, MAX_FILENAME_LENGTH - MD5_HEX_LENGTH - 1) + "_" + md5(filename);
- } else {
- return filename;
- }
- }
-
- private static String randomString(int length) {
- StringBuilder sb = new StringBuilder(length);
- for (int i = 0; i < length; i++) {
- sb.append(validChars[(int) (Math.random() * validChars.length)]);
- }
- return sb.toString();
- }
-
- private static String md5(String md5) {
- try {
- java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
- byte[] array = md.digest(md5.getBytes("UTF-8"));
- StringBuilder sb = new StringBuilder();
- for (byte b : array) {
- sb.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3));
- }
- return sb.toString();
- } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
- return null;
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/InvalidFeedException.java b/core/src/main/java/de/danoeh/antennapod/core/util/InvalidFeedException.java
deleted file mode 100644
index a45136432..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/InvalidFeedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-/**
- * Thrown if a feed has invalid attribute values.
- */
-public class InvalidFeedException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public InvalidFeedException(String message) {
- super(message);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManagerImpl.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManagerImpl.java
deleted file mode 100644
index 17077c237..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/download/FeedUpdateManagerImpl.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package de.danoeh.antennapod.core.util.download;
-
-import android.content.Context;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-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.net.common.NetworkUtils;
-import de.danoeh.antennapod.event.MessageEvent;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import org.greenrobot.eventbus.EventBus;
-
-import java.util.concurrent.TimeUnit;
-
-public class FeedUpdateManagerImpl extends 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";
- public static final String EXTRA_EVEN_ON_MOBILE = "even_on_mobile";
- private static final String TAG = "AutoUpdateManager";
-
- /**
- * Start / restart periodic auto feed refresh
- * @param context Context
- */
- public 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(new Constraints.Builder()
- .setRequiredNetworkType(UserPreferences.isAllowMobileFeedRefresh()
- ? NetworkType.CONNECTED : NetworkType.UNMETERED).build())
- .build();
- WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_ID_FEED_UPDATE,
- replace ? ExistingPeriodicWorkPolicy.REPLACE : ExistingPeriodicWorkPolicy.KEEP, workRequest);
- }
- }
-
- public void runOnce(Context context) {
- runOnce(context, null, false);
- }
-
- public void runOnce(Context context, Feed feed) {
- runOnce(context, feed, false);
- }
-
- public 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 || !feed.isLocalFeed()) {
- workRequest.setConstraints(new Constraints.Builder()
- .setRequiredNetworkType(NetworkType.CONNECTED).build());
- }
- Data.Builder builder = new Data.Builder();
- builder.putBoolean(EXTRA_EVEN_ON_MOBILE, true);
- if (feed != null) {
- 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 void runOnceOrAsk(@NonNull Context context) {
- runOnceOrAsk(context, null);
- }
-
- public void runOnceOrAsk(@NonNull Context context, @Nullable Feed feed) {
- Log.d(TAG, "Run auto update immediately in background.");
- if (feed != null && feed.isLocalFeed()) {
- runOnce(context, feed);
- } else if (!NetworkUtils.networkAvailable()) {
- EventBus.getDefault().post(new MessageEvent(context.getString(R.string.download_error_no_connection)));
- } else if (NetworkUtils.isFeedRefreshAllowed()) {
- runOnce(context, feed);
- } else {
- confirmMobileRefresh(context, feed);
- }
- }
-
- private void confirmMobileRefresh(final Context context, @Nullable Feed feed) {
- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
- .setTitle(R.string.feed_refresh_title)
- .setPositiveButton(R.string.confirm_mobile_streaming_button_once,
- (dialog, which) -> runOnce(context, feed))
- .setNeutralButton(R.string.confirm_mobile_streaming_button_always, (dialog, which) -> {
- UserPreferences.setAllowMobileFeedRefresh(true);
- runOnce(context, feed);
- })
- .setNegativeButton(R.string.no, null);
- if (NetworkUtils.isNetworkRestricted() && NetworkUtils.isVpnOverWifi()) {
- builder.setMessage(R.string.confirm_mobile_feed_refresh_dialog_message_vpn);
- } else {
- builder.setMessage(R.string.confirm_mobile_feed_refresh_dialog_message);
- }
- builder.show();
- }
-}
diff --git a/core/src/main/res/values/ids.xml b/core/src/main/res/values/ids.xml
index 7bb78c1c9..8678b4413 100644
--- a/core/src/main/res/values/ids.xml
+++ b/core/src/main/res/values/ids.xml
@@ -16,10 +16,6 @@
<item name="view_type_episode_item" type="id"/>
<!-- Notifications need unique IDs to update/cancel them -->
- <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"/>
<item name="notification_streaming_confirmation" type="id"/>
</resources> \ No newline at end of file