summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/build.gradle1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java183
-rw-r--r--core/build.gradle1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java31
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java7
-rw-r--r--settings.gradle1
-rw-r--r--storage/importexport/README.md3
-rw-r--r--storage/importexport/build.gradle21
-rw-r--r--storage/importexport/src/main/assets/html-export-favorites-item-template.html (renamed from core/src/main/assets/html-export-favorites-item-template.html)0
-rw-r--r--storage/importexport/src/main/assets/html-export-feed-template.html (renamed from core/src/main/assets/html-export-feed-template.html)0
-rw-r--r--storage/importexport/src/main/assets/html-export-template.html (renamed from core/src/main/assets/html-export-template.html)1
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/DatabaseExporter.java (renamed from core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java)3
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/FavoritesWriter.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java)24
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/HtmlWriter.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java)13
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlElement.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlElement.java)2
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlReader.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlReader.java)12
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlSymbols.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java)11
-rw-r--r--storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlWriter.java (renamed from core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java)20
24 files changed, 184 insertions, 334 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 06b387e7b..5553e174b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -86,6 +86,7 @@ dependencies {
implementation project(':playback:base')
implementation project(':playback:cast')
implementation project(':storage:database')
+ implementation project(':storage:importexport')
implementation project(':storage:preferences')
implementation project(':ui:app-start-intent')
implementation project(':ui:common')
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java
index 6b85f3bf8..ec4a58e33 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java
@@ -6,13 +6,15 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.OutputStream;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.parser.feed.namespace.PodcastIndex;
-import de.danoeh.antennapod.core.util.DateFormatter;
/**
* Creates RSS 2.0 feeds. See FeedGenerator for more information.
@@ -98,7 +100,7 @@ public class Rss2Generator implements FeedGenerator {
}
if (item.getPubDate() != null) {
xml.startTag(null, "pubDate");
- xml.text(DateFormatter.formatRfc822Date(item.getPubDate()));
+ xml.text(formatRfc822Date(item.getPubDate()));
xml.endTag(null, "pubDate");
}
if ((flags & FEATURE_WRITE_GUID) != 0) {
@@ -132,4 +134,9 @@ public class Rss2Generator implements FeedGenerator {
xml.endDocument();
}
+
+ private static String formatRfc822Date(Date date) {
+ SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
+ return format.format(date);
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
index 3f1c17cdc..caafe989d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
@@ -26,14 +26,14 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.export.opml.OpmlElement;
-import de.danoeh.antennapod.core.export.opml.OpmlReader;
import de.danoeh.antennapod.core.preferences.ThemeSwitcher;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.databinding.OpmlSelectionBinding;
import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.storage.importexport.OpmlElement;
+import de.danoeh.antennapod.storage.importexport.OpmlReader;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
deleted file mode 100644
index 8acfcb58f..000000000
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.content.Context;
-import android.net.Uri;
-import androidx.annotation.NonNull;
-import androidx.documentfile.provider.DocumentFile;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.storage.DBReader;
-import io.reactivex.Observable;
-
-/**
- * Writes an OPML file into the user selected export directory in the background.
- */
-public class DocumentFileExportWorker {
-
- private final @NonNull ExportWriter exportWriter;
- private @NonNull Context context;
- private @NonNull Uri outputFileUri;
-
- public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) {
- this.exportWriter = exportWriter;
- this.context = context;
- this.outputFileUri = outputFileUri;
- }
-
- public Observable<DocumentFile> exportObservable() {
- DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri);
- return Observable.create(subscriber -> {
- OutputStream outputStream = null;
- OutputStreamWriter writer = null;
- try {
- Uri uri = output.getUri();
- outputStream = context.getContentResolver().openOutputStream(uri, "wt");
- if (outputStream == null) {
- throw new IOException();
- }
- writer = new OutputStreamWriter(outputStream, Charset.forName("UTF-8"));
- exportWriter.writeDocument(DBReader.getFeedList(), writer, context);
- subscriber.onNext(output);
- } catch (IOException e) {
- subscriber.onError(e);
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (IOException e) {
- subscriber.onError(e);
- }
- }
- if (outputStream != null) {
- try {
- outputStream.close();
- } catch (IOException e) {
- subscriber.onError(e);
- }
- }
- subscriber.onComplete();
- }
- });
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
deleted file mode 100644
index 97a5f157b..000000000
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.content.Context;
-import androidx.annotation.NonNull;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
-import io.reactivex.Observable;
-
-/**
- * Writes an OPML file into the export directory in the background.
- */
-public class ExportWorker {
-
- private static final String EXPORT_DIR = "export/";
- private static final String TAG = "ExportWorker";
- private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
-
- private final @NonNull ExportWriter exportWriter;
- private final @NonNull File output;
- private final Context context;
-
- public ExportWorker(@NonNull ExportWriter exportWriter, Context context) {
- this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
- DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()), context);
- }
-
- private ExportWorker(@NonNull ExportWriter exportWriter, @NonNull File output, Context context) {
- this.exportWriter = exportWriter;
- this.output = output;
- this.context = context;
- }
-
- public Observable<File> exportObservable() {
- if (output.exists()) {
- boolean success = output.delete();
- Log.w(TAG, "Overwriting previously exported file: " + success);
- }
- return Observable.create(subscriber -> {
- OutputStreamWriter writer = null;
- try {
- writer = new OutputStreamWriter(new FileOutputStream(output), Charset.forName("UTF-8"));
- exportWriter.writeDocument(DBReader.getFeedList(), writer, context);
- subscriber.onNext(output);
- } catch (IOException e) {
- subscriber.onError(e);
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (IOException e) {
- subscriber.onError(e);
- }
- }
- subscriber.onComplete();
- }
- });
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
index 71ba326dd..9191825aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
@@ -16,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts.GetContent;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
+import androidx.documentfile.provider.DocumentFile;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.core.app.ShareCompat;
import androidx.core.content.FileProvider;
@@ -25,13 +26,15 @@ import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OpmlImportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
-import de.danoeh.antennapod.asynctask.ExportWorker;
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.export.favorites.FavoritesWriter;
-import de.danoeh.antennapod.core.export.html.HtmlWriter;
-import de.danoeh.antennapod.core.export.opml.OpmlWriter;
-import de.danoeh.antennapod.core.storage.DatabaseExporter;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.SortOrder;
+import de.danoeh.antennapod.storage.importexport.DatabaseExporter;
+import de.danoeh.antennapod.storage.importexport.FavoritesWriter;
+import de.danoeh.antennapod.storage.importexport.HtmlWriter;
+import de.danoeh.antennapod.storage.importexport.OpmlWriter;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -39,8 +42,14 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
import java.util.Locale;
public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
@@ -57,18 +66,29 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
private static final String CONTENT_TYPE_HTML = "text/html";
private static final String DEFAULT_FAVORITES_OUTPUT_NAME = "antennapod-favorites-%s.html";
private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db";
+
private final ActivityResultLauncher<Intent> chooseOpmlExportPathLauncher =
- registerForActivityResult(new StartActivityForResult(), this::chooseOpmlExportPathResult);
+ registerForActivityResult(new StartActivityForResult(),
+ result -> exportToDocument(result, Export.OPML));
private final ActivityResultLauncher<Intent> chooseHtmlExportPathLauncher =
- registerForActivityResult(new StartActivityForResult(), this::chooseHtmlExportPathResult);
+ registerForActivityResult(new StartActivityForResult(),
+ result -> exportToDocument(result, Export.HTML));
private final ActivityResultLauncher<Intent> chooseFavoritesExportPathLauncher =
- registerForActivityResult(new StartActivityForResult(), this::chooseFavoritesExportPathResult);
+ registerForActivityResult(new StartActivityForResult(),
+ result -> exportToDocument(result, Export.FAVORITES));
private final ActivityResultLauncher<Intent> restoreDatabaseLauncher =
registerForActivityResult(new StartActivityForResult(), this::restoreDatabaseResult);
private final ActivityResultLauncher<String> backupDatabaseLauncher =
registerForActivityResult(new BackupDatabase(), this::backupDatabaseResult);
private final ActivityResultLauncher<String> chooseOpmlImportPathLauncher =
- registerForActivityResult(new GetContent(), this::chooseOpmlImportPathResult);
+ registerForActivityResult(new GetContent(), uri -> {
+ if (uri != null) {
+ final Intent intent = new Intent(getContext(), OpmlImportActivity.class);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+ });
+
private Disposable disposable;
private ProgressDialog progressDialog;
@@ -95,20 +115,16 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
}
}
- private String dateStampFilename(String fname) {
- return String.format(fname, new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date()));
- }
-
private void setupStorageScreen() {
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
preference -> {
- openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher, new OpmlWriter());
+ openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher);
return true;
}
);
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
preference -> {
- openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher, new HtmlWriter());
+ openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher);
return true;
});
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
@@ -132,32 +148,13 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
});
findPreference(PREF_FAVORITE_EXPORT).setOnPreferenceClickListener(
preference -> {
- openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher, new FavoritesWriter());
+ openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher);
return true;
});
}
- private void exportWithWriter(ExportWriter exportWriter, Uri uri, Export exportType) {
- Context context = getActivity();
- progressDialog.show();
- if (uri == null) {
- Observable<File> observable = new ExportWorker(exportWriter, getContext()).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
- context.getString(R.string.provider_authority), output);
- showExportSuccessSnackbar(fileUri, exportType.contentType);
- }, this::showExportErrorDialog, progressDialog::dismiss);
- } else {
- DocumentFileExportWorker worker = new DocumentFileExportWorker(exportWriter, context, uri);
- disposable = worker.exportObservable()
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output ->
- showExportSuccessSnackbar(output.getUri(), exportType.contentType),
- this::showExportErrorDialog, progressDialog::dismiss);
- }
+ private String dateStampFilename(String fname) {
+ return String.format(fname, new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date()));
}
private void exportDatabase() {
@@ -211,30 +208,6 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
alert.show();
}
- private void chooseOpmlExportPathResult(final ActivityResult result) {
- if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
- return;
- }
- final Uri uri = result.getData().getData();
- exportWithWriter(new OpmlWriter(), uri, Export.OPML);
- }
-
- private void chooseHtmlExportPathResult(final ActivityResult result) {
- if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
- return;
- }
- final Uri uri = result.getData().getData();
- exportWithWriter(new HtmlWriter(), uri, Export.HTML);
- }
-
- private void chooseFavoritesExportPathResult(final ActivityResult result) {
- if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
- return;
- }
- final Uri uri = result.getData().getData();
- exportWithWriter(new FavoritesWriter(), uri, Export.FAVORITES);
- }
-
private void restoreDatabaseResult(final ActivityResult result) {
if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
return;
@@ -264,16 +237,7 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
}, this::showExportErrorDialog);
}
- private void chooseOpmlImportPathResult(final Uri uri) {
- if (uri == null) {
- return;
- }
- final Intent intent = new Intent(getContext(), OpmlImportActivity.class);
- intent.setData(uri);
- startActivity(intent);
- }
-
- private void openExportPathPicker(Export exportType, ActivityResultLauncher<Intent> result, ExportWriter writer) {
+ private void openExportPathPicker(Export exportType, ActivityResultLauncher<Intent> result) {
String title = dateStampFilename(exportType.outputNameTemplate);
Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
@@ -292,7 +256,78 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
// If we are using a SDK lower than API 21 or the implicit intent failed
// fallback to the legacy export process
- exportWithWriter(writer, null, exportType);
+ File output = new File(UserPreferences.getDataFolder("export/"), title);
+ exportToFile(exportType, output);
+ }
+
+ private void exportToFile(Export exportType, File output) {
+ progressDialog.show();
+ disposable = Observable.create(
+ subscriber -> {
+ if (output.exists()) {
+ boolean success = output.delete();
+ Log.w(TAG, "Overwriting previously exported file: " + success);
+ }
+ try (FileOutputStream fileOutputStream = new FileOutputStream(output)) {
+ writeToStream(fileOutputStream, exportType);
+ subscriber.onNext(output);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(outputFile -> {
+ progressDialog.dismiss();
+ Uri fileUri = FileProvider.getUriForFile(getActivity().getApplicationContext(),
+ getString(R.string.provider_authority), output);
+ showExportSuccessSnackbar(fileUri, exportType.contentType);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ }
+
+ private void exportToDocument(final ActivityResult result, Export exportType) {
+ if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
+ return;
+ }
+ progressDialog.show();
+ DocumentFile output = DocumentFile.fromSingleUri(getContext(), result.getData().getData());
+ disposable = Observable.create(
+ subscriber -> {
+ try (OutputStream outputStream = getContext().getContentResolver()
+ .openOutputStream(output.getUri(), "wt")) {
+ writeToStream(outputStream, exportType);
+ subscriber.onNext(output);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ignore -> {
+ progressDialog.dismiss();
+ showExportSuccessSnackbar(output.getUri(), exportType.contentType);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ }
+
+ private void writeToStream(OutputStream outputStream, Export type) throws IOException {
+ try (OutputStreamWriter writer = new OutputStreamWriter(outputStream, Charset.forName("UTF-8"))) {
+ switch (type) {
+ case HTML:
+ HtmlWriter.writeDocument(DBReader.getFeedList(), writer, getContext());
+ break;
+ case OPML:
+ OpmlWriter.writeDocument(DBReader.getFeedList(), writer);
+ break;
+ case FAVORITES:
+ List<FeedItem> allFavorites = DBReader.getEpisodes(0, Integer.MAX_VALUE,
+ new FeedItemFilter(FeedItemFilter.IS_FAVORITE), SortOrder.DATE_NEW_OLD);
+ FavoritesWriter.writeDocument(allFavorites, writer, getContext());
+ break;
+ default:
+ showExportErrorDialog(new Exception("Invalid export type"));
+ break;
+ }
+ }
}
private static class BackupDatabase extends ActivityResultContracts.CreateDocument {
diff --git a/core/build.gradle b/core/build.gradle
index b36d87129..4385b7cd6 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -35,6 +35,7 @@ dependencies {
implementation project(':playback:base')
implementation project(':playback:cast')
implementation project(':storage:database')
+ implementation project(':storage:importexport')
implementation project(':storage:preferences')
implementation project(':ui:app-start-intent')
implementation project(':ui:common')
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index 2058d5b2f..a78ae71a3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -10,6 +10,9 @@ import android.util.Log;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+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;
@@ -31,9 +34,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import de.danoeh.antennapod.core.export.opml.OpmlElement;
-import de.danoeh.antennapod.core.export.opml.OpmlReader;
-import de.danoeh.antennapod.core.export.opml.OpmlWriter;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -81,7 +81,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
try {
// Write OPML
- new OpmlWriter().writeDocument(DBReader.getFeedList(), writer, mContext);
+ 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) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java
deleted file mode 100644
index 2ce340328..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package de.danoeh.antennapod.core.export;
-
-public class CommonSymbols {
-
- public static final String HEAD = "head";
- public static final String BODY = "body";
- public static final String TITLE = "title";
-
- public static final String XML_FEATURE_INDENT_OUTPUT = "http://xmlpull.org/v1/doc/features.html#indent-output";
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
deleted file mode 100644
index e60609569..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package de.danoeh.antennapod.core.export;
-
-import android.content.Context;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.List;
-
-import de.danoeh.antennapod.model.feed.Feed;
-
-public interface ExportWriter {
-
- void writeDocument(List<Feed> feeds, Writer writer, Context context)
- throws IllegalArgumentException, IllegalStateException, IOException;
-
- String fileExtension();
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java
index dc7ed4508..c67e13db3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java
@@ -3,11 +3,9 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
-import java.util.Locale;
/**
* Formats dates.
@@ -17,11 +15,6 @@ public class DateFormatter {
}
- public static String formatRfc822Date(Date date) {
- SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
- return format.format(date);
- }
-
public static String formatAbbrev(final Context context, final Date date) {
if (date == null) {
return "";
diff --git a/settings.gradle b/settings.gradle
index ed7f5d8d2..5e7e973f4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -34,6 +34,7 @@ include ':playback:base'
include ':playback:cast'
include ':storage:database'
+include ':storage:importexport'
include ':storage:preferences'
include ':ui:app-start-intent'
diff --git a/storage/importexport/README.md b/storage/importexport/README.md
new file mode 100644
index 000000000..925d537d3
--- /dev/null
+++ b/storage/importexport/README.md
@@ -0,0 +1,3 @@
+# :storage:importexport
+
+Import/Export of the AntennaPod database.
diff --git a/storage/importexport/build.gradle b/storage/importexport/build.gradle
new file mode 100644
index 000000000..ddbbd1951
--- /dev/null
+++ b/storage/importexport/build.gradle
@@ -0,0 +1,21 @@
+plugins {
+ id("com.android.library")
+}
+apply from: "../../common.gradle"
+
+android {
+ namespace "de.danoeh.antennapod.storage.importexport"
+}
+
+dependencies {
+ implementation project(':storage:database')
+ implementation project(':storage:preferences')
+ implementation project(':ui:i18n')
+ implementation project(':model')
+
+ annotationProcessor "androidx.annotation:annotation:$annotationVersion"
+ implementation "commons-io:commons-io:$commonsioVersion"
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
+ implementation 'androidx.documentfile:documentfile:1.0.1'
+}
diff --git a/core/src/main/assets/html-export-favorites-item-template.html b/storage/importexport/src/main/assets/html-export-favorites-item-template.html
index 83f06a458..83f06a458 100644
--- a/core/src/main/assets/html-export-favorites-item-template.html
+++ b/storage/importexport/src/main/assets/html-export-favorites-item-template.html
diff --git a/core/src/main/assets/html-export-feed-template.html b/storage/importexport/src/main/assets/html-export-feed-template.html
index 0646d5953..0646d5953 100644
--- a/core/src/main/assets/html-export-feed-template.html
+++ b/storage/importexport/src/main/assets/html-export-feed-template.html
diff --git a/core/src/main/assets/html-export-template.html b/storage/importexport/src/main/assets/html-export-template.html
index e4d3ffd31..9137ccd65 100644
--- a/core/src/main/assets/html-export-template.html
+++ b/storage/importexport/src/main/assets/html-export-template.html
@@ -16,6 +16,7 @@
background-image: linear-gradient(180deg, #0f9cff, #0682ff);
text-align: center;
padding: 10px;
+ min-height: 100%;
}
h1 {
color: #fff;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/DatabaseExporter.java
index d4a863b8b..d152ba34a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/DatabaseExporter.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.core.storage;
+package de.danoeh.antennapod.storage.importexport;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
@@ -7,7 +7,6 @@ import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.format.Formatter;
import android.util.Log;
-import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/FavoritesWriter.java
index 649ec815a..280cd1028 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/FavoritesWriter.java
@@ -1,9 +1,8 @@
-package de.danoeh.antennapod.core.export.favorites;
+package de.danoeh.antennapod.storage.importexport;
import android.content.Context;
import android.util.Log;
-import de.danoeh.antennapod.model.feed.FeedItemFilter;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -14,21 +13,17 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
-import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.model.feed.SortOrder;
/** Writes saved favorites to file. */
-public class FavoritesWriter implements ExportWriter {
+public class FavoritesWriter {
private static final String TAG = "FavoritesWriter";
private static final String FAVORITE_TEMPLATE = "html-export-favorites-item-template.html";
private static final String FEED_TEMPLATE = "html-export-feed-template.html";
private static final String UTF_8 = "UTF-8";
- @Override
- public void writeDocument(List<Feed> feeds, Writer writer, Context context)
+ public static void writeDocument(List<FeedItem> allFavorites, Writer writer, Context context)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
@@ -43,8 +38,6 @@ public class FavoritesWriter implements ExportWriter {
InputStream feedTemplateStream = context.getAssets().open(FEED_TEMPLATE);
String feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8);
- List<FeedItem> allFavorites = DBReader.getEpisodes(0, Integer.MAX_VALUE,
- new FeedItemFilter(FeedItemFilter.IS_FAVORITE), SortOrder.DATE_NEW_OLD);
Map<Long, List<FeedItem>> favoriteByFeed = getFeedMap(allFavorites);
writer.append(templateParts[0]);
@@ -72,7 +65,7 @@ public class FavoritesWriter implements ExportWriter {
* @param favoritesList {@code List} of all favorite episodes.
* @return A {@code Map} favorite episodes, keyed by feed ID.
*/
- private Map<Long, List<FeedItem>> getFeedMap(List<FeedItem> favoritesList) {
+ private static Map<Long, List<FeedItem>> getFeedMap(List<FeedItem> favoritesList) {
Map<Long, List<FeedItem>> feedMap = new TreeMap<>();
for (FeedItem item : favoritesList) {
@@ -89,7 +82,7 @@ public class FavoritesWriter implements ExportWriter {
return feedMap;
}
- private void writeFeed(Writer writer, Feed feed, String feedTemplate) throws IOException {
+ private static void writeFeed(Writer writer, Feed feed, String feedTemplate) throws IOException {
String feedInfo = feedTemplate
.replace("{FEED_IMG}", feed.getImageUrl())
.replace("{FEED_TITLE}", feed.getTitle())
@@ -99,7 +92,7 @@ public class FavoritesWriter implements ExportWriter {
writer.append(feedInfo);
}
- private void writeFavoriteItem(Writer writer, FeedItem item, String favoriteTemplate) throws IOException {
+ private static void writeFavoriteItem(Writer writer, FeedItem item, String favoriteTemplate) throws IOException {
String favItem = favoriteTemplate.replace("{FAV_TITLE}", item.getTitle().trim());
if (item.getLink() != null) {
favItem = favItem.replace("{FAV_WEBSITE}", item.getLink());
@@ -114,9 +107,4 @@ public class FavoritesWriter implements ExportWriter {
writer.append(favItem);
}
-
- @Override
- public String fileExtension() {
- return "html";
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/HtmlWriter.java
index 8a660600f..6ad1feb3d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/HtmlWriter.java
@@ -1,8 +1,7 @@
-package de.danoeh.antennapod.core.export.html;
+package de.danoeh.antennapod.storage.importexport;
import android.content.Context;
import android.util.Log;
-import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.model.feed.Feed;
import java.io.IOException;
import java.io.InputStream;
@@ -11,15 +10,14 @@ import java.util.List;
import org.apache.commons.io.IOUtils;
/** Writes HTML documents. */
-public class HtmlWriter implements ExportWriter {
+public class HtmlWriter {
private static final String TAG = "HtmlWriter";
/**
* Takes a list of feeds and a writer and writes those into an HTML
* document.
*/
- @Override
- public void writeDocument(List<Feed> feeds, Writer writer, Context context)
+ public static void writeDocument(List<Feed> feeds, Writer writer, Context context)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
@@ -43,9 +41,4 @@ public class HtmlWriter implements ExportWriter {
writer.append(templateParts[1]);
Log.d(TAG, "Finished writing document");
}
-
- public String fileExtension() {
- return "html";
- }
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlElement.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlElement.java
index e4ba08440..c644ae43e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlElement.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlElement.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.core.export.opml;
+package de.danoeh.antennapod.storage.importexport;
/**
* Represents a single feed in an OPML file.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlReader.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlReader.java
index 9bcca5caf..e5165d63b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlReader.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlReader.java
@@ -1,7 +1,9 @@
-package de.danoeh.antennapod.core.export.opml;
+package de.danoeh.antennapod.storage.importexport;
+import android.text.TextUtils;
import android.util.Log;
+import de.danoeh.antennapod.storage.preferences.BuildConfig;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@@ -10,8 +12,6 @@ import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
-import de.danoeh.antennapod.core.BuildConfig;
-
/**
* Reads OPML documents.
*/
@@ -53,7 +53,7 @@ public class OpmlReader {
OpmlElement element = new OpmlElement();
final String title = xpp.getAttributeValue(null, OpmlSymbols.TITLE);
- if (title != null) {
+ if (!TextUtils.isEmpty(title)) {
Log.i(TAG, "Using title: " + title);
element.setText(title);
} else {
@@ -63,8 +63,8 @@ public class OpmlReader {
element.setXmlUrl(xpp.getAttributeValue(null, OpmlSymbols.XMLURL));
element.setHtmlUrl(xpp.getAttributeValue(null, OpmlSymbols.HTMLURL));
element.setType(xpp.getAttributeValue(null, OpmlSymbols.TYPE));
- if (element.getXmlUrl() != null) {
- if (element.getText() == null) {
+ if (!TextUtils.isEmpty(element.getXmlUrl())) {
+ if (TextUtils.isEmpty(element.getText())) {
Log.i(TAG, "Opml element has no text attribute.");
element.setText(element.getXmlUrl());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlSymbols.java
index 0cdfd0d87..886650230 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlSymbols.java
@@ -1,12 +1,14 @@
-package de.danoeh.antennapod.core.export.opml;
-
-import de.danoeh.antennapod.core.export.CommonSymbols;
+package de.danoeh.antennapod.storage.importexport;
/**
* Contains symbols for reading and writing OPML documents.
*/
-final class OpmlSymbols extends CommonSymbols {
+final class OpmlSymbols {
+ public static final String XML_FEATURE_INDENT_OUTPUT = "http://xmlpull.org/v1/doc/features.html#indent-output";
+ public static final String HEAD = "head";
+ public static final String BODY = "body";
+ public static final String TITLE = "title";
public static final String OPML = "opml";
static final String OUTLINE = "outline";
static final String TEXT = "text";
@@ -19,5 +21,4 @@ final class OpmlSymbols extends CommonSymbols {
private OpmlSymbols() {
}
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlWriter.java
index a44d90557..75be4bffe 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
+++ b/storage/importexport/src/main/java/de/danoeh/antennapod/storage/importexport/OpmlWriter.java
@@ -1,22 +1,21 @@
-package de.danoeh.antennapod.core.export.opml;
+package de.danoeh.antennapod.storage.importexport;
-import android.content.Context;
import android.util.Log;
import android.util.Xml;
-import de.danoeh.antennapod.core.util.DateFormatter;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.Writer;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
+import java.util.Locale;
-import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.model.feed.Feed;
/** Writes OPML documents. */
-public class OpmlWriter implements ExportWriter {
+public class OpmlWriter {
private static final String TAG = "OpmlWriter";
private static final String ENCODING = "UTF-8";
@@ -27,8 +26,7 @@ public class OpmlWriter implements ExportWriter {
* Takes a list of feeds and a writer and writes those into an OPML
* document.
*/
- @Override
- public void writeDocument(List<Feed> feeds, Writer writer, Context context)
+ public static void writeDocument(List<Feed> feeds, Writer writer)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
XmlSerializer xs = Xml.newSerializer();
@@ -44,7 +42,7 @@ public class OpmlWriter implements ExportWriter {
xs.text(OPML_TITLE);
xs.endTag(null, OpmlSymbols.TITLE);
xs.startTag(null, OpmlSymbols.DATE_CREATED);
- xs.text(DateFormatter.formatRfc822Date(new Date()));
+ xs.text(formatRfc822Date(new Date()));
xs.endTag(null, OpmlSymbols.DATE_CREATED);
xs.endTag(null, OpmlSymbols.HEAD);
@@ -68,8 +66,8 @@ public class OpmlWriter implements ExportWriter {
Log.d(TAG, "Finished writing document");
}
- public String fileExtension() {
- return "opml";
+ private static String formatRfc822Date(Date date) {
+ SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
+ return format.format(date);
}
-
}