Move import/export to its own module (#6986)
Also clean up ImportExportPreferencesFragment a bit.
This commit is contained in:
parent
5c98a33ed2
commit
2f3f1fd186
@ -86,6 +86,7 @@ dependencies {
|
|||||||
implementation project(':playback:base')
|
implementation project(':playback:base')
|
||||||
implementation project(':playback:cast')
|
implementation project(':playback:cast')
|
||||||
implementation project(':storage:database')
|
implementation project(':storage:database')
|
||||||
|
implementation project(':storage:importexport')
|
||||||
implementation project(':storage:preferences')
|
implementation project(':storage:preferences')
|
||||||
implementation project(':ui:app-start-intent')
|
implementation project(':ui:app-start-intent')
|
||||||
implementation project(':ui:common')
|
implementation project(':ui:common')
|
||||||
|
@ -6,13 +6,15 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
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.Feed;
|
||||||
import de.danoeh.antennapod.model.feed.FeedFunding;
|
import de.danoeh.antennapod.model.feed.FeedFunding;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.parser.feed.namespace.PodcastIndex;
|
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.
|
* Creates RSS 2.0 feeds. See FeedGenerator for more information.
|
||||||
@ -98,7 +100,7 @@ public class Rss2Generator implements FeedGenerator {
|
|||||||
}
|
}
|
||||||
if (item.getPubDate() != null) {
|
if (item.getPubDate() != null) {
|
||||||
xml.startTag(null, "pubDate");
|
xml.startTag(null, "pubDate");
|
||||||
xml.text(DateFormatter.formatRfc822Date(item.getPubDate()));
|
xml.text(formatRfc822Date(item.getPubDate()));
|
||||||
xml.endTag(null, "pubDate");
|
xml.endTag(null, "pubDate");
|
||||||
}
|
}
|
||||||
if ((flags & FEATURE_WRITE_GUID) != 0) {
|
if ((flags & FEATURE_WRITE_GUID) != 0) {
|
||||||
@ -132,4 +134,9 @@ public class Rss2Generator implements FeedGenerator {
|
|||||||
|
|
||||||
xml.endDocument();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,14 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import de.danoeh.antennapod.R;
|
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.preferences.ThemeSwitcher;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
|
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
|
||||||
import de.danoeh.antennapod.databinding.OpmlSelectionBinding;
|
import de.danoeh.antennapod.databinding.OpmlSelectionBinding;
|
||||||
import de.danoeh.antennapod.model.feed.Feed;
|
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.Completable;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -16,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts.GetContent;
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import androidx.core.app.ShareCompat;
|
import androidx.core.app.ShareCompat;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
@ -25,13 +26,15 @@ import de.danoeh.antennapod.PodcastApp;
|
|||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.OpmlImportActivity;
|
import de.danoeh.antennapod.activity.OpmlImportActivity;
|
||||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||||
import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
import de.danoeh.antennapod.asynctask.ExportWorker;
|
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||||
import de.danoeh.antennapod.core.export.favorites.FavoritesWriter;
|
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||||
import de.danoeh.antennapod.core.export.html.HtmlWriter;
|
import de.danoeh.antennapod.storage.importexport.DatabaseExporter;
|
||||||
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
|
import de.danoeh.antennapod.storage.importexport.FavoritesWriter;
|
||||||
import de.danoeh.antennapod.core.storage.DatabaseExporter;
|
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.Completable;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
@ -39,8 +42,14 @@ import io.reactivex.disposables.Disposable;
|
|||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
import java.io.File;
|
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.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
|
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 CONTENT_TYPE_HTML = "text/html";
|
||||||
private static final String DEFAULT_FAVORITES_OUTPUT_NAME = "antennapod-favorites-%s.html";
|
private static final String DEFAULT_FAVORITES_OUTPUT_NAME = "antennapod-favorites-%s.html";
|
||||||
private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db";
|
private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db";
|
||||||
|
|
||||||
private final ActivityResultLauncher<Intent> chooseOpmlExportPathLauncher =
|
private final ActivityResultLauncher<Intent> chooseOpmlExportPathLauncher =
|
||||||
registerForActivityResult(new StartActivityForResult(), this::chooseOpmlExportPathResult);
|
registerForActivityResult(new StartActivityForResult(),
|
||||||
|
result -> exportToDocument(result, Export.OPML));
|
||||||
private final ActivityResultLauncher<Intent> chooseHtmlExportPathLauncher =
|
private final ActivityResultLauncher<Intent> chooseHtmlExportPathLauncher =
|
||||||
registerForActivityResult(new StartActivityForResult(), this::chooseHtmlExportPathResult);
|
registerForActivityResult(new StartActivityForResult(),
|
||||||
|
result -> exportToDocument(result, Export.HTML));
|
||||||
private final ActivityResultLauncher<Intent> chooseFavoritesExportPathLauncher =
|
private final ActivityResultLauncher<Intent> chooseFavoritesExportPathLauncher =
|
||||||
registerForActivityResult(new StartActivityForResult(), this::chooseFavoritesExportPathResult);
|
registerForActivityResult(new StartActivityForResult(),
|
||||||
|
result -> exportToDocument(result, Export.FAVORITES));
|
||||||
private final ActivityResultLauncher<Intent> restoreDatabaseLauncher =
|
private final ActivityResultLauncher<Intent> restoreDatabaseLauncher =
|
||||||
registerForActivityResult(new StartActivityForResult(), this::restoreDatabaseResult);
|
registerForActivityResult(new StartActivityForResult(), this::restoreDatabaseResult);
|
||||||
private final ActivityResultLauncher<String> backupDatabaseLauncher =
|
private final ActivityResultLauncher<String> backupDatabaseLauncher =
|
||||||
registerForActivityResult(new BackupDatabase(), this::backupDatabaseResult);
|
registerForActivityResult(new BackupDatabase(), this::backupDatabaseResult);
|
||||||
private final ActivityResultLauncher<String> chooseOpmlImportPathLauncher =
|
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 Disposable disposable;
|
||||||
private ProgressDialog progressDialog;
|
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() {
|
private void setupStorageScreen() {
|
||||||
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
|
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
|
||||||
preference -> {
|
preference -> {
|
||||||
openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher, new OpmlWriter());
|
openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
|
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
|
||||||
preference -> {
|
preference -> {
|
||||||
openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher, new HtmlWriter());
|
openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
|
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
|
||||||
@ -132,32 +148,13 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
});
|
});
|
||||||
findPreference(PREF_FAVORITE_EXPORT).setOnPreferenceClickListener(
|
findPreference(PREF_FAVORITE_EXPORT).setOnPreferenceClickListener(
|
||||||
preference -> {
|
preference -> {
|
||||||
openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher, new FavoritesWriter());
|
openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportWithWriter(ExportWriter exportWriter, Uri uri, Export exportType) {
|
private String dateStampFilename(String fname) {
|
||||||
Context context = getActivity();
|
return String.format(fname, new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date()));
|
||||||
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 void exportDatabase() {
|
private void exportDatabase() {
|
||||||
@ -211,30 +208,6 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
alert.show();
|
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) {
|
private void restoreDatabaseResult(final ActivityResult result) {
|
||||||
if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
|
if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
|
||||||
return;
|
return;
|
||||||
@ -264,16 +237,7 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
}, this::showExportErrorDialog);
|
}, this::showExportErrorDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void chooseOpmlImportPathResult(final Uri uri) {
|
private void openExportPathPicker(Export exportType, ActivityResultLauncher<Intent> result) {
|
||||||
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) {
|
|
||||||
String title = dateStampFilename(exportType.outputNameTemplate);
|
String title = dateStampFilename(exportType.outputNameTemplate);
|
||||||
|
|
||||||
Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
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
|
// If we are using a SDK lower than API 21 or the implicit intent failed
|
||||||
// fallback to the legacy export process
|
// 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 {
|
private static class BackupDatabase extends ActivityResultContracts.CreateDocument {
|
||||||
|
@ -35,6 +35,7 @@ dependencies {
|
|||||||
implementation project(':playback:base')
|
implementation project(':playback:base')
|
||||||
implementation project(':playback:cast')
|
implementation project(':playback:cast')
|
||||||
implementation project(':storage:database')
|
implementation project(':storage:database')
|
||||||
|
implementation project(':storage:importexport')
|
||||||
implementation project(':storage:preferences')
|
implementation project(':storage:preferences')
|
||||||
implementation project(':ui:app-start-intent')
|
implementation project(':ui:app-start-intent')
|
||||||
implementation project(':ui:common')
|
implementation project(':ui:common')
|
||||||
|
@ -10,6 +10,9 @@ import android.util.Log;
|
|||||||
|
|
||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
|
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.apache.commons.io.IOUtils;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@ -31,9 +34,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
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.model.feed.Feed;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Write OPML
|
// 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
|
// Compare checksum of new and old file to see if we need to perform a backup at all
|
||||||
if (digester != null) {
|
if (digester != 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";
|
|
||||||
|
|
||||||
}
|
|
@ -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();
|
|
||||||
|
|
||||||
}
|
|
@ -3,11 +3,9 @@ package de.danoeh.antennapod.core.util;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats dates.
|
* 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) {
|
public static String formatAbbrev(final Context context, final Date date) {
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -34,6 +34,7 @@ include ':playback:base'
|
|||||||
include ':playback:cast'
|
include ':playback:cast'
|
||||||
|
|
||||||
include ':storage:database'
|
include ':storage:database'
|
||||||
|
include ':storage:importexport'
|
||||||
include ':storage:preferences'
|
include ':storage:preferences'
|
||||||
|
|
||||||
include ':ui:app-start-intent'
|
include ':ui:app-start-intent'
|
||||||
|
3
storage/importexport/README.md
Normal file
3
storage/importexport/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# :storage:importexport
|
||||||
|
|
||||||
|
Import/Export of the AntennaPod database.
|
21
storage/importexport/build.gradle
Normal file
21
storage/importexport/build.gradle
Normal file
@ -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'
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
background-image: linear-gradient(180deg, #0f9cff, #0682ff);
|
background-image: linear-gradient(180deg, #0f9cff, #0682ff);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
color: #fff;
|
color: #fff;
|
@ -1,4 +1,4 @@
|
|||||||
package de.danoeh.antennapod.core.storage;
|
package de.danoeh.antennapod.storage.importexport;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
@ -7,7 +7,6 @@ import android.net.Uri;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import de.danoeh.antennapod.core.R;
|
|
||||||
import de.danoeh.antennapod.storage.database.PodDBAdapter;
|
import de.danoeh.antennapod.storage.database.PodDBAdapter;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
@ -1,9 +1,8 @@
|
|||||||
package de.danoeh.antennapod.core.export.favorites;
|
package de.danoeh.antennapod.storage.importexport;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -14,21 +13,17 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
|
||||||
import de.danoeh.antennapod.model.feed.Feed;
|
import de.danoeh.antennapod.model.feed.Feed;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
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. */
|
/** Writes saved favorites to file. */
|
||||||
public class FavoritesWriter implements ExportWriter {
|
public class FavoritesWriter {
|
||||||
private static final String TAG = "FavoritesWriter";
|
private static final String TAG = "FavoritesWriter";
|
||||||
private static final String FAVORITE_TEMPLATE = "html-export-favorites-item-template.html";
|
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 FEED_TEMPLATE = "html-export-feed-template.html";
|
||||||
private static final String UTF_8 = "UTF-8";
|
private static final String UTF_8 = "UTF-8";
|
||||||
|
|
||||||
@Override
|
public static void writeDocument(List<FeedItem> allFavorites, Writer writer, Context context)
|
||||||
public void writeDocument(List<Feed> feeds, Writer writer, Context context)
|
|
||||||
throws IllegalArgumentException, IllegalStateException, IOException {
|
throws IllegalArgumentException, IllegalStateException, IOException {
|
||||||
Log.d(TAG, "Starting to write document");
|
Log.d(TAG, "Starting to write document");
|
||||||
|
|
||||||
@ -43,8 +38,6 @@ public class FavoritesWriter implements ExportWriter {
|
|||||||
InputStream feedTemplateStream = context.getAssets().open(FEED_TEMPLATE);
|
InputStream feedTemplateStream = context.getAssets().open(FEED_TEMPLATE);
|
||||||
String feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8);
|
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);
|
Map<Long, List<FeedItem>> favoriteByFeed = getFeedMap(allFavorites);
|
||||||
|
|
||||||
writer.append(templateParts[0]);
|
writer.append(templateParts[0]);
|
||||||
@ -72,7 +65,7 @@ public class FavoritesWriter implements ExportWriter {
|
|||||||
* @param favoritesList {@code List} of all favorite episodes.
|
* @param favoritesList {@code List} of all favorite episodes.
|
||||||
* @return A {@code Map} favorite episodes, keyed by feed ID.
|
* @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<>();
|
Map<Long, List<FeedItem>> feedMap = new TreeMap<>();
|
||||||
|
|
||||||
for (FeedItem item : favoritesList) {
|
for (FeedItem item : favoritesList) {
|
||||||
@ -89,7 +82,7 @@ public class FavoritesWriter implements ExportWriter {
|
|||||||
return feedMap;
|
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
|
String feedInfo = feedTemplate
|
||||||
.replace("{FEED_IMG}", feed.getImageUrl())
|
.replace("{FEED_IMG}", feed.getImageUrl())
|
||||||
.replace("{FEED_TITLE}", feed.getTitle())
|
.replace("{FEED_TITLE}", feed.getTitle())
|
||||||
@ -99,7 +92,7 @@ public class FavoritesWriter implements ExportWriter {
|
|||||||
writer.append(feedInfo);
|
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());
|
String favItem = favoriteTemplate.replace("{FAV_TITLE}", item.getTitle().trim());
|
||||||
if (item.getLink() != null) {
|
if (item.getLink() != null) {
|
||||||
favItem = favItem.replace("{FAV_WEBSITE}", item.getLink());
|
favItem = favItem.replace("{FAV_WEBSITE}", item.getLink());
|
||||||
@ -114,9 +107,4 @@ public class FavoritesWriter implements ExportWriter {
|
|||||||
|
|
||||||
writer.append(favItem);
|
writer.append(favItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String fileExtension() {
|
|
||||||
return "html";
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,8 +1,7 @@
|
|||||||
package de.danoeh.antennapod.core.export.html;
|
package de.danoeh.antennapod.storage.importexport;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
|
||||||
import de.danoeh.antennapod.model.feed.Feed;
|
import de.danoeh.antennapod.model.feed.Feed;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -11,15 +10,14 @@ import java.util.List;
|
|||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
/** Writes HTML documents. */
|
/** Writes HTML documents. */
|
||||||
public class HtmlWriter implements ExportWriter {
|
public class HtmlWriter {
|
||||||
private static final String TAG = "HtmlWriter";
|
private static final String TAG = "HtmlWriter";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a list of feeds and a writer and writes those into an HTML
|
* Takes a list of feeds and a writer and writes those into an HTML
|
||||||
* document.
|
* document.
|
||||||
*/
|
*/
|
||||||
@Override
|
public static void writeDocument(List<Feed> feeds, Writer writer, Context context)
|
||||||
public void writeDocument(List<Feed> feeds, Writer writer, Context context)
|
|
||||||
throws IllegalArgumentException, IllegalStateException, IOException {
|
throws IllegalArgumentException, IllegalStateException, IOException {
|
||||||
Log.d(TAG, "Starting to write document");
|
Log.d(TAG, "Starting to write document");
|
||||||
|
|
||||||
@ -43,9 +41,4 @@ public class HtmlWriter implements ExportWriter {
|
|||||||
writer.append(templateParts[1]);
|
writer.append(templateParts[1]);
|
||||||
Log.d(TAG, "Finished writing document");
|
Log.d(TAG, "Finished writing document");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String fileExtension() {
|
|
||||||
return "html";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -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.
|
* Represents a single feed in an OPML file.
|
@ -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 android.util.Log;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.storage.preferences.BuildConfig;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlPullParserFactory;
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
@ -10,8 +12,6 @@ import java.io.IOException;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.BuildConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads OPML documents.
|
* Reads OPML documents.
|
||||||
*/
|
*/
|
||||||
@ -53,7 +53,7 @@ public class OpmlReader {
|
|||||||
OpmlElement element = new OpmlElement();
|
OpmlElement element = new OpmlElement();
|
||||||
|
|
||||||
final String title = xpp.getAttributeValue(null, OpmlSymbols.TITLE);
|
final String title = xpp.getAttributeValue(null, OpmlSymbols.TITLE);
|
||||||
if (title != null) {
|
if (!TextUtils.isEmpty(title)) {
|
||||||
Log.i(TAG, "Using title: " + title);
|
Log.i(TAG, "Using title: " + title);
|
||||||
element.setText(title);
|
element.setText(title);
|
||||||
} else {
|
} else {
|
||||||
@ -63,8 +63,8 @@ public class OpmlReader {
|
|||||||
element.setXmlUrl(xpp.getAttributeValue(null, OpmlSymbols.XMLURL));
|
element.setXmlUrl(xpp.getAttributeValue(null, OpmlSymbols.XMLURL));
|
||||||
element.setHtmlUrl(xpp.getAttributeValue(null, OpmlSymbols.HTMLURL));
|
element.setHtmlUrl(xpp.getAttributeValue(null, OpmlSymbols.HTMLURL));
|
||||||
element.setType(xpp.getAttributeValue(null, OpmlSymbols.TYPE));
|
element.setType(xpp.getAttributeValue(null, OpmlSymbols.TYPE));
|
||||||
if (element.getXmlUrl() != null) {
|
if (!TextUtils.isEmpty(element.getXmlUrl())) {
|
||||||
if (element.getText() == null) {
|
if (TextUtils.isEmpty(element.getText())) {
|
||||||
Log.i(TAG, "Opml element has no text attribute.");
|
Log.i(TAG, "Opml element has no text attribute.");
|
||||||
element.setText(element.getXmlUrl());
|
element.setText(element.getXmlUrl());
|
||||||
}
|
}
|
@ -1,12 +1,14 @@
|
|||||||
package de.danoeh.antennapod.core.export.opml;
|
package de.danoeh.antennapod.storage.importexport;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.export.CommonSymbols;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains symbols for reading and writing OPML documents.
|
* 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";
|
public static final String OPML = "opml";
|
||||||
static final String OUTLINE = "outline";
|
static final String OUTLINE = "outline";
|
||||||
static final String TEXT = "text";
|
static final String TEXT = "text";
|
||||||
@ -19,5 +21,4 @@ final class OpmlSymbols extends CommonSymbols {
|
|||||||
private OpmlSymbols() {
|
private OpmlSymbols() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.util.DateFormatter;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
|
||||||
import de.danoeh.antennapod.model.feed.Feed;
|
import de.danoeh.antennapod.model.feed.Feed;
|
||||||
|
|
||||||
/** Writes OPML documents. */
|
/** Writes OPML documents. */
|
||||||
public class OpmlWriter implements ExportWriter {
|
public class OpmlWriter {
|
||||||
|
|
||||||
private static final String TAG = "OpmlWriter";
|
private static final String TAG = "OpmlWriter";
|
||||||
private static final String ENCODING = "UTF-8";
|
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
|
* Takes a list of feeds and a writer and writes those into an OPML
|
||||||
* document.
|
* document.
|
||||||
*/
|
*/
|
||||||
@Override
|
public static void writeDocument(List<Feed> feeds, Writer writer)
|
||||||
public void writeDocument(List<Feed> feeds, Writer writer, Context context)
|
|
||||||
throws IllegalArgumentException, IllegalStateException, IOException {
|
throws IllegalArgumentException, IllegalStateException, IOException {
|
||||||
Log.d(TAG, "Starting to write document");
|
Log.d(TAG, "Starting to write document");
|
||||||
XmlSerializer xs = Xml.newSerializer();
|
XmlSerializer xs = Xml.newSerializer();
|
||||||
@ -44,7 +42,7 @@ public class OpmlWriter implements ExportWriter {
|
|||||||
xs.text(OPML_TITLE);
|
xs.text(OPML_TITLE);
|
||||||
xs.endTag(null, OpmlSymbols.TITLE);
|
xs.endTag(null, OpmlSymbols.TITLE);
|
||||||
xs.startTag(null, OpmlSymbols.DATE_CREATED);
|
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.DATE_CREATED);
|
||||||
xs.endTag(null, OpmlSymbols.HEAD);
|
xs.endTag(null, OpmlSymbols.HEAD);
|
||||||
|
|
||||||
@ -68,8 +66,8 @@ public class OpmlWriter implements ExportWriter {
|
|||||||
Log.d(TAG, "Finished writing document");
|
Log.d(TAG, "Finished writing document");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String fileExtension() {
|
private static String formatRfc822Date(Date date) {
|
||||||
return "opml";
|
SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
|
||||||
|
return format.format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user