diff options
Diffstat (limited to 'core/src/main/java/de/danoeh')
11 files changed, 140 insertions, 261 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java deleted file mode 100644 index 062a6abac..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.danoeh.antennapod.core.feed; - -import de.danoeh.antennapod.core.storage.SearchLocation; - -public class SearchResult { - private final FeedComponent component; - private SearchLocation location; - - public SearchResult(FeedComponent component, SearchLocation location) { - super(); - this.component = component; - this.location = location; - } - - public FeedComponent getComponent() { - return component; - } - - public SearchLocation getLocation() { - return location; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index 9db5eb212..383697fa2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -389,7 +389,7 @@ public class UserPreferences { return Float.parseFloat(prefs.getString(PREF_PLAYBACK_SPEED, "1.00")); } catch (NumberFormatException e) { Log.e(TAG, Log.getStackTraceString(e)); - UserPreferences.setPlaybackSpeed("1.00"); + UserPreferences.setPlaybackSpeed(1.0f); return 1.0f; } } @@ -399,7 +399,7 @@ public class UserPreferences { return Float.parseFloat(prefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")); } catch (NumberFormatException e) { Log.e(TAG, Log.getStackTraceString(e)); - UserPreferences.setVideoPlaybackSpeed("1.00"); + UserPreferences.setVideoPlaybackSpeed(1.0f); return 1.0f; } } @@ -408,7 +408,7 @@ public class UserPreferences { return prefs.getBoolean(PREF_PLAYBACK_SKIP_SILENCE, false); } - public static String[] getPlaybackSpeedArray() { + public static float[] getPlaybackSpeedArray() { return readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null)); } @@ -638,15 +638,15 @@ public class UserPreferences { .apply(); } - public static void setPlaybackSpeed(String speed) { + public static void setPlaybackSpeed(float speed) { prefs.edit() - .putString(PREF_PLAYBACK_SPEED, speed) + .putString(PREF_PLAYBACK_SPEED, String.valueOf(speed)) .apply(); } - public static void setVideoPlaybackSpeed(String speed) { + public static void setVideoPlaybackSpeed(float speed) { prefs.edit() - .putString(PREF_VIDEO_PLAYBACK_SPEED, speed) + .putString(PREF_VIDEO_PLAYBACK_SPEED, String.valueOf(speed)) .apply(); } @@ -769,24 +769,22 @@ public class UserPreferences { } } - private static String[] readPlaybackSpeedArray(String valueFromPrefs) { - String[] selectedSpeeds = null; - // If this preference hasn't been set yet, return the default options - if (valueFromPrefs == null) { - selectedSpeeds = new String[] { "0.75", "1.00", "1.25", "1.50", "1.75", "2.00" }; - } else { + private static float[] readPlaybackSpeedArray(String valueFromPrefs) { + if (valueFromPrefs != null) { try { JSONArray jsonArray = new JSONArray(valueFromPrefs); - selectedSpeeds = new String[jsonArray.length()]; + float[] selectedSpeeds = new float[jsonArray.length()]; for (int i = 0; i < jsonArray.length(); i++) { - selectedSpeeds[i] = jsonArray.getString(i); + selectedSpeeds[i] = (float) jsonArray.getDouble(i); } + return selectedSpeeds; } catch (JSONException e) { Log.e(TAG, "Got JSON error when trying to get speeds from JSONArray"); e.printStackTrace(); } } - return selectedSpeeds; + // If this preference hasn't been set yet, return the default options + return new float[] { 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f }; } public static String getMediaPlayer() { @@ -816,11 +814,11 @@ public class UserPreferences { } public static VideoBackgroundBehavior getVideoBackgroundBehavior() { - switch (prefs.getString(PREF_VIDEO_BEHAVIOR, "stop")) { + switch (prefs.getString(PREF_VIDEO_BEHAVIOR, "pip")) { case "stop": return VideoBackgroundBehavior.STOP; case "pip": return VideoBackgroundBehavior.PICTURE_IN_PICTURE; case "continue": return VideoBackgroundBehavior.CONTINUE_PLAYING; - default: return VideoBackgroundBehavior.STOP; + default: return VideoBackgroundBehavior.PICTURE_IN_PICTURE; } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index 9c32e42e0..6f7401698 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java @@ -646,6 +646,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { float retVal = 1; if ((playerStatus == PlayerStatus.PLAYING || playerStatus == PlayerStatus.PAUSED + || playerStatus == PlayerStatus.INITIALIZED || playerStatus == PlayerStatus.PREPARED) && mediaPlayer.canSetSpeed()) { retVal = mediaPlayer.getCurrentSpeedMultiplier(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index 37deb6dc0..fbd8d65d8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -54,10 +54,10 @@ import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent; import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; -import de.danoeh.antennapod.core.feed.SearchResult; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; @@ -574,6 +574,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { } else if (mediaPlayer.getPlayable() == null) { startPlayingFromPreferences(); } + taskManager.restartSleepTimer(); return true; case KeyEvent.KEYCODE_MEDIA_PLAY: if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) { @@ -584,6 +585,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { } else if (mediaPlayer.getPlayable() == null) { startPlayingFromPreferences(); } + taskManager.restartSleepTimer(); return true; case KeyEvent.KEYCODE_MEDIA_PAUSE: if (status == PlayerStatus.PLAYING) { @@ -833,9 +835,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onPlaybackStart(@NonNull Playable playable, int position) { - if (taskManager.isSleepTimerActive()) { - taskManager.restartSleepTimer(); - } taskManager.startWidgetUpdater(); if (position != PlaybackServiceMediaPlayer.INVALID_TIME) { playable.setPosition(position); @@ -1468,10 +1467,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void resume() { mediaPlayer.resume(); + taskManager.restartSleepTimer(); } public void prepare() { mediaPlayer.prepare(); + taskManager.restartSleepTimer(); } public void pause(boolean abandonAudioFocus, boolean reinit) { @@ -1634,16 +1635,17 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onPlayFromSearch(String query, Bundle extras) { Log.d(TAG, "onPlayFromSearch query=" + query + " extras=" + extras.toString()); - List<SearchResult> results = FeedSearcher.performSearch(getBaseContext(), query, 0); - for (SearchResult result : results) { - try { - FeedMedia p = ((FeedItem) (result.getComponent())).getMedia(); - mediaPlayer.playMediaObject(p, !p.localFileAvailable(), true, true); - return; - } catch (Exception e) { - Log.d(TAG, e.getMessage()); - e.printStackTrace(); - continue; + List<FeedComponent> results = FeedSearcher.performSearch(getBaseContext(), query, 0); + for (FeedComponent result : results) { + if (result instanceof FeedItem) { + try { + FeedMedia media = ((FeedItem) result).getMedia(); + mediaPlayer.playMediaObject(media, !media.localFileAvailable(), true, true); + return; + } catch (Exception e) { + Log.d(TAG, e.getMessage()); + e.printStackTrace(); + } } } onPlay(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index 91f656bf1..8ebe18dc0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -553,6 +553,23 @@ public final class DBTasks { }); } + public static FutureTask<List<Feed>> searchFeeds(final Context context, final String query) { + return new FutureTask<>(new QueryTask<List<Feed>>(context) { + @Override + public void execute(PodDBAdapter adapter) { + Cursor cursor = adapter.searchFeeds(query); + List<Feed> items = new ArrayList<>(); + if (cursor.moveToFirst()) { + do { + items.add(Feed.fromCursor(cursor)); + } while (cursor.moveToNext()); + } + setResult(items); + cursor.close(); + } + }); + } + /** * A runnable which should be used for database queries. The onCompletion * method is executed on the database executor to handle Cursors correctly. diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java index af3d1206c..234c01b20 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java @@ -1,6 +1,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -14,21 +16,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; -import java.util.Arrays; public class DatabaseExporter { private static final String TAG = "DatabaseExporter"; - private static final byte[] SQLITE3_MAGIC = "SQLite format 3\0".getBytes(); - - public static boolean validateDB(Uri inputUri, Context context) throws IOException { - try (InputStream inputStream = context.getContentResolver().openInputStream(inputUri)) { - byte[] magicBuf = new byte[SQLITE3_MAGIC.length]; - if (inputStream.read(magicBuf) == magicBuf.length) { - return Arrays.equals(SQLITE3_MAGIC, magicBuf); - } - } - return false; - } + private static final String TEMP_DB_NAME = PodDBAdapter.DATABASE_NAME + "_tmp"; public static void exportToDocument(Uri uri, Context context) throws IOException { ParcelFileDescriptor pfd = null; @@ -78,14 +69,21 @@ public class DatabaseExporter { public static void importBackup(Uri inputUri, Context context) throws IOException { InputStream inputStream = null; try { - if (!validateDB(inputUri, context)) { - throw new IOException(context.getString(R.string.import_bad_file)); + File tempDB = context.getDatabasePath(TEMP_DB_NAME); + inputStream = context.getContentResolver().openInputStream(inputUri); + FileUtils.copyInputStreamToFile(inputStream, tempDB); + + SQLiteDatabase db = SQLiteDatabase.openDatabase(tempDB.getAbsolutePath(), + null, SQLiteDatabase.OPEN_READONLY); + if (db.getVersion() > PodDBAdapter.VERSION) { + throw new IOException(context.getString(R.string.import_no_downgrade)); } + db.close(); File currentDB = context.getDatabasePath(PodDBAdapter.DATABASE_NAME); - inputStream = context.getContentResolver().openInputStream(inputUri); - FileUtils.copyInputStreamToFile(inputStream, currentDB); - } catch (IOException e) { + currentDB.delete(); + FileUtils.moveFile(tempDB, currentDB); + } catch (IOException | SQLiteException e) { Log.e(TAG, Log.getStackTraceString(e)); throw e; } finally { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index 1d9e33d0e..bbe8b26f1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -2,21 +2,15 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import androidx.annotation.NonNull; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedComponent; +import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.feed.Chapter; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.feed.SearchResult; -import de.danoeh.antennapod.core.util.comparator.InReverseChronologicalOrder; - /** * Performs search on Feeds and FeedItems. */ @@ -37,48 +31,19 @@ public class FeedSearcher { * @return list of episodes containing the query */ @NonNull - public static List<SearchResult> performSearch(final Context context, final String query, final long selectedFeed) { - final List<SearchResult> result = new ArrayList<>(); + public static List<FeedComponent> performSearch(final Context context, final String query, final long selectedFeed) { + final List<FeedComponent> result = new ArrayList<>(); try { - FutureTask<List<FeedItem>> searchTask = DBTasks.searchFeedItems(context, selectedFeed, query); - searchTask.run(); - final List<FeedItem> items = searchTask.get(); - for (FeedItem item : items) { - SearchLocation location; - if (safeContains(item.getTitle(), query)) { - location = SearchLocation.TITLE; - } else if (safeContains(item.getContentEncoded(), query)) { - location = SearchLocation.SHOWNOTES; - } else if (safeContains(item.getDescription(), query)) { - location = SearchLocation.SHOWNOTES; - } else if (safeContains(item.getChapters(), query)) { - location = SearchLocation.CHAPTERS; - } else if (safeContains(item.getFeed().getAuthor(), query)) { - location = SearchLocation.AUTHORS; - } else { - location = SearchLocation.FEED; - } - result.add(new SearchResult(item, location)); - } + FutureTask<List<FeedItem>> itemSearchTask = DBTasks.searchFeedItems(context, selectedFeed, query); + FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(context, query); + itemSearchTask.run(); + feedSearchTask.run(); + + result.addAll(feedSearchTask.get()); + result.addAll(itemSearchTask.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return result; } - - private static boolean safeContains(String haystack, String needle) { - return haystack != null && haystack.contains(needle); - } - - private static boolean safeContains(List<Chapter> haystack, String needle) { - if (haystack == null) { - return false; - } - for (Chapter chapter : haystack) { - if (safeContains(chapter.getTitle(), needle)) { - return true; - } - } - return false; - } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 17b79a3da..4e2588f22 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -48,6 +48,7 @@ public class PodDBAdapter { private static final String TAG = "PodDBAdapter"; public static final String DATABASE_NAME = "Antennapod.db"; + public static final int VERSION = 1090000; /** * Maximum number of arguments for IN-operator. @@ -284,10 +285,13 @@ public class PodDBAdapter { * Contains FEEDITEM_SEL_FI_SMALL as comma-separated list. Useful for raw queries. */ private static final String SEL_FI_SMALL_STR; + private static final String FEED_SEL_STD_STR; static { String selFiSmall = Arrays.toString(FEEDITEM_SEL_FI_SMALL); SEL_FI_SMALL_STR = selFiSmall.substring(1, selFiSmall.length() - 1); + String selFeedSmall = Arrays.toString(FEED_SEL_STD); + FEED_SEL_STD_STR = selFeedSmall.substring(1, selFeedSmall.length() - 1); } /** @@ -1274,21 +1278,29 @@ public class PodDBAdapter { } String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS - + " LEFT JOIN " + TABLE_NAME_SIMPLECHAPTERS - + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM - + "=" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID - + " LEFT JOIN " + TABLE_NAME_FEEDS - + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED - + "=" + TABLE_NAME_FEEDS + "." + KEY_ID + " WHERE " + queryFeedId + " AND (" - + TABLE_NAME_FEED_ITEMS + "." + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' OR " - + TABLE_NAME_FEED_ITEMS + "." + KEY_CONTENT_ENCODED + " LIKE '%" + preparedQuery + "%' OR " - + TABLE_NAME_FEED_ITEMS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " - + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " - + TABLE_NAME_FEEDS + "." + KEY_AUTHOR + " LIKE '%" + preparedQuery + "%' OR " - + TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER + " LIKE '%" + preparedQuery + "%'" + + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' OR " + + KEY_CONTENT_ENCODED + " LIKE '%" + preparedQuery + "%' OR " + + KEY_TITLE + " LIKE '%" + preparedQuery + "%'" + ") ORDER BY " + KEY_PUBDATE + " DESC " - + "LIMIT 500"; + + "LIMIT 300"; + return db.rawQuery(query, null); + } + + /** + * Searches for the given query in various values of all feeds. + * + * @return A cursor with all search results in SEL_FI_EXTRA selection. + */ + public Cursor searchFeeds(String searchQuery) { + String preparedQuery = prepareSearchQuery(searchQuery); + String query = "SELECT " + FEED_SEL_STD_STR + " FROM " + TABLE_NAME_FEEDS + " WHERE " + + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + KEY_CUSTOM_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + KEY_AUTHOR + " LIKE '%" + preparedQuery + "%' OR " + + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' " + + "ORDER BY " + KEY_TITLE + " ASC " + + "LIMIT 300"; return db.rawQuery(query, null); } @@ -1336,8 +1348,6 @@ public class PodDBAdapter { * Helper class for opening the Antennapod database. */ private static class PodDBHelper extends SQLiteOpenHelper { - private static final int VERSION = 1090000; - /** * Constructor. * @@ -1345,8 +1355,7 @@ public class PodDBAdapter { * @param name Name of the database * @param factory to use for creating cursor objects */ - public PodDBHelper(final Context context, final String name, - final CursorFactory factory) { + public PodDBHelper(final Context context, final String name, final CursorFactory factory) { super(context, name, factory, VERSION, new PodDbErrorHandler()); } @@ -1369,10 +1378,8 @@ public class PodDBAdapter { } @Override - public void onUpgrade(final SQLiteDatabase db, final int oldVersion, - final int newVersion) { - Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to " - + newVersion + "."); + public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { + Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to " + newVersion + "."); DBUpgrader.upgrade(db, oldVersion, newVersion); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java b/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java deleted file mode 100644 index fabe85b2c..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import androidx.annotation.StringRes; -import de.danoeh.antennapod.core.R; - -public enum SearchLocation { - TITLE(R.string.found_in_title_label), - CHAPTERS(R.string.found_in_chapters_label), - SHOWNOTES(R.string.found_in_shownotes_label), - AUTHORS(R.string.found_in_authors_label), - FEED(R.string.found_in_feeds_label); - - private int description; - SearchLocation(@StringRes int description) { - this.description = description; - } - - public @StringRes int getDescription() { - return description; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/InReverseChronologicalOrder.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/InReverseChronologicalOrder.java deleted file mode 100644 index 80246af8f..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/InReverseChronologicalOrder.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.danoeh.antennapod.core.util.comparator; - -import java.util.Comparator; - -import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.feed.SearchResult; - -public class InReverseChronologicalOrder implements Comparator<SearchResult> { - /** - * Compare items and sort it on chronological order. - */ - @Override - public int compare(SearchResult o1, SearchResult o2) { - if ((o1.getComponent() instanceof FeedItem) && (o2.getComponent() instanceof FeedItem)) { - FeedItem item1 = (FeedItem) o1.getComponent(); - FeedItem item2 = (FeedItem) o2.getComponent(); - return item2.getPubDate().compareTo(item1.getPubDate()); - } - return 0; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java index 8624ec7e5..0fd658853 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java @@ -9,11 +9,15 @@ import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; +import de.danoeh.antennapod.core.feed.FeedItem; +import org.apache.commons.io.IOUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import java.io.IOException; +import java.io.InputStream; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,48 +31,20 @@ import de.danoeh.antennapod.core.util.ShownotesProvider; * shownotes to navigate to another position in the podcast or by highlighting certain parts of the shownotesProvider's * shownotes. * <p/> - * A timeline object needs a shownotesProvider from which the chapter information is retrieved and shownotes are generated. + * A timeline object needs a shownotesProvider from which the chapter information + * is retrieved and shownotes are generated. */ public class Timeline { private static final String TAG = "Timeline"; - private static final String WEBVIEW_STYLE = - "@font-face {" - + "font-family: 'Roboto-Light';" - + "src: url('file:///android_asset/Roboto-Light.ttf');" - + "}" - + "* {" - + "color: %s;" - + "font-family: roboto-Light;" - + "font-size: 13pt;" - + "overflow-wrap: break-word;" - + "}" - + "a {" - + "font-style: normal;" - + "text-decoration: none;" - + "font-weight: normal;" - + "color: #00A8DF;" - + "}" - + "a.timecode {" - + "color: #669900;" - + "}" - + "img, iframe {" - + "display: block;" - + "margin: 10 auto;" - + "max-width: %s;" - + "height: auto;" - + "}" - + "body {" - + "margin: %dpx %dpx %dpx %dpx;" - + "}"; - - - private ShownotesProvider shownotesProvider; + private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/((\\d+))"); + private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>"; + private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b"); + private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>"); + private final ShownotesProvider shownotesProvider; private final String noShownotesLabel; - private final String colorPrimaryString; - private final String colorSecondaryString; - private final int pageMargin; + private final String webviewStyle; public Timeline(Context context, ShownotesProvider shownotesProvider) { if (shownotesProvider == null) { @@ -80,26 +56,21 @@ public class Timeline { TypedArray res = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorPrimary}); @ColorInt int col = res.getColor(0, 0); - colorPrimaryString = "rgba(" + Color.red(col) + "," + Color.green(col) + "," + - Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")"; - res.recycle(); - res = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorSecondary}); - col = res.getColor(0, 0); - colorSecondaryString = "rgba(" + Color.red(col) + "," + Color.green(col) + "," + - Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")"; + final String colorPrimary = "rgba(" + Color.red(col) + "," + Color.green(col) + "," + + Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")"; res.recycle(); - - pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, - context.getResources().getDisplayMetrics() - ); + final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, + context.getResources().getDisplayMetrics()); + String styleString = ""; + try { + InputStream templateStream = context.getAssets().open("shownotes-style.css"); + styleString = IOUtils.toString(templateStream, "UTF-8"); + } catch (IOException e) { + e.printStackTrace(); + } + webviewStyle = String.format(Locale.getDefault(), styleString, colorPrimary, margin, margin, margin, margin); } - private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/((\\d+))"); - private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>"; - private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b"); - private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>"); - - /** * Applies an app-specific CSS stylesheet and adds timecode links (optional). * <p/> @@ -110,10 +81,6 @@ public class Timeline { */ @NonNull public String processShownotes() { - final Playable playable = (shownotesProvider instanceof Playable) ? (Playable) shownotesProvider : null; - - // load shownotes - String shownotes; try { shownotes = shownotesProvider.loadShownotes().call(); @@ -124,21 +91,7 @@ public class Timeline { if (TextUtils.isEmpty(shownotes)) { Log.d(TAG, "shownotesProvider contained no shownotes. Returning 'no shownotes' message"); - shownotes = "<html>" + - "<head>" + - "<style type='text/css'>" + - "html, body { margin: 0; padding: 0; width: 100%; height: 100%; } " + - "html { display: table; }" + - "body { display: table-cell; vertical-align: middle; text-align:center;" + - "-webkit-text-size-adjust: none; font-size: 87%; color: " + colorSecondaryString + ";} " + - "</style>" + - "</head>" + - "<body>" + - "<p>" + noShownotesLabel + "</p>" + - "</body>" + - "</html>"; - Log.d(TAG, "shownotes: " + shownotes); - return shownotes; + shownotes = "<html><head></head><body><p id='apNoShownotes'>" + noShownotesLabel + "</p></body></html>"; } // replace ASCII line breaks with HTML ones if shownotes don't contain HTML line breaks already @@ -147,14 +100,10 @@ public class Timeline { } Document document = Jsoup.parse(shownotes); - - // apply style - String styleStr = String.format(Locale.getDefault(), WEBVIEW_STYLE, colorPrimaryString, "100%", - pageMargin, pageMargin, pageMargin, pageMargin); - document.head().appendElement("style").attr("type", "text/css").text(styleStr); + document.head().appendElement("style").attr("type", "text/css").text(webviewStyle); // apply timecode links - addTimecodes(document, playable); + addTimecodes(document); return document.toString(); } @@ -184,7 +133,7 @@ public class Timeline { return -1; } - private void addTimecodes(Document document, final Playable playable) { + private void addTimecodes(Document document) { Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX); Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes"); @@ -193,7 +142,13 @@ public class Timeline { return; } - int playableDuration = playable == null ? Integer.MAX_VALUE : playable.getDuration(); + int playableDuration = Integer.MAX_VALUE; + if (shownotesProvider instanceof Playable) { + playableDuration = ((Playable) shownotesProvider).getDuration(); + } else if (shownotesProvider instanceof FeedItem && ((FeedItem) shownotesProvider).getMedia() != null) { + playableDuration = ((FeedItem) shownotesProvider).getMedia().getDuration(); + } + boolean useHourFormat = true; if (playableDuration != Integer.MAX_VALUE) { |