Merge pull request #4106 from malockin/export-favorites

Export favourites
This commit is contained in:
H. Lehmann 2020-05-24 22:42:29 +02:00 committed by GitHub
commit e0d1f5d529
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 171 additions and 3 deletions

View File

@ -5,7 +5,6 @@ import android.app.ProgressDialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
@ -25,6 +24,7 @@ import de.danoeh.antennapod.activity.SplashActivity;
import de.danoeh.antennapod.asynctask.DocumentFileExportWorker; import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker; import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter; 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.html.HtmlWriter;
import de.danoeh.antennapod.core.export.opml.OpmlWriter; import de.danoeh.antennapod.core.export.opml.OpmlWriter;
import de.danoeh.antennapod.core.storage.DatabaseExporter; import de.danoeh.antennapod.core.storage.DatabaseExporter;
@ -47,15 +47,18 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
private static final String PREF_HTML_EXPORT = "prefHtmlExport"; private static final String PREF_HTML_EXPORT = "prefHtmlExport";
private static final String PREF_DATABASE_IMPORT = "prefDatabaseImport"; private static final String PREF_DATABASE_IMPORT = "prefDatabaseImport";
private static final String PREF_DATABASE_EXPORT = "prefDatabaseExport"; private static final String PREF_DATABASE_EXPORT = "prefDatabaseExport";
private static final String PREF_FAVORITE_EXPORT = "prefFavoritesExport";
private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds-%s.opml"; private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds-%s.opml";
private static final String CONTENT_TYPE_OPML = "text/x-opml"; private static final String CONTENT_TYPE_OPML = "text/x-opml";
private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds-%s.html"; private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds-%s.html";
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 int REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH = 1; private static final int REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH = 1;
private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 2; private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 2;
private static final int REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH = 3; private static final int REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH = 3;
private static final int REQUEST_CODE_RESTORE_DATABASE = 4; private static final int REQUEST_CODE_RESTORE_DATABASE = 4;
private static final int REQUEST_CODE_BACKUP_DATABASE = 5; private static final int REQUEST_CODE_BACKUP_DATABASE = 5;
private static final int REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH = 6;
private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db"; private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db";
private Disposable disposable; private Disposable disposable;
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
@ -125,6 +128,12 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
exportDatabase(); exportDatabase();
return true; return true;
}); });
findPreference(PREF_FAVORITE_EXPORT).setOnPreferenceClickListener(
preference -> {
openExportPathPicker(CONTENT_TYPE_HTML, dateStampFilename(DEFAULT_FAVORITES_OUTPUT_NAME),
REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH, new FavoritesWriter());
return true;
});
} }
private void exportWithWriter(ExportWriter exportWriter, final Uri uri) { private void exportWithWriter(ExportWriter exportWriter, final Uri uri) {
@ -257,6 +266,8 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
exportWithWriter(new OpmlWriter(), uri); exportWithWriter(new OpmlWriter(), uri);
} else if (requestCode == REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH) { } else if (requestCode == REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH) {
exportWithWriter(new HtmlWriter(), uri); exportWithWriter(new HtmlWriter(), uri);
} else if (requestCode == REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH) {
exportWithWriter(new FavoritesWriter(), uri);
} else if (requestCode == REQUEST_CODE_RESTORE_DATABASE) { } else if (requestCode == REQUEST_CODE_RESTORE_DATABASE) {
progressDialog.show(); progressDialog.show();
disposable = Completable.fromAction(() -> DatabaseExporter.importBackup(uri, getContext())) disposable = Completable.fromAction(() -> DatabaseExporter.importBackup(uri, getContext()))

View File

@ -32,5 +32,9 @@
android:key="prefHtmlExport" android:key="prefHtmlExport"
android:title="@string/html_export_label" android:title="@string/html_export_label"
android:summary="@string/html_export_summary"/> android:summary="@string/html_export_summary"/>
<Preference
android:key="prefFavoritesExport"
android:title="@string/favorites_export_label"
android:summary="@string/favorites_export_summary"/>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -0,0 +1,4 @@
<li><span>
{FAV_TITLE}<br>
<a href="{FAV_WEBSITE}">Website</a><a href="{FAV_MEDIA}">Media</a>
</span></li>

View File

@ -0,0 +1,7 @@
<img src="{FEED_IMG}" />
<p>
{FEED_TITLE}
<span>
<a href="{FEED_LINK}">Website</a><a href="{FEED_WEBSITE}">Feed</a>
</span>
</p>

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='no' ?> <?xml version='1.0' encoding='UTF-8' standalone='no' ?>
<html> <html>
<head> <head>
<title>AntennaPod Subscriptions</title> <title>AntennaPod {TITLE}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style> <style>
* { * {
@ -72,11 +72,18 @@
margin-top: 10px; margin-top: 10px;
clear:left; clear:left;
} }
ul > li > span {
width: 100%;
border-top: 1px solid #eee8e8;
padding: 5px;
box-shadow: none;
text-align: left;
}
</style> </style>
</head> </head>
<body> <body>
<img src="https://antennapod.org/assets/img/antennapod-logo.png" /> <img src="https://antennapod.org/assets/img/antennapod-logo.png" />
<h1>AntennaPod Subscriptions</h1> <h1>AntennaPod {TITLE}</h1>
<ul> <ul>
{FEEDS} {FEEDS}
</ul> </ul>

View File

@ -0,0 +1,132 @@
package de.danoeh.antennapod.core.export.favorites;
import android.content.Context;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
/** Writes saved favorites to file. */
public class FavoritesWriter implements ExportWriter {
private static final String TAG = "FavoritesWriter";
private static final int PAGE_LIMIT = 100;
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)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
InputStream templateStream = context.getAssets().open("html-export-template.html");
String template = IOUtils.toString(templateStream, UTF_8);
template = template.replaceAll("\\{TITLE\\}", "Favorites");
String[] templateParts = template.split("\\{FEEDS\\}");
InputStream favTemplateStream = context.getAssets().open(FAVORITE_TEMPLATE);
String favTemplate = IOUtils.toString(favTemplateStream, UTF_8);
InputStream feedTemplateStream = context.getAssets().open(FEED_TEMPLATE);
String feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8);
Map<Long, List<FeedItem>> favoriteByFeed = getFeedMap(getFavorites());
writer.append(templateParts[0]);
for (Long feedId : favoriteByFeed.keySet()) {
List<FeedItem> favorites = favoriteByFeed.get(feedId);
writer.append("<li><div>\n");
writeFeed(writer, favorites.get(0).getFeed(), feedTemplate);
writer.append("<ul>\n");
for (FeedItem item : favorites) {
writeFavoriteItem(writer, item, favTemplate);
}
writer.append("</ul></div></li>\n");
}
writer.append(templateParts[1]);
Log.d(TAG, "Finished writing document");
}
private List<FeedItem> getFavorites() {
int page = 0;
List<FeedItem> favoritesList = new ArrayList<>();
List<FeedItem> favoritesPage;
do {
favoritesPage = DBReader.getFavoriteItemsList(page * PAGE_LIMIT, PAGE_LIMIT);
favoritesList.addAll(favoritesPage);
++page;
} while (!favoritesPage.isEmpty() && favoritesPage.size() == PAGE_LIMIT);
// sort in descending order
Collections.sort(favoritesList, (lhs, rhs) -> rhs.getPubDate().compareTo(lhs.getPubDate()));
return favoritesList;
}
/**
* Group favorite episodes by feed, sorting them by publishing date in descending order.
*
* @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) {
Map<Long, List<FeedItem>> feedMap = new TreeMap<>();
for (FeedItem item : favoritesList) {
List<FeedItem> feedEpisodes = feedMap.get(item.getFeedId());
if (feedEpisodes == null) {
feedEpisodes = new ArrayList<>();
feedMap.put(item.getFeedId(), feedEpisodes);
}
feedEpisodes.add(item);
}
return feedMap;
}
private void writeFeed(Writer writer, Feed feed, String feedTemplate) throws IOException {
String feedInfo = feedTemplate
.replace("{FEED_IMG}", feed.getImageUrl())
.replace("{FEED_TITLE}", feed.getTitle())
.replace("{FEED_LINK}", feed.getLink())
.replace("{FEED_WEBSITE}", feed.getDownload_url());
writer.append(feedInfo);
}
private void writeFavoriteItem(Writer writer, FeedItem item, String favoriteTemplate) throws IOException {
String favItem = favoriteTemplate
.replace("{FAV_TITLE}", item.getTitle().trim())
.replace("{FAV_WEBSITE}", item.getLink())
.replace("{FAV_MEDIA}", item.getMedia().getDownload_url());
writer.append(favItem);
}
@Override
public String fileExtension() {
return "html";
}
}

View File

@ -25,6 +25,7 @@ public class HtmlWriter implements ExportWriter {
InputStream templateStream = context.getAssets().open("html-export-template.html"); InputStream templateStream = context.getAssets().open("html-export-template.html");
String template = IOUtils.toString(templateStream, "UTF-8"); String template = IOUtils.toString(templateStream, "UTF-8");
template = template.replaceAll("\\{TITLE\\}", "Subscriptions");
String[] templateParts = template.split("\\{FEEDS\\}"); String[] templateParts = template.split("\\{FEEDS\\}");
writer.append(templateParts[0]); writer.append(templateParts[0]);

View File

@ -580,6 +580,8 @@
<string name="import_select_file">Select file to import</string> <string name="import_select_file">Select file to import</string>
<string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string> <string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string>
<string name="import_no_downgrade">This database was exported with a newer version of AntennaPod. Your current installation does not yet know how to handle this file.</string> <string name="import_no_downgrade">This database was exported with a newer version of AntennaPod. Your current installation does not yet know how to handle this file.</string>
<string name="favorites_export_label">Favorites export</string>
<string name="favorites_export_summary">Export saved favorites to file</string>
<!-- Sleep timer --> <!-- Sleep timer -->
<string name="set_sleeptimer_label">Set sleep timer</string> <string name="set_sleeptimer_label">Set sleep timer</string>