Add option to export subscriptions as HTML

This commit is contained in:
Martin Fietz 2016-10-16 12:40:01 +02:00
parent fee2acb5ab
commit 695a73c09d
11 changed files with 231 additions and 37 deletions

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.asynctask;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.File;
@ -7,7 +8,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.LangUtils;
@ -16,18 +17,22 @@ import rx.Observable;
/**
* Writes an OPML file into the export directory in the background.
*/
public class OpmlExportWorker {
public class ExportWorker {
public static final String EXPORT_DIR = "export/";
private static final String TAG = "OpmlExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds.opml";
private static final String EXPORT_DIR = "export/";
private static final String TAG = "ExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
private ExportWriter exportWriter;
private File output;
public OpmlExportWorker() {
this(new File(UserPreferences.getDataFolder(EXPORT_DIR), DEFAULT_OUTPUT_NAME));
public ExportWorker(ExportWriter exportWriter) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
}
public OpmlExportWorker(File output) {
public ExportWorker(ExportWriter exportWriter, @NonNull File output) {
this.exportWriter = exportWriter;
this.output = output;
}
@ -36,12 +41,11 @@ public class OpmlExportWorker {
Log.w(TAG, "Overwriting previously exported file.");
output.delete();
}
OpmlWriter opmlWriter = new OpmlWriter();
return Observable.create(subscriber -> {
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8);
opmlWriter.writeDocument(DBReader.getFeedList(), writer);
exportWriter.writeDocument(DBReader.getFeedList(), writer);
subscriber.onNext(output);
} catch (IOException e) {
subscriber.onError(e);

View File

@ -55,7 +55,10 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.activity.StatisticsActivity;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.export.html.HtmlWriter;
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
@ -85,6 +88,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
private static final String PREF_OPML_EXPORT = "prefOpmlExport";
private static final String PREF_HTML_EXPORT = "prefHtmlExport";
private static final String STATISTICS = "statistics";
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
@ -181,7 +185,9 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
);
ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
preference -> exportOpml());
preference -> export(new OpmlWriter()));
ui.findPreference(PreferenceController.PREF_HTML_EXPORT).setOnPreferenceClickListener(
preference -> export(new HtmlWriter()));
ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
preference -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
@ -438,7 +444,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
}
private boolean exportOpml() {
private boolean export(ExportWriter exportWriter) {
Context context = ui.getActivity();
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(context.getString(R.string.exporting_label));
@ -446,7 +452,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
progressDialog.show();
final AlertDialog.Builder alert = new AlertDialog.Builder(context)
.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
Observable<File> observable = new OpmlExportWorker().exportObservable();
Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
subscription = observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(output -> {

View File

@ -288,6 +288,9 @@
<Preference
android:key="prefOpmlExport"
android:title="@string/opml_export_label"/>
<Preference
android:key="prefHtmlExport"
android:title="@string/html_export_label"/>
<Preference
android:key="statistics"
android:title="@string/statistics_label"/>

View File

@ -8,6 +8,7 @@ import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayOutputStream;
@ -27,10 +28,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import de.danoeh.antennapod.core.BuildConfig;
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.core.feed.Feed;
import de.danoeh.antennapod.core.opml.OpmlElement;
import de.danoeh.antennapod.core.opml.OpmlReader;
import de.danoeh.antennapod.core.opml.OpmlWriter;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@ -56,7 +57,9 @@ public class OpmlBackupAgent extends BackupAgentHelper {
}
}
/** Class for backing up and restoring the OPML file. */
/**
* Class for backing up and restoring the OPML file.
*/
private static class OpmlBackupHelper implements BackupHelper {
private static final String TAG = "OpmlBackupHelper";
@ -64,7 +67,9 @@ public class OpmlBackupAgent extends BackupAgentHelper {
private final Context mContext;
/** Checksum of restored OPML file */
/**
* Checksum of restored OPML file
*/
private byte[] mChecksum;
public OpmlBackupHelper(Context context) {
@ -170,12 +175,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
} catch (IOException e) {
Log.e(TAG, "Failed to restore OPML backup", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
}
}
IOUtils.closeQuietly(reader);
}
}

View File

@ -0,0 +1,22 @@
/*
* 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";
}

View File

@ -0,0 +1,29 @@
/*
* 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 java.io.IOException;
import java.io.Writer;
import java.util.List;
import de.danoeh.antennapod.core.feed.Feed;
public interface ExportWriter {
void writeDocument(List<Feed> feeds, Writer writer)
throws IllegalArgumentException, IllegalStateException, IOException;
String fileExtension();
}

View File

@ -0,0 +1,30 @@
/*
* 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.html;
import de.danoeh.antennapod.core.export.CommonSymbols;
class HtmlSymbols extends CommonSymbols {
static final String HTML = "html";
static final String ORDERED_LIST = "ol";
static final String LIST_ITEM = "li";
static String HEADING = "h1";
static final String LINK = "a";
static final String LINK_DESTINATION = "href";
}

View File

@ -0,0 +1,92 @@
package de.danoeh.antennapod.core.export.html;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.feed.Feed;
/** Writes HTML documents. */
public class HtmlWriter implements ExportWriter {
private static final String TAG = "HtmlWriter";
private static final String ENCODING = "UTF-8";
private static final String HTML_TITLE = "AntennaPod Subscriptions";
/**
* Takes a list of feeds and a writer and writes those into an HTML
* document.
*
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
@Override
public void writeDocument(List<Feed> feeds, Writer writer)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
XmlSerializer xs = Xml.newSerializer();
xs.setOutput(writer);
xs.startDocument(ENCODING, false);
xs.text("\n");
xs.startTag(null, HtmlSymbols.HTML);
xs.text("\n");
xs.startTag(null, HtmlSymbols.HEAD);
xs.text("\n");
xs.startTag(null, HtmlSymbols.TITLE);
xs.text(HTML_TITLE);
xs.endTag(null, HtmlSymbols.TITLE);
xs.text("\n");
xs.endTag(null, HtmlSymbols.HEAD);
xs.text("\n");
xs.startTag(null, HtmlSymbols.BODY);
xs.text("\n");
xs.startTag(null, HtmlSymbols.HEADING);
xs.text(HTML_TITLE);
xs.endTag(null, HtmlSymbols.HEADING);
xs.text("\n");
xs.startTag(null, HtmlSymbols.ORDERED_LIST);
xs.text("\n");
for (Feed feed : feeds) {
xs.startTag(null, HtmlSymbols.LIST_ITEM);
xs.text(feed.getTitle());
if (!TextUtils.isEmpty(feed.getLink())) {
xs.text(" [");
xs.startTag(null, HtmlSymbols.LINK);
xs.attribute(null, HtmlSymbols.LINK_DESTINATION, feed.getLink());
xs.text("Website");
xs.endTag(null, HtmlSymbols.LINK);
xs.text("]");
}
xs.text(" [");
xs.startTag(null, HtmlSymbols.LINK);
xs.attribute(null, HtmlSymbols.LINK_DESTINATION, feed.getDownload_url());
xs.text("Feed");
xs.endTag(null, HtmlSymbols.LINK);
xs.text("]");
xs.endTag(null, HtmlSymbols.LIST_ITEM);
xs.text("\n");
}
xs.endTag(null, HtmlSymbols.ORDERED_LIST);
xs.endTag(null, HtmlSymbols.BODY);
xs.text("\n");
xs.endTag(null, HtmlSymbols.HTML);
xs.text("\n");
xs.endDocument();
Log.d(TAG, "Finished writing document");
}
public String fileExtension() {
return "html";
}
}

View File

@ -1,19 +1,18 @@
package de.danoeh.antennapod.core.export.opml;
import de.danoeh.antennapod.core.export.CommonSymbols;
/** Contains symbols for reading and writing OPML documents. */
public final class OpmlSymbols {
public final class OpmlSymbols extends CommonSymbols {
public static final String OPML = "opml";
public static final String BODY = "body";
public static final String OUTLINE = "outline";
public static final String TEXT = "text";
public static final String XMLURL = "xmlUrl";
public static final String HTMLURL = "htmlUrl";
public static final String TYPE = "type";
public static final String VERSION = "version";
public static final String HEAD = "head";
public static final String TITLE = "title";
public static final String DATE_CREATED = "dateCreated";
static final String OUTLINE = "outline";
static final String TEXT = "text";
static final String XMLURL = "xmlUrl";
static final String HTMLURL = "htmlUrl";
static final String TYPE = "type";
static final String VERSION = "version";
static final String DATE_CREATED = "dateCreated";
private OpmlSymbols() {

View File

@ -10,11 +10,13 @@ import java.io.Writer;
import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.util.DateUtils;
/** Writes OPML documents. */
public class OpmlWriter {
public class OpmlWriter implements ExportWriter {
private static final String TAG = "OpmlWriter";
private static final String ENCODING = "UTF-8";
private static final String OPML_VERSION = "2.0";
@ -28,6 +30,7 @@ public class OpmlWriter {
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
@Override
public void writeDocument(List<Feed> feeds, Writer writer)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
@ -83,4 +86,9 @@ public class OpmlWriter {
xs.endDocument();
Log.d(TAG, "Finished writing document");
}
public String fileExtension() {
return "opml";
}
}

View File

@ -451,6 +451,7 @@
<string name="choose_file_from_filesystem">From local filesystem</string>
<string name="choose_file_from_external_application">Use external application</string>
<string name="opml_export_label">OPML export</string>
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Exporting&#8230;</string>
<string name="export_error_label">Export error</string>
<string name="opml_export_success_title">OPML Export successful.</string>