Merge pull request #4106 from malockin/export-favorites
Export favourites
This commit is contained in:
commit
e0d1f5d529
|
@ -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()))
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<li><span>
|
||||||
|
{FAV_TITLE}<br>
|
||||||
|
<a href="{FAV_WEBSITE}">Website</a> • <a href="{FAV_MEDIA}">Media</a>
|
||||||
|
</span></li>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue