summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/assets/html-export-template.html85
-rw-r--r--core/src/main/assets/shownotes-style.css37
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java30
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java72
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java101
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java159
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java62
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java181
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java111
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java42
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java83
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java61
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java87
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java17
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java93
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java58
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java65
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java51
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java73
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LongList.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/TimeSpeedConverter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java47
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/InReverseChronologicalOrder.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java238
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java126
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java174
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java334
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_24dp.pngbin256 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_36dp.pngbin324 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_forward_white_24dp.pngbin253 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.pngbin315 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_24dp.pngbin267 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_36dp.pngbin331 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_rewind_white_24dp.pngbin261 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_fast_rewind_white_36dp.pngbin321 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_hearing_grey600_18dp.pngbin478 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_hearing_white_18dp.pngbin449 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_new_releases_grey600_24dp.pngbin399 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_new_releases_white_24dp.pngbin381 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_pause_grey600_24dp.pngbin103 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_pause_grey600_36dp.pngbin126 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_pause_white_24dp.pngbin103 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_pause_white_36dp.pngbin124 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_24dp.pngbin195 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_36dp.pngbin235 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.pngbin194 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_play_arrow_white_36dp.pngbin232 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_remove_red_eye_grey600_18dp.pngbin380 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_remove_red_eye_white_18dp.pngbin358 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_skip_grey600_36dp.pngbin256 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_skip_white_36dp.pngbin251 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_24dp.pngbin162 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_36dp.pngbin256 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_forward_white_24dp.pngbin163 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.pngbin253 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_24dp.pngbin167 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_36dp.pngbin267 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_rewind_white_24dp.pngbin162 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_fast_rewind_white_36dp.pngbin261 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_hearing_grey600_18dp.pngbin324 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_hearing_white_18dp.pngbin301 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_new_releases_grey600_24dp.pngbin288 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_new_releases_white_24dp.pngbin277 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_pause_grey600_24dp.pngbin84 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_pause_grey600_36dp.pngbin103 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_pause_white_24dp.pngbin83 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_pause_white_36dp.pngbin103 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_24dp.pngbin151 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_36dp.pngbin195 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.pngbin154 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_play_arrow_white_36dp.pngbin194 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_remove_red_eye_grey600_18dp.pngbin265 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_remove_red_eye_white_18dp.pngbin259 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_skip_grey600_36dp.pngbin183 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_skip_white_36dp.pngbin183 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_24dp.pngbin252 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_36dp.pngbin332 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_forward_white_24dp.pngbin253 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.pngbin327 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_24dp.pngbin279 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_36dp.pngbin348 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_24dp.pngbin263 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_36dp.pngbin331 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_hearing_grey600_18dp.pngbin621 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_hearing_white_18dp.pngbin588 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_new_releases_grey600_24dp.pngbin496 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_new_releases_white_24dp.pngbin464 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_pause_grey600_24dp.pngbin105 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_pause_grey600_36dp.pngbin109 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_pause_white_24dp.pngbin90 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_pause_white_36dp.pngbin92 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_24dp.pngbin211 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_36dp.pngbin270 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.pngbin206 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.pngbin270 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_remove_red_eye_grey600_18dp.pngbin492 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_remove_red_eye_white_18dp.pngbin472 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_skip_grey600_36dp.pngbin285 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_skip_white_36dp.pngbin285 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_24dp.pngbin332 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_36dp.pngbin520 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_24dp.pngbin327 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.pngbin518 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_24dp.pngbin348 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_36dp.pngbin547 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_24dp.pngbin331 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.pngbin548 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_hearing_grey600_18dp.pngbin885 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_hearing_white_18dp.pngbin840 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_new_releases_grey600_24dp.pngbin718 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_new_releases_white_24dp.pngbin679 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_pause_grey600_24dp.pngbin109 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_pause_grey600_36dp.pngbin143 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.pngbin92 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.pngbin143 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_24dp.pngbin270 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_36dp.pngbin367 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.pngbin270 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_36dp.pngbin351 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_grey600_18dp.pngbin697 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_white_18dp.pngbin669 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_skip_grey600_36dp.pngbin369 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_skip_white_36dp.pngbin361 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_skip_grey600_36dp.pngbin443 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_skip_white_36dp.pngbin433 -> 0 bytes
-rw-r--r--core/src/main/res/drawable/ic_av_fast_forward_dark_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_fast_rewind_dark_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_pause_dark_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_pause_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_play_dark_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_play_dark_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_play_white_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_play_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_skip_dark_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_skip_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_notification_cast_off.xml4
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_forward.xml4
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_rewind.xml4
-rw-r--r--core/src/main/res/drawable/ic_notification_pause.xml4
-rw-r--r--core/src/main/res/drawable/ic_notification_play.xml4
-rw-r--r--core/src/main/res/drawable/ic_notification_skip.xml4
-rw-r--r--core/src/main/res/drawable/ic_videocam_grey600_24dp.xml8
-rw-r--r--core/src/main/res/drawable/ic_videocam_white_24dp.xml8
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption_grey.xml9
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption_white.xml9
-rw-r--r--core/src/main/res/drawable/overlay_drawable.xml20
-rw-r--r--core/src/main/res/drawable/overlay_drawable_dark.xml15
-rw-r--r--core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml15
-rw-r--r--core/src/main/res/layout/player_widget.xml6
-rw-r--r--core/src/main/res/values-ca/strings.xml6
-rw-r--r--core/src/main/res/values-da/strings.xml89
-rw-r--r--core/src/main/res/values-de/strings.xml30
-rw-r--r--core/src/main/res/values-fr/strings.xml12
-rw-r--r--core/src/main/res/values-gl-rES/strings.xml79
-rw-r--r--core/src/main/res/values-h768dp/dimens.xml4
-rw-r--r--core/src/main/res/values-hu/strings.xml1
-rw-r--r--core/src/main/res/values-it/strings.xml92
-rw-r--r--core/src/main/res/values-large/dimens.xml2
-rw-r--r--core/src/main/res/values-nl/strings.xml10
-rw-r--r--core/src/main/res/values-pl-rPL/strings.xml2
-rw-r--r--core/src/main/res/values-uk-rUA/strings.xml53
-rw-r--r--core/src/main/res/values-v26/styles.xml6
-rw-r--r--core/src/main/res/values-zh-rCN/strings.xml2
-rw-r--r--core/src/main/res/values/arrays.xml12
-rw-r--r--core/src/main/res/values/attrs.xml26
-rw-r--r--core/src/main/res/values/colors.xml4
-rw-r--r--core/src/main/res/values/dimens.xml9
-rw-r--r--core/src/main/res/values/strings.xml70
-rw-r--r--core/src/main/res/values/styles.xml70
202 files changed, 2106 insertions, 1574 deletions
diff --git a/core/src/main/assets/html-export-template.html b/core/src/main/assets/html-export-template.html
new file mode 100644
index 000000000..ddab27a43
--- /dev/null
+++ b/core/src/main/assets/html-export-template.html
@@ -0,0 +1,85 @@
+<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
+<html>
+ <head>
+ <title>AntennaPod Subscriptions</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style>
+ * {
+ font-family: 'Lato', sans-serif;
+ font-weight: 300;
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+ html {
+ background: #3498db;
+ text-align: center;
+ padding: 10px;
+ }
+ h1 {
+ color: #fff;
+ font-weight: 300;
+ display: inline-block;
+ margin-top: 40px;
+ margin-bottom: 20px;
+ vertical-align: top;
+ }
+ ul {
+ text-align: center;
+ }
+ li {
+ width: 100%;
+ max-width: 500px;
+ display: block;
+ display: inline-flex;
+ padding: 10px;
+ }
+ li > div {
+ background: #fefefe;
+ padding: 10px;
+ display: inline-block;
+ width: 100%;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
+ text-align: left;
+ }
+ li span {
+ margin-top: 10px;
+ display: block;
+ }
+ a {
+ text-decoration: none;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+ span a {
+ color: #3498db;
+ }
+ img {
+ width: 100px;
+ height: 100px;
+ margin-right: 10px;
+ }
+ li > div > img {
+ float: left;
+ }
+ li > div > p {
+ width: 100%;
+ }
+ body > a {
+ color: #ffffff;
+ display: inline-block;
+ margin-top: 10px;
+ clear:left;
+ }
+ </style>
+ </head>
+ <body>
+ <img src="https://antennapod.org/assets/img/antennapod-logo.png" />
+ <h1>AntennaPod Subscriptions</h1>
+ <ul>
+ {FEEDS}
+ </ul>
+ <a href="https://play.google.com/store/apps/details?id=de.danoeh.antennapod" target="_blank">Get AntennaPod</a>
+ </body>
+</html>
diff --git a/core/src/main/assets/shownotes-style.css b/core/src/main/assets/shownotes-style.css
new file mode 100644
index 000000000..d0c8564aa
--- /dev/null
+++ b/core/src/main/assets/shownotes-style.css
@@ -0,0 +1,37 @@
+@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: 100%%;
+ height: auto;
+}
+body {
+ margin: %dpx %dpx %dpx %dpx;
+}
+p#apNoShownotes {
+ position: fixed;
+ top: 50%%;
+ left: 50%%;
+ transform: translate(-50%%, -50%%);
+ text-align: center;
+ -webkit-text-size-adjust: none;
+ font-size: 80%%;
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index 80ce6cf56..4c11d0489 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -27,7 +27,6 @@ import java.security.NoSuchAlgorithmException;
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;
@@ -45,18 +44,6 @@ public class OpmlBackupAgent extends BackupAgentHelper {
addHelper(OPML_BACKUP_KEY, new OpmlBackupHelper(this));
}
- private static void LOGD(String tag, String msg) {
- if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.DEBUG)) {
- Log.d(tag, msg);
- }
- }
-
- private static void LOGD(String tag, String msg, Throwable tr) {
- if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.DEBUG)) {
- Log.d(tag, msg, tr);
- }
- }
-
/**
* Class for backing up and restoring the OPML file.
*/
@@ -93,12 +80,12 @@ public class OpmlBackupAgent extends BackupAgentHelper {
try {
// Write OPML
- new OpmlWriter().writeDocument(DBReader.getFeedList(), writer);
+ new OpmlWriter().writeDocument(DBReader.getFeedList(), writer, mContext);
// Compare checksum of new and old file to see if we need to perform a backup at all
if (digester != null) {
byte[] newChecksum = digester.digest();
- LOGD(TAG, "New checksum: " + new BigInteger(1, newChecksum).toString(16));
+ Log.d(TAG, "New checksum: " + new BigInteger(1, newChecksum).toString(16));
// Get the old checksum
if (oldState != null) {
@@ -108,10 +95,10 @@ public class OpmlBackupAgent extends BackupAgentHelper {
if (len != -1) {
byte[] oldChecksum = new byte[len];
inState.read(oldChecksum);
- LOGD(TAG, "Old checksum: " + new BigInteger(1, oldChecksum).toString(16));
+ Log.d(TAG, "Old checksum: " + new BigInteger(1, oldChecksum).toString(16));
if (Arrays.equals(oldChecksum, newChecksum)) {
- LOGD(TAG, "Checksums are the same; won't backup");
+ Log.d(TAG, "Checksums are the same; won't backup");
return;
}
}
@@ -120,7 +107,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
writeNewStateDescription(newState, newChecksum);
}
- LOGD(TAG, "Backing up OPML");
+ Log.d(TAG, "Backing up OPML");
byte[] bytes = byteStream.toByteArray();
data.writeEntityHeader(OPML_ENTITY_KEY, bytes.length);
data.writeEntityData(bytes, bytes.length);
@@ -138,10 +125,10 @@ public class OpmlBackupAgent extends BackupAgentHelper {
@Override
public void restoreEntity(BackupDataInputStream data) {
- LOGD(TAG, "Backup restore");
+ Log.d(TAG, "Backup restore");
if (!OPML_ENTITY_KEY.equals(data.getKey())) {
- LOGD(TAG, "Unknown entity key: " + data.getKey());
+ Log.d(TAG, "Unknown entity key: " + data.getKey());
return;
}
@@ -167,7 +154,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
try {
downloader.downloadFeed(mContext, feed);
} catch (DownloadRequestException e) {
- LOGD(TAG, "Error while restoring/downloading feed", e);
+ Log.d(TAG, "Error while restoring/downloading feed", e);
}
}
} catch (XmlPullParserException e) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java
new file mode 100644
index 000000000..0ac7e1316
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java
@@ -0,0 +1,19 @@
+package de.danoeh.antennapod.core.event.settings;
+
+public class SpeedPresetChangedEvent {
+ private final float speed;
+ private final long feedId;
+
+ public SpeedPresetChangedEvent(float speed, long feedId) {
+ this.speed = speed;
+ this.feedId = feedId;
+ }
+
+ public float getSpeed() {
+ return speed;
+ }
+
+ public long getFeedId() {
+ return feedId;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
new file mode 100644
index 000000000..3ed84f6a8
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
@@ -0,0 +1,21 @@
+package de.danoeh.antennapod.core.event.settings;
+
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+
+public class VolumeAdaptionChangedEvent {
+ private final VolumeAdaptionSetting volumeAdaptionSetting;
+ private final long feedId;
+
+ public VolumeAdaptionChangedEvent(VolumeAdaptionSetting volumeAdaptionSetting, long feedId) {
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
+ this.feedId = feedId;
+ }
+
+ public VolumeAdaptionSetting getVolumeAdaptionSetting() {
+ return volumeAdaptionSetting;
+ }
+
+ public long getFeedId() {
+ return feedId;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
index d6a187b21..e0f0d4626 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
@@ -11,8 +11,10 @@
* 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;
@@ -21,9 +23,9 @@ import de.danoeh.antennapod.core.feed.Feed;
public interface ExportWriter {
- void writeDocument(List<Feed> feeds, Writer writer)
- throws IllegalArgumentException, IllegalStateException, IOException;
+ void writeDocument(List<Feed> feeds, Writer writer, Context context)
+ throws IllegalArgumentException, IllegalStateException, IOException;
- String fileExtension();
+ String fileExtension();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java
deleted file mode 100644
index 1ca126469..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java
+++ /dev/null
@@ -1,30 +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.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 final String HEADING = "h1";
-
- static final String LINK = "a";
- static final String LINK_DESTINATION = "href";
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
index c24b39812..93b66daed 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
@@ -1,77 +1,45 @@
package de.danoeh.antennapod.core.export.html;
-import android.text.TextUtils;
+import android.content.Context;
import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlSerializer;
-
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.feed.Feed;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Writer;
import java.util.List;
-
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.feed.Feed;
+import org.apache.commons.io.IOUtils;
/** 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)
+ public void writeDocument(List<Feed> feeds, Writer writer, Context context)
throws IllegalArgumentException, IllegalStateException, IOException {
Log.d(TAG, "Starting to write document");
- XmlSerializer xs = Xml.newSerializer();
- xs.setFeature(HtmlSymbols.XML_FEATURE_INDENT_OUTPUT, true);
- xs.setOutput(writer);
- xs.startDocument(ENCODING, false);
- xs.startTag(null, HtmlSymbols.HTML);
- xs.startTag(null, HtmlSymbols.HEAD);
- xs.startTag(null, HtmlSymbols.TITLE);
- xs.text(HTML_TITLE);
- xs.endTag(null, HtmlSymbols.TITLE);
- xs.endTag(null, HtmlSymbols.HEAD);
+ InputStream templateStream = context.getAssets().open("html-export-template.html");
+ String template = IOUtils.toString(templateStream, "UTF-8");
+ String[] templateParts = template.split("\\{FEEDS\\}");
- xs.startTag(null, HtmlSymbols.BODY);
- xs.startTag(null, HtmlSymbols.HEADING);
- xs.text(HTML_TITLE);
- xs.endTag(null, HtmlSymbols.HEADING);
- xs.startTag(null, HtmlSymbols.ORDERED_LIST);
+ writer.append(templateParts[0]);
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);
+ writer.append("<li><div><img src=\"");
+ writer.append(feed.getImageUrl());
+ writer.append("\" /><p>");
+ writer.append(feed.getTitle());
+ writer.append(" <span><a href=\"");
+ writer.append(feed.getLink());
+ writer.append("\">Website</a> • <a href=\"");
+ writer.append(feed.getDownload_url());
+ writer.append("\">Feed</a></span></p></div></li>\n");
}
- xs.endTag(null, HtmlSymbols.ORDERED_LIST);
- xs.endTag(null, HtmlSymbols.BODY);
- xs.endTag(null, HtmlSymbols.HTML);
- xs.endDocument();
+ writer.append(templateParts[1]);
Log.d(TAG, "Finished writing document");
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
index fd0922f72..c93d4e8e0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.export.opml;
+import android.content.Context;
import android.util.Log;
import android.util.Xml;
@@ -17,62 +18,58 @@ import de.danoeh.antennapod.core.util.DateUtils;
/** Writes OPML documents. */
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";
- private static final String OPML_TITLE = "AntennaPod Subscriptions";
+ private static final String TAG = "OpmlWriter";
+ private static final String ENCODING = "UTF-8";
+ private static final String OPML_VERSION = "2.0";
+ private static final String OPML_TITLE = "AntennaPod Subscriptions";
- /**
- * Takes a list of feeds and a writer and writes those into an OPML
- * 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.setFeature(OpmlSymbols.XML_FEATURE_INDENT_OUTPUT, true);
- xs.setOutput(writer);
+ /**
+ * Takes a list of feeds and a writer and writes those into an OPML
+ * document.
+ */
+ @Override
+ public void writeDocument(List<Feed> feeds, Writer writer, Context context)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ Log.d(TAG, "Starting to write document");
+ XmlSerializer xs = Xml.newSerializer();
+ xs.setFeature(OpmlSymbols.XML_FEATURE_INDENT_OUTPUT, true);
+ xs.setOutput(writer);
- xs.startDocument(ENCODING, false);
- xs.startTag(null, OpmlSymbols.OPML);
- xs.attribute(null, OpmlSymbols.VERSION, OPML_VERSION);
+ xs.startDocument(ENCODING, false);
+ xs.startTag(null, OpmlSymbols.OPML);
+ xs.attribute(null, OpmlSymbols.VERSION, OPML_VERSION);
- xs.startTag(null, OpmlSymbols.HEAD);
- xs.startTag(null, OpmlSymbols.TITLE);
- xs.text(OPML_TITLE);
- xs.endTag(null, OpmlSymbols.TITLE);
- xs.startTag(null, OpmlSymbols.DATE_CREATED);
- xs.text(DateUtils.formatRFC822Date(new Date()));
- xs.endTag(null, OpmlSymbols.DATE_CREATED);
- xs.endTag(null, OpmlSymbols.HEAD);
+ xs.startTag(null, OpmlSymbols.HEAD);
+ xs.startTag(null, OpmlSymbols.TITLE);
+ xs.text(OPML_TITLE);
+ xs.endTag(null, OpmlSymbols.TITLE);
+ xs.startTag(null, OpmlSymbols.DATE_CREATED);
+ xs.text(DateUtils.formatRFC822Date(new Date()));
+ xs.endTag(null, OpmlSymbols.DATE_CREATED);
+ xs.endTag(null, OpmlSymbols.HEAD);
- xs.startTag(null, OpmlSymbols.BODY);
- for (Feed feed : feeds) {
- xs.startTag(null, OpmlSymbols.OUTLINE);
- xs.attribute(null, OpmlSymbols.TEXT, feed.getTitle());
- xs.attribute(null, OpmlSymbols.TITLE, feed.getTitle());
- if (feed.getType() != null) {
- xs.attribute(null, OpmlSymbols.TYPE, feed.getType());
- }
- xs.attribute(null, OpmlSymbols.XMLURL, feed.getDownload_url());
- if (feed.getLink() != null) {
- xs.attribute(null, OpmlSymbols.HTMLURL, feed.getLink());
- }
- xs.endTag(null, OpmlSymbols.OUTLINE);
- }
- xs.endTag(null, OpmlSymbols.BODY);
- xs.endTag(null, OpmlSymbols.OPML);
- xs.endDocument();
- Log.d(TAG, "Finished writing document");
- }
+ xs.startTag(null, OpmlSymbols.BODY);
+ for (Feed feed : feeds) {
+ xs.startTag(null, OpmlSymbols.OUTLINE);
+ xs.attribute(null, OpmlSymbols.TEXT, feed.getTitle());
+ xs.attribute(null, OpmlSymbols.TITLE, feed.getTitle());
+ if (feed.getType() != null) {
+ xs.attribute(null, OpmlSymbols.TYPE, feed.getType());
+ }
+ xs.attribute(null, OpmlSymbols.XMLURL, feed.getDownload_url());
+ if (feed.getLink() != null) {
+ xs.attribute(null, OpmlSymbols.HTMLURL, feed.getLink());
+ }
+ xs.endTag(null, OpmlSymbols.OUTLINE);
+ }
+ xs.endTag(null, OpmlSymbols.BODY);
+ xs.endTag(null, OpmlSymbols.OPML);
+ xs.endDocument();
+ Log.d(TAG, "Finished writing document");
+ }
- public String fileExtension() {
- return "opml";
- }
+ public String fileExtension() {
+ return "opml";
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
index f3dfdfdb6..08a531d17 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
@@ -6,81 +6,92 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public abstract class Chapter extends FeedComponent {
- /** Defines starting point in milliseconds. */
+ /** Defines starting point in milliseconds. */
long start;
- String title;
- String link;
-
- Chapter() {
- }
-
- Chapter(long start) {
- super();
- this.start = start;
- }
-
- Chapter(long start, String title, FeedItem item, String link) {
- super();
- this.start = start;
- this.title = title;
- this.link = link;
- }
-
- public static Chapter fromCursor(Cursor cursor, FeedItem item) {
- int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
- int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
- int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
- int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
-
- long id = cursor.getLong(indexId);
- String title = cursor.getString(indexTitle);
- long start = cursor.getLong(indexStart);
- String link = cursor.getString(indexLink);
- int chapterType = cursor.getInt(indexChapterType);
-
- Chapter chapter = null;
- switch (chapterType) {
- case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
- chapter = new SimpleChapter(start, title, item, link);
- break;
- case ID3Chapter.CHAPTERTYPE_ID3CHAPTER:
- chapter = new ID3Chapter(start, title, item, link);
- break;
- case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
- chapter = new VorbisCommentChapter(start, title, item, link);
- break;
- }
- chapter.setId(id);
- return chapter;
- }
-
-
- public abstract int getChapterType();
-
- public long getStart() {
- return start;
- }
-
- public String getTitle() {
- return title;
- }
-
- public String getLink() {
- return link;
- }
-
- public void setStart(long start) {
- this.start = start;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
+ String title;
+ String link;
+ String imageUrl;
+
+ Chapter() {
+ }
+
+ Chapter(long start) {
+ super();
+ this.start = start;
+ }
+
+ Chapter(long start, String title, String link, String imageUrl) {
+ super();
+ this.start = start;
+ this.title = title;
+ this.link = link;
+ this.imageUrl = imageUrl;
+ }
+
+ public static Chapter fromCursor(Cursor cursor) {
+ int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
+ int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
+ int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
+ int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
+ int indexImage = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
+ int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
+
+ long id = cursor.getLong(indexId);
+ String title = cursor.getString(indexTitle);
+ long start = cursor.getLong(indexStart);
+ String link = cursor.getString(indexLink);
+ String imageUrl = cursor.getString(indexImage);
+ int chapterType = cursor.getInt(indexChapterType);
+
+ Chapter chapter = null;
+ switch (chapterType) {
+ case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
+ chapter = new SimpleChapter(start, title, link, imageUrl);
+ break;
+ case ID3Chapter.CHAPTERTYPE_ID3CHAPTER:
+ chapter = new ID3Chapter(start, title, link, imageUrl);
+ break;
+ case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
+ chapter = new VorbisCommentChapter(start, title, link, imageUrl);
+ break;
+ }
+ chapter.setId(id);
+ return chapter;
+ }
+
+ public abstract int getChapterType();
+
+ public long getStart() {
+ return start;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setLink(String link) {
+ this.link = link;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
@Override
public String getHumanReadableIdentifier() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index d078ec1d1..b1598f111 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -171,7 +171,7 @@ public class Feed extends FeedFile implements ImageResource {
*/
public Feed(String url, String lastUpdate, String title, String username, String password) {
this(url, lastUpdate, title);
- preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, username, password);
+ preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password);
}
public static Feed fromCursor(Cursor cursor) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
index 8fdf2034f..b24c52266 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
@@ -28,19 +28,23 @@ public class FeedPreferences {
NO
}
private AutoDeleteAction auto_delete_action;
+
+ private VolumeAdaptionSetting volumeAdaptionSetting;
+
private String username;
private String password;
private float feedPlaybackSpeed;
- public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, String username, String password) {
- this(feedID, autoDownload, true, auto_delete_action, username, password, new FeedFilter(), SPEED_USE_GLOBAL);
+ public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) {
+ this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, username, password, new FeedFilter(), SPEED_USE_GLOBAL);
}
- private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) {
+ private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) {
this.feedID = feedID;
this.autoDownload = autoDownload;
this.keepUpdated = keepUpdated;
this.auto_delete_action = auto_delete_action;
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
this.username = username;
this.password = password;
this.filter = filter;
@@ -52,6 +56,7 @@ public class FeedPreferences {
int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
int indexAutoRefresh = cursor.getColumnIndex(PodDBAdapter.KEY_KEEP_UPDATED);
int indexAutoDeleteAction = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DELETE_ACTION);
+ int indexVolumeAdaption = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_VOLUME_ADAPTION);
int indexUsername = cursor.getColumnIndex(PodDBAdapter.KEY_USERNAME);
int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD);
int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER);
@@ -63,12 +68,14 @@ public class FeedPreferences {
boolean autoRefresh = cursor.getInt(indexAutoRefresh) > 0;
int autoDeleteActionIndex = cursor.getInt(indexAutoDeleteAction);
AutoDeleteAction autoDeleteAction = AutoDeleteAction.values()[autoDeleteActionIndex];
+ int volumeAdaptionValue = cursor.getInt(indexVolumeAdaption);
+ VolumeAdaptionSetting volumeAdaptionSetting = VolumeAdaptionSetting.fromInteger(volumeAdaptionValue);
String username = cursor.getString(indexUsername);
String password = cursor.getString(indexPassword);
String includeFilter = cursor.getString(indexIncludeFilter);
String excludeFilter = cursor.getString(indexExcludeFilter);
float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed);
- return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, username, password, new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed);
+ return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, volumeAdaptionSetting, username, password, new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed);
}
/**
@@ -144,10 +151,18 @@ public class FeedPreferences {
return auto_delete_action;
}
+ public VolumeAdaptionSetting getVolumeAdaptionSetting() {
+ return volumeAdaptionSetting;
+ }
+
public void setAutoDeleteAction(AutoDeleteAction auto_delete_action) {
this.auto_delete_action = auto_delete_action;
}
+ public void setVolumeAdaptionSetting(VolumeAdaptionSetting volumeAdaptionSetting) {
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
+ }
+
public boolean getCurrentAutoDelete() {
switch (auto_delete_action) {
case GLOBAL:
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
index f0ff03a93..b69d537fb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
@@ -1,36 +1,36 @@
package de.danoeh.antennapod.core.feed;
public class ID3Chapter extends Chapter {
- public static final int CHAPTERTYPE_ID3CHAPTER = 2;
-
- /**
- * Identifies the chapter in its ID3 tag. This attribute does not have to be
- * store in the DB and is only used for parsing.
- */
- private String id3ID;
-
- public ID3Chapter(String id3ID, long start) {
- super(start);
- this.id3ID = id3ID;
- }
-
- public ID3Chapter(long start, String title, FeedItem item, String link) {
- super(start, title, item, link);
- }
-
- @Override
- public String toString() {
- return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start="
- + start + ", url=" + link + "]";
- }
-
- @Override
- public int getChapterType() {
- return CHAPTERTYPE_ID3CHAPTER;
- }
-
- public String getId3ID() {
- return id3ID;
- }
+ public static final int CHAPTERTYPE_ID3CHAPTER = 2;
+
+ /**
+ * Identifies the chapter in its ID3 tag. This attribute does not have to be
+ * store in the DB and is only used for parsing.
+ */
+ private String id3ID;
+
+ public ID3Chapter(String id3ID, long start) {
+ super(start);
+ this.id3ID = id3ID;
+ }
+
+ public ID3Chapter(long start, String title, String link, String imageUrl) {
+ super(start, title, link, imageUrl);
+ }
+
+ @Override
+ public String toString() {
+ return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start="
+ + start + ", url=" + link + "]";
+ }
+
+ @Override
+ public int getChapterType() {
+ return CHAPTERTYPE_ID3CHAPTER;
+ }
+
+ public String getId3ID() {
+ return id3ID;
+ }
}
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/feed/SimpleChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
index 2dadd3ec8..45c71a014 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
@@ -1,25 +1,14 @@
package de.danoeh.antennapod.core.feed;
public class SimpleChapter extends Chapter {
- public static final int CHAPTERTYPE_SIMPLECHAPTER = 0;
-
- public SimpleChapter(long start, String title, FeedItem item, String link) {
- super(start, title, item, link);
- }
+ public static final int CHAPTERTYPE_SIMPLECHAPTER = 0;
- @Override
- public int getChapterType() {
- return CHAPTERTYPE_SIMPLECHAPTER;
- }
+ public SimpleChapter(long start, String title, String link, String imageUrl) {
+ super(start, title, link, imageUrl);
+ }
- public void updateFromOther(SimpleChapter other) {
- super.updateFromOther(other);
- start = other.start;
- if (other.title != null) {
- title = other.title;
- }
- if (other.link != null) {
- link = other.link;
- }
- }
+ @Override
+ public int getChapterType() {
+ return CHAPTERTYPE_SIMPLECHAPTER;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
new file mode 100644
index 000000000..bf4fc582a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
@@ -0,0 +1,32 @@
+package de.danoeh.antennapod.core.feed;
+
+public enum VolumeAdaptionSetting {
+ OFF(0, 1.0f),
+ LIGHT_REDUCTION(1, 0.5f),
+ HEAVY_REDUCTION(2, 0.2f);
+
+ private final int value;
+ private float adaptionFactor;
+
+ VolumeAdaptionSetting(int value, float adaptionFactor) {
+ this.value = value;
+ this.adaptionFactor = adaptionFactor;
+ }
+
+ public static VolumeAdaptionSetting fromInteger(int value) {
+ for (VolumeAdaptionSetting setting : values()) {
+ if (setting.value == value) {
+ return setting;
+ }
+ }
+ throw new IllegalArgumentException("Cannot map value to VolumeAdaptionSetting: " + value);
+ }
+
+ public int toInteger() {
+ return value;
+ }
+
+ public float getAdaptionFactor() {
+ return adaptionFactor;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
index 5ab9868a6..eac50aed8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
@@ -5,105 +5,84 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
public class VorbisCommentChapter extends Chapter {
- public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
-
- private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
-
- private int vorbisCommentId;
-
- public VorbisCommentChapter(int vorbisCommentId) {
- this.vorbisCommentId = vorbisCommentId;
- }
-
- public VorbisCommentChapter(long start, String title, FeedItem item,
- String link) {
- super(start, title, item, link);
- }
-
- @Override
- public String toString() {
- return "VorbisCommentChapter [id=" + id + ", title=" + title
- + ", link=" + link + ", start=" + start + "]";
- }
-
- public static long getStartTimeFromValue(String value)
- throws VorbisCommentReaderException {
- String[] parts = value.split(":");
- if (parts.length >= 3) {
- try {
- long hours = TimeUnit.MILLISECONDS.convert(
- Long.parseLong(parts[0]), TimeUnit.HOURS);
- long minutes = TimeUnit.MILLISECONDS.convert(
- Long.parseLong(parts[1]), TimeUnit.MINUTES);
- if (parts[2].contains("-->")) {
- parts[2] = parts[2].substring(0, parts[2].indexOf("-->"));
- }
- long seconds = TimeUnit.MILLISECONDS.convert(
- ((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS);
- return hours + minutes + seconds;
- } catch (NumberFormatException e) {
- throw new VorbisCommentReaderException(e);
- }
- } else {
- throw new VorbisCommentReaderException("Invalid time string");
- }
- }
-
- /**
- * Return the id of a vorbiscomment chapter from a string like CHAPTERxxx*
- *
- * @return the id of the chapter key or -1 if the id couldn't be read.
- * @throws VorbisCommentReaderException
- * */
- public static int getIDFromKey(String key)
- throws VorbisCommentReaderException {
- if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx
- try {
- String strId = key.substring(8, 10);
- return Integer.parseInt(strId);
- } catch (NumberFormatException e) {
- throw new VorbisCommentReaderException(e);
- }
- }
- throw new VorbisCommentReaderException("key is too short (" + key + ")");
- }
-
- /**
- * Get the string that comes after 'CHAPTERxxx', for example 'name' or
- * 'url'.
- */
- public static String getAttributeTypeFromKey(String key) {
- if (key.length() > CHAPTERXXX_LENGTH) {
- return key.substring(CHAPTERXXX_LENGTH, key.length());
- }
- return null;
- }
-
- @Override
- public int getChapterType() {
- return CHAPTERTYPE_VORBISCOMMENT_CHAPTER;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
-
- public void setStart(long start) {
- this.start = start;
- }
-
- public int getVorbisCommentId() {
- return vorbisCommentId;
- }
-
- public void setVorbisCommentId(int vorbisCommentId) {
- this.vorbisCommentId = vorbisCommentId;
- }
-
-
-
+ public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
+
+ private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
+
+ private int vorbisCommentId;
+
+ public VorbisCommentChapter(int vorbisCommentId) {
+ this.vorbisCommentId = vorbisCommentId;
+ }
+
+ public VorbisCommentChapter(long start, String title, String link, String imageUrl) {
+ super(start, title, link, imageUrl);
+ }
+
+ @Override
+ public String toString() {
+ return "VorbisCommentChapter [id=" + id + ", title=" + title
+ + ", link=" + link + ", start=" + start + "]";
+ }
+
+ public static long getStartTimeFromValue(String value)
+ throws VorbisCommentReaderException {
+ String[] parts = value.split(":");
+ if (parts.length >= 3) {
+ try {
+ long hours = TimeUnit.MILLISECONDS.convert(
+ Long.parseLong(parts[0]), TimeUnit.HOURS);
+ long minutes = TimeUnit.MILLISECONDS.convert(
+ Long.parseLong(parts[1]), TimeUnit.MINUTES);
+ if (parts[2].contains("-->")) {
+ parts[2] = parts[2].substring(0, parts[2].indexOf("-->"));
+ }
+ long seconds = TimeUnit.MILLISECONDS.convert(
+ ((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS);
+ return hours + minutes + seconds;
+ } catch (NumberFormatException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ } else {
+ throw new VorbisCommentReaderException("Invalid time string");
+ }
+ }
+
+ /**
+ * Return the id of a vorbiscomment chapter from a string like CHAPTERxxx*
+ *
+ * @return the id of the chapter key or -1 if the id couldn't be read.
+ * @throws VorbisCommentReaderException
+ * */
+ public static int getIDFromKey(String key) throws VorbisCommentReaderException {
+ if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx
+ try {
+ String strId = key.substring(8, 10);
+ return Integer.parseInt(strId);
+ } catch (NumberFormatException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ }
+ throw new VorbisCommentReaderException("key is too short (" + key + ")");
+ }
+
+ /**
+ * Get the string that comes after 'CHAPTERxxx', for example 'name' or
+ * 'url'.
+ */
+ public static String getAttributeTypeFromKey(String key) {
+ if (key.length() > CHAPTERXXX_LENGTH) {
+ return key.substring(CHAPTERXXX_LENGTH);
+ }
+ return null;
+ }
+
+ @Override
+ public int getChapterType() {
+ return CHAPTERTYPE_VORBISCOMMENT_CHAPTER;
+ }
+
+ public int getVorbisCommentId() {
+ return vorbisCommentId;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index b2c809e90..50511526f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -11,10 +11,12 @@ import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
import com.bumptech.glide.module.AppGlideModule;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import java.io.InputStream;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import java.nio.ByteBuffer;
/**
* {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule}
@@ -32,5 +34,6 @@ public class ApGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.replace(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
new file mode 100644
index 000000000..bc0a06a07
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
@@ -0,0 +1,111 @@
+package de.danoeh.antennapod.core.glide;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.Options;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+import com.bumptech.glide.signature.ObjectKey;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.apache.commons.io.IOUtils;
+
+public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapterImage, ByteBuffer> {
+
+ public static class Factory implements ModelLoaderFactory<EmbeddedChapterImage, ByteBuffer> {
+ @Override
+ public ModelLoader<EmbeddedChapterImage, ByteBuffer> build(MultiModelLoaderFactory unused) {
+ return new ChapterImageModelLoader();
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing.
+ }
+ }
+
+ @Nullable
+ @Override
+ public LoadData<ByteBuffer> buildLoadData(EmbeddedChapterImage model, int width, int height, Options options) {
+ return new LoadData<>(new ObjectKey(model), new EmbeddedImageFetcher(model));
+ }
+
+ @Override
+ public boolean handles(EmbeddedChapterImage model) {
+ return true;
+ }
+
+ static class EmbeddedImageFetcher implements DataFetcher<ByteBuffer> {
+ private final EmbeddedChapterImage image;
+
+ public EmbeddedImageFetcher(EmbeddedChapterImage image) {
+ this.image = image;
+ }
+
+ @Override
+ public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
+
+ BufferedInputStream stream = null;
+ try {
+ if (image.getMedia().localFileAvailable()) {
+ File localFile = new File(image.getMedia().getLocalMediaUrl());
+ stream = new BufferedInputStream(new FileInputStream(localFile));
+ stream.skip(image.getPosition());
+ byte[] imageContent = new byte[image.getLength()];
+ stream.read(imageContent, 0, image.getLength());
+ callback.onDataReady(ByteBuffer.wrap(imageContent));
+ } else {
+ Request.Builder httpReq = new Request.Builder();
+ httpReq.header("User-Agent", ClientConfig.USER_AGENT);
+ // Skipping would download the whole file
+ httpReq.header("Range", "bytes=" + image.getPosition()
+ + "-" + (image.getPosition() + image.getLength()));
+ httpReq.url(image.getMedia().getStreamUrl());
+ Response response = AntennapodHttpClient.getHttpClient().newCall(httpReq.build()).execute();
+ if (!response.isSuccessful() || response.body() == null) {
+ throw new IOException("Invalid response: " + response.code() + " " + response.message());
+ }
+ callback.onDataReady(ByteBuffer.wrap(response.body().bytes()));
+ }
+ } catch (IOException e) {
+ callback.onLoadFailed(e);
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ }
+
+ @Override public void cleanup() {
+ // nothing to clean up
+ }
+ @Override public void cancel() {
+ // cannot cancel
+ }
+
+ @NonNull
+ @Override
+ public Class<ByteBuffer> getDataClass() {
+ return ByteBuffer.class;
+ }
+
+ @NonNull
+ @Override
+ public DataSource getDataSource() {
+ return DataSource.LOCAL;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
index 3cc906b7f..d2d9e5947 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
@@ -14,19 +14,15 @@ public class FastBlurTransformation extends BitmapTransformation {
private static final String TAG = FastBlurTransformation.class.getSimpleName();
- private static final int STACK_BLUR_RADIUS = 1;
- private static final int BLUR_IMAGE_WIDTH = 150;
+ private static final int STACK_BLUR_RADIUS = 5;
public FastBlurTransformation() {
super();
}
@Override
- protected Bitmap transform(BitmapPool pool, Bitmap source,
- int outWidth, int outHeight) {
- int targetWidth = BLUR_IMAGE_WIDTH;
- int targetHeight = (int) (1.0 * outHeight * targetWidth / outWidth);
- Bitmap resized = ThumbnailUtils.extractThumbnail(source, targetWidth, targetHeight);
+ protected Bitmap transform(BitmapPool pool, Bitmap source, int outWidth, int outHeight) {
+ Bitmap resized = ThumbnailUtils.extractThumbnail(source, outWidth / 3, outHeight / 3);
Bitmap result = fastBlur(resized, STACK_BLUR_RADIUS);
if (result == null) {
Log.w(TAG, "result was null");
@@ -80,9 +76,9 @@ public class FastBlurTransformation extends BitmapTransformation {
int wh = w * h;
int div = radius + radius + 1;
- int r[] = new int[wh];
- int g[] = new int[wh];
- int b[] = new int[wh];
+ int[] r = new int[wh];
+ int[] g = new int[wh];
+ int[] b = new int[wh];
int rsum;
int gsum;
int bsum;
@@ -93,11 +89,11 @@ public class FastBlurTransformation extends BitmapTransformation {
int yp;
int yi;
int yw;
- int vmin[] = new int[Math.max(w, h)];
+ int[] vmin = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
- int dv[] = new int[256 * divsum];
+ int[] dv = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
@@ -225,8 +221,8 @@ public class FastBlurTransformation extends BitmapTransformation {
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
- // Preserve alpha channel: ( 0xff000000 & pix[yi] )
- pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+ // Set alpha to 1
+ pix[yi] = 0xff000000 | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
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 4f871e83b..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() {
@@ -805,14 +803,6 @@ public class UserPreferences {
prefs.edit().putString(PREF_MEDIA_PLAYER, "sonic").apply();
}
- public static void enableExoplayer() {
- prefs.edit().putString(PREF_MEDIA_PLAYER, PREF_MEDIA_PLAYER_EXOPLAYER).apply();
- }
-
- public static void enableBuiltin() {
- prefs.edit().putString(PREF_MEDIA_PLAYER, "builtin").apply();
- }
-
public static boolean stereoToMono() {
return prefs.getBoolean(PREF_STEREO_TO_MONO, false);
}
@@ -824,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/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
index b254cfc59..e4d9ff361 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
@@ -38,6 +38,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
@@ -185,15 +186,14 @@ public class GpodnetSyncService extends SafeJobIntentService {
// local changes are always superior to remote changes!
// add subscription if (1) not already subscribed and (2) not just unsubscribed
for (String downloadUrl : changes.getAdded()) {
- if (!localSubscriptions.contains(downloadUrl) &&
- !localRemoved.contains(downloadUrl)) {
+ if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !localRemoved.contains(downloadUrl)) {
Feed feed = new Feed(downloadUrl, null);
DownloadRequester.getInstance().downloadFeed(this, feed);
}
}
// remove subscription if not just subscribed (again)
for (String downloadUrl : changes.getRemoved()) {
- if(!localAdded.contains(downloadUrl)) {
+ if (!localAdded.contains(downloadUrl)) {
DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
index e6f2176f7..4562f1393 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
@@ -8,7 +8,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
-import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import androidx.annotation.NonNull;
@@ -158,10 +157,10 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
}
if (status == PlayerStatus.PLAYING) {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_pause_white_48dp);
views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
} else {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
}
views.setOnClickPendingIntent(R.id.butPlay, createMediaButtonIntent());
@@ -177,7 +176,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
views.setViewVisibility(R.id.txtvTitle, View.GONE);
views.setViewVisibility(R.id.txtNoPlaying, View.VISIBLE);
views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_foreground);
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index f7c338729..b8215232c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -336,7 +336,12 @@ public class DownloadService extends Service {
&& String.valueOf(HttpURLConnection.HTTP_FORBIDDEN).equals(status.getReasonDetailed());
boolean notEnoughSpace = status.getReason() == DownloadError.ERROR_NOT_ENOUGH_SPACE;
boolean wrongFileType = status.getReason() == DownloadError.ERROR_FILE_TYPE;
- if (httpNotFound || forbidden || notEnoughSpace || wrongFileType) {
+ boolean httpGone = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
+ && String.valueOf(HttpURLConnection.HTTP_GONE).equals(status.getReasonDetailed());
+ boolean httpBadReq = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
+ && String.valueOf(HttpURLConnection.HTTP_BAD_REQUEST).equals(status.getReasonDetailed());
+
+ if (httpNotFound || forbidden || notEnoughSpace || wrongFileType || httpGone || httpBadReq ) {
try {
DBWriter.saveFeedItemAutoDownloadFailed(item).get();
} catch (ExecutionException | InterruptedException e) {
@@ -553,6 +558,7 @@ public class DownloadService extends Service {
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown");
stopSelf();
+ notificationUpdater.run();
} else {
setupNotificationUpdater();
Notification notification = notificationManager.updateNotifications(
@@ -611,8 +617,8 @@ public class DownloadService extends Service {
* Schedules the notification updater task if it hasn't been scheduled yet.
*/
private void setupNotificationUpdater() {
- Log.d(TAG, "Setting up notification updater");
if (notificationUpdater == null) {
+ Log.d(TAG, "Setting up notification updater");
notificationUpdater = new NotificationUpdater();
notificationUpdaterFuture = schedExecutor.scheduleAtFixedRate(notificationUpdater, 1, 1, TimeUnit.SECONDS);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
index 10d5bfa15..5900f84fc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
@@ -4,6 +4,7 @@ import android.util.Log;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -37,7 +38,7 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
feed.setId(request.getFeedfileId());
feed.setDownloaded(true);
feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
- request.getUsername(), request.getPassword()));
+ VolumeAdaptionSetting.OFF, request.getUsername(), request.getPassword()));
feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
DownloadError reason = null;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
index 40be2895c..9ecabd14b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
@@ -6,6 +6,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
+import java.io.File;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
@@ -50,6 +51,7 @@ public class MediaDownloadedHandler implements Runnable {
boolean broadcastUnreadStateUpdate = media.getItem() != null && media.getItem().isNew();
media.setDownloaded(true);
media.setFile_url(request.getDestination());
+ media.setSize(new File(request.getDestination()).length());
media.checkEmbeddedPicture(); // enforce check
// check if file has chapters
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
index 5d2c48679..7c998146d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download.handler;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
@@ -23,6 +24,7 @@ public class PostDownloaderTask implements Runnable {
runningDownloads.add(downloader);
}
}
+ DownloadRequester.getInstance().updateProgress(downloads);
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index c250cd17f..8f7e84561 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -16,8 +16,9 @@ import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes;
-import com.google.android.exoplayer2.source.ExtractorMediaSource;
+import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
@@ -71,7 +72,7 @@ public class ExoPlayerWrapper implements IPlayer {
loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true);
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext),
new DefaultTrackSelector(), loadControl.createDefaultLoadControl());
- p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
+ p.setSeekParameters(SeekParameters.EXACT);
p.addListener(new Player.EventListener() {
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
@@ -170,7 +171,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void prepare() throws IllegalStateException {
- mExoPlayer.prepare(mediaSource);
+ mExoPlayer.prepare(mediaSource, false, true);
}
@Override
@@ -216,7 +217,9 @@ public class ExoPlayerWrapper implements IPlayer {
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory);
- ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory);
+ DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
+ extractorsFactory.setConstantBitrateSeekingEnabled(true);
+ ProgressiveMediaSource.Factory f = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
mediaSource = f.createMediaSource(Uri.parse(s));
}
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 485349cb1..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
@@ -12,11 +12,11 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
-import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import org.antennapod.audio.MediaPlayer;
import java.io.File;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
@@ -26,13 +26,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.core.util.playback.VideoPlayer;
/**
@@ -308,7 +312,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
acquireWifiLockIfNecessary();
setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
- setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
+
+ float leftVolume = UserPreferences.getLeftVolume();
+ float rightVolume = UserPreferences.getRightVolume();
+ setVolume(leftVolume, rightVolume);
if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
@@ -639,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();
}
@@ -661,6 +669,15 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
*/
private void setVolumeSync(float volumeLeft, float volumeRight) {
playerLock.lock();
+ Playable playable = getPlayable();
+ if (playable instanceof FeedMedia) {
+ FeedMedia feedMedia = (FeedMedia) playable;
+ FeedPreferences preferences = feedMedia.getItem().getFeed().getPreferences();
+ VolumeAdaptionSetting volumeAdaptionSetting = preferences.getVolumeAdaptionSetting();
+ float adaptionFactor = volumeAdaptionSetting.getAdaptionFactor();
+ volumeLeft *= adaptionFactor;
+ volumeRight *= adaptionFactor;
+ }
mediaPlayer.setVolume(volumeLeft, volumeRight);
Log.d(TAG, "Media player volume was set to " + volumeLeft + " " + volumeRight);
playerLock.unlock();
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 d53f7d669..bf59b0ffd 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
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.service.playback;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -51,12 +50,14 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
+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;
@@ -70,7 +71,6 @@ import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -79,6 +79,10 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
@@ -151,7 +155,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final int EXTRA_CODE_CAST = 3;
public static final int NOTIFICATION_TYPE_ERROR = 0;
- public static final int NOTIFICATION_TYPE_INFO = 1;
public static final int NOTIFICATION_TYPE_BUFFER_UPDATE = 2;
/**
@@ -277,6 +280,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(ACTION_SKIP_CURRENT_EPISODE));
registerReceiver(pausePlayCurrentEpisodeReceiver, new IntentFilter(ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ EventBus.getDefault().register(this);
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
flavorHelper = new PlaybackServiceFlavorHelper(PlaybackService.this, flavorHelperCallback);
@@ -569,7 +573,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.prepare();
} else if (mediaPlayer.getPlayable() == null) {
startPlayingFromPreferences();
+ } else {
+ return false;
}
+ taskManager.restartSleepTimer();
return true;
case KeyEvent.KEYCODE_MEDIA_PLAY:
if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
@@ -579,17 +586,19 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.prepare();
} else if (mediaPlayer.getPlayable() == null) {
startPlayingFromPreferences();
+ } else {
+ return false;
}
+ taskManager.restartSleepTimer();
return true;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(!UserPreferences.isPersistNotify(), false);
+ return true;
}
-
- return true;
+ return false;
case KeyEvent.KEYCODE_MEDIA_NEXT:
- if (notificationButton ||
- UserPreferences.shouldHardwareButtonSkip()) {
+ if (notificationButton || UserPreferences.shouldHardwareButtonSkip()) {
// assume the skip command comes from a notification or the lockscreen
// a >| skip button should actually skip
mediaPlayer.skip();
@@ -744,10 +753,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
setupPositionUpdater();
stateManager.validStartCommandWasReceived();
// set sleep timer if auto-enabled
- if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
- SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
+ if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING
+ && SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
setSleepTimer(SleepTimerPreferences.timerMillis(), SleepTimerPreferences.shakeToReset(),
SleepTimerPreferences.vibrate());
+ EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
+ PlaybackService.this::disableSleepTimer));
}
break;
@@ -827,9 +838,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);
@@ -1006,17 +1014,14 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
public void setSleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
- Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
+ Log.d(TAG, "Setting sleep timer to " + waitingTime + " milliseconds");
taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate);
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
- this::disableSleepTimer));
}
public void disableSleepTimer() {
taskManager.disableSleepTimer();
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label)));
}
private void sendNotificationBroadcast(int type, int code) {
@@ -1326,7 +1331,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
- if (isInitialStickyBroadcast ()) {
+ if (isInitialStickyBroadcast()) {
// Don't pause playback after we just started, just because the receiver
// delivers the current headset state (instead of a change)
return;
@@ -1436,6 +1441,25 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
};
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void volumeAdaptionChanged(VolumeAdaptionChangedEvent event) {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, event.getFeedId(), event.getVolumeAdaptionSetting());
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void speedPresetChanged(SpeedPresetChangedEvent event) {
+ if (getPlayable() instanceof FeedMedia) {
+ if (((FeedMedia) getPlayable()).getItem().getFeed().getId() == event.getFeedId()) {
+ if (event.getSpeed() == SPEED_USE_GLOBAL) {
+ setSpeed(UserPreferences.getPlaybackSpeed(getPlayable().getMediaType()));
+ } else {
+ setSpeed(event.getSpeed());
+ }
+ }
+ }
+ }
+
public static MediaType getCurrentMediaType() {
return currentMediaType;
}
@@ -1446,10 +1470,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) {
@@ -1612,16 +1638,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/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index 736cf8cf2..62eda415e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -397,41 +397,42 @@ public class PlaybackServiceTaskManager {
while (timeLeft > 0) {
try {
Thread.sleep(UPDATE_INTERVAL);
- long now = System.currentTimeMillis();
- timeLeft -= now - lastTick;
- lastTick = now;
-
- if(timeLeft < NOTIFICATION_THRESHOLD && !notifiedAlmostExpired) {
- Log.d(TAG, "Sleep timer is about to expire");
- if(vibrate) {
- Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if(v != null) {
- v.vibrate(500);
- }
- }
- if(shakeListener == null && shakeToReset) {
- shakeListener = new ShakeListener(context, this);
- }
- postCallback(callback::onSleepTimerAlmostExpired);
- notifiedAlmostExpired = true;
- }
- if (timeLeft <= 0) {
- Log.d(TAG, "Sleep timer expired");
- if(shakeListener != null) {
- shakeListener.pause();
- shakeListener = null;
- }
- if (!Thread.currentThread().isInterrupted()) {
- postCallback(callback::onSleepTimerExpired);
- } else {
- Log.d(TAG, "Sleep timer interrupted");
- }
- }
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted while waiting");
e.printStackTrace();
break;
}
+
+ long now = System.currentTimeMillis();
+ timeLeft -= now - lastTick;
+ lastTick = now;
+
+ if (timeLeft < NOTIFICATION_THRESHOLD && !notifiedAlmostExpired) {
+ Log.d(TAG, "Sleep timer is about to expire");
+ if (vibrate) {
+ Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (v != null) {
+ v.vibrate(500);
+ }
+ }
+ if (shakeListener == null && shakeToReset) {
+ shakeListener = new ShakeListener(context, this);
+ }
+ postCallback(callback::onSleepTimerAlmostExpired);
+ notifiedAlmostExpired = true;
+ }
+ if (timeLeft <= 0) {
+ Log.d(TAG, "Sleep timer expired");
+ if (shakeListener != null) {
+ shakeListener.pause();
+ shakeListener = null;
+ }
+ if (!Thread.currentThread().isInterrupted()) {
+ postCallback(callback::onSleepTimerExpired);
+ } else {
+ Log.d(TAG, "Sleep timer interrupted");
+ }
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
new file mode 100644
index 000000000..d03830387
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
@@ -0,0 +1,36 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+class PlaybackVolumeUpdater {
+
+ public void updateVolumeIfNecessary(PlaybackServiceMediaPlayer mediaPlayer, long feedId,
+ VolumeAdaptionSetting volumeAdaptionSetting) {
+ Playable playable = mediaPlayer.getPlayable();
+
+ if (playable instanceof FeedMedia) {
+ updateFeedMediaVolumeIfNecessary(mediaPlayer, feedId, volumeAdaptionSetting, (FeedMedia) playable);
+ }
+ }
+
+ private void updateFeedMediaVolumeIfNecessary(PlaybackServiceMediaPlayer mediaPlayer, long feedId,
+ VolumeAdaptionSetting volumeAdaptionSetting, FeedMedia feedMedia) {
+ if (feedMedia.getItem().getFeed().getId() == feedId) {
+ FeedPreferences preferences = feedMedia.getItem().getFeed().getPreferences();
+ preferences.setVolumeAdaptionSetting(volumeAdaptionSetting);
+
+ if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
+ forceUpdateVolume(mediaPlayer);
+ }
+ }
+ }
+
+ private void forceUpdateVolume(PlaybackServiceMediaPlayer mediaPlayer) {
+ mediaPlayer.pause(false, false);
+ mediaPlayer.resume();
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index e6d21794c..e818f3072 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -689,7 +689,7 @@ public final class DBReader {
* Returns credentials based on image URL
*
* @param imageUrl The URL of the image
- * @return Credentials in format "<Username>:<Password>", empty String if no authorization given
+ * @return Credentials in format "Username:Password", empty String if no authorization given
*/
public static String getImageAuthentication(final String imageUrl) {
Log.d(TAG, "getImageAuthentication() called with: " + "imageUrl = [" + imageUrl + "]");
@@ -795,9 +795,7 @@ public final class DBReader {
}
private static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) {
- Cursor cursor = null;
- try {
- cursor = adapter.getSimpleChaptersOfFeedItemCursor(item);
+ try (Cursor cursor = adapter.getSimpleChaptersOfFeedItemCursor(item)) {
int chaptersCount = cursor.getCount();
if (chaptersCount == 0) {
item.setChapters(null);
@@ -805,14 +803,7 @@ public final class DBReader {
}
item.setChapters(new ArrayList<>(chaptersCount));
while (cursor.moveToNext()) {
- Chapter chapter = Chapter.fromCursor(cursor, item);
- if (chapter != null) {
- item.getChapters().add(chapter);
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
+ item.getChapters().add(Chapter.fromCursor(cursor));
}
}
}
@@ -866,17 +857,15 @@ public final class DBReader {
}
/**
- * Searches the DB for statistics
+ * Searches the DB for statistics.
*
- * @return The StatisticsInfo object
+ * @return The list of statistics objects
*/
@NonNull
- public static StatisticsData getStatistics() {
+ public static List<StatisticsItem> getStatistics() {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- long totalTimeCountAll = 0;
- long totalTime = 0;
List<StatisticsItem> feedTime = new ArrayList<>();
List<Feed> feeds = getFeedList();
@@ -922,72 +911,10 @@ public final class DBReader {
feedTime.add(new StatisticsItem(
feed, feedTotalTime, feedPlayedTime, feedPlayedTimeCountAll, episodes,
episodesStarted, episodesStartedIncludingMarked, totalDownloadSize));
- totalTime += feedPlayedTime;
- totalTimeCountAll += feedPlayedTimeCountAll;
}
adapter.close();
- return new StatisticsData(totalTime, totalTimeCountAll, feedTime);
- }
-
- public static class StatisticsData {
- /**
- * Simply sums up time of podcasts that are marked as played
- */
- public final long totalTimeCountAll;
-
- /**
- * Respects speed, listening twice, ...
- */
- public final long totalTime;
-
- public final List<StatisticsItem> feeds;
-
- public StatisticsData(long totalTime, long totalTimeCountAll, List<StatisticsItem> feeds) {
- this.totalTime = totalTime;
- this.totalTimeCountAll = totalTimeCountAll;
- this.feeds = feeds;
- }
- }
-
- public static class StatisticsItem {
- public final Feed feed;
- public final long time;
-
- /**
- * Respects speed, listening twice, ...
- */
- public final long timePlayed;
- /**
- * Simply sums up time of podcasts that are marked as played
- */
- public final long timePlayedCountAll;
- public final long episodes;
- /**
- * Episodes that are actually played
- */
- public final long episodesStarted;
- /**
- * All episodes that are marked as played (or have position != 0)
- */
- public final long episodesStartedIncludingMarked;
- /**
- * Simply sums up the size of download podcasts
- */
- public final long totalDownloadSize;
-
- public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll,
- long episodes, long episodesStarted, long episodesStartedIncludingMarked,
- long totalDownloadSize) {
- this.feed = feed;
- this.time = time;
- this.timePlayed = timePlayed;
- this.timePlayedCountAll = timePlayedCountAll;
- this.episodes = episodes;
- this.episodesStarted = episodesStarted;
- this.episodesStartedIncludingMarked = episodesStartedIncludingMarked;
- this.totalDownloadSize = totalDownloadSize;
- }
+ return feedTime;
}
/**
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/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
index 0c8f89348..ee31c8cc4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
@@ -95,7 +95,7 @@ class DBUpgrader {
+ " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD
+ " TEXT");
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE
+ + " ADD COLUMN image"
+ " INTEGER");
}
if (oldVersion <= 12) {
@@ -280,13 +280,13 @@ class DBUpgrader {
+ " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
- + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_IMAGE + ")");
+ + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + ".image)");
db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + " SET " + PodDBAdapter.KEY_IMAGE_URL + " = ("
+ " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
- + " = " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_IMAGE + ")");
+ + " = " + PodDBAdapter.TABLE_NAME_FEEDS + ".image)");
db.execSQL("DROP TABLE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES);
}
@@ -294,11 +294,16 @@ class DBUpgrader {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL);
}
-
if (oldVersion < 1070401) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_SORT_ORDER + " TEXT");
}
+ if (oldVersion < 1090000) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE_URL + " TEXT DEFAULT NULL");
+ }
}
}
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
new file mode 100644
index 000000000..234c01b20
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
@@ -0,0 +1,93 @@
+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;
+import de.danoeh.antennapod.core.R;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+
+public class DatabaseExporter {
+ private static final String TAG = "DatabaseExporter";
+ private static final String TEMP_DB_NAME = PodDBAdapter.DATABASE_NAME + "_tmp";
+
+ public static void exportToDocument(Uri uri, Context context) throws IOException {
+ ParcelFileDescriptor pfd = null;
+ FileOutputStream fileOutputStream = null;
+ try {
+ pfd = context.getContentResolver().openFileDescriptor(uri, "w");
+ fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
+ exportToStream(fileOutputStream, context);
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(fileOutputStream);
+
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to close ParcelFileDescriptor");
+ }
+ }
+ }
+ }
+
+ public static void exportToStream(FileOutputStream outFileStream, Context context) throws IOException {
+ FileChannel src = null;
+ FileChannel dst = null;
+ try {
+ File currentDB = context.getDatabasePath(PodDBAdapter.DATABASE_NAME);
+
+ if (currentDB.exists()) {
+ src = new FileInputStream(currentDB).getChannel();
+ dst = outFileStream.getChannel();
+ dst.transferFrom(src, 0, src.size());
+ } else {
+ throw new IOException("Can not access current database");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(src);
+ IOUtils.closeQuietly(dst);
+ }
+ }
+
+ public static void importBackup(Uri inputUri, Context context) throws IOException {
+ InputStream inputStream = null;
+ try {
+ 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);
+ currentDB.delete();
+ FileUtils.moveFile(tempDB, currentDB);
+ } catch (IOException | SQLiteException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index 3d4ee443b..8bd9afe38 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
+import de.danoeh.antennapod.core.service.download.Downloader;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
@@ -344,6 +345,16 @@ public class DownloadRequester implements DownloadStateProvider {
}
/**
+ * Get the downloader for this item.
+ */
+ public synchronized DownloadRequest getRequestFor(FeedFile item) {
+ if (isDownloadingFile(item)) {
+ return downloads.get(item.getDownload_url());
+ }
+ return null;
+ }
+
+ /**
* Checks if feedfile with the given download url is in the downloads list
*/
public synchronized boolean isDownloadingFile(String downloadUrl) {
@@ -428,4 +439,13 @@ public class DownloadRequester implements DownloadStateProvider {
}
return filename;
}
+
+ public void updateProgress(List<Downloader> newDownloads) {
+ for (Downloader downloader : newDownloads) {
+ DownloadRequest request = downloader.getDownloadRequest();
+ if (downloads.containsKey(request.getSource())) {
+ downloads.put(request.getSource(), request);
+ }
+ }
+ }
}
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..77c8d3b7f 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,20 @@ 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);
+ itemSearchTask.run();
+ if (selectedFeed == 0) {
+ FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(context, query);
+ 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 a7b9e44e6..af6a8ef81 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
@@ -12,7 +12,6 @@ import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
-import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
@@ -49,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.
@@ -69,7 +69,6 @@ public class PodDBAdapter {
public static final String KEY_POSITION = "position";
public static final String KEY_SIZE = "filesize";
public static final String KEY_MIME_TYPE = "mime_type";
- public static final String KEY_IMAGE = "image";
public static final String KEY_IMAGE_URL = "image_url";
public static final String KEY_FEED = "feed";
public static final String KEY_MEDIA = "media";
@@ -97,6 +96,7 @@ public class PodDBAdapter {
public static final String KEY_AUTO_DOWNLOAD = "auto_download";
public static final String KEY_KEEP_UPDATED = "keep_updated";
public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action";
+ public static final String KEY_FEED_VOLUME_ADAPTION = "feed_volume_adaption";
public static final String KEY_PLAYED_DURATION = "played_duration";
public static final String KEY_USERNAME = "username";
public static final String KEY_PASSWORD = "password";
@@ -144,7 +144,8 @@ public class PodDBAdapter {
+ KEY_SORT_ORDER + " TEXT,"
+ KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0,"
+ KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0,"
- + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + ")";
+ + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + ","
+ + KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0)";
private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@@ -181,7 +182,7 @@ public class PodDBAdapter {
private static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE "
+ TABLE_NAME_SIMPLECHAPTERS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER,"
- + KEY_LINK + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
+ + KEY_LINK + " TEXT," + KEY_IMAGE_URL + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
// SQL Statements for creating indexes
static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX "
@@ -241,6 +242,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_SORT_ORDER,
TABLE_NAME_FEEDS + "." + KEY_LAST_UPDATE_FAILED,
TABLE_NAME_FEEDS + "." + KEY_AUTO_DELETE_ACTION,
+ TABLE_NAME_FEEDS + "." + KEY_FEED_VOLUME_ADAPTION,
TABLE_NAME_FEEDS + "." + KEY_INCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED
@@ -282,10 +284,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);
}
/**
@@ -403,6 +408,7 @@ public class PodDBAdapter {
values.put(KEY_AUTO_DOWNLOAD, prefs.getAutoDownload());
values.put(KEY_KEEP_UPDATED, prefs.getKeepUpdated());
values.put(KEY_AUTO_DELETE_ACTION, prefs.getAutoDeleteAction().ordinal());
+ values.put(KEY_FEED_VOLUME_ADAPTION, prefs.getVolumeAdaptionSetting().toInteger());
values.put(KEY_USERNAME, prefs.getUsername());
values.put(KEY_PASSWORD, prefs.getPassword());
values.put(KEY_INCLUDE_FILTER, prefs.getFilter().getIncludeFilter());
@@ -667,6 +673,7 @@ public class PodDBAdapter {
values.put(KEY_START, chapter.getStart());
values.put(KEY_FEEDITEM, item.getId());
values.put(KEY_LINK, chapter.getLink());
+ values.put(KEY_IMAGE_URL, chapter.getImageUrl());
values.put(KEY_CHAPTER_TYPE, chapter.getChapterType());
if (chapter.getId() == 0) {
chapter.setId(db.insert(TABLE_NAME_SIMPLECHAPTERS, null, values));
@@ -1271,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);
}
@@ -1333,11 +1348,6 @@ public class PodDBAdapter {
* Helper class for opening the Antennapod database.
*/
private static class PodDBHelper extends SQLiteOpenHelper {
-
- private static final int VERSION = 1070401;
-
- private final Context context;
-
/**
* Constructor.
*
@@ -1345,10 +1355,8 @@ 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());
- this.context = context;
}
@Override
@@ -1367,14 +1375,11 @@ public class PodDBAdapter {
db.execSQL(CREATE_INDEX_FEEDMEDIA_FEEDITEM);
db.execSQL(CREATE_INDEX_QUEUE_FEEDITEM);
db.execSQL(CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
-
}
@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/storage/StatisticsItem.java b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java
new file mode 100644
index 000000000..f96af185b
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java
@@ -0,0 +1,51 @@
+package de.danoeh.antennapod.core.storage;
+
+import de.danoeh.antennapod.core.feed.Feed;
+
+public class StatisticsItem {
+ public final Feed feed;
+ public final long time;
+
+ /**
+ * Respects speed, listening twice, ...
+ */
+ public final long timePlayed;
+
+ /**
+ * Simply sums up time of podcasts that are marked as played.
+ */
+ public final long timePlayedCountAll;
+
+ /**
+ * Number of episodes.
+ */
+ public final long episodes;
+
+ /**
+ * Episodes that are actually played.
+ */
+ public final long episodesStarted;
+
+ /**
+ * All episodes that are marked as played (or have position != 0).
+ */
+ public final long episodesStartedIncludingMarked;
+
+ /**
+ * Simply sums up the size of download podcasts.
+ */
+ public final long totalDownloadSize;
+
+ public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll,
+ long episodes, long episodesStarted, long episodesStartedIncludingMarked,
+ long totalDownloadSize) {
+ this.feed = feed;
+ this.time = time;
+ this.timePlayed = timePlayed;
+ this.timePlayedCountAll = timePlayedCountAll;
+ this.episodes = episodes;
+ this.episodesStarted = episodesStarted;
+ this.episodesStartedIncludingMarked = episodesStartedIncludingMarked;
+ this.totalDownloadSize = totalDownloadSize;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
index 45266569a..5761f37c8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
@@ -22,12 +22,12 @@ public class NSSimpleChapters extends Namespace {
private static final String START = "start";
private static final String TITLE = "title";
private static final String HREF = "href";
+ private static final String IMAGE = "image";
@Override
- public SyndElement handleElementStart(String localName, HandlerState state,
- Attributes attributes) {
+ public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) {
FeedItem currentItem = state.getCurrentItem();
- if(currentItem != null) {
+ if (currentItem != null) {
if (localName.equals(CHAPTERS)) {
currentItem.setChapters(new ArrayList<>());
} else if (localName.equals(CHAPTER)) {
@@ -35,7 +35,8 @@ public class NSSimpleChapters extends Namespace {
long start = DateUtils.parseTimeString(attributes.getValue(START));
String title = attributes.getValue(TITLE);
String link = attributes.getValue(HREF);
- SimpleChapter chapter = new SimpleChapter(start, title, currentItem, link);
+ String imageUrl = attributes.getValue(IMAGE);
+ SimpleChapter chapter = new SimpleChapter(start, title, link, imageUrl);
currentItem.getChapters().add(chapter);
} catch (NumberFormatException e) {
Log.e(TAG, "Unable to read chapter", e);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
index 300e0038a..b75887154 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.Log;
+import java.util.zip.CheckedOutputStream;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
@@ -23,6 +24,7 @@ import de.danoeh.antennapod.core.util.id3reader.ID3ReaderException;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentChapterReader;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
+import org.apache.commons.io.input.CountingInputStream;
/**
* Utility class for getting chapter data from media files.
@@ -34,24 +36,17 @@ public class ChapterUtils {
private ChapterUtils() {
}
- @Nullable
- public static Chapter getCurrentChapter(Playable media) {
- if (media.getChapters() == null) {
- return null;
+ public static int getCurrentChapterIndex(Playable media, int position) {
+ if (media == null || media.getChapters() == null || media.getChapters().size() == 0) {
+ return -1;
}
List<Chapter> chapters = media.getChapters();
- if (chapters == null) {
- return null;
- }
- Chapter current = chapters.get(0);
- for (Chapter sc : chapters) {
- if (sc.getStart() > media.getPosition()) {
- break;
- } else {
- current = sc;
+ for (int i = 0; i < chapters.size(); i++) {
+ if (chapters.get(i).getStart() > position) {
+ return i - 1;
}
}
- return current;
+ return chapters.size() - 1;
}
public static void loadChaptersFromStreamUrl(Playable media) {
@@ -82,13 +77,12 @@ public class ChapterUtils {
return;
}
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
- InputStream in = null;
+ CountingInputStream in = null;
try {
URL url = new URL(p.getStreamUrl());
-
- in = url.openStream();
+ in = new CountingInputStream(url.openStream());
List<Chapter> chapters = readChaptersFrom(in);
- if(!chapters.isEmpty()) {
+ if (!chapters.isEmpty()) {
p.setChapters(chapters);
}
Log.i(TAG, "Chapters loaded");
@@ -114,9 +108,9 @@ public class ChapterUtils {
return;
}
- InputStream in = null;
+ CountingInputStream in = null;
try {
- in = new BufferedInputStream(new FileInputStream(source));
+ in = new CountingInputStream(new BufferedInputStream(new FileInputStream(source)));
List<Chapter> chapters = readChaptersFrom(in);
if (!chapters.isEmpty()) {
p.setChapters(chapters);
@@ -130,7 +124,7 @@ public class ChapterUtils {
}
@NonNull
- private static List<Chapter> readChaptersFrom(InputStream in) throws IOException, ID3ReaderException {
+ private static List<Chapter> readChaptersFrom(CountingInputStream in) throws IOException, ID3ReaderException {
ChapterReader reader = new ChapterReader();
reader.readInputStream(in);
List<Chapter> chapters = reader.getChapters();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
new file mode 100644
index 000000000..deeba9238
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
@@ -0,0 +1,73 @@
+package de.danoeh.antennapod.core.util;
+
+import android.text.TextUtils;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EmbeddedChapterImage {
+ private static final Pattern EMBEDDED_IMAGE_MATCHER = Pattern.compile("embedded-image://(\\d+)/(\\d+)");
+
+ private final int position;
+ private final int length;
+ private final String imageUrl;
+ private final Playable media;
+
+ public EmbeddedChapterImage(Playable media, String imageUrl) {
+ this.media = media;
+ this.imageUrl = imageUrl;
+ Matcher m = EMBEDDED_IMAGE_MATCHER.matcher(imageUrl);
+ if (m.find()) {
+ this.position = Integer.parseInt(m.group(1));
+ this.length = Integer.parseInt(m.group(2));
+ } else {
+ throw new IllegalArgumentException("Not an embedded chapter");
+ }
+ }
+
+ public static String makeUrl(int position, int length) {
+ return "embedded-image://" + position + "/" + length;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public Playable getMedia() {
+ return media;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EmbeddedChapterImage that = (EmbeddedChapterImage) o;
+ return TextUtils.equals(imageUrl, that.imageUrl);
+ }
+
+ @Override
+ public int hashCode() {
+ return imageUrl.hashCode();
+ }
+
+ private static boolean isEmbeddedChapterImage(String imageUrl) {
+ return EMBEDDED_IMAGE_MATCHER.matcher(imageUrl).matches();
+ }
+
+ public static Object getModelFor(Playable media, int chapter) {
+ String imageUrl = media.getChapters().get(chapter).getImageUrl();
+ if (isEmbeddedChapterImage(imageUrl)) {
+ return new EmbeddedChapterImage(media, imageUrl);
+ } else {
+ return imageUrl;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
index 656b518bf..959a3e574 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
@@ -38,6 +38,7 @@ public class IntentUtils {
public static void openInBrowser(Context context, String url) {
try {
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
index fdc244517..742920702 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
@@ -154,7 +154,7 @@ public final class LongList {
growIfNeeded();
- System.arraycopy (values, n, values, n+1, size - n);
+ System.arraycopy(values, n, values, n+1, size - n);
values[n] = value;
size++;
}
@@ -211,7 +211,7 @@ public final class LongList {
throw new IndexOutOfBoundsException("n < 0");
}
size--;
- System.arraycopy (values, index + 1, values, index, size - index);
+ System.arraycopy(values, index + 1, values, index, size - index);
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
index 9408be348..ec8b8a430 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
@@ -26,36 +26,4 @@ public abstract class QueueAccess {
private QueueAccess() {
}
-
- public static QueueAccess ItemListAccess(final List<FeedItem> items) {
- return new QueueAccess() {
- @Override
- public boolean contains(long id) {
- if (items == null) {
- return false;
- }
- for (FeedItem item : items) {
- if (item.getId() == id) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean remove(long id) {
- Iterator<FeedItem> it = items.iterator();
- FeedItem item;
- while (it.hasNext()) {
- item = it.next();
- if (item.getId() == id) {
- it.remove();
- return true;
- }
- }
- return false;
- }
- };
- }
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
index eabaaa828..44b31f0be 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
@@ -4,6 +4,7 @@ import android.content.Context;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import android.util.TypedValue;
+import androidx.annotation.DrawableRes;
public class ThemeUtils {
private ThemeUtils() {
@@ -15,4 +16,10 @@ public class ThemeUtils {
context.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.data;
}
+
+ public static @DrawableRes int getDrawableFromAttr(Context context, @AttrRes int attr) {
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(attr, typedValue, true);
+ return typedValue.resourceId;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/TimeSpeedConverter.java b/core/src/main/java/de/danoeh/antennapod/core/util/TimeSpeedConverter.java
index 5d44c14b8..bbfe528be 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/TimeSpeedConverter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/TimeSpeedConverter.java
@@ -10,7 +10,7 @@ public class TimeSpeedConverter {
}
/** Convert millisecond according to the current playback speed
- * @param time: time to convert
+ * @param time time to convert
* @return converted time (can be < 0 if time is < 0)
*/
public int convert(int time) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
index 7e3870a20..dbdb37c3b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
@@ -1,10 +1,16 @@
package de.danoeh.antennapod.core.util;
import android.net.Uri;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
+import okhttp3.HttpUrl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Provides methods for checking and editing a URL.
@@ -78,4 +84,45 @@ public final class URLChecker {
return prepareURL(url);
}
}
+
+ public static boolean containsUrl(List<String> list, String url) {
+ for (String item : list) {
+ if (urlEquals(item, url)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean urlEquals(String string1, String string2) {
+ HttpUrl url1 = HttpUrl.parse(string1);
+ HttpUrl url2 = HttpUrl.parse(string2);
+ if (!url1.host().equals(url2.host())) {
+ return false;
+ }
+ List<String> pathSegments1 = normalizePathSegments(url1.pathSegments());
+ List<String> pathSegments2 = normalizePathSegments(url2.pathSegments());
+ if (!pathSegments1.equals(pathSegments2)) {
+ return false;
+ }
+ if (TextUtils.isEmpty(url1.query())) {
+ return TextUtils.isEmpty(url2.query());
+ }
+ return url1.query().equals(url2.query());
+ }
+
+ /**
+ * Removes empty segments and converts all to lower case.
+ * @param input List of path segments
+ * @return Normalized list of path segments
+ */
+ private static List<String> normalizePathSegments(List<String> input) {
+ List<String> result = new ArrayList<>();
+ for (String string : input) {
+ if (!TextUtils.isEmpty(string)) {
+ result.add(string.toLowerCase());
+ }
+ }
+ return result;
+ }
}
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/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index a3f747e09..ce3577a9e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -1,124 +1,154 @@
package de.danoeh.antennapod.core.util.id3reader;
+import android.text.TextUtils;
import android.util.Log;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.ID3Chapter;
+import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
+import org.apache.commons.io.input.CountingInputStream;
public class ChapterReader extends ID3Reader {
private static final String TAG = "ID3ChapterReader";
- private static final String FRAME_ID_CHAPTER = "CHAP";
- private static final String FRAME_ID_TITLE = "TIT2";
+ private static final String FRAME_ID_CHAPTER = "CHAP";
+ private static final String FRAME_ID_TITLE = "TIT2";
private static final String FRAME_ID_LINK = "WXXX";
+ private static final String FRAME_ID_PICTURE = "APIC";
+ private static final int IMAGE_TYPE_COVER = 3;
- private List<Chapter> chapters;
- private ID3Chapter currentChapter;
-
- @Override
- public int onStartTagHeader(TagHeader header) {
- chapters = new ArrayList<>();
- Log.d(TAG, "header: " + header);
- return ID3Reader.ACTION_DONT_SKIP;
- }
-
- @Override
- public int onStartFrameHeader(FrameHeader header, InputStream input)
- throws IOException, ID3ReaderException {
- Log.d(TAG, "header: " + header);
- switch (header.getId()) {
- case FRAME_ID_CHAPTER:
- if (currentChapter != null) {
- if (!hasId3Chapter(currentChapter)) {
- chapters.add(currentChapter);
- Log.d(TAG, "Found chapter: " + currentChapter);
- currentChapter = null;
- }
- }
- StringBuilder elementId = new StringBuilder();
- readISOString(elementId, input, Integer.MAX_VALUE);
- char[] startTimeSource = readBytes(input, 4);
- long startTime = ((int) startTimeSource[0] << 24)
- | ((int) startTimeSource[1] << 16)
- | ((int) startTimeSource[2] << 8) | startTimeSource[3];
- currentChapter = new ID3Chapter(elementId.toString(), startTime);
- skipBytes(input, 12);
- return ID3Reader.ACTION_DONT_SKIP;
- case FRAME_ID_TITLE:
- if (currentChapter != null && currentChapter.getTitle() == null) {
- StringBuilder title = new StringBuilder();
- readString(title, input, header.getSize());
- currentChapter
- .setTitle(title.toString());
- Log.d(TAG, "Found title: " + currentChapter.getTitle());
-
- return ID3Reader.ACTION_DONT_SKIP;
- }
- break;
- case FRAME_ID_LINK:
- if (currentChapter != null) {
- // skip description
- int descriptionLength = readString(null, input, header.getSize());
- StringBuilder link = new StringBuilder();
- readISOString(link, input, header.getSize() - descriptionLength);
- try {
- String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
- currentChapter.setLink(decodedLink);
- Log.d(TAG, "Found link: " + currentChapter.getLink());
- } catch (IllegalArgumentException iae) {
- Log.w(TAG, "Bad URL found in ID3 data");
- }
-
- return ID3Reader.ACTION_DONT_SKIP;
- }
- break;
- case "APIC":
- Log.d(TAG, header.toString());
- break;
- }
-
- return super.onStartFrameHeader(header, input);
- }
-
- private boolean hasId3Chapter(ID3Chapter chapter) {
- for (Chapter c : chapters) {
- if (((ID3Chapter) c).getId3ID().equals(chapter.getId3ID())) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onEndTag() {
- if (currentChapter != null) {
- if (!hasId3Chapter(currentChapter)) {
- chapters.add(currentChapter);
- }
- }
- Log.d(TAG, "Reached end of tag");
- if (chapters != null) {
- for (Chapter c : chapters) {
- Log.d(TAG, "chapter: " + c);
- }
- }
- }
-
- @Override
- public void onNoTagHeaderFound() {
- Log.d(TAG, "No tag header found");
- super.onNoTagHeaderFound();
- }
-
- public List<Chapter> getChapters() {
- return chapters;
- }
+ private List<Chapter> chapters;
+ private ID3Chapter currentChapter;
+
+ @Override
+ public int onStartTagHeader(TagHeader header) {
+ chapters = new ArrayList<>();
+ Log.d(TAG, "header: " + header);
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+
+ @Override
+ public int onStartFrameHeader(FrameHeader header, CountingInputStream input) throws IOException, ID3ReaderException {
+ Log.d(TAG, "header: " + header);
+ switch (header.getId()) {
+ case FRAME_ID_CHAPTER:
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ Log.d(TAG, "Found chapter: " + currentChapter);
+ currentChapter = null;
+ }
+ }
+ StringBuilder elementId = new StringBuilder();
+ readISOString(elementId, input, Integer.MAX_VALUE);
+ char[] startTimeSource = readChars(input, 4);
+ long startTime = ((int) startTimeSource[0] << 24)
+ | ((int) startTimeSource[1] << 16)
+ | ((int) startTimeSource[2] << 8) | startTimeSource[3];
+ currentChapter = new ID3Chapter(elementId.toString(), startTime);
+ skipBytes(input, 12);
+ return ID3Reader.ACTION_DONT_SKIP;
+ case FRAME_ID_TITLE:
+ if (currentChapter != null && currentChapter.getTitle() == null) {
+ StringBuilder title = new StringBuilder();
+ readString(title, input, header.getSize());
+ currentChapter
+ .setTitle(title.toString());
+ Log.d(TAG, "Found title: " + currentChapter.getTitle());
+
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ break;
+ case FRAME_ID_LINK:
+ if (currentChapter != null) {
+ // skip description
+ int descriptionLength = readString(null, input, header.getSize());
+ StringBuilder link = new StringBuilder();
+ readISOString(link, input, header.getSize() - descriptionLength);
+ try {
+ String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
+ currentChapter.setLink(decodedLink);
+ Log.d(TAG, "Found link: " + currentChapter.getLink());
+ } catch (IllegalArgumentException iae) {
+ Log.w(TAG, "Bad URL found in ID3 data");
+ }
+
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ break;
+ case FRAME_ID_PICTURE:
+ if (currentChapter != null) {
+ Log.d(TAG, header.toString());
+ StringBuilder mime = new StringBuilder();
+ int read = readString(mime, input, header.getSize());
+ byte type = (byte) readChars(input, 1)[0];
+ read++;
+ StringBuilder description = new StringBuilder();
+ read += readISOString(description, input, header.getSize()); // Should use same encoding as mime
+
+ Log.d(TAG, "Found apic: " + mime + "," + description);
+ if (mime.toString().equals("-->")) {
+ // Data contains a link to a picture
+ StringBuilder link = new StringBuilder();
+ readISOString(link, input, header.getSize());
+ Log.d(TAG, "link: " + link.toString());
+ if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
+ currentChapter.setImageUrl(link.toString());
+ }
+ } else {
+ // Data contains the picture
+ int length = header.getSize() - read;
+ if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
+ currentChapter.setImageUrl(EmbeddedChapterImage.makeUrl(input.getCount(), length));
+ }
+ skipBytes(input, length);
+ }
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ break;
+ }
+
+ return super.onStartFrameHeader(header, input);
+ }
+
+ private boolean hasId3Chapter(ID3Chapter chapter) {
+ for (Chapter c : chapters) {
+ if (((ID3Chapter) c).getId3ID().equals(chapter.getId3ID())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onEndTag() {
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ }
+ }
+ Log.d(TAG, "Reached end of tag");
+ if (chapters != null) {
+ for (Chapter c : chapters) {
+ Log.d(TAG, "chapter: " + c);
+ }
+ }
+ }
+
+ @Override
+ public void onNoTagHeaderFound() {
+ Log.d(TAG, "No tag header found");
+ super.onNoTagHeaderFound();
+ }
+
+ public List<Chapter> getChapters() {
+ return chapters;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java
index 3f5993700..dfc21a5f2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java
@@ -9,6 +9,7 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
+import org.apache.commons.io.input.CountingInputStream;
/**
* Reads the ID3 Tag of a given file. In order to use this class, you should
@@ -33,11 +34,10 @@ public class ID3Reader {
ID3Reader() {
}
- public final void readInputStream(InputStream input) throws IOException,
- ID3ReaderException {
+ public final void readInputStream(CountingInputStream input) throws IOException, ID3ReaderException {
int rc;
readerPosition = 0;
- char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
+ char[] tagHeaderSource = readChars(input, HEADER_LENGTH);
tagHeader = createTagHeader(tagHeaderSource);
if (tagHeader == null) {
onNoTagHeaderFound();
@@ -47,7 +47,7 @@ public class ID3Reader {
onEndTag();
} else {
while (readerPosition < tagHeader.getSize()) {
- FrameHeader frameHeader = createFrameHeader(readBytes(input, HEADER_LENGTH));
+ FrameHeader frameHeader = createFrameHeader(readChars(input, HEADER_LENGTH));
if (checkForNullString(frameHeader.getId())) {
break;
}
@@ -84,11 +84,10 @@ public class ID3Reader {
}
/**
- * Read a certain number of bytes from the given input stream. This method
+ * Read a certain number of chars from the given input stream. This method
* changes the readerPosition-attribute.
*/
- char[] readBytes(InputStream input, int number)
- throws IOException, ID3ReaderException {
+ char[] readChars(InputStream input, int number) throws IOException, ID3ReaderException {
char[] header = new char[number];
for (int i = 0; i < number; i++) {
int b = input.read();
@@ -168,7 +167,7 @@ public class ID3Reader {
protected int readString(StringBuilder buffer, InputStream input, int max) throws IOException,
ID3ReaderException {
if (max > 0) {
- char[] encoding = readBytes(input, 1);
+ char[] encoding = readChars(input, 1);
max--;
if (encoding[0] == ENCODING_UTF16_WITH_BOM || encoding[0] == ENCODING_UTF16_WITHOUT_BOM) {
@@ -230,8 +229,7 @@ public class ID3Reader {
return ACTION_SKIP;
}
- int onStartFrameHeader(FrameHeader header, InputStream input)
- throws IOException, ID3ReaderException {
+ int onStartFrameHeader(FrameHeader header, CountingInputStream input) throws IOException, ID3ReaderException {
return ACTION_SKIP;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index e7f6ad4f1..5cc2bbc64 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -21,6 +21,7 @@ import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
+import de.danoeh.antennapod.core.util.ThemeUtils;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import de.danoeh.antennapod.core.R;
@@ -397,12 +398,10 @@ public class PlaybackController {
final CharSequence playText = activity.getString(R.string.play_label);
final CharSequence pauseText = activity.getString(R.string.pause_label);
- if (PlaybackService.getCurrentMediaType() == MediaType.AUDIO ||
- PlaybackService.isCasting()) {
- TypedArray res = activity.obtainStyledAttributes(new int[]{
- R.attr.av_play_big, R.attr.av_pause_big});
- playResource = res.getResourceId(0, R.drawable.ic_play_arrow_grey600_36dp);
- pauseResource = res.getResourceId(1, R.drawable.ic_pause_grey600_36dp);
+ if (PlaybackService.getCurrentMediaType() == MediaType.AUDIO || PlaybackService.isCasting()) {
+ TypedArray res = activity.obtainStyledAttributes(new int[]{ R.attr.av_play, R.attr.av_pause});
+ playResource = res.getResourceId(0, R.drawable.ic_av_play_dark_48dp);
+ pauseResource = res.getResourceId(1, R.drawable.ic_av_pause_dark_48dp);
res.recycle();
} else {
playResource = R.drawable.ic_av_play_white_80dp;
@@ -547,7 +546,7 @@ public class PlaybackController {
*/
public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
if (playbackService != null && media != null) {
- playbackService.seekTo((int) (prog * media.getDuration()));
+ seekTo((int) (prog * getDuration()));
}
}
@@ -628,10 +627,6 @@ public class PlaybackController {
return playbackService != null && playbackService.sleepTimerActive();
}
- public boolean sleepTimerNotActive() {
- return playbackService != null && !playbackService.sleepTimerActive();
- }
-
public void disableSleepTimer() {
if (playbackService != null) {
playbackService.disableSleepTimer();
@@ -783,11 +778,8 @@ public class PlaybackController {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> {
if (media.getMediaType() == MediaType.AUDIO) {
- TypedArray res = activity.obtainStyledAttributes(new int[]{
- de.danoeh.antennapod.core.R.attr.av_play_big});
getPlayButton().setImageResource(
- res.getResourceId(0, de.danoeh.antennapod.core.R.drawable.ic_play_arrow_grey600_36dp));
- res.recycle();
+ ThemeUtils.getDrawableFromAttr(activity, de.danoeh.antennapod.core.R.attr.av_play));
} else {
getPlayButton().setImageResource(R.drawable.ic_av_play_white_80dp);
}
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 5b9452d6f..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,41 +56,31 @@ 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) + ")";
+ final String colorPrimary = "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) + ")";
- 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/>
* This method does NOT change the original shownotes string of the shownotesProvider object and it should
* also not be changed by the caller.
*
- * @param addTimecodes True if this method should add timecode links
* @return The processed HTML string.
*/
@NonNull
- public String processShownotes(final boolean addTimecodes) {
- final Playable playable = (shownotesProvider instanceof Playable) ? (Playable) shownotesProvider : null;
-
- // load shownotes
-
+ public String processShownotes() {
String shownotes;
try {
shownotes = shownotesProvider.loadShownotes().call();
@@ -125,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
@@ -148,17 +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
- if (addTimecodes) {
- addTimecodes(document, playable);
- }
-
+ addTimecodes(document);
return document.toString();
}
@@ -188,12 +133,7 @@ public class Timeline {
return -1;
}
-
- public void setShownotesProvider(@NonNull ShownotesProvider shownotesProvider) {
- this.shownotesProvider = shownotesProvider;
- }
-
- 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");
@@ -202,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) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
index 3550f28c6..84d98a905 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
@@ -57,7 +57,7 @@ public class HtmlToPlainText {
* Use this method to determine if a given text has any HTML tag
*
* @param str String to be tested for presence of HTML content
- * @return <b>True</b> if text contains any HTML tags</br><b>False</b> is no HTML tag is found
+ * @return <b>True</b> if text contains any HTML tags<br /><b>False</b> is no HTML tag is found
*/
private static boolean isHtml(String str) {
final String HTML_TAG_PATTERN = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
index 569ff3438..c4fe76780 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
@@ -10,93 +10,91 @@ import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
public class VorbisCommentChapterReader extends VorbisCommentReader {
- private static final String TAG = "VorbisCommentChptrReadr";
-
- private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*";
- private static final String CHAPTER_ATTRIBUTE_TITLE = "name";
- private static final String CHAPTER_ATTRIBUTE_LINK = "url";
-
- private List<Chapter> chapters;
-
- public VorbisCommentChapterReader() {
- }
-
- @Override
- public void onVorbisCommentFound() {
- System.out.println("Vorbis comment found");
- }
-
- @Override
- public void onVorbisCommentHeaderFound(VorbisCommentHeader header) {
- chapters = new ArrayList<>();
- System.out.println(header.toString());
- }
-
- @Override
- public boolean onContentVectorKey(String content) {
- return content.matches(CHAPTER_KEY);
- }
-
- @Override
- public void onContentVectorValue(String key, String value)
- throws VorbisCommentReaderException {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Key: " + key + ", value: " + value);
- String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key);
- int id = VorbisCommentChapter.getIDFromKey(key);
- Chapter chapter = getChapterById(id);
- if (attribute == null) {
- if (getChapterById(id) == null) {
- // new chapter
- long start = VorbisCommentChapter.getStartTimeFromValue(value);
- chapter = new VorbisCommentChapter(id);
- chapter.setStart(start);
- chapters.add(chapter);
- } else {
- throw new VorbisCommentReaderException(
- "Found chapter with duplicate ID (" + key + ", "
- + value + ")");
- }
- } else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) {
- if (chapter != null) {
- chapter.setTitle(value);
- }
- } else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) {
- if (chapter != null) {
- chapter.setLink(value);
- }
- }
- }
-
- @Override
- public void onNoVorbisCommentFound() {
- System.out.println("No vorbis comment found");
- }
-
- @Override
- public void onEndOfComment() {
- System.out.println("End of comment");
- for (Chapter c : chapters) {
- System.out.println(c.toString());
- }
- }
-
- @Override
- public void onError(VorbisCommentReaderException exception) {
- exception.printStackTrace();
- }
-
- private Chapter getChapterById(long id) {
- for (Chapter c : chapters) {
- if (((VorbisCommentChapter) c).getVorbisCommentId() == id) {
- return c;
- }
- }
- return null;
- }
-
- public List<Chapter> getChapters() {
- return chapters;
- }
+ private static final String TAG = "VorbisCommentChptrReadr";
+
+ private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*";
+ private static final String CHAPTER_ATTRIBUTE_TITLE = "name";
+ private static final String CHAPTER_ATTRIBUTE_LINK = "url";
+
+ private List<Chapter> chapters;
+
+ public VorbisCommentChapterReader() {
+ }
+
+ @Override
+ public void onVorbisCommentFound() {
+ System.out.println("Vorbis comment found");
+ }
+
+ @Override
+ public void onVorbisCommentHeaderFound(VorbisCommentHeader header) {
+ chapters = new ArrayList<>();
+ System.out.println(header.toString());
+ }
+
+ @Override
+ public boolean onContentVectorKey(String content) {
+ return content.matches(CHAPTER_KEY);
+ }
+
+ @Override
+ public void onContentVectorValue(String key, String value) throws VorbisCommentReaderException {
+ if (BuildConfig.DEBUG) {
+ Log.d(TAG, "Key: " + key + ", value: " + value);
+ }
+ String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key);
+ int id = VorbisCommentChapter.getIDFromKey(key);
+ Chapter chapter = getChapterById(id);
+ if (attribute == null) {
+ if (getChapterById(id) == null) {
+ // new chapter
+ long start = VorbisCommentChapter.getStartTimeFromValue(value);
+ chapter = new VorbisCommentChapter(id);
+ chapter.setStart(start);
+ chapters.add(chapter);
+ } else {
+ throw new VorbisCommentReaderException("Found chapter with duplicate ID (" + key + ", " + value + ")");
+ }
+ } else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) {
+ if (chapter != null) {
+ chapter.setTitle(value);
+ }
+ } else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) {
+ if (chapter != null) {
+ chapter.setLink(value);
+ }
+ }
+ }
+
+ @Override
+ public void onNoVorbisCommentFound() {
+ System.out.println("No vorbis comment found");
+ }
+
+ @Override
+ public void onEndOfComment() {
+ System.out.println("End of comment");
+ for (Chapter c : chapters) {
+ System.out.println(c.toString());
+ }
+ }
+
+ @Override
+ public void onError(VorbisCommentReaderException exception) {
+ exception.printStackTrace();
+ }
+
+ private Chapter getChapterById(long id) {
+ for (Chapter c : chapters) {
+ if (((VorbisCommentChapter) c).getVorbisCommentId() == id) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public List<Chapter> getChapters() {
+ return chapters;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
index 55498afcb..e910e2be4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.util.vorbiscommentreader;
+import androidx.annotation.NonNull;
import org.apache.commons.io.EndianUtils;
import org.apache.commons.io.IOUtils;
@@ -8,187 +9,158 @@ import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import java.util.Arrays;
-
public abstract class VorbisCommentReader {
- /** Length of first page in an ogg file in bytes. */
- private static final int FIRST_PAGE_LENGTH = 58;
- private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
- private static final int PACKET_TYPE_IDENTIFICATION = 1;
- private static final int PACKET_TYPE_COMMENT = 3;
-
- /** Called when Reader finds identification header. */
- protected abstract void onVorbisCommentFound();
-
- protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
-
- /**
- * Is called every time the Reader finds a content vector. The handler
- * should return true if it wants to handle the content vector.
- */
- protected abstract boolean onContentVectorKey(String content);
-
- /**
- * Is called if onContentVectorKey returned true for the key.
- *
- * @throws VorbisCommentReaderException
- */
- protected abstract void onContentVectorValue(String key, String value)
- throws VorbisCommentReaderException;
-
- protected abstract void onNoVorbisCommentFound();
-
- protected abstract void onEndOfComment();
-
- protected abstract void onError(VorbisCommentReaderException exception);
-
- public void readInputStream(InputStream input)
- throws VorbisCommentReaderException {
- try {
- // look for identification header
- if (findIdentificationHeader(input)) {
-
- onVorbisCommentFound();
- input = new OggInputStream(input);
- if (findCommentHeader(input)) {
- VorbisCommentHeader commentHeader = readCommentHeader(input);
- if (commentHeader != null) {
- onVorbisCommentHeaderFound(commentHeader);
- for (int i = 0; i < commentHeader
- .getUserCommentLength(); i++) {
- try {
- long vectorLength = EndianUtils
- .readSwappedUnsignedInteger(input);
- String key = readContentVectorKey(input,
- vectorLength).toLowerCase();
- boolean readValue = onContentVectorKey(key);
- if (readValue) {
- String value = readUTF8String(
- input,
- (int) (vectorLength - key.length() - 1));
- onContentVectorValue(key, value);
- } else {
- IOUtils.skipFully(input,
- vectorLength - key.length() - 1);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- onEndOfComment();
- }
-
- } else {
- onError(new VorbisCommentReaderException(
- "No comment header found"));
- }
- } else {
- onNoVorbisCommentFound();
- }
- } catch (IOException e) {
- onError(new VorbisCommentReaderException(e));
- }
- }
-
- private String readUTF8String(InputStream input, long length)
- throws IOException {
- byte[] buffer = new byte[(int) length];
-
- IOUtils.readFully(input, buffer);
- Charset charset = Charset.forName("UTF-8");
- return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
- }
-
- /**
- * Looks for an identification header in the first page of the file. If an
- * identification header is found, it will be skipped completely and the
- * method will return true, otherwise false.
- *
- * @throws IOException
- */
- private boolean findIdentificationHeader(InputStream input)
- throws IOException {
- byte[] buffer = new byte[FIRST_PAGE_LENGTH];
- IOUtils.readFully(input, buffer);
- int i;
- for (i = 6; i < buffer.length; i++) {
- if (buffer[i - 5] == 'v' && buffer[i - 4] == 'o'
- && buffer[i - 3] == 'r' && buffer[i - 2] == 'b'
- && buffer[i - 1] == 'i' && buffer[i] == 's'
- && buffer[i - 6] == PACKET_TYPE_IDENTIFICATION) {
- return true;
- }
- }
- return false;
- }
-
- private boolean findCommentHeader(InputStream input) throws IOException {
- char[] buffer = new char["vorbis".length() + 1];
- for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
- char c = (char) input.read();
- int dest = -1;
- switch (c) {
- case PACKET_TYPE_COMMENT:
- dest = 0;
- break;
- case 'v':
- dest = 1;
- break;
- case 'o':
- dest = 2;
- break;
- case 'r':
- dest = 3;
- break;
- case 'b':
- dest = 4;
- break;
- case 'i':
- dest = 5;
- break;
- case 's':
- dest = 6;
- break;
- }
- if (dest >= 0) {
- buffer[dest] = c;
- if (buffer[1] == 'v' && buffer[2] == 'o' && buffer[3] == 'r'
- && buffer[4] == 'b' && buffer[5] == 'i'
- && buffer[6] == 's' && buffer[0] == PACKET_TYPE_COMMENT) {
- return true;
- }
- } else {
- Arrays.fill(buffer, (char) 0);
- }
- }
- return false;
- }
-
- private VorbisCommentHeader readCommentHeader(InputStream input)
- throws IOException, VorbisCommentReaderException {
- try {
- long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
- String vendorName = readUTF8String(input, vendorLength);
- long userCommentLength = EndianUtils
- .readSwappedUnsignedInteger(input);
- return new VorbisCommentHeader(vendorName, userCommentLength);
- } catch (UnsupportedEncodingException e) {
- throw new VorbisCommentReaderException(e);
- }
- }
-
- private String readContentVectorKey(InputStream input, long vectorLength)
- throws IOException {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < vectorLength; i++) {
- char c = (char) input.read();
- if (c == '=') {
- return builder.toString();
- } else {
- builder.append(c);
- }
- }
- return null; // no key found
- }
+ /** Length of first page in an ogg file in bytes. */
+ private static final int FIRST_OGG_PAGE_LENGTH = 58;
+ private static final int FIRST_OPUS_PAGE_LENGTH = 47;
+ private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
+ private static final int PACKET_TYPE_IDENTIFICATION = 1;
+ private static final int PACKET_TYPE_COMMENT = 3;
+
+ /** Called when Reader finds identification header. */
+ protected abstract void onVorbisCommentFound();
+
+ protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
+
+ /**
+ * Is called every time the Reader finds a content vector. The handler
+ * should return true if it wants to handle the content vector.
+ */
+ protected abstract boolean onContentVectorKey(String content);
+
+ /**
+ * Is called if onContentVectorKey returned true for the key.
+ */
+ protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException;
+
+ protected abstract void onNoVorbisCommentFound();
+
+ protected abstract void onEndOfComment();
+
+ protected abstract void onError(VorbisCommentReaderException exception);
+
+ public void readInputStream(InputStream input) throws VorbisCommentReaderException {
+ try {
+ // look for identification header
+ if (findIdentificationHeader(input)) {
+ onVorbisCommentFound();
+ input = new OggInputStream(input);
+ if (findCommentHeader(input)) {
+ VorbisCommentHeader commentHeader = readCommentHeader(input);
+ onVorbisCommentHeaderFound(commentHeader);
+ for (int i = 0; i < commentHeader.getUserCommentLength(); i++) {
+ readUserComment(input);
+ }
+ onEndOfComment();
+ } else {
+ onError(new VorbisCommentReaderException("No comment header found"));
+ }
+ } else {
+ onNoVorbisCommentFound();
+ }
+ } catch (IOException e) {
+ onError(new VorbisCommentReaderException(e));
+ }
+ }
+
+ private void readUserComment(InputStream input) throws VorbisCommentReaderException {
+ try {
+ long vectorLength = EndianUtils.readSwappedUnsignedInteger(input);
+ String key = readContentVectorKey(input, vectorLength).toLowerCase();
+ boolean readValue = onContentVectorKey(key);
+ if (readValue) {
+ String value = readUtf8String(input, (int) (vectorLength - key.length() - 1));
+ onContentVectorValue(key, value);
+ } else {
+ IOUtils.skipFully(input, vectorLength - key.length() - 1);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private String readUtf8String(InputStream input, long length) throws IOException {
+ byte[] buffer = new byte[(int) length];
+ IOUtils.readFully(input, buffer);
+ Charset charset = Charset.forName("UTF-8");
+ return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
+ }
+
+ /**
+ * Looks for an identification header in the first page of the file. If an
+ * identification header is found, it will be skipped completely and the
+ * method will return true, otherwise false.
+ */
+ private boolean findIdentificationHeader(InputStream input) throws IOException {
+ byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH];
+ IOUtils.readFully(input, buffer);
+ final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' };
+ for (int i = 6; i < buffer.length; i++) {
+ if (bufferMatches(buffer, oggIdentificationHeader, i)) {
+ IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH);
+ return true;
+ } else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean findCommentHeader(InputStream input) throws IOException {
+ byte[] buffer = new byte[64]; // Enough space for some bytes. Used circularly.
+ final byte[] oggCommentHeader = new byte[]{ PACKET_TYPE_COMMENT, 'v', 'o', 'r', 'b', 'i', 's' };
+ for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
+ buffer[bytesRead % buffer.length] = (byte) input.read();
+ if (bufferMatches(buffer, oggCommentHeader, bytesRead)) {
+ return true;
+ } else if (bufferMatches(buffer, "OpusTags".getBytes(), bytesRead)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reads backwards in haystack, starting at position. Checks if the bytes match needle.
+ * Uses haystack circularly, so when reading at (-1), it reads at (length - 1).
+ */
+ boolean bufferMatches(byte[] haystack, byte[] needle, int position) {
+ for (int i = 0; i < needle.length; i++) {
+ int posInHaystack = position - i;
+ while (posInHaystack < 0) {
+ posInHaystack += haystack.length;
+ }
+ posInHaystack = posInHaystack % haystack.length;
+ if (haystack[posInHaystack] != needle[needle.length - 1 - i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @NonNull
+ private VorbisCommentHeader readCommentHeader(InputStream input) throws IOException, VorbisCommentReaderException {
+ try {
+ long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
+ String vendorName = readUtf8String(input, vendorLength);
+ long userCommentLength = EndianUtils.readSwappedUnsignedInteger(input);
+ return new VorbisCommentHeader(vendorName, userCommentLength);
+ } catch (UnsupportedEncodingException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ }
+
+ private String readContentVectorKey(InputStream input, long vectorLength) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < vectorLength; i++) {
+ char c = (char) input.read();
+ if (c == '=') {
+ return builder.toString();
+ } else {
+ builder.append(c);
+ }
+ }
+ return null; // no key found
+ }
}
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_24dp.png
deleted file mode 100644
index df9e662c1..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_36dp.png
deleted file mode 100644
index 59ae40266..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_forward_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_forward_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_fast_forward_white_24dp.png
deleted file mode 100644
index 2d61b31f1..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_forward_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.png
deleted file mode 100644
index e5ae251d3..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_24dp.png
deleted file mode 100644
index f661ca723..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_36dp.png
deleted file mode 100644
index d36891a8d..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_rewind_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_24dp.png
deleted file mode 100644
index ab8b48ec3..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_36dp.png
deleted file mode 100644
index 75796d7a9..000000000
--- a/core/src/main/res/drawable-hdpi/ic_fast_rewind_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_hearing_grey600_18dp.png b/core/src/main/res/drawable-hdpi/ic_hearing_grey600_18dp.png
deleted file mode 100644
index 2452cfa92..000000000
--- a/core/src/main/res/drawable-hdpi/ic_hearing_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_hearing_white_18dp.png b/core/src/main/res/drawable-hdpi/ic_hearing_white_18dp.png
deleted file mode 100644
index 96a06141a..000000000
--- a/core/src/main/res/drawable-hdpi/ic_hearing_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_new_releases_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_new_releases_grey600_24dp.png
deleted file mode 100644
index 2aa5de2ad..000000000
--- a/core/src/main/res/drawable-hdpi/ic_new_releases_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_new_releases_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_new_releases_white_24dp.png
deleted file mode 100644
index 8f109572b..000000000
--- a/core/src/main/res/drawable-hdpi/ic_new_releases_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_pause_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_pause_grey600_24dp.png
deleted file mode 100644
index 7281f37e1..000000000
--- a/core/src/main/res/drawable-hdpi/ic_pause_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_pause_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_pause_grey600_36dp.png
deleted file mode 100644
index dde9bb25c..000000000
--- a/core/src/main/res/drawable-hdpi/ic_pause_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_pause_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
deleted file mode 100644
index 1701f34b0..000000000
--- a/core/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_pause_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_pause_white_36dp.png
deleted file mode 100644
index 1d024393a..000000000
--- a/core/src/main/res/drawable-hdpi/ic_pause_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_24dp.png
deleted file mode 100644
index b540e4a63..000000000
--- a/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_36dp.png
deleted file mode 100644
index a12b921e4..000000000
--- a/core/src/main/res/drawable-hdpi/ic_play_arrow_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
deleted file mode 100644
index f77ad6b57..000000000
--- a/core/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_play_arrow_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_play_arrow_white_36dp.png
deleted file mode 100644
index 2b8e3513f..000000000
--- a/core/src/main/res/drawable-hdpi/ic_play_arrow_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_remove_red_eye_grey600_18dp.png b/core/src/main/res/drawable-hdpi/ic_remove_red_eye_grey600_18dp.png
deleted file mode 100644
index 4f1af39ab..000000000
--- a/core/src/main/res/drawable-hdpi/ic_remove_red_eye_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_remove_red_eye_white_18dp.png b/core/src/main/res/drawable-hdpi/ic_remove_red_eye_white_18dp.png
deleted file mode 100644
index abc338d51..000000000
--- a/core/src/main/res/drawable-hdpi/ic_remove_red_eye_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_skip_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_skip_grey600_36dp.png
deleted file mode 100644
index 6e1dffc93..000000000
--- a/core/src/main/res/drawable-hdpi/ic_skip_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_skip_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_skip_white_36dp.png
deleted file mode 100644
index 760ec9987..000000000
--- a/core/src/main/res/drawable-hdpi/ic_skip_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_24dp.png
deleted file mode 100644
index c67fc25f1..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_36dp.png
deleted file mode 100644
index df9e662c1..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_forward_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_forward_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_fast_forward_white_24dp.png
deleted file mode 100644
index fceffcb7b..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_forward_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.png
deleted file mode 100644
index 2d61b31f1..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_24dp.png
deleted file mode 100644
index de04575da..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_36dp.png
deleted file mode 100644
index f661ca723..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_rewind_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_24dp.png
deleted file mode 100644
index bfb476b4a..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_36dp.png
deleted file mode 100644
index ab8b48ec3..000000000
--- a/core/src/main/res/drawable-mdpi/ic_fast_rewind_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_hearing_grey600_18dp.png b/core/src/main/res/drawable-mdpi/ic_hearing_grey600_18dp.png
deleted file mode 100644
index ea44e3f07..000000000
--- a/core/src/main/res/drawable-mdpi/ic_hearing_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_hearing_white_18dp.png b/core/src/main/res/drawable-mdpi/ic_hearing_white_18dp.png
deleted file mode 100644
index 78e98fe8d..000000000
--- a/core/src/main/res/drawable-mdpi/ic_hearing_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_new_releases_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_new_releases_grey600_24dp.png
deleted file mode 100644
index 4b3ffdbdc..000000000
--- a/core/src/main/res/drawable-mdpi/ic_new_releases_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_new_releases_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_new_releases_white_24dp.png
deleted file mode 100644
index c5bb20f49..000000000
--- a/core/src/main/res/drawable-mdpi/ic_new_releases_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_pause_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_pause_grey600_24dp.png
deleted file mode 100644
index 126ee03ef..000000000
--- a/core/src/main/res/drawable-mdpi/ic_pause_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_pause_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_pause_grey600_36dp.png
deleted file mode 100644
index 7281f37e1..000000000
--- a/core/src/main/res/drawable-mdpi/ic_pause_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_pause_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_pause_white_24dp.png
deleted file mode 100644
index 2272d478c..000000000
--- a/core/src/main/res/drawable-mdpi/ic_pause_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_pause_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_pause_white_36dp.png
deleted file mode 100644
index 1701f34b0..000000000
--- a/core/src/main/res/drawable-mdpi/ic_pause_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_24dp.png
deleted file mode 100644
index 9c8f2c555..000000000
--- a/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_36dp.png
deleted file mode 100644
index b540e4a63..000000000
--- a/core/src/main/res/drawable-mdpi/ic_play_arrow_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
deleted file mode 100644
index 172c211ab..000000000
--- a/core/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_play_arrow_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_play_arrow_white_36dp.png
deleted file mode 100644
index f77ad6b57..000000000
--- a/core/src/main/res/drawable-mdpi/ic_play_arrow_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_remove_red_eye_grey600_18dp.png b/core/src/main/res/drawable-mdpi/ic_remove_red_eye_grey600_18dp.png
deleted file mode 100644
index daa8e568e..000000000
--- a/core/src/main/res/drawable-mdpi/ic_remove_red_eye_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_remove_red_eye_white_18dp.png b/core/src/main/res/drawable-mdpi/ic_remove_red_eye_white_18dp.png
deleted file mode 100644
index a6b4ff0da..000000000
--- a/core/src/main/res/drawable-mdpi/ic_remove_red_eye_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_skip_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_skip_grey600_36dp.png
deleted file mode 100644
index 229eeca47..000000000
--- a/core/src/main/res/drawable-mdpi/ic_skip_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_skip_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_skip_white_36dp.png
deleted file mode 100644
index 9032328d4..000000000
--- a/core/src/main/res/drawable-mdpi/ic_skip_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_24dp.png
deleted file mode 100644
index 6f8b42221..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_36dp.png
deleted file mode 100644
index 521f7490b..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_forward_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_24dp.png
deleted file mode 100644
index 2b34fb82d..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.png
deleted file mode 100644
index 2d9a7e3c9..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_24dp.png
deleted file mode 100644
index 9468020b7..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_36dp.png
deleted file mode 100644
index ea5493251..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_24dp.png
deleted file mode 100644
index f4182f174..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png
deleted file mode 100644
index de9ec1d90..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_hearing_grey600_18dp.png b/core/src/main/res/drawable-xhdpi/ic_hearing_grey600_18dp.png
deleted file mode 100644
index d014684e1..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_hearing_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_hearing_white_18dp.png b/core/src/main/res/drawable-xhdpi/ic_hearing_white_18dp.png
deleted file mode 100644
index 91adb4437..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_hearing_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_new_releases_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_new_releases_grey600_24dp.png
deleted file mode 100644
index a2bb4d1b8..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_new_releases_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_new_releases_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_new_releases_white_24dp.png
deleted file mode 100644
index e78b002f2..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_new_releases_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_pause_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_pause_grey600_24dp.png
deleted file mode 100644
index 6708b4161..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_pause_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_pause_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_pause_grey600_36dp.png
deleted file mode 100644
index aeb13ebc4..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_pause_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
deleted file mode 100644
index f49aed757..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png
deleted file mode 100644
index 7192ad487..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_24dp.png
deleted file mode 100644
index 6874b8236..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_36dp.png
deleted file mode 100644
index dabd252ee..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_play_arrow_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
deleted file mode 100644
index 5b0110454..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png
deleted file mode 100644
index fff3e1f56..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_grey600_18dp.png b/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_grey600_18dp.png
deleted file mode 100644
index 2039d9ce8..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_white_18dp.png b/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_white_18dp.png
deleted file mode 100644
index 6dd240bca..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_remove_red_eye_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_skip_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_skip_grey600_36dp.png
deleted file mode 100644
index 31aa09ca2..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_skip_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_skip_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_skip_white_36dp.png
deleted file mode 100644
index e664f607c..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_skip_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_24dp.png
deleted file mode 100644
index 521f7490b..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_36dp.png
deleted file mode 100644
index 644645c8b..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_24dp.png
deleted file mode 100644
index 2d9a7e3c9..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png
deleted file mode 100644
index 76c94c3ba..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_24dp.png
deleted file mode 100644
index ea5493251..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_36dp.png
deleted file mode 100644
index 831fda2ab..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_24dp.png
deleted file mode 100644
index de9ec1d90..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png
deleted file mode 100644
index 8e11ac89e..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_hearing_grey600_18dp.png b/core/src/main/res/drawable-xxhdpi/ic_hearing_grey600_18dp.png
deleted file mode 100644
index 19456de04..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_hearing_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_hearing_white_18dp.png b/core/src/main/res/drawable-xxhdpi/ic_hearing_white_18dp.png
deleted file mode 100644
index 82de8bb65..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_hearing_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_new_releases_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_new_releases_grey600_24dp.png
deleted file mode 100644
index 79507d800..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_new_releases_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_new_releases_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_new_releases_white_24dp.png
deleted file mode 100644
index a4dbba8ae..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_new_releases_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_24dp.png
deleted file mode 100644
index aeb13ebc4..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_36dp.png
deleted file mode 100644
index 8753d9c50..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_pause_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
deleted file mode 100644
index 7192ad487..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.png
deleted file mode 100644
index fb63ddc5a..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_24dp.png
deleted file mode 100644
index dabd252ee..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_36dp.png
deleted file mode 100644
index 9fcf99558..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
deleted file mode 100644
index fff3e1f56..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png
deleted file mode 100644
index 9b31e2d19..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_grey600_18dp.png b/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_grey600_18dp.png
deleted file mode 100644
index 16cdc31c1..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_grey600_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_white_18dp.png b/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_white_18dp.png
deleted file mode 100644
index 695eb950e..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_remove_red_eye_white_18dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_skip_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_skip_grey600_36dp.png
deleted file mode 100644
index 75a4a9545..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_skip_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_skip_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_skip_white_36dp.png
deleted file mode 100644
index a31299c81..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_skip_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_skip_grey600_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_skip_grey600_36dp.png
deleted file mode 100644
index b599c2207..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_skip_grey600_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_skip_white_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_skip_white_36dp.png
deleted file mode 100644
index a0dd670c3..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_skip_white_36dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable/ic_av_fast_forward_dark_48dp.xml b/core/src/main/res/drawable/ic_av_fast_forward_dark_48dp.xml
new file mode 100644
index 000000000..cf0faa33e
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_fast_forward_dark_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml b/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml
new file mode 100644
index 000000000..aca5bcf29
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_rewind_dark_48dp.xml b/core/src/main/res/drawable/ic_av_fast_rewind_dark_48dp.xml
new file mode 100644
index 000000000..47d1189cb
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_fast_rewind_dark_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml b/core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml
new file mode 100644
index 000000000..ba2b8ae4f
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_pause_dark_48dp.xml b/core/src/main/res/drawable/ic_av_pause_dark_48dp.xml
new file mode 100644
index 000000000..61fdd4b5b
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_pause_dark_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_pause_white_48dp.xml b/core/src/main/res/drawable/ic_av_pause_white_48dp.xml
new file mode 100644
index 000000000..3512563ec
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_pause_white_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_dark_24dp.xml b/core/src/main/res/drawable/ic_av_play_dark_24dp.xml
new file mode 100644
index 000000000..5bc12c0e1
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_play_dark_24dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_dark_48dp.xml b/core/src/main/res/drawable/ic_av_play_dark_48dp.xml
new file mode 100644
index 000000000..dbe5e7104
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_play_dark_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_white_24dp.xml b/core/src/main/res/drawable/ic_av_play_white_24dp.xml
new file mode 100644
index 000000000..0e896a8d4
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_play_white_24dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_white_48dp.xml b/core/src/main/res/drawable/ic_av_play_white_48dp.xml
new file mode 100644
index 000000000..bf94a000b
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_play_white_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_dark_48dp.xml b/core/src/main/res/drawable/ic_av_skip_dark_48dp.xml
new file mode 100644
index 000000000..2552f8d73
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_skip_dark_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_white_48dp.xml b/core/src/main/res/drawable/ic_av_skip_white_48dp.xml
new file mode 100644
index 000000000..7e0073e88
--- /dev/null
+++ b/core/src/main/res/drawable/ic_av_skip_white_48dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_cast_off.xml b/core/src/main/res/drawable/ic_notification_cast_off.xml
index 63a21fbe2..3e3accd0b 100644
--- a/core/src/main/res/drawable/ic_notification_cast_off.xml
+++ b/core/src/main/res/drawable/ic_notification_cast_off.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M1.6,1.27L0.25,2.75L1.41,3.8C1.16,4.13 1,4.55 1,5V8H3V5.23L18.2,19H14V21H20.41L22.31,22.72L23.65,21.24M6.5,3L8.7,5H21V16.14L23,17.95V5C23,3.89 22.1,3 21,3M1,10V12A9,9 0 0,1 10,21H12C12,14.92 7.08,10 1,10M1,14V16A5,5 0 0,1 6,21H8A7,7 0 0,0 1,14M1,18V21H4A3,3 0 0,0 1,18Z" />
</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_notification_fast_forward.xml b/core/src/main/res/drawable/ic_notification_fast_forward.xml
index bf564977c..8ee82f4ed 100644
--- a/core/src/main/res/drawable/ic_notification_fast_forward.xml
+++ b/core/src/main/res/drawable/ic_notification_fast_forward.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_notification_fast_rewind.xml b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
index 847159cc5..261ed7e6f 100644
--- a/core/src/main/res/drawable/ic_notification_fast_rewind.xml
+++ b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_notification_pause.xml b/core/src/main/res/drawable/ic_notification_pause.xml
index d46efb2f5..16ebd4eab 100644
--- a/core/src/main/res/drawable/ic_notification_pause.xml
+++ b/core/src/main/res/drawable/ic_notification_pause.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_notification_play.xml b/core/src/main/res/drawable/ic_notification_play.xml
index d571460c6..eb4acd983 100644
--- a/core/src/main/res/drawable/ic_notification_play.xml
+++ b/core/src/main/res/drawable/ic_notification_play.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_notification_skip.xml b/core/src/main/res/drawable/ic_notification_skip.xml
index 0c65448cc..6bf03002a 100644
--- a/core/src/main/res/drawable/ic_notification_skip.xml
+++ b/core/src/main/res/drawable/ic_notification_skip.xml
@@ -1,5 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
+ android:height="30dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="30dp">
<path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_videocam_grey600_24dp.xml b/core/src/main/res/drawable/ic_videocam_grey600_24dp.xml
new file mode 100644
index 000000000..40c893883
--- /dev/null
+++ b/core/src/main/res/drawable/ic_videocam_grey600_24dp.xml
@@ -0,0 +1,8 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FF757575"
+ android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_videocam_white_24dp.xml b/core/src/main/res/drawable/ic_videocam_white_24dp.xml
new file mode 100644
index 000000000..a8cfd71e3
--- /dev/null
+++ b/core/src/main/res/drawable/ic_videocam_white_24dp.xml
@@ -0,0 +1,8 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_volume_adaption_grey.xml b/core/src/main/res/drawable/ic_volume_adaption_grey.xml
new file mode 100644
index 000000000..fe39e1c70
--- /dev/null
+++ b/core/src/main/res/drawable/ic_volume_adaption_grey.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF757575"
+ android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_volume_adaption_white.xml b/core/src/main/res/drawable/ic_volume_adaption_white.xml
new file mode 100644
index 000000000..27d7c6e7b
--- /dev/null
+++ b/core/src/main/res/drawable/ic_volume_adaption_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/overlay_drawable.xml b/core/src/main/res/drawable/overlay_drawable.xml
deleted file mode 100644
index 185ffefc1..000000000
--- a/core/src/main/res/drawable/overlay_drawable.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item>
- <shape android:shape="rectangle" >
- <solid android:color="#F0BABABA" />
- </shape>
- </item>
- <item android:top="1dp">
- <shape android:shape="rectangle" >
- <solid android:color="#D2D2D2" />
- </shape>
- </item>
- <item android:top="2dp">
- <shape android:shape="rectangle" >
- <solid android:color="@color/overlay_light" />
- </shape>
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/core/src/main/res/drawable/overlay_drawable_dark.xml b/core/src/main/res/drawable/overlay_drawable_dark.xml
deleted file mode 100644
index fb78f5633..000000000
--- a/core/src/main/res/drawable/overlay_drawable_dark.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item>
- <shape android:shape="rectangle" >
- <solid android:color="#45B3E1" />
- </shape>
- </item>
- <item android:top="1dp">
- <shape android:shape="rectangle" >
- <solid android:color="@color/overlay_dark" />
- </shape>
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml b/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml
deleted file mode 100644
index 5f58e8421..000000000
--- a/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item>
- <shape android:shape="rectangle" >
- <solid android:color="#45B3E1" />
- </shape>
- </item>
- <item android:top="1dp">
- <shape android:shape="rectangle" >
- <solid android:color="@color/black" />
- </shape>
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/core/src/main/res/layout/player_widget.xml b/core/src/main/res/layout/player_widget.xml
index 5f49fb7ef..b0e5e0fd8 100644
--- a/core/src/main/res/layout/player_widget.xml
+++ b/core/src/main/res/layout/player_widget.xml
@@ -19,8 +19,10 @@
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_margin="12dp"
- android:background="@android:color/transparent"
- android:src="@drawable/ic_play_arrow_white_24dp" />
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="fitCenter"
+ android:padding="8dp"
+ android:src="@drawable/ic_av_play_white_24dp" />
<LinearLayout
android:id="@+id/layout_left"
diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml
index aeb5738b2..4d16c469e 100644
--- a/core/src/main/res/values-ca/strings.xml
+++ b/core/src/main/res/values-ca/strings.xml
@@ -91,11 +91,14 @@
<item quantity="one">1 dia després d\'acabar</item>
<item quantity="other">%d dies després d\'acabar</item>
</plurals>
+ <string name="num_selected_label">%d sel·leccionat</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Enllaç del canal</string>
<string name="etxtFeedurlHint">URL, canal o lloc web</string>
<string name="txtvfeedurl_label">Afegeix podcast amb l\'URL</string>
<string name="browse_gpoddernet_label">Navega gpodder.net</string>
+ <string name="discover">Descobreix</string>
+ <string name="discover_more">més »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marca-ho tot com a llegit</string>
<string name="mark_all_read_msg">S\'han marcat tots els episodis com a llegits</string>
@@ -246,6 +249,7 @@
<string name="appearance">Aparença</string>
<string name="external_elements">Elements externs</string>
<string name="interruptions">Interrupcions</string>
+ <string name="preference_search_hint">Cerca…</string>
<string name="media_player">Reproductor multimèdia</string>
<string name="pref_episode_cleanup_title">Neteja l\'episodi</string>
<string name="pref_episode_cleanup_summary">Els episodis que no es troben a la cua i no són preferits seran candidats a ser suprimits si l\'Auto Descàrrega necessita espai per a nous episodis</string>
@@ -494,8 +498,10 @@
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">S\'estan important les subscripcions des de les apps de propòsit únic...</string>
<!--Add podcast fragment-->
+ <string name="search_podcast_hint">Cerca podcast…</string>
<string name="search_itunes_label">Cerca a iTunes</string>
<string name="search_fyyd_label">Cerca a fyyd</string>
+ <string name="advanced_search">Cerca avançada</string>
<string name="filter">Filtra</string>
<!--Episodes apply actions-->
<string name="all_label">Tot</string>
diff --git a/core/src/main/res/values-da/strings.xml b/core/src/main/res/values-da/strings.xml
index 728975847..58192ee30 100644
--- a/core/src/main/res/values-da/strings.xml
+++ b/core/src/main/res/values-da/strings.xml
@@ -64,6 +64,7 @@
<string name="cover_label">Billede</string>
<string name="error_label">Fejl</string>
<string name="error_msg_prefix">En fejl er opstået:</string>
+ <string name="needs_storage_permission">Tilladelse til lageradgang kræves for denne handling</string>
<string name="refresh_label">Opdater</string>
<string name="external_storage_error_msg">Ingen ekstern harddisk er tilgængelig. Vær venlig at sørge for at den eksterne hukommelse er monteret så app\'en kan fungere korrekt.</string>
<string name="chapters_label">Kapitler</string>
@@ -86,6 +87,10 @@
<string name="episode_cleanup_never">Aldrig</string>
<string name="episode_cleanup_queue_removal">Når ikke i kø</string>
<string name="episode_cleanup_after_listening">Efter færdig afspilning</string>
+ <plurals name="episode_cleanup_hours_after_listening">
+ <item quantity="one">1 time efter afslutning</item>
+ <item quantity="other">%d timer efter afslutning</item>
+ </plurals>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 dag efter færdig afspilning</item>
<item quantity="other">%d dage efter færdig afspilning</item>
@@ -95,18 +100,28 @@
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Tilføj podcast via webadresse</string>
<string name="browse_gpoddernet_label">Gennemse gpodder.net</string>
+ <string name="discover">Opdag</string>
+ <string name="discover_more">mere »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marker alle som afspillet</string>
<string name="mark_all_read_msg">Marker alle udsendelser som afspillet</string>
<string name="mark_all_read_confirmation_msg">Bekræft venligst at du ønsker at markere alle udsendelser som værende afspillet.</string>
+ <string name="remove_all_new_flags_label">Fjern alle \"ny\"-markeringer</string>
+ <string name="removed_all_new_flags_msg">Fjernede alle \"ny\"-markeringer</string>
<string name="show_info_label">Vis information</string>
+ <string name="show_feed_settings_label">Vis podcast-indstillinger</string>
<string name="feed_info_label">Podcast-info</string>
+ <string name="feed_settings_label">Podcast-indstillinger</string>
<string name="rename_feed_label">Omdøb podcast</string>
<string name="remove_feed_label">Fjern podcast</string>
<string name="share_label">Del…</string>
<string name="share_link_label">Del udsendelsens webadresse</string>
+ <string name="share_link_with_position_label">Del webadresse for udsendelse med position</string>
<string name="share_file_label">Del fil</string>
+ <string name="share_website_url_label">Del webstedets adresse</string>
<string name="share_feed_url_label">Del webadresse for feedet</string>
+ <string name="share_item_url_label">Del webadresse for mediefil</string>
+ <string name="share_item_url_with_position_label">Del webadresse for mediefil med position</string>
<string name="feed_delete_confirmation_msg">Bekræft venligst at du ønsker at slette podcasten \"%1$s\" og ALLE dens udsendelser (inklusive overførte udsendelser)</string>
<string name="feed_remover_msg">Fjerner podcast</string>
<string name="load_complete_feed">Opdater komplet podcast</string>
@@ -127,17 +142,40 @@
<string name="open_podcast">Åbn podcast</string>
<!--actions on feeditems-->
<string name="download_label">Hent</string>
+ <plurals name="downloading_batch_label">
+ <item quantity="one">Henter %d udsendelse.</item>
+ <item quantity="other">Henter %d udsendelser.</item>
+ </plurals>
<string name="play_label">Afspil</string>
<string name="pause_label">Sæt på pause</string>
<string name="stream_label">Stream</string>
<string name="delete_label">Slet</string>
<string name="delete_failed">Kan ikke slette fil. En genstart af enheden vil sandsynligvis hjælpe.</string>
+ <string name="delete_episode_label">Slet udsendelse</string>
+ <plurals name="deleted_episode_batch_label">
+ <item quantity="one">%d udsendelse slettet.</item>
+ <item quantity="other">%d udsendelser slettet.</item>
+ </plurals>
+ <string name="remove_new_flag_label">Fjern \"ny\"-markering</string>
+ <string name="removed_new_flag_label">Fjernet \"ny\"-markering</string>
<string name="mark_read_label">Marker som læst</string>
<string name="marked_as_read_label">Markeret som afspillet</string>
+ <plurals name="marked_read_batch_label">
+ <item quantity="one">%d udsendelse markeret som afspillet.</item>
+ <item quantity="other">%d udsendelser markeret som afspillet.</item>
+ </plurals>
<string name="mark_unread_label">Marker som uafspillet</string>
+ <plurals name="marked_unread_batch_label">
+ <item quantity="one">%d udsendelse markeret som uafspillet.</item>
+ <item quantity="other">%d udsendelser markeret som uafspillede.</item>
+ </plurals>
<string name="add_to_queue_label">Føj til kø</string>
<string name="added_to_queue_label">Føjet til kø</string>
<string name="remove_from_queue_label">Fjern fra kø</string>
+ <plurals name="removed_from_queue_batch_label">
+ <item quantity="one">%d udsendelse fjernet fra køen.</item>
+ <item quantity="other">%d udsendelser fjernet fra køen.</item>
+ </plurals>
<string name="add_to_favorite_label">Føj til foretrukne</string>
<string name="added_to_favorites">Føjet til foretrukne</string>
<string name="remove_from_favorite_label">Fjern fra foretrukne</string>
@@ -189,6 +227,9 @@
<string name="confirm_mobile_download_dialog_title">Bekræft brug af mobildata</string>
<string name="confirm_mobile_download_dialog_message_not_in_queue">Overførsel over mobil dataforbindelse er slået fra i indstillingerne.\n\nDu kan vælge blot at føje udsendelsen til køen eller du kan tillade overførsel midlertidigt.\n\n<small>Dit valg vil blive husket i 10 minutter.</small></string>
<string name="confirm_mobile_download_dialog_message">Overførsel over mobil dataforbindelse er slået fra i indstillingerne.\n\nVil du tillade overførsel midlertidigt?\n\n<small>Dit valg vil blive husket i 10 minutter.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Bekræft mobil streaming</string>
+ <string name="confirm_mobile_streaming_notification_message">Streaming via mobildata er slået fra i indstillingerne. Tryk for at streame alligevel.</string>
+ <string name="confirm_mobile_streaming_button_always">Tillad altid</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Sæt i kø</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Tillad midlertidigt</string>
<!--Mediaplayer messages-->
@@ -198,6 +239,7 @@
<string name="player_ready_msg">Klar</string>
<string name="player_seeking_msg">Søger</string>
<string name="playback_error_server_died">Server døde</string>
+ <string name="playback_error_unsupported">Kan ikke afspille denne medietype</string>
<string name="playback_error_unknown">Ukendt fejl</string>
<string name="no_media_playing_label">Ingen medier afspiller</string>
<string name="player_buffering_msg">Henter data til buffer</string>
@@ -208,27 +250,44 @@
<string name="unlock_queue">Lås kø op</string>
<string name="queue_locked">Kø låst</string>
<string name="queue_unlocked">Kø låst op</string>
+ <string name="checkbox_do_not_show_again">Vis ikke igen</string>
<string name="clear_queue_label">Ryd kø</string>
<string name="undo">Fortryd</string>
<string name="move_to_top_label">Flyt til toppen</string>
<string name="move_to_bottom_label">Flyt til bunden</string>
<string name="sort">Sorter</string>
+ <string name="keep_sorted">Hold sorteret</string>
<string name="date">Dato</string>
<string name="duration">Varighed</string>
<string name="episode_title">Titel på udsendelse</string>
<string name="feed_title">Titel på podcast</string>
<string name="random">Tilfældig</string>
+ <string name="smart_shuffle">Smart blanding</string>
<string name="ascending">Stigende</string>
<string name="descending">Faldende</string>
<string name="clear_queue_confirmation_msg">Bekræft venligst at du vil rydde ALLE udsendelser fra køen</string>
+ <string name="sort_old_to_new">Gamle til nye</string>
+ <string name="sort_new_to_old">Nye til gamle</string>
<!--Variable Speed-->
<string name="download_plugin_label">Hent plugin</string>
<string name="no_playback_plugin_title">Plugin er ikke installeret</string>
<string name="set_playback_speed_label">Afspilningshastigheder</string>
<string name="enable_sonic">Slå Sonic til</string>
<!--Empty list labels-->
+ <string name="no_items_header_label">Ingen udsendelser i køen</string>
<string name="no_shownotes_label">Denne udsendelse har ingen beskrivelse.</string>
+ <string name="no_run_downloads_head_label">Ingen overførsler i gang</string>
+ <string name="no_comp_downloads_head_label">Ingen overførte udsendelser</string>
+ <string name="no_log_downloads_head_label">Ingen overførselslog</string>
+ <string name="no_history_head_label">Ingen historik</string>
+ <string name="no_all_episodes_head_label">Ingen udsendelser</string>
+ <string name="no_all_episodes_label">Når du tilføjer en podcast, vil udsendelserne blive vist her.</string>
+ <string name="no_new_episodes_label">Når nye udsendelser ankommer, vil de blive vist her.</string>
+ <string name="no_fav_episodes_head_label">Ingen foretrukne udsendelser</string>
+ <string name="no_fav_episodes_label">Du kan føje udsendelser til foretrukne ved at trykke længe på dem</string>
+ <string name="no_chapters_head_label">Ingen kapitler</string>
<string name="no_chapters_label">Denne udsendelse har ingen kapitler.</string>
+ <string name="no_subscriptions_head_label">Ingen abonnementer</string>
<!--Preferences-->
<string name="storage_pref">Lagring</string>
<string name="project_pref">Projekt</string>
@@ -237,8 +296,12 @@
<string name="automation">Automatisering</string>
<string name="download_pref_details">Detaljer</string>
<string name="import_export_pref">Import/eksport</string>
+ <string name="import_export_search_keywords">sikkerhedskopiering, sikkerhedskopi, backup, gendan, gendannelse, restore</string>
<string name="appearance">Udseende</string>
<string name="interruptions">Afbrydelser</string>
+ <string name="preference_search_hint">Søg...</string>
+ <string name="preference_search_no_results">Ingen resultater</string>
+ <string name="preference_search_clear_history">Slet historik</string>
<string name="media_player">Medieafspiller</string>
<string name="pref_episode_cleanup_title">Oprydning i udsendelser</string>
<string name="pref_episode_cleanup_summary">Tillad at udsendelser, som ikke er i køen og som ikke er markeret som foretrukne, kan fjernes, hvis Automatisk overførsel har brug for plads til nye udsendelser</string>
@@ -271,7 +334,11 @@
<string name="pref_pauseOnHeadsetDisconnect_title">Afbrydelse af hovedtelefoner</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Tilslutning af hovedtelefoner igen</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth forbundet igen</string>
+ <string name="pref_stream_over_download_title">Foretræk streaming</string>
<string name="pref_mobileUpdate_title">Mobile opdateringer</string>
+ <string name="pref_mobileUpdate_auto_download">Hent automatisk</string>
+ <string name="pref_mobileUpdate_episode_download">Download af udsendelser</string>
+ <string name="pref_mobileUpdate_streaming">Streaming</string>
<string name="user_interface_label">Brugergrænseflade</string>
<string name="pref_set_theme_title">Vælg tema</string>
<string name="pref_nav_drawer_items_title">Vælg elementer i navigationspanelet</string>
@@ -288,6 +355,7 @@
<string name="pref_automatic_download_on_battery_sum">Tillad automatisk overførsel, når batteriet ikke oplades</string>
<string name="pref_parallel_downloads_title">Parallelle overførsler</string>
<string name="pref_episode_cache_title">Mellemlager for udsendelser</string>
+ <string name="pref_theme_title_use_system">Brug systemtema</string>
<string name="pref_theme_title_light">Lys</string>
<string name="pref_theme_title_dark">Mørk</string>
<string name="pref_theme_title_trueblack">Sort (AMOLED-klar)</string>
@@ -331,9 +399,18 @@
<string name="pref_showDownloadReport_title">Vis rapport over overførsler</string>
<string name="pref_showDownloadReport_sum">Lav en rapport, som viser detaljer om fejlene, hvis overførsler fejler</string>
<string name="pref_expand_notify_unsupport_toast">Android-versioner før 4.1 understøtter ikke udvidede notifikationer.</string>
+ <string name="pref_enqueue_location_title">Placering i kø</string>
+ <string name="pref_enqueue_location_sum">Føj udsendelser til: %1$s</string>
+ <string name="enqueue_location_back">Slutningen</string>
+ <string name="enqueue_location_front">Starten</string>
+ <string name="enqueue_location_after_current">Efter aktuel udsendelse</string>
<string name="pref_smart_mark_as_played_disabled">Slået fra</string>
<string name="pref_image_cache_size_title">Størrelse på mellemlager (cache) for billeder</string>
<string name="pref_image_cache_size_sum">Størrelse på diskmellemlageret (disk cache) for billeder</string>
+ <string name="bug_report_title">Rapportér fejl i appen</string>
+ <string name="open_bug_tracker">Åbn programfejlsdatabase</string>
+ <string name="copy_to_clipboard">Kopier til udklipsholder</string>
+ <string name="copied_to_clipboard">Kopieret til udklipsholder</string>
<string name="experimental_pref">Eksperimentelt</string>
<string name="pref_current_value">Nuværende værdi: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
@@ -343,9 +420,10 @@
<string name="pref_cast_message_play_flavor">Aktiver understøttelse af fjernafspilning på Cast-enheder (såsom Chromecast, højttalere med indbygget Chromecast, eller Android TV)</string>
<string name="pref_cast_message_free_flavor">Chromecast kræver tredjeparts proprietære biblioteker, som er slået fra i denne version af AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Sæt overførte udsendelser i kø</string>
- <string name="pref_enqueue_downloaded_summary">Føj udsendelser til køen, så snart de er overført</string>
+ <string name="pref_enqueue_downloaded_summary">Føj downloadede udsendelser til køen</string>
<string name="media_player_builtin">Indbygget Android-afspiller</string>
<string name="pref_videoBehavior_title">Når videoen forlades</string>
+ <string name="pref_videoBehavior_sum">Opførsel når videoafspilning forlades</string>
<string name="stop_playback">Stop afspilning</string>
<string name="continue_playback">Fortsæt afspilning af lyd</string>
<string name="behavior">Funktion</string>
@@ -355,11 +433,13 @@
<string name="back_button_open_drawer">Åbn navigationsskuffen</string>
<string name="back_button_double_tap">Dobbelttryk for at afslutte</string>
<string name="back_button_show_prompt">Bekræft for at afslutte</string>
+ <string name="close_prompt">Er du sikker på at du vil lukke AntennaPod?</string>
<string name="double_tap_toast">Tryk på tilbageknappen igen for at afslutte</string>
<string name="back_button_go_to_page">Gå til side ...</string>
<string name="back_button_go_to_page_title">Vælg side</string>
<!--About screen-->
<string name="about_pref">Om</string>
+ <string name="translators">Oversættere</string>
<!--Search-->
<string name="search_hint">Søg efter udsendelser</string>
<string name="found_in_shownotes_label">Fundet i beskrivelse</string>
@@ -453,6 +533,7 @@
<string name="choose_data_directory">Vælg datamappe</string>
<string name="choose_data_directory_message">Vælg venligst roden af din datamappe. AntennaPod vil oprette passende undermapper.</string>
<string name="choose_data_directory_permission_rationale">Adgang til eksternt lager er kræver for at ændre datamappen</string>
+ <string name="choose_data_directory_available_space">%1$s af %2$s ledig</string>
<string name="create_folder_msg">Opret en ny mappe med navnet \"%1$s\"?</string>
<string name="create_folder_success">Oprettede ny mappe</string>
<string name="create_folder_error_no_write_access">Kan ikke skrive til denne mappe</string>
@@ -471,6 +552,7 @@
<string name="pref_restart_required">AntennaPod skal genstartes for at denne indstilling kan træde i kraft</string>
<!--Online feed view-->
<string name="subscribe_label">Abonner</string>
+ <string name="subscribing_label">Abonnerer ...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Spol tilbage</string>
<string name="fast_forward_label">Spol frem</string>
@@ -496,8 +578,10 @@
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importerer abonnementer fra enkeltformålsapps…</string>
<!--Add podcast fragment-->
+ <string name="search_podcast_hint">Søg efter podcast ...</string>
<string name="search_itunes_label">Søg i iTunes</string>
<string name="search_fyyd_label">Søg i fyyd</string>
+ <string name="advanced_search">Avanceret søgning</string>
<string name="filter">Filtrér</string>
<!--Episodes apply actions-->
<string name="all_label">Alle</string>
@@ -579,6 +663,9 @@
<string name="notification_channel_user_action">Handling påkrævet</string>
<string name="notification_channel_downloading">Henter</string>
<string name="notification_channel_downloading_description">Vises samtidig med den hentes.</string>
+ <string name="notification_channel_playing">Spiller nu</string>
<string name="notification_channel_error">Fejl</string>
+ <string name="import_bad_file">Ugyldig/ødelagt fil</string>
<!--Widget settings-->
+ <string name="widget_opacity">Ugennemsigtighed</string>
</resources>
diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml
index 0d5158d24..379693f9f 100644
--- a/core/src/main/res/values-de/strings.xml
+++ b/core/src/main/res/values-de/strings.xml
@@ -114,10 +114,10 @@
<string name="removed_all_new_flags_msg">Alle \"neu\"-Markierungen entfernt</string>
<string name="remove_all_new_flags_confirmation_msg">Bitte bestätige, dass du die \"neu\"-Markierung aller Episoden entfernen willst.</string>
<string name="show_info_label">Informationen anzeigen</string>
- <string name="show_feed_settings_label">Zeige Feed-Einstellungen</string>
- <string name="feed_info_label">Feed-Informationen</string>
- <string name="feed_settings_label">Feed-Einstellungen</string>
- <string name="rename_feed_label">Feed umbenennen</string>
+ <string name="show_feed_settings_label">Zeige Podcast-Einstellungen</string>
+ <string name="feed_info_label">Podcast-Informationen</string>
+ <string name="feed_settings_label">Podcast-Einstellungen</string>
+ <string name="rename_feed_label">Podcast umbenennen</string>
<string name="remove_feed_label">Podcast entfernen</string>
<string name="share_label">Teilen…</string>
<string name="share_link_label">Episoden URL Teilen</string>
@@ -127,9 +127,9 @@
<string name="share_feed_url_label">Teile URL des Podcasts</string>
<string name="share_item_url_label">Teile URL der Episodendatei</string>
<string name="share_item_url_with_position_label">Teile URL der Episodendatei mit Zeitmarke</string>
- <string name="feed_delete_confirmation_msg">Bitte bestätige, dass du den Podcast \"%1$s\" und ALLE heruntergeladenen Episoden dieses Feeds entfernen möchtest.</string>
- <string name="feed_remover_msg">Entferne Podcast</string>
- <string name="load_complete_feed">Kompletten Feed aktualisieren</string>
+ <string name="feed_delete_confirmation_msg">Bitte bestätige, dass du den Podcast \"%1$s\" und ALL dessen Episoden entfernen möchtest (auch heruntergeladene).</string>
+ <string name="feed_remover_msg">Podcast wird entfernt</string>
+ <string name="load_complete_feed">Kompletten Podcast aktualisieren</string>
<string name="batch_edit">Stapelbearbeitung</string>
<string name="select_all_above">Alles oberhalb auswählen</string>
<string name="select_all_below">Alles unterhalb auswählen</string>
@@ -156,7 +156,7 @@
<string name="stream_label">Streamen</string>
<string name="delete_label">Löschen</string>
<string name="delete_failed">Die Datei kann nicht gelöscht werden. Eventuell hilft es, das Gerät neu zu starten.</string>
- <string name="delete_episode_label">Episode entfernen</string>
+ <string name="delete_episode_label">Episode löschen</string>
<plurals name="deleted_episode_batch_label">
<item quantity="one">%d Episode gelöscht.</item>
<item quantity="other">%d Episoden gelöscht.</item>
@@ -272,7 +272,7 @@
<string name="date">Datum</string>
<string name="duration">Dauer</string>
<string name="episode_title">Episodentitel</string>
- <string name="feed_title">Feedname</string>
+ <string name="feed_title">Podcast-Name</string>
<string name="random">Zufällig</string>
<string name="smart_shuffle">Schlaues Mischen</string>
<string name="ascending">Aufsteigend</string>
@@ -320,7 +320,7 @@
<string name="appearance">Erscheinungsbild</string>
<string name="external_elements">Externe Elemente</string>
<string name="interruptions">Unterbrechungen</string>
- <string name="playback_control">Wiedergabesteurung</string>
+ <string name="playback_control">Wiedergabesteuerung</string>
<string name="preference_search_hint">Suchen…</string>
<string name="preference_search_no_results">Keine Ergebnisse</string>
<string name="preference_search_clear_history">Verlauf leeren</string>
@@ -369,7 +369,7 @@
<string name="user_interface_label">Benutzeroberfläche</string>
<string name="pref_set_theme_title">Theme auswählen</string>
<string name="pref_nav_drawer_items_title">Seitenleiste ändern</string>
- <string name="pref_nav_drawer_items_sum">Ändere, welche Listen in der Seitenleiste erscheinen</string>
+ <string name="pref_nav_drawer_items_sum">Ändere, welche Einträge in der Seitenleiste erscheinen</string>
<string name="pref_nav_drawer_feed_order_title">Reihenfolge der Abonnements einstellen</string>
<string name="pref_nav_drawer_feed_order_sum">Ändere die Reihenfolge deiner Abonnements</string>
<string name="pref_nav_drawer_feed_counter_title">Abonnement-Zähler einstellen</string>
@@ -462,7 +462,7 @@
<string name="pref_enqueue_downloaded_summary">Füge heruntergeladene Episoden zur Abspielliste hinzu</string>
<string name="media_player_builtin">Androids eingebauter Abspieler</string>
<string name="pref_skip_silence_title">Stille im Ton überspringen</string>
- <string name="pref_videoBehavior_title">Beim Beenden des Videos</string>
+ <string name="pref_videoBehavior_title">Beim Beenden von Videos</string>
<string name="pref_videoBehavior_sum">Verhalten beim Verlassen der Video-Wiedergabe</string>
<string name="stop_playback">Wiedergabe anhalten</string>
<string name="continue_playback">Audiowiedergabe fortsetzen</string>
@@ -493,8 +493,8 @@
<string name="search_hint">Suche nach Episoden</string>
<string name="found_in_shownotes_label">In Shownotizen gefunden</string>
<string name="found_in_chapters_label">In Kapiteln gefunden</string>
- <string name="found_in_authors_label">Gefunden in Autor(en)</string>
- <string name="found_in_feeds_label">Gefunden in Podcast</string>
+ <string name="found_in_authors_label">In Autor(en) gefunden</string>
+ <string name="found_in_feeds_label">In Podcast gefunden</string>
<string name="search_status_no_results">Keine Ergebnisse gefunden</string>
<string name="search_label">Suchen</string>
<string name="found_in_title_label">In Titel gefunden</string>
@@ -676,7 +676,7 @@
<string name="audio_effects">Audioeffekte</string>
<string name="stereo_to_mono">Heruntermischen: Stereo zu Mono</string>
<string name="sonic_only">nur Sonic</string>
- <string name="exoplayer_only">Nur ExoPlayer</string>
+ <string name="exoplayer_only">nur ExoPlayer</string>
<!--proxy settings-->
<string name="proxy_type_label">Typ</string>
<string name="host_label">Host</string>
diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml
index 4418289e9..a27013c44 100644
--- a/core/src/main/res/values-fr/strings.xml
+++ b/core/src/main/res/values-fr/strings.xml
@@ -261,7 +261,7 @@
<string name="unlock_queue">Déverrouiller la liste de lecture</string>
<string name="queue_locked">Liste de lecture verrouillée</string>
<string name="queue_unlocked">Liste de lecture déverrouillée</string>
- <string name="queue_lock_warning">Lorsque la liste est verrouillée le swipe n\'est pas possible et l\'ordre des épisodes ne peut être changé.</string>
+ <string name="queue_lock_warning">Quand la liste est verrouillée il n\'est plus possible d\'utiliser le swipe ou de changer l\'ordre des épisodes.</string>
<string name="checkbox_do_not_show_again">Ne pas réafficher</string>
<string name="clear_queue_label">Effacer la liste de lecture</string>
<string name="undo">Annuler</string>
@@ -358,10 +358,10 @@
<string name="pref_unpauseOnHeadsetReconnect_title">Connexion des écouteurs</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Connexion du Bluetooth</string>
<string name="pref_stream_over_download_title">Préférer le streaming</string>
- <string name="pref_stream_over_download_sum">Afficher dans les listes le bouton du stream au lieu de celui de téléchargement.</string>
+ <string name="pref_stream_over_download_sum">Afficher dans les listes le bouton du streaming au lieu de celui du téléchargement.</string>
<string name="pref_mobileUpdate_title">Utilisation de la connexion mobile</string>
<string name="pref_mobileUpdate_sum">Choisir ce qui est autorisé lorsque la connexion mobile est utilisée</string>
- <string name="pref_mobileUpdate_refresh">Rafraîchissement des flux</string>
+ <string name="pref_mobileUpdate_refresh">Mise à jour des flux</string>
<string name="pref_mobileUpdate_images">Récupération des images</string>
<string name="pref_mobileUpdate_auto_download">Téléchargement automatique</string>
<string name="pref_mobileUpdate_episode_download">Téléchargement d\'épisodes</string>
@@ -387,7 +387,7 @@
<string name="pref_episode_cache_title">Nombre d\'épisodes stockés</string>
<string name="pref_episode_cache_summary">Nombre maximum d\'épisodes stockés sur l\'appareil. Le téléchargement automatique sera suspendu si ce nombre est atteint.</string>
<string name="pref_episode_cover_title">Image des épisodes</string>
- <string name="pref_episode_cover_summary">Lorsqu\'elle existe, utiliser l\'image propre aux épisodes au lieu de celle du podcast.</string>
+ <string name="pref_episode_cover_summary">Lorsqu\'elles existent, utiliser les images propres aux épisodes au lieu de celle du podcast.</string>
<string name="pref_theme_title_use_system">Thème du système</string>
<string name="pref_theme_title_light">Clair</string>
<string name="pref_theme_title_dark">Sombre</string>
@@ -413,7 +413,7 @@
<string name="pref_gpodnet_notifications_sum">Ce paramètre ne s\'applique pas aux erreurs d\'authentification.</string>
<string name="pref_playback_speed_title">Vitesses de lecture</string>
<string name="pref_playback_speed_sum">Définir les vitesses disponibles lors de la lecture audio</string>
- <string name="pref_feed_playback_speed_sum">Vitesse de lecture pour les épisodes de ce flux</string>
+ <string name="pref_feed_playback_speed_sum">Vitesse de lecture par défaut pour les épisodes de ce flux</string>
<string name="pref_playback_time_respects_speed_title">Ajuster les informations en fonction la vitesse de lecture</string>
<string name="pref_playback_time_respects_speed_sum">La position et la durée affichée tiendront compte de la vitesse de lecture.</string>
<string name="pref_fast_forward">Durée du saut avant</string>
@@ -725,7 +725,7 @@
<string name="notification_channel_error_description">S\'affiche en cas de problème. Par exemple, un téléchargement ou une synchronisation qui échoue.</string>
<string name="import_bad_file">Fichier invalide/corrompu</string>
<!--Widget settings-->
- <string name="widget_settings">Préférences des widget</string>
+ <string name="widget_settings">Préférences des widgets</string>
<string name="widget_create_button">Créer un widget</string>
<string name="widget_opacity">Opacité</string>
</resources>
diff --git a/core/src/main/res/values-gl-rES/strings.xml b/core/src/main/res/values-gl-rES/strings.xml
index 4a0131a20..42b46be47 100644
--- a/core/src/main/res/values-gl-rES/strings.xml
+++ b/core/src/main/res/values-gl-rES/strings.xml
@@ -23,7 +23,7 @@
<string name="gpodnet_summary">Sincronizar con outros dispositivos</string>
<string name="gpodnet_auth_label">gpodder.net Conexión</string>
<string name="episode_cache_full_title">Caché de episodios chea</string>
- <string name="episode_cache_full_message">Acadouse o límite de espazo na caché de episodios. Pode incrementalo nos Axustes do tamaño da caché.</string>
+ <string name="episode_cache_full_message">Acadouse o límite de espazo na caché de episodios. Podes incrementalo nos Axustes do tamaño da caché.</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Tempo total dos podcast reproducidos:</string>
<string name="statistics_details_dialog">%1$d de %2$d episodios iniciados.\n\nReproducidos %3$s de %4$s.</string>
@@ -68,7 +68,7 @@
<string name="error_msg_prefix">Produciuse un fallo:</string>
<string name="needs_storage_permission">Precísase o permiso de almacenamento para esta operación</string>
<string name="refresh_label">Actualizar</string>
- <string name="external_storage_error_msg">Non se dispón de almacenamento externo. Por favor asegúrese de que o almacenamento externo está montado e así a aplicación poderá funcionar correctamente.</string>
+ <string name="external_storage_error_msg">Non se dispón de almacenamento externo. Por favor asegúrate de que o almacenamento externo está montado e así a aplicación poderá funcionar correctamente.</string>
<string name="chapters_label">Capítulos</string>
<string name="chapter_duration">Duración: %1$s</string>
<string name="description_label">Descrición</string>
@@ -79,7 +79,7 @@
<string name="retry_label">Reintentar</string>
<string name="auto_download_label">Incluír en descargas automáticas</string>
<string name="auto_download_apply_to_items_title">Aplicar a episodios previos</string>
- <string name="auto_download_apply_to_items_message">A nova función <i>Descarga automática</i> aplicarase automáticamente aos novos episodios.\nQuere que tamén se aplique aos episodios publicados con anterioridade?</string>
+ <string name="auto_download_apply_to_items_message">A nova función <i>Descarga automática</i> aplicarase automáticamente aos novos episodios.\nQueres que tamén se aplique aos episodios publicados con anterioridade?</string>
<string name="auto_delete_label">Borrado automático do episodio</string>
<string name="parallel_downloads_suffix">\u0020descargas paralelas</string>
<string name="feed_auto_download_global">Valor xeral por omisión</string>
@@ -111,8 +111,8 @@
<string name="mark_all_read_confirmation_msg">Por favor confirme que quere marcar todos os episodios como reproducidos.</string>
<string name="mark_all_read_feed_confirmation_msg">Por favor, confirme que quere marcar todos os episodios deste podcast como reproducidos.</string>
<string name="remove_all_new_flags_label">Quitarlle a marca de \"novidade\" a todo</string>
- <string name="removed_all_new_flags_msg">Eliminou todas as \"novas\" marcas</string>
- <string name="remove_all_new_flags_confirmation_msg">Por favor, confirme que quere quitar marca de \"novidade\" a todos os episodios.</string>
+ <string name="removed_all_new_flags_msg">Eliminaches o aviso de \"novos\" episodios</string>
+ <string name="remove_all_new_flags_confirmation_msg">Confirma que queres quitarlle marca de \"novidade\" a todos os episodios.</string>
<string name="show_info_label">Mostrar información</string>
<string name="show_feed_settings_label">Mostrar axustes do podcast</string>
<string name="feed_info_label">Información do podcast</string>
@@ -235,7 +235,7 @@
<string name="authentication_notification_title">Precísase autenticación</string>
<string name="authentication_notification_msg">O recurso solicitado require un usuario e contrasinal</string>
<string name="confirm_mobile_download_dialog_title">Confirme a descarga con datos do móbil</string>
- <string name="confirm_mobile_download_dialog_message_not_in_queue">Descargar coa conexión de datos do móbil está desactivada nos axustes.\n\nPode escoller ben só engadir o episodio a cola ou pode permitir a descarga temporalmente.\n\n<small> A súa elección lembrarase durante 10 minutos.</small></string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Descargar coa conexión de datos do móbil está desactivada nos axustes.\n\nPodes escoller ben só engadir o episodio a cola ou podes permitir a descarga temporalmente.\n\n<small> A túa elección lembrarase durante 10 minutos.</small></string>
<string name="confirm_mobile_download_dialog_message">A descarga con datos móbiles está desactivada nos axustes.\n\nQuere permitir a descarga temporalmente?\n\n <small>A súa decisión lembrarase durante 10 minutos.</small></string>
<string name="confirm_mobile_streaming_notification_title">Confirmar retransmisión Móbil</string>
<string name="confirm_mobile_streaming_notification_message">Desactivouse nos axustes Retransmitir mediante a conexión de datos. Toque para retransmitir igualmente.</string>
@@ -262,7 +262,7 @@
<string name="queue_locked">Cola bloqueada</string>
<string name="queue_unlocked">Cola desbloqueada</string>
<string name="queue_lock_warning">Se bloqueas a cola, non poderás quitar ou mover os episodios.</string>
- <string name="checkbox_do_not_show_again">Non mostrar outra vez</string>
+ <string name="checkbox_do_not_show_again">Non mostrar de novo</string>
<string name="clear_queue_label">Limpar cola</string>
<string name="undo">Desfacer</string>
<string name="move_to_top_label">Mover arriba</string>
@@ -288,12 +288,12 @@
<string name="enable_sonic">Habilitar Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Sen episodios na cola</string>
- <string name="no_items_label">Engada un episodio descargándoo, ou manteña preso un episodio e escolla \"Engadir a cola\".</string>
+ <string name="no_items_label">Engade un episodio descargándoo, ou mantén preso un episodio e escolle \"Engadir a cola\".</string>
<string name="no_shownotes_label">Este episodio non ten notas de episodio.</string>
<string name="no_run_downloads_head_label">Sen descargas activas</string>
- <string name="no_run_downloads_label">Pode descargar episodios na pantalla con detalles do episodio.</string>
+ <string name="no_run_downloads_label">Podes descargar episodios na pantalla con detalles do podcast.</string>
<string name="no_comp_downloads_head_label">Sen episodios descargados</string>
- <string name="no_comp_downloads_label">Pode descargar episodios na pantalla de detalles do podcast.</string>
+ <string name="no_comp_downloads_label">Podes descargar episodios na pantalla de detalles do podcast.</string>
<string name="no_log_downloads_head_label">Sen rexistro da descarga</string>
<string name="no_log_downloads_label">Os rexistros de descarga aparecerán aquí se están dispoñibles.</string>
<string name="no_history_head_label">Sen Historial</string>
@@ -301,9 +301,9 @@
<string name="no_all_episodes_head_label">Sen episodios</string>
<string name="no_all_episodes_label">Cando engade un podcast, os episodios mostraranse aquí.</string>
<string name="no_new_episodes_head_label">Sen episodios novos</string>
- <string name="no_new_episodes_label">Cando reciba novos episodios, mostraranse aquí.</string>
+ <string name="no_new_episodes_label">Cando recibas novos episodios, mostraranse aquí.</string>
<string name="no_fav_episodes_head_label">Sen episodios favoritos</string>
- <string name="no_fav_episodes_label">Pode engadir episodios aos favoritos manténdoos pulsados.</string>
+ <string name="no_fav_episodes_label">Podes engadir episodios aos favoritos manténdoos presos.</string>
<string name="no_chapters_head_label">Sen capítulos</string>
<string name="no_chapters_label">Este episodio non ten capítulos.</string>
<string name="no_subscriptions_head_label">Sen subscricións</string>
@@ -326,12 +326,12 @@
<string name="preference_search_clear_history">Limpar historial</string>
<string name="media_player">Reprodutor de medios</string>
<string name="pref_episode_cleanup_title">Limpeza de episodios</string>
- <string name="pref_episode_cleanup_summary">Os episodios que non están na cola e tampouco son favoritos deberían poder ser candidatos a ser eliminados si a función Descarga Automática precisa espazo para novos episodios.</string>
+ <string name="pref_episode_cleanup_summary">Os episodios que non están na cola e tampouco son favoritos deberían poder ser candidatos a ser eliminados se a función Descarga Automática precisa espazo para novos episodios.</string>
<string name="pref_pauseOnDisconnect_sum">Deter a reprodución cando se desconectan os auriculares ou bluetooth</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Retomar a reprodución cando se conectan os auriculares</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Retomar a reprodución cando se reconecta o bluetooth</string>
<string name="pref_hardwareForwardButtonSkips_title">O botón Adiante salta</string>
- <string name="pref_hardwareForwardButtonSkips_sum">A premer no botón de adiante nun dispositivo conectado por bluetooth ir ao episodio seguinte en vez dun avance rápido</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">A premer no botón de adiante nun dispositivo conectado por bluetooth ir ao episodio seguinte no lugar de avance rápido</string>
<string name="pref_hardwarePreviousButtonRestarts_title">O botón Anterior reinicia</string>
<string name="pref_hardwarePreviousButtonRestarts_sum">Cando se presiona Anterior no dispositivo reinicia o episodio no lugar de ir cara atrás</string>
<string name="pref_followQueue_sum">Saltar ao seguinte elemento na cola cando remata o episodio</string>
@@ -347,7 +347,7 @@
<string name="network_pref">Rede</string>
<string name="pref_autoUpdateIntervallOrTime_title">Intervalo de actualización ou Hora do día</string>
<string name="pref_autoUpdateIntervallOrTime_sum">Indicar un intervalo ou unha hora en concreto para actualizar automaticamente as fontes</string>
- <string name="pref_autoUpdateIntervallOrTime_message">Pode establecer un <i>intervalo</i> como \"2 horas\", unha <i>hora do día</i> en concreto como \"7:00 AM\" ou <i>desactivar</i> totalmente a actualización automática.\n\n<small>Aviso: pode haber un lixeiro retardo do momento da actualización.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Podes establecer un <i>intervalo</i> como \"2 horas\", unha <i>hora do día</i> en concreto como \"7:00 AM\" ou <i>desactivar</i> totalmente a actualización automática.\n\n<small>Aviso: pode haber un lixeiro retardo do momento da actualización.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Desactivar</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Establecer intervalo</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Establecer hora do día</string>
@@ -360,17 +360,17 @@
<string name="pref_stream_over_download_title">Preferir Difusión</string>
<string name="pref_stream_over_download_sum">Mostrar botón de difusión no lugar de botón de descarga nas listas.</string>
<string name="pref_mobileUpdate_title">Actualizacións Móbil</string>
- <string name="pref_mobileUpdate_sum">Escolla o que estará permitido utilizando conexión de datos do móbil</string>
+ <string name="pref_mobileUpdate_sum">Escolle o que estará permitido utilizando conexión de datos do móbil</string>
<string name="pref_mobileUpdate_refresh">Actualizar fontes</string>
<string name="pref_mobileUpdate_images">Imaxes de portadas</string>
<string name="pref_mobileUpdate_auto_download">Descarga automática</string>
<string name="pref_mobileUpdate_episode_download">Descarga de episodio</string>
<string name="pref_mobileUpdate_streaming">Retransmisión</string>
<string name="user_interface_label">Interface de usuaria</string>
- <string name="pref_set_theme_title">Escolla o decorado</string>
- <string name="pref_nav_drawer_items_title">Estableza os elementos da Caixa de navegación</string>
+ <string name="pref_set_theme_title">Escolle o decorado</string>
+ <string name="pref_nav_drawer_items_title">Escoller os elementos da Caixa de navegación</string>
<string name="pref_nav_drawer_items_sum">Cambie os elementos que aparecerán na Caixa de navegación</string>
- <string name="pref_nav_drawer_feed_order_title">Estableza a orde das subscricións</string>
+ <string name="pref_nav_drawer_feed_order_title">Indicar a orde das subscricións</string>
<string name="pref_nav_drawer_feed_order_sum">Cambie a orde das subscricións</string>
<string name="pref_nav_drawer_feed_counter_title">Establecer o contador de subscricións</string>
<string name="pref_nav_drawer_feed_counter_sum">Cambiar a información mostrada polo contador de subscricións. Tamén afecta á orde das subscricións se \"Orde das subscricións\" está establecida a \"Contador\".</string>
@@ -412,14 +412,14 @@
<string name="pref_gpodnet_notifications_title">Mostrar notificacións de erros na sincronización.</string>
<string name="pref_gpodnet_notifications_sum">Esta preferencia non se aplica a fallos na autenticación.</string>
<string name="pref_playback_speed_title">Velocidades de reprodución</string>
- <string name="pref_playback_speed_sum">Personalice a velocidade variable de reprodución de audio</string>
+ <string name="pref_playback_speed_sum">Personaliza a velocidade variable de reprodución de audio</string>
<string name="pref_feed_playback_speed_sum">A velocidade a utilizar cando se reproduce o audio dos episodios de esta fonte</string>
<string name="pref_playback_time_respects_speed_title">Axustar info dos medios a velocidade de reprodución</string>
<string name="pref_playback_time_respects_speed_sum">A posición mostrada e a duración están adaptadas a velocidade de reprodución</string>
<string name="pref_fast_forward">Avance rápido Salta tempo</string>
- <string name="pref_fast_forward_sum">Personalice o número de segundos a avanzar cando o se pulsa o botón de avance rápido</string>
+ <string name="pref_fast_forward_sum">Personaliza o número de segundos a avanzar cando o se preme o botón de avance rápido</string>
<string name="pref_rewind">Retroceso Salta tempo</string>
- <string name="pref_rewind_sum">Personalice o número de segundos que se retrocede na reprodución cando se pulsa o botón retroceso</string>
+ <string name="pref_rewind_sum">Personaliza o número de segundos que se retrocede na reprodución cando se preme o botón retroceso</string>
<string name="pref_gpodnet_sethostname_title">Establecer servidor</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utilizar servidor por omisión</string>
<string name="pref_expandNotify_title">Alta prioridade nas notificacións</string>
@@ -428,13 +428,14 @@
<string name="pref_persistNotify_sum">Manter notificación e controles na pantalla de bloqueo cando a reprodución está pausada.</string>
<string name="pref_compact_notification_buttons_title">Establecer botóns de pantalla de bloqueo</string>
<string name="pref_compact_notification_buttons_sum">Cambiar os botóns de reprodución na pantalla de bloqueo. O botón reproducir/pausa sempre se inclúe.</string>
- <string name="pref_compact_notification_buttons_dialog_title">Escolla un máximo de %1$d elementos</string>
- <string name="pref_compact_notification_buttons_dialog_error">Só pode selecionar un máximo de %1$d elementos.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">Escolle un máximo de %1$d elementos</string>
+ <string name="pref_compact_notification_buttons_dialog_error">Só podes selecionar un máximo de %1$d elementos.</string>
<string name="pref_lockscreen_background_title">Establecer fondo da pantalla de bloqueo</string>
<string name="pref_lockscreen_background_sum">Establecer o fondo de pantalla de bloqueo coa imaxe do episodio actual. Como consecuencia, esto tamén mostrará a imaxe en aplicacións de terceiros.</string>
<string name="pref_showDownloadReport_title">Mostrar informe de descarga</string>
<string name="pref_showDownloadReport_sum">Si falla a descarga, xerar un informe que informe dos detalles do fallo.</string>
<string name="pref_expand_notify_unsupport_toast">As versións de Android anteriores a 4.1 non teñen soporte para notificacións expandidas.</string>
+ <string name="pref_enqueue_location_title">Situación na cola</string>
<string name="pref_enqueue_location_sum">Engadir episodios a: %1$s</string>
<string name="enqueue_location_back">Atrás</string>
<string name="enqueue_location_front">Diante</string>
@@ -448,7 +449,7 @@
<string name="copy_to_clipboard">Copiar ó portapapeis</string>
<string name="copied_to_clipboard">Copiado ó portapapeis</string>
<string name="experimental_pref">En probas</string>
- <string name="pref_media_player_message">Escolla o reprodutor de medios para reproducir ficheiros</string>
+ <string name="pref_media_player_message">Escolle o reprodutor de medios para reproducir ficheiros</string>
<string name="pref_current_value">Valor actual: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Establecer un proxy para a rede</string>
@@ -500,7 +501,7 @@
<string name="no_results_for_query">Non hai resultados para \"%1$s\"</string>
<!--OPML import and export-->
<string name="opml_import_option">Opción %1$d</string>
- <string name="opml_import_explanation_1">Escolla unha ruta de ficheiro concreta no sistema de ficheiros local.</string>
+ <string name="opml_import_explanation_1">Escolle unha ruta de ficheiro concreta no sistema de ficheiros local.</string>
<string name="opml_import_explanation_3">Moitas aplicacións, como Google Mail, Dropbox, Google Drive e a maioría dos xestores de ficheiros poden <i>abrir</i> ficheiros OPML <i>con</i> AntennaPod.</string>
<string name="opml_import_label">Importar OPML</string>
<string name="reading_opml_label">Lendo ficheiro OPML</string>
@@ -552,21 +553,21 @@
<string name="gpodnetauth_login_title">Conexión</string>
<string name="gpodnetauth_login_descr">Benvida ao proceso de conexión a gpodder.net. Primeiro, escriba os seus datos de conexión:</string>
<string name="gpodnetauth_login_butLabel">Conexión</string>
- <string name="gpodnetauth_login_register">Se aínda non ten unha conta, pode crear unha aquí:\nhttps://gpodder.net/register/</string>
+ <string name="gpodnetauth_login_register">Se aínda non tes unha conta, podes crear unha aquí:\nhttps://gpodder.net/register/</string>
<string name="username_label">Nome de usuaria</string>
<string name="password_label">Contrasinal</string>
<string name="gpodnetauth_device_title">Selección de dispositivo</string>
- <string name="gpodnetauth_device_descr">Crear un novo dispositivo para usar coa sua conta gpodder.net ou escoller un existente:</string>
+ <string name="gpodnetauth_device_descr">Crear un novo dispositivo para usar coa túa conta gpodder.net ou escoller un existente:</string>
<string name="gpodnetauth_device_deviceID">ID de dispositivo:\u0020</string>
<string name="gpodnetauth_device_caption">Título</string>
<string name="gpodnetauth_device_butCreateNewDevice">Crear un novo dispositivo</string>
<string name="gpodnetauth_device_chooseExistingDevice">Escolle un novo dispositivo:</string>
- <string name="gpodnetauth_device_errorEmpty">O ID do dispositivo non pode quedar baldeiro</string>
+ <string name="gpodnetauth_device_errorEmpty">O ID do dispositivo non pode quedar baleiro</string>
<string name="gpodnetauth_device_errorAlreadyUsed">ID de dispositivo xa en uso</string>
- <string name="gpodnetauth_device_caption_errorEmpty">Non pode quedar baldeiro o titulo</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">O título non pode estar baleiro</string>
<string name="gpodnetauth_device_butChoose">Escoller</string>
<string name="gpodnetauth_finish_title">Conexión correcta!</string>
- <string name="gpodnetauth_finish_descr">Parabéns! A súa conta gpodder.net está conectada ao dispositivo. AntennaPod poderá agora sincronizar automaticamente as súas subscricións no dispositivo na conta de gpodder.net</string>
+ <string name="gpodnetauth_finish_descr">Parabéns! A túa conta gpodder.net está conectada ao dispositivo. AntennaPod poderá agora sincronizar automaticamente as túas subscricións no dispositivo na conta de gpodder.net</string>
<string name="gpodnetauth_finish_butsyncnow">Iniciar a sincronización</string>
<string name="gpodnetauth_finish_butgomainscreen">Ir a pantalla principal</string>
<string name="gpodnetsync_auth_error_title">fallo na autenticación en gpodder.net</string>
@@ -580,20 +581,20 @@
<string name="selected_folder_label">Cartafol escollido:</string>
<string name="create_folder_label">Crear cartafol</string>
<string name="choose_data_directory">Escoller cartafol de datos</string>
- <string name="choose_data_directory_message">Por favor escolla a base do cartafol dos seus datos. AntennaPod creará os subcartafoles axeitados.</string>
+ <string name="choose_data_directory_message">Por favor escolle a base do cartafol dos teus datos. AntennaPod creará os subcartafoles axeitados.</string>
<string name="choose_data_directory_permission_rationale">Precísase o acceso ao almacenamento externo para mudar o cartafol de datos</string>
<string name="choose_data_directory_available_space">%1$s de %2$s libre</string>
<string name="create_folder_msg">Crear un novo cartafol de nome \"%1$s\"?</string>
<string name="create_folder_success">Novo cartafol creado</string>
- <string name="create_folder_error_no_write_access">Non se pode escribir en este cartafol</string>
+ <string name="create_folder_error_no_write_access">Non se pode escribir neste cartafol</string>
<string name="create_folder_error_already_exists">O cartafol xa existe</string>
<string name="create_folder_error">Non se creou o cartafol</string>
<string name="folder_does_not_exist_error">\"%1$s\" non existe</string>
<string name="folder_not_readable_error">\"%1$s\" non se pode ler</string>
<string name="folder_not_writable_error">non se pode escribir en \"%1$s\"</string>
- <string name="folder_not_empty_dialog_title">O cartafol non está baldeiro</string>
- <string name="folder_not_empty_dialog_msg">O cartafol escollido non está baldeiro. As descargas de medios e outros ficheiros situaranse directamente en este cartafol. Proceder de todos xeitos?</string>
- <string name="set_to_default_folder">Escolla o cartafol por omisión</string>
+ <string name="folder_not_empty_dialog_title">O cartafol non está baleiro</string>
+ <string name="folder_not_empty_dialog_msg">O cartafol escollido non está baleiro. As descargas de medios e outros ficheiros situaranse directamente neste cartafol. Utilizalo igualmente?</string>
+ <string name="set_to_default_folder">Escolle o cartafol por omisión</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Pausar a reprodución en lugar de baixar o volume cando outra aplicación quere reproducir un son.</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pausa para interrupcións</string>
<string name="pref_resumeAfterCall_sum">Retomar a reprodución despois de rematar a chamada telefónica</string>
@@ -685,7 +686,7 @@
<string name="proxy_checking">Comprobando...</string>
<string name="proxy_test_successful">Proba exitosa</string>
<string name="proxy_test_failed">Fallo na proba</string>
- <string name="proxy_host_empty_error">Servidor non pode quedar baldeiro</string>
+ <string name="proxy_host_empty_error">Servidor non pode quedar baleiro</string>
<string name="proxy_host_invalid_error">O servidor indicado non é un dominio ou IP válidos</string>
<string name="proxy_port_invalid_error">Porto non válido</string>
<!--Subscriptions fragment-->
@@ -695,7 +696,7 @@
<string name="import_export_warning">Esta función experimental utilízase para transferir as súas subscricións e episodios reproducidos noutro dispositivo.\n\nAs bases de datos exportadas só se poden importar se utiliza a misma versión de AntennaPod. De todos xeitos, esta función pode comportarse de xeito raro.\n\nDespois de importar, os episodios poderían ser mostrados como descargados sin telo sido. Simplemente pulse o botón de reprodución dos episodios para que AntennaPod detecte esto.</string>
<string name="label_import">Importar</string>
<string name="label_export">Exportar</string>
- <string name="import_select_file">Escolla o ficheiro a importar</string>
+ <string name="import_select_file">Escolle o ficheiro a importar</string>
<string name="export_ok">Exportado con éxito.</string>
<string name="import_ok">Importación correcta.\n\nPulse OK para reiniciar AntennaPod</string>
<!--Casting-->
@@ -708,7 +709,7 @@
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<string name="cast_failed_setting_volume">Non se puido establecer o volume</string>
<string name="cast_failed_no_connection">Non hai conexión ao dispositivo de emisión</string>
- <string name="cast_failed_no_connection_trans">Perdeuse a conexión ao dispositivo de emisión. A aplicación está a intentar restablecela se fose posible. Por favor, agarde uns segundos e inténteo de novo.</string>
+ <string name="cast_failed_no_connection_trans">Perdeuse a conexión ao dispositivo de emisión. A aplicación está a intentar restablecela se fose posible. Por favor, agarda uns intres e inténtao outra vez.</string>
<string name="cast_failed_status_request">Fallo de sincronización co dispositivo de emisión</string>
<string name="cast_failed_seek">Non se puido cambiar a posición no dispositivo de emisión</string>
<string name="cast_failed_receiver_player_error">O reprodutor receptor atopou un fallo grave</string>
diff --git a/core/src/main/res/values-h768dp/dimens.xml b/core/src/main/res/values-h768dp/dimens.xml
deleted file mode 100644
index fd744e422..000000000
--- a/core/src/main/res/values-h768dp/dimens.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <dimen name="scrubber_vertical_padding">12dp</dimen>
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-hu/strings.xml b/core/src/main/res/values-hu/strings.xml
index b84d1900a..c82d505db 100644
--- a/core/src/main/res/values-hu/strings.xml
+++ b/core/src/main/res/values-hu/strings.xml
@@ -720,6 +720,7 @@
<string name="notification_channel_error_description">Akkor látszik, ha hiba történt, például ha a letöltés vagy a gpodder szinkronizálás sikertelen.</string>
<string name="import_bad_file">Érvénytelen/sérült fájl</string>
<!--Widget settings-->
+ <string name="widget_settings">Widget beállítások</string>
<string name="widget_create_button">Widget létrehozása</string>
<string name="widget_opacity">Átlátszatlanság</string>
</resources>
diff --git a/core/src/main/res/values-it/strings.xml b/core/src/main/res/values-it/strings.xml
index 384368d35..cef128007 100644
--- a/core/src/main/res/values-it/strings.xml
+++ b/core/src/main/res/values-it/strings.xml
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
- <string name="feed_update_receiver_name">Aggiorna Sottoscrizioni</string>
+ <string name="feed_update_receiver_name">Aggiorna sottoscrizioni</string>
<string name="feeds_label">Feed</string>
<string name="statistics_label">Statistiche</string>
<string name="add_feed_label">Aggiungi un podcast</string>
@@ -17,7 +17,7 @@
<string name="downloads_log_label">Registro</string>
<string name="subscriptions_label">Sottoscrizioni</string>
<string name="subscriptions_list_label">Elenco sottoscrizioni</string>
- <string name="cancel_download_label">Annulla\nil Download</string>
+ <string name="cancel_download_label">Annulla\nil download</string>
<string name="playback_history_label">Cronologia riproduzioni</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronizza con altri dispositivi</string>
@@ -28,15 +28,15 @@
<string name="total_time_listened_to_podcasts">Tempo totale di riproduzione:</string>
<string name="statistics_details_dialog">%1$d di %2$d episodi iniziati.\n\nRiprodotti %3$s di %4$s.</string>
<string name="statistics_mode">Modalità di calcolo</string>
- <string name="statistics_mode_normal">Calcola il tempo di riproduzione reale. Riprodurre un podcast due volte verrà contato due volte, segnarlo come riprodotto no.</string>
+ <string name="statistics_mode_normal">Tempo di riproduzione reale. Riprodurre due volte un episodio verrà contato doppio, segnarlo come riprodotto no.</string>
<string name="statistics_mode_count_all">Somma il tempo di riproduzione di tutti i podcast segnati come riprodotti</string>
- <string name="statistics_speed_not_counted">Avviso: La velocità di riproduzione non viene considerata.</string>
+ <string name="statistics_speed_not_counted">Avviso: la velocità di riproduzione non viene considerata.</string>
<string name="statistics_reset_data">Resetta statistiche</string>
- <string name="statistics_reset_data_msg">Verranno eliminate le statistiche del tempo di riproduzione per tutti gli episodi. Sei sicuro?</string>
+ <string name="statistics_reset_data_msg">Verranno eliminate le statistiche sul tempo di riproduzione di tutti gli episodi. Sei sicuro?</string>
<!--Main activity-->
<string name="drawer_open">Apri il menù</string>
<string name="drawer_close">Chiudi il menù</string>
- <string name="drawer_preferences">Preferenze del Drawer</string>
+ <string name="drawer_preferences">Preferenze del drawer</string>
<string name="drawer_feed_order_unplayed_episodes">Ordina per contatore</string>
<string name="drawer_feed_order_alphabetical">Ordina alfabeticamente</string>
<string name="drawer_feed_order_last_update">Ordina per data di pubblicazione</string>
@@ -47,13 +47,13 @@
<string name="drawer_feed_counter_downloaded">Numero di episodi scaricati</string>
<string name="drawer_feed_counter_none">Nulla</string>
<!--Webview actions-->
- <string name="open_in_browser_label">Apri nel Browser</string>
+ <string name="open_in_browser_label">Apri nel browser</string>
<string name="copy_url_label">Copia URL</string>
<string name="share_url_label">Condividi URL</string>
<string name="copied_url_msg">URL copiato negli appunti</string>
<string name="go_to_position_label">Vai a questa posizione</string>
<!--Playback history-->
- <string name="clear_history_label">Pulisci la cronologia</string>
+ <string name="clear_history_label">Svuota la cronologia</string>
<!--Other-->
<string name="confirm_label">Conferma</string>
<string name="cancel_label">Annulla</string>
@@ -68,7 +68,7 @@
<string name="error_msg_prefix">È stato rilevato un errore:</string>
<string name="needs_storage_permission">Questa operazione richiede l\'accesso alla memoria</string>
<string name="refresh_label">Aggiorna</string>
- <string name="external_storage_error_msg">Non risulta disponibile lo spazio di archiviazione esterno. Assicurati che lo spazio di archiviazione sia montato per permettere all\'applicazione di funzionare correttamente.</string>
+ <string name="external_storage_error_msg">Spazio di archiviazione esterno non disponibile. Assicurati che sia montato per consentire all\'app di funzionare correttamente.</string>
<string name="chapters_label">Capitoli</string>
<string name="chapter_duration">Durata: %1$s</string>
<string name="description_label">Descrizione</string>
@@ -78,9 +78,9 @@
<string name="close_label">Chiudi</string>
<string name="retry_label">Riprova</string>
<string name="auto_download_label">Includi nei download automatici</string>
- <string name="auto_download_apply_to_items_title">Applica ai Precedenti Episodi</string>
- <string name="auto_download_apply_to_items_message">L\'opzione <i>Download Automatico</i> verrà applicata ai nuovi episodi.\nVuoi anche applicarla agli episodi precedenti?</string>
- <string name="auto_delete_label">Elimina Episodi Automaticamente</string>
+ <string name="auto_download_apply_to_items_title">Applica agli episodi precedenti</string>
+ <string name="auto_download_apply_to_items_message">L\'opzione <i>Download automatico</i> verrà applicata ai nuovi episodi.\nVuoi applicarla anche agli episodi precedenti?</string>
+ <string name="auto_delete_label">Elimina episodi automaticamente</string>
<string name="parallel_downloads_suffix">\u0020download paralleli</string>
<string name="feed_auto_download_global">Predefinita globale</string>
<string name="feed_auto_download_always">Sempre</string>
@@ -101,7 +101,7 @@
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL del feed</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
- <string name="txtvfeedurl_label">Aggiungi un Podcast tramite URL</string>
+ <string name="txtvfeedurl_label">Aggiungi un podcast tramite URL</string>
<string name="browse_gpoddernet_label">Esplora gpodder.net</string>
<string name="discover">Scopri</string>
<string name="discover_more">altro »</string>
@@ -121,14 +121,14 @@
<string name="remove_feed_label">Rimuovi podcast</string>
<string name="share_label">Condividi...</string>
<string name="share_link_label">Condividi URL episodio</string>
- <string name="share_link_with_position_label">Condividi URL dell\'episodio con la posizione</string>
+ <string name="share_link_with_position_label">Condividi URL episodio con posizione</string>
<string name="share_file_label">Condividi il file</string>
<string name="share_website_url_label">Condividi URL del sito</string>
<string name="share_feed_url_label">Condividi URL del feed</string>
<string name="share_item_url_label">Condividi URL del media</string>
- <string name="share_item_url_with_position_label">Condividi URL del media con la posizione</string>
+ <string name="share_item_url_with_position_label">Condividi URL del media con posizione</string>
<string name="feed_delete_confirmation_msg">Conferma di voler eliminare il podcast \"%1$s\" e TUTTI i suoi episodi (compresi quelli scaricati).</string>
- <string name="feed_remover_msg">Rimozione del Feed in corso</string>
+ <string name="feed_remover_msg">Rimozione podcast in corso</string>
<string name="load_complete_feed">Aggiorna podcast completo</string>
<string name="batch_edit">Modifica in gruppo</string>
<string name="select_all_above">Seleziona tutti sopra</string>
@@ -144,7 +144,7 @@
<string name="hide_is_favorite_label">E\' preferito</string>
<string name="filtered_label">Filtrati</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Ultimo aggiornamento fallito</string>
- <string name="open_podcast">Apri Podcast</string>
+ <string name="open_podcast">Apri podcast</string>
<!--actions on feeditems-->
<string name="download_label">Download</string>
<plurals name="downloading_batch_label">
@@ -186,14 +186,14 @@
<item quantity="other">%d episodi rimossi dalla coda.</item>
</plurals>
<string name="add_to_favorite_label">Aggiungi ai preferiti</string>
- <string name="added_to_favorites">Aggiunto ai Preferiti</string>
+ <string name="added_to_favorites">Aggiunto ai preferiti</string>
<string name="remove_from_favorite_label">Rimuovi dai preferiti</string>
- <string name="removed_from_favorites">Rimosso dai Preferiti</string>
+ <string name="removed_from_favorites">Rimosso dai preferiti</string>
<string name="visit_website_label">Visita il sito</string>
<string name="skip_episode_label">Salta l\'episodio</string>
<string name="activate_auto_download">Attiva il download automatico</string>
<string name="deactivate_auto_download">Disattiva il download automatico</string>
- <string name="reset_position">Azzera la Posizione di Riproduzione</string>
+ <string name="reset_position">Azzera la posizione di riproduzione</string>
<string name="removed_item">Elemento rimosso</string>
<!--Download messages and labels-->
<string name="download_successful">successo</string>
@@ -210,10 +210,10 @@
<string name="download_error_connection_error">Errore di connessione</string>
<string name="download_error_unknown_host">Host sconosciuto</string>
<string name="download_error_unauthorized">Errore di autenticazione</string>
- <string name="download_error_file_type_type">Errore Formato FIle</string>
+ <string name="download_error_file_type_type">Errore del formato file</string>
<string name="download_error_forbidden">Proibito</string>
<string name="download_canceled_msg">Download annullato</string>
- <string name="download_canceled_autodownload_enabled_msg">Download annullato\n<i>Download Automatico</i> disabilitato per questo elemento</string>
+ <string name="download_canceled_autodownload_enabled_msg">Download annullato\n<i>Download automatico</i> disabilitato per questo elemento</string>
<string name="download_report_title">Download completato con un errore (o errori)</string>
<string name="download_report_content_title">Rapporto del download</string>
<string name="download_error_malformed_url">URL malformato</string>
@@ -225,17 +225,17 @@
<item quantity="other">%d download rimanenti</item>
</plurals>
<string name="downloads_processing">Elaborazione dei download in corso</string>
- <string name="download_notification_title">Download podcast in corso</string>
+ <string name="download_notification_title">Scaricamento podcast in corso</string>
<string name="download_report_content">%1$d download con successo, %2$d falliti</string>
- <string name="download_log_title_unknown">Titolo Sconosciuto</string>
+ <string name="download_log_title_unknown">Titolo sconosciuto</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">File multimediali</string>
<string name="download_request_error_dialog_message_prefix">Rilevato errore durante il download del file:\u0020</string>
<string name="null_value_podcast_error">Non è stato fornito alcun podcast da mostrare.</string>
<string name="authentication_notification_title">Autenticazione richiesta</string>
<string name="authentication_notification_msg">La risorsa che hai richiesto richiede un nome utente e una password</string>
- <string name="confirm_mobile_download_dialog_title">Conferma il download su cellulare</string>
- <string name="confirm_mobile_download_dialog_message_not_in_queue">Il download tramite rete mobile è disattivato nelle impostazioni.\n\nÈ possibile scegliere di aggiungere semplicemente l\'episodio alla coda o consentire temporaneamente il download.\n\n<small>La scelta verrà ricordata per 10 minuti.</small></string>
+ <string name="confirm_mobile_download_dialog_title">Conferma download su rete mobile</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Il download tramite rete mobile è disattivato nelle impostazioni.\n\nPuoi scegliere di aggiungere semplicemente l\'episodio alla coda o consentire temporaneamente i download.\n\n<small>La scelta sarà valida per 10 minuti.</small></string>
<string name="confirm_mobile_download_dialog_message">Il download tramite rete mobile è disattivato nelle impostazioni.\n\nVuoi abilitare temporaneamente il download?\n\n<small>La scelta verrà ricordata per 10 minuti.</small></string>
<string name="confirm_mobile_streaming_notification_title">Conferma streaming su rete mobile</string>
<string name="confirm_mobile_streaming_notification_message">Lo streaming su rete mobile è disattivato nelle impostazioni. Tocca per avviare comunque.</string>
@@ -263,7 +263,7 @@
<string name="queue_unlocked">Coda sbloccata</string>
<string name="queue_lock_warning">Se blocchi la coda non potrai più fare swipe o riordinare gli episodi.</string>
<string name="checkbox_do_not_show_again">Non mostrare più</string>
- <string name="clear_queue_label">Svuota la Coda</string>
+ <string name="clear_queue_label">Svuota la coda</string>
<string name="undo">Undo</string>
<string name="move_to_top_label">Sposta all\'inizio</string>
<string name="move_to_bottom_label">Sposta in fondo</string>
@@ -271,7 +271,7 @@
<string name="keep_sorted">Mantieni ordinato</string>
<string name="date">Per data</string>
<string name="duration">Per durata</string>
- <string name="episode_title">Titolo dell\'episodio</string>
+ <string name="episode_title">Titolo episodio</string>
<string name="feed_title">Titolo podcast</string>
<string name="random">Casuale</string>
<string name="smart_shuffle">Casuale intelligente</string>
@@ -326,14 +326,14 @@
<string name="preference_search_clear_history">Svuota cronologia</string>
<string name="media_player">Media player</string>
<string name="pref_episode_cleanup_title">Pulizia episodi</string>
- <string name="pref_episode_cleanup_summary">Gli episodi che non sono in coda e non sono tra i preferiti potrebbero essere rimossi se i Download Automatici richiedono maggiore spazio.</string>
+ <string name="pref_episode_cleanup_summary">Gli episodi che non sono in coda e non sono tra i preferiti potrebbero essere rimossi se i Download automatici richiedono altro spazio.</string>
<string name="pref_pauseOnDisconnect_sum">Sospendi la riproduzione quando le cuffie o il bluetooth vengono disconnessi</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Riprendi la riproduzione quando vengono riconnesse le cuffie</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Riprendi la riproduzione quando il Bluetooth si riconnette</string>
<string name="pref_hardwareForwardButtonSkips_title">Il tasto Avanti salta la traccia</string>
<string name="pref_hardwareForwardButtonSkips_sum">Quando viene premuto il tasto Avanti sul dispositivo bluetooth connesso, passa all\'episodio successivo invece di andare avanti veloce</string>
<string name="pref_hardwarePreviousButtonRestarts_title">Il tasto Indietro riavvia la traccia</string>
- <string name="pref_hardwarePreviousButtonRestarts_sum">Quando viene premuto il tasto fisico Indietro, viene riavviata la traccia invece di tornare indietro</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">Quando viene premuto il tasto fisico Indietro, viene riavviata la traccia invece di saltare indietro</string>
<string name="pref_followQueue_sum">Passa al prossimo episodio in coda quando viene completata la riproduzione</string>
<string name="pref_auto_delete_sum">Elimina l\'episodio quando viene completata la riproduzione</string>
<string name="pref_auto_delete_title">Elimina automaticamente</string>
@@ -347,7 +347,7 @@
<string name="network_pref">Rete</string>
<string name="pref_autoUpdateIntervallOrTime_title">Intervallo o orario di aggiornamento</string>
<string name="pref_autoUpdateIntervallOrTime_sum">Imposta un intervallo di tempo o un orario specifico in cui le sottoscrizioni vengono aggiornate automaticamente</string>
- <string name="pref_autoUpdateIntervallOrTime_message">Puoi impostare un <i>intervallo</i> come \"ogni 2 ore\", impostare <i>un\'ora del giorno</i> specifica, come \"7:00\" oppure <i>disabilitare</i> gli aggiornamenti automatici del tutto.\n\n<small>Nota: I tempi di aggiornamento non sono perfetti. Potrai riscontrare dei brevi ritardi.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Puoi impostare un <i>intervallo</i> come \"ogni 2 ore\", <i>un\'ora del giorno</i> specifica come \"7:00\" oppure <i>disabilitare</i> gli aggiornamenti automatici.\n\n<small>Nota: I tempi di aggiornamento non sono perfetti. Potrebbero esserci dei leggeri ritardi.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Disabilita</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Imposta Intervallo</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Imposta orario</string>
@@ -381,9 +381,9 @@
<string name="pref_autodl_wifi_filter_sum">Abilita il download automatico solo per alcune reti Wi-Fi selezionate.</string>
<string name="autodl_wifi_filter_permission_title">Autorizzazione richiesta</string>
<string name="autodl_wifi_filter_permission_message">Per il filtro Wi-Fi è richiesto l\'accesso alla posizione. Tocca per autorizzare.</string>
- <string name="pref_automatic_download_on_battery_title">Scarica quando la batteria non è in carica</string>
+ <string name="pref_automatic_download_on_battery_title">Scarica episodi con batteria non in carica</string>
<string name="pref_automatic_download_on_battery_sum">Permetti il download automatico quando la batteria non è in carica</string>
- <string name="pref_parallel_downloads_title">Download simulanei</string>
+ <string name="pref_parallel_downloads_title">Download simultanei</string>
<string name="pref_episode_cache_title">Cache degli episodi</string>
<string name="pref_episode_cache_summary">Numero di episodi scaricati memorizzabili sul dispositivo. I download automatici vengono interrotti se si raggiunge questo valore.</string>
<string name="pref_episode_cover_title">Usa immagine episodio</string>
@@ -434,7 +434,7 @@
<string name="pref_lockscreen_background_sum">Sostituisce l\'immagine della schermata di blocco con quella dell\'episodio in riproduzione. Mostrerà l\'immagine anche in app di terze parti.</string>
<string name="pref_showDownloadReport_title">Mostra il rapporto del download</string>
<string name="pref_showDownloadReport_sum">Se il download fallisce, genera un report che mostra i dettagli dell\'errore.</string>
- <string name="pref_expand_notify_unsupport_toast">Le versioni di Android prima della 4.1 non supportano le notifiche estese.</string>
+ <string name="pref_expand_notify_unsupport_toast">Le versioni Android precedenti alla 4.1 non supportano le notifiche estese.</string>
<string name="pref_enqueue_location_title">Posizione in coda</string>
<string name="pref_enqueue_location_sum">Aggiungi episodi a: %1$s</string>
<string name="enqueue_location_back">Retro</string>
@@ -463,7 +463,7 @@
<string name="media_player_builtin">Player Android integrato</string>
<string name="pref_skip_silence_title">Salta il silenzio audio</string>
<string name="pref_videoBehavior_title">Uscita dal video</string>
- <string name="pref_videoBehavior_sum">Comportamento quando si esce dalla riproduzione video</string>
+ <string name="pref_videoBehavior_sum">Comportamento quando si termina una riproduzione video</string>
<string name="stop_playback">Interrompi riproduzione</string>
<string name="continue_playback">Continua riproduzione audio</string>
<string name="behavior">Comportamento</string>
@@ -551,7 +551,7 @@
<string name="gpodnet_suggestions_header">SUGGERIMENTI</string>
<string name="gpodnet_search_hint">Cerca su gpodder.net</string>
<string name="gpodnetauth_login_title">Login</string>
- <string name="gpodnetauth_login_descr">Benvenuto sul processo di login di gpodder.net. Per prima cosa, inserisci le tue informazioni di login:</string>
+ <string name="gpodnetauth_login_descr">Benvenuto alla procedura di login di gpodder.net. Come prima cosa, inserisci le credenziali:</string>
<string name="gpodnetauth_login_butLabel">Login</string>
<string name="gpodnetauth_login_register">Se non hai ancora un account, puoi crearne uno qui:\nhttps://gpodder.net/register/</string>
<string name="username_label">Username</string>
@@ -563,11 +563,11 @@
<string name="gpodnetauth_device_butCreateNewDevice">Crea un nuovo dispositivo</string>
<string name="gpodnetauth_device_chooseExistingDevice">Scegli un dispositivo esistente:</string>
<string name="gpodnetauth_device_errorEmpty">L\'ID del dispositivo non può essere vuoto</string>
- <string name="gpodnetauth_device_errorAlreadyUsed">ID di dispositivo già in uso</string>
+ <string name="gpodnetauth_device_errorAlreadyUsed">ID del dispositivo già in uso</string>
<string name="gpodnetauth_device_caption_errorEmpty">La didascalia non può essere vuota</string>
<string name="gpodnetauth_device_butChoose">Scegli</string>
<string name="gpodnetauth_finish_title">Login effettuato!</string>
- <string name="gpodnetauth_finish_descr">Congraturazioni! Il tuo account gpodder.net è stato collegato con il tuo dispositivo. Da ora AntennaPod sincronizzerà automaticamente le sottoscrizioni sul tuo dispositivo con il tuo account gpodder.net.</string>
+ <string name="gpodnetauth_finish_descr">Congraturazioni! Il tuo account gpodder.net è stato collegato con il dispositivo. Ora AntennaPod sincronizzerà automaticamente le sottoscrizioni sul dispositivo con il tuo account gpodder.net.</string>
<string name="gpodnetauth_finish_butsyncnow">Avvia la sincronizzazione</string>
<string name="gpodnetauth_finish_butgomainscreen">Vai alla schermata principale</string>
<string name="gpodnetsync_auth_error_title">errore di autenticazione su gpodder.net</string>
@@ -584,7 +584,7 @@
<string name="choose_data_directory_message">Scegli la base della tua cartella dati. AntennaPod creerà le sottocartelle appropriate.</string>
<string name="choose_data_directory_permission_rationale">E\' necessario accedere alla memoria esterna per cambiare la cartella dei dati</string>
<string name="choose_data_directory_available_space">%1$s di %2$s liberi</string>
- <string name="create_folder_msg">Crea una nuova directory con nome \"%1$s\"?</string>
+ <string name="create_folder_msg">Creare una nuova directory con nome \"%1$s\"?</string>
<string name="create_folder_success">Crea una nuova directory</string>
<string name="create_folder_error_no_write_access">Impossibile scrivere in questa directory</string>
<string name="create_folder_error_already_exists">La cartella esiste già</string>
@@ -595,7 +595,7 @@
<string name="folder_not_empty_dialog_title">La cartella non è vuota</string>
<string name="folder_not_empty_dialog_msg">La cartella che hai selezionato non è vuota. I download dei media e altri file saranno creati in questa cartella. Continuare?</string>
<string name="set_to_default_folder">Scegli la cartella predefinita</string>
- <string name="pref_pausePlaybackForFocusLoss_sum">Sospendi la riproduzione invece di abbassare il volume quando un\'altra app emette un suono</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Quando un\'altra app emette un suono, sospendi la riproduzione invece di abbassare il volume</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pausa su interruzione</string>
<string name="pref_resumeAfterCall_sum">Riprendi la riproduzione al termine di una chiamata</string>
<string name="pref_resumeAfterCall_title">Riprendi dopo la chiamata</string>
@@ -622,7 +622,7 @@
<string name="episode_filters_include">Includi</string>
<string name="episode_filters_exclude">Escludi</string>
<string name="episode_filters_hint">Parole singole \n\"Parole multiple\"</string>
- <string name="keep_updated">Mantieni Aggiornato</string>
+ <string name="keep_updated">Mantieni aggiornato</string>
<string name="keep_updated_summary">Includi nell\'(auto-)aggiornamento di tutti i feed</string>
<string name="auto_download_disabled_globally">Il download automatico è disabilitato nelle Impostazioni generali.</string>
<!--Progress information-->
@@ -637,9 +637,9 @@
<string name="filter">Filtro</string>
<!--Episodes apply actions-->
<string name="all_label">Tutto</string>
- <string name="selected_all_label">Seleziona tutti gli Episodi</string>
+ <string name="selected_all_label">Seleziona tutti gli episodi</string>
<string name="none_label">Nulla</string>
- <string name="deselected_all_label">De-seleziona tutti gli episodi</string>
+ <string name="deselected_all_label">Deseleziona tutti gli episodi</string>
<string name="played_label">Riprodotti</string>
<string name="selected_played_label">Selezionati gli episodi riprodotti</string>
<string name="unplayed_label">Non riprodotti</string>
@@ -675,7 +675,7 @@
<string name="right_short">Dx</string>
<string name="audio_effects">Effetti Audio</string>
<string name="stereo_to_mono">Downmix: da Stereo a Mono</string>
- <string name="sonic_only">solo Sonic</string>
+ <string name="sonic_only">Solo Sonic</string>
<string name="exoplayer_only">Solo ExoPlayer</string>
<!--proxy settings-->
<string name="proxy_type_label">Tipo</string>
@@ -693,7 +693,7 @@
<string name="subscription_num_columns">Numero di colonne</string>
<!--Database import/export-->
<string name="import_export">Importa/Esporta database</string>
- <string name="import_export_warning">Questa funzione sperimentale può essere usata per trasferire le sottoscrizioni e gli episoti completati ad un altro dispositivo.\n\nIl database potrà essere importato solo se si usa la stessa versione di AntennaPod, altrimenti potrebbero generarsi comportamenti anomali.\n\nDopo l\'importazione, gli episodi potrebbero essere mostrati come scaricati anche se non lo sono. Basta premere Play per farli riconoscere ad AntennaPod.</string>
+ <string name="import_export_warning">Questa funzione sperimentale può essere usata per trasferire le sottoscrizioni e gli episodi completati ad un altro dispositivo.\n\nIl database potrà essere importato solo se si usa la stessa versione di AntennaPod, altrimenti potrebbero generarsi comportamenti anomali.\n\nDopo l\'importazione, gli episodi potrebbero essere mostrati come scaricati anche se non lo sono. Basta premere Play per farli riconoscere ad AntennaPod.</string>
<string name="label_import">Importa</string>
<string name="label_export">Esporta</string>
<string name="import_select_file">Scegli file da importare</string>
@@ -716,7 +716,7 @@
<string name="cast_failed_media_error_skipping">Errore nella riproduzione. Salto...</string>
<!--Notification channels-->
<string name="notification_channel_user_action">Azione richesta</string>
- <string name="notification_channel_user_action_description">Mostra se è richesto un tuo intervento, per sempio se è necessario inserire la password.</string>
+ <string name="notification_channel_user_action_description">Mostra se è richesto un tuo intervento, per esempio se è necessario inserire la password.</string>
<string name="notification_channel_downloading">Scaricando</string>
<string name="notification_channel_downloading_description">Mostra mentre è in corso il download</string>
<string name="notification_channel_playing">In riproduzione</string>
diff --git a/core/src/main/res/values-large/dimens.xml b/core/src/main/res/values-large/dimens.xml
index 2d107eef0..27e269099 100644
--- a/core/src/main/res/values-large/dimens.xml
+++ b/core/src/main/res/values-large/dimens.xml
@@ -2,6 +2,4 @@
<resources>
<dimen name="thumbnail_length">170dp</dimen>
- <dimen name="thumbnail_length_queue_item">64dp</dimen>
- <dimen name="thumbnail_length_downloaded_item">64dp</dimen>
</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml
index 338f4c193..f6cb6c26d 100644
--- a/core/src/main/res/values-nl/strings.xml
+++ b/core/src/main/res/values-nl/strings.xml
@@ -68,7 +68,7 @@
<string name="error_msg_prefix">Er is een fout opgetreden:</string>
<string name="needs_storage_permission">Machtiging vereist voor gebruik van opslag</string>
<string name="refresh_label">Verversen</string>
- <string name="external_storage_error_msg">Geen externe opslag beschikbaar. Zorg ervoor dat de externe opslag is aangekoppeld zodat de app goed kan werken.</string>
+ <string name="external_storage_error_msg">Geen externe opslag beschikbaar. Zorg ervoor dat de externe opslag gekoppeld is zodat de app juist kan functioneren.</string>
<string name="chapters_label">Hoofdstukken</string>
<string name="chapter_duration">Duur: %1$s</string>
<string name="description_label">Omschrijving</string>
@@ -101,12 +101,12 @@
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Feed-url</string>
<string name="etxtFeedurlHint">www.voorbeeld.nl/feed</string>
- <string name="txtvfeedurl_label">Podcast toevoegen middels url</string>
+ <string name="txtvfeedurl_label">Podcast toevoegen middels URL</string>
<string name="browse_gpoddernet_label">Verken gpodder.net</string>
<string name="discover">Ontdekken</string>
<string name="discover_more">meer »</string>
<!--Actions on feeds-->
- <string name="mark_all_read_label">Alles markeren als afgespeeld</string>
+ <string name="mark_all_read_label">Alles als afgespeeld markeren</string>
<string name="mark_all_read_msg">Alle afleveringen zijn gemarkeerd als afgespeeld</string>
<string name="mark_all_read_confirmation_msg">Bevestig dat je alle afleveringen wilt markeren als afgespeeld.</string>
<string name="mark_all_read_feed_confirmation_msg">Bevestig dat je alle afleveringen van deze podcast wilt markeren als afgespeeld.</string>
@@ -163,13 +163,13 @@
</plurals>
<string name="remove_new_flag_label">\'Nieuw\'-label verwijderen</string>
<string name="removed_new_flag_label">\'Nieuw\'-label is verwijderd</string>
- <string name="mark_read_label">Markeren als afgespeeld</string>
+ <string name="mark_read_label">Als afgespeeld markeren</string>
<string name="marked_as_read_label">Gemarkeerd als afgespeeld</string>
<plurals name="marked_read_batch_label">
<item quantity="one">%d aflevering gemarkeerd als afgespeeld.</item>
<item quantity="other">%d afleveringen gemarkeerd als afgespeeld.</item>
</plurals>
- <string name="mark_unread_label">Markeren als niet-afgespeeld</string>
+ <string name="mark_unread_label">Als niet-afgespeeld markeren</string>
<plurals name="marked_unread_batch_label">
<item quantity="one">%d aflevering gemarkeerd als niet-afgespeeld.</item>
<item quantity="other">%d afleveringen gemarkeerd als niet-afgespeeld.</item>
diff --git a/core/src/main/res/values-pl-rPL/strings.xml b/core/src/main/res/values-pl-rPL/strings.xml
index 22f0cc64e..05204fd4f 100644
--- a/core/src/main/res/values-pl-rPL/strings.xml
+++ b/core/src/main/res/values-pl-rPL/strings.xml
@@ -302,6 +302,7 @@
<!--Variable Speed-->
<string name="download_plugin_label">Pobierz wtyczkę</string>
<string name="no_playback_plugin_title">Wtyczka nie zainstalowana</string>
+ <string name="no_playback_plugin_or_sonic_msg">Aby używać zmiennej prędkości odtwarzania, zalecamy używanie wbudowanego odtwarzacza Sonic.</string>
<string name="set_playback_speed_label">Prędkość odtwarzania</string>
<string name="enable_sonic">Włącz Sonic</string>
<!--Empty list labels-->
@@ -750,6 +751,7 @@ https://gpodder.net/register/</string>
<string name="notification_channel_error_description">Pokazywane gdy coś pójdzie nie tak, na przykład jeśli nie powiedzie się pobieranie lub synchronizacja z gpodder.</string>
<string name="import_bad_file">Nieprawidłowy/uszkodzony plik</string>
<!--Widget settings-->
+ <string name="widget_settings">Ustawienia widżetu</string>
<string name="widget_create_button">Dodaj widżet</string>
<string name="widget_opacity">Nieprzezroczystość</string>
</resources>
diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml
index 34d2b0ebe..37f1049ac 100644
--- a/core/src/main/res/values-uk-rUA/strings.xml
+++ b/core/src/main/res/values-uk-rUA/strings.xml
@@ -105,6 +105,7 @@
<string name="etxtFeedurlHint">Посилання на канал чи сторінку</string>
<string name="txtvfeedurl_label">Додати подкаст за URL</string>
<string name="browse_gpoddernet_label">Переглянути gpodder.net</string>
+ <string name="discover_more">більше »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Позначити всі як відтворені</string>
<string name="mark_all_read_msg">Позначено всі епізоди як відтворені</string>
@@ -117,12 +118,13 @@
<string name="rename_feed_label">Перейменувати подкаст</string>
<string name="remove_feed_label">Видалити подкаст</string>
<string name="share_label">Поділитися…</string>
- <string name="share_link_label">Поділитись посиланням на епізод</string>
+ <string name="share_link_label">Поділитися посиланням на епізод</string>
<string name="share_link_with_position_label">Поділіться посиланням на епізод з позицією відтворення</string>
- <string name="share_file_label">Поділитись файлом</string>
- <string name="share_feed_url_label">Поділитись посиланням на канал</string>
- <string name="share_item_url_label">Поділитись посиланням на медіа-файл</string>
- <string name="share_item_url_with_position_label">Поділитись посиланням на медіа-файл з позицією відтворення</string>
+ <string name="share_file_label">Поділитися файлом</string>
+ <string name="share_website_url_label">Поділитися посиланням на сайт</string>
+ <string name="share_feed_url_label">Поділитися посиланням на канал</string>
+ <string name="share_item_url_label">Поділитися посиланням на медіа-файл</string>
+ <string name="share_item_url_with_position_label">Поділитися посиланням на медіа-файл з позицією відтворення</string>
<string name="feed_delete_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте видалити подкаст \"%1$s\" і ВСІ його епізоди (разом з завантаженими).</string>
<string name="feed_remover_msg">Видалення подкасту</string>
<string name="load_complete_feed">Оновити весь подкаст</string>
@@ -245,6 +247,7 @@
<string name="confirm_mobile_download_dialog_title">Підтвердження завантажень через мобільні мережі</string>
<string name="confirm_mobile_download_dialog_message_not_in_queue">Завантаження через мобільні мережі вимкнено в налаштуваннях.\n\nВибрати додавання епізоду до черги чи тимчасове завантаження?\n\n<small>Ваш вибір буде дійсним 10 хвилин.</small></string>
<string name="confirm_mobile_download_dialog_message">Завантаження через мобільні мережі вимкнено в налаштуваннях.\n\nУвімкнути тимчасово?\n\n<small>Ваш вибір буде дійсним 10 хвилин.</small></string>
+ <string name="confirm_mobile_streaming_button_always">Завжди дозволяти</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Лише додати до черги</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Увімкнути тимчасово</string>
<!--Mediaplayer messages-->
@@ -254,6 +257,7 @@
<string name="player_ready_msg">Готов</string>
<string name="player_seeking_msg">Шукаю</string>
<string name="playback_error_server_died">Сервер помер</string>
+ <string name="playback_error_unsupported">Тип медіа не підтримується</string>
<string name="playback_error_unknown">Невідома помилка</string>
<string name="no_media_playing_label">Немає що грати</string>
<string name="player_buffering_msg">Буферізую</string>
@@ -264,6 +268,7 @@
<string name="unlock_queue">Розблокувати чергу</string>
<string name="queue_locked">Чергу заблоковано</string>
<string name="queue_unlocked">Чергу розблоковано</string>
+ <string name="checkbox_do_not_show_again">Більше не показувати</string>
<string name="clear_queue_label">Очистити чергу</string>
<string name="undo">Скасувати</string>
<string name="move_to_top_label">Догори</string>
@@ -278,6 +283,8 @@
<string name="ascending">За зростанням</string>
<string name="descending">За спаданням</string>
<string name="clear_queue_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте вилучити всі епізоди з черги.</string>
+ <string name="sort_old_to_new">Спочатку старіші</string>
+ <string name="sort_new_to_old">Спочатку новіші</string>
<!--Variable Speed-->
<string name="download_plugin_label">Завантажити додаток</string>
<string name="no_playback_plugin_title">Додаток не встановлено</string>
@@ -301,7 +308,10 @@
<string name="no_new_episodes_label">Коли з’являться нові епізоди, іх буде показано тут.</string>
<string name="no_fav_episodes_head_label">Немає улюблених епізодів</string>
<string name="no_fav_episodes_label">Ви можете додавати епізоди до улюблених за допомогою довгого натискання.</string>
- <string name="no_chapters_label">В цьому епізоді немає розділів.</string>
+ <string name="no_chapters_head_label">Немає глав</string>
+ <string name="no_chapters_label">В цьому епізоді немає глав.</string>
+ <string name="no_subscriptions_head_label">Немає підписок</string>
+ <string name="no_subscriptions_label">Щоб підписатись на подкаст, натисніть на значок плюса нижче</string>
<!--Preferences-->
<string name="storage_pref">Зберігання</string>
<string name="project_pref">Проект</string>
@@ -314,6 +324,9 @@
<string name="appearance">Вигляд</string>
<string name="external_elements">Зовнішні елементи</string>
<string name="interruptions">Тимчасові паузи</string>
+ <string name="preference_search_hint">Пошук...</string>
+ <string name="preference_search_no_results">Немає результатів</string>
+ <string name="preference_search_clear_history">Очистити історію</string>
<string name="media_player">Медіа програвач</string>
<string name="pref_episode_cleanup_title">Очищення епізодів</string>
<string name="pref_episode_cleanup_summary">Епізоди що не знаходяться в черзі та не помічені як улюблені можуть бути видалені якщо Автозавантажувач потребуватиме місце для нових епізодів.</string>
@@ -365,6 +378,7 @@
<string name="pref_automatic_download_on_battery_sum">Дозволити автозавантаження коли зарядний пристрій не підключений</string>
<string name="pref_parallel_downloads_title">Паралельні завантаження</string>
<string name="pref_episode_cache_title">Кеш епізодів</string>
+ <string name="pref_theme_title_use_system">Використовувати системну тему</string>
<string name="pref_theme_title_light">Світла</string>
<string name="pref_theme_title_dark">Темна</string>
<string name="pref_theme_title_trueblack">Чорна (для AMOLED)</string>
@@ -389,6 +403,7 @@
<string name="pref_gpodnet_notifications_sum">Це налаштування не застосовується до помилок автентифікації.</string>
<string name="pref_playback_speed_title">Швидкість програвання</string>
<string name="pref_playback_speed_sum">Налаштування швідкості доступно для змінної швидкості програвання</string>
+ <string name="pref_feed_playback_speed_sum">Виберіть швидкість відтворення епізодів з цього каналу</string>
<string name="pref_fast_forward">Час, що пропускається кнопкою перемотки вперед</string>
<string name="pref_fast_forward_sum">Налаштувати кількість секунд, які пропускаються при натисканні кнопки перемотки вперед</string>
<string name="pref_rewind">Час, що пропускається кнопкою відмотки назад</string>
@@ -408,14 +423,21 @@
<string name="pref_showDownloadReport_title">Показати звіт про завантаження</string>
<string name="pref_showDownloadReport_sum">У разі помилки при завантаженні створити детальний звіт про помилку.</string>
<string name="pref_expand_notify_unsupport_toast">Android до версії 4.1 не підтримує розширені повідомлення.</string>
+ <string name="pref_enqueue_location_sum">Додати епізоди до: %1$s</string>
+ <string name="enqueue_location_after_current">Після поточного епізоду</string>
<string name="pref_smart_mark_as_played_disabled">Вимкнено</string>
<string name="pref_image_cache_size_title">Розмір кеша зображень</string>
<string name="pref_image_cache_size_sum">Розмір дискового кеша для зображень.</string>
+ <string name="bug_report_title">Повідомити про помилку</string>
+ <string name="open_bug_tracker">Відкрити трекер помилок</string>
+ <string name="copy_to_clipboard">Копіювати</string>
+ <string name="copied_to_clipboard">Скопійовано</string>
<string name="experimental_pref">Експериментальні</string>
<string name="pref_media_player_message">Оберіть медіа плеєр для програвання файлів</string>
<string name="pref_current_value">Поточне значення: %1$s</string>
<string name="pref_proxy_title">Проксі</string>
<string name="pref_proxy_sum">Застосувати проксі сервер</string>
+ <string name="pref_faq">Часті питання</string>
<string name="pref_no_browser_found">Веб браузер не знайдено.</string>
<string name="pref_cast_title">Підтримка для Chromecast</string>
<string name="pref_cast_message_play_flavor">Включити підтримку програвання на таких пристроях як Chromecast або Android TV</string>
@@ -443,6 +465,14 @@
<string name="pref_delete_removes_from_queue_sum">Автоматично видаляти епізод із черги, коли він буде видалений.</string>
<!--About screen-->
<string name="about_pref">Про програму</string>
+ <string name="antennapod_version">Версія AntennaPod</string>
+ <string name="developers">Розробники</string>
+ <string name="developers_summary">Кожен може допомогти AntennaPod стати краще</string>
+ <string name="translators">Перекладачі</string>
+ <string name="translators_summary">Переклади створюються користувачами AntennaPod за допомогою Transifex</string>
+ <string name="privacy_policy">Політика конфіденційності</string>
+ <string name="licenses">Ліцензії</string>
+ <string name="licenses_summary">AntennaPod використовує інше чудове програмне забезпечення</string>
<!--Search-->
<string name="search_hint">Пошук епізодів</string>
<string name="found_in_shownotes_label">Знайдено в нотатках епізода</string>
@@ -536,12 +566,14 @@
<string name="gpodnetsync_error_descr">Трапилась помилка при сінхронизації:\u0020</string>
<string name="gpodnetsync_pref_report_successful">Успішно</string>
<string name="gpodnetsync_pref_report_failed">Невдало</string>
+ <string name="gpodnetsync_username_characters_error">Імена користувачів можуть містити лише літери, цифри, дефіси та підкреслення.</string>
<!--Directory chooser-->
<string name="selected_folder_label">Обрати папку:</string>
<string name="create_folder_label">Нова папка</string>
<string name="choose_data_directory">Обрати папку</string>
<string name="choose_data_directory_message">Оберіть, будь ласка, базовий каталог даних. AntennaPod створить відповідні підрозділи.</string>
<string name="choose_data_directory_permission_rationale">Для зміни папки зберігання даних потрібен доступ до зовнішнього носія</string>
+ <string name="choose_data_directory_available_space">%1$s з %2$s вільно</string>
<string name="create_folder_msg">Створити папку з ім\'ям \"%1$s\"?</string>
<string name="create_folder_success">Створена нова папка</string>
<string name="create_folder_error_no_write_access">Не можу записати в цю папку</string>
@@ -560,6 +592,7 @@
<string name="pref_restart_required">Для застосування змін потрібно перезапустити AntennaPod</string>
<!--Online feed view-->
<string name="subscribe_label">Підписатися</string>
+ <string name="subscribing_label">Підписка...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Перемотка назад</string>
<string name="fast_forward_label">Перемотка вперед</string>
@@ -580,13 +613,17 @@
<string name="episode_filters_exclude">Виключити</string>
<string name="episode_filters_hint">Окремі слова \n\"Кілька слів\"</string>
<string name="keep_updated">Підтримувати оновленим</string>
+ <string name="keep_updated_summary">Включити цей канал під час (авто-)оновлення всіх каналів</string>
+ <string name="auto_download_disabled_globally">Автозавантаження вимкнено в налаштуваннях AntennaPod</string>
<!--Progress information-->
<string name="progress_upgrading_database">Оновлення бази даних</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Імпорт подкастів з інших програм...</string>
<!--Add podcast fragment-->
+ <string name="search_podcast_hint">Шукати подкаст...</string>
<string name="search_itunes_label">Пошук в iTunes</string>
<string name="search_fyyd_label">Шукати в fyyd</string>
+ <string name="advanced_search">Розширений пошук</string>
<string name="filter">Фільтр</string>
<!--Episodes apply actions-->
<string name="all_label">Всі</string>
@@ -676,5 +713,9 @@
<string name="notification_channel_playing_description">Дозволяє керувати відтворенням. Це основне сповіщення, яке ви бачите під час відтворення подкасту.</string>
<string name="notification_channel_error">Помилки</string>
<string name="notification_channel_error_description">Відображається, якщо щось пішло не так, наприклад, якщо не вдалося завантажити або синхронізувати з gpodder.</string>
+ <string name="import_bad_file">Недоступний/пошкоджений файл</string>
<!--Widget settings-->
+ <string name="widget_settings">Налаштування віджету</string>
+ <string name="widget_create_button">Створити віджет</string>
+ <string name="widget_opacity">Непрозорість</string>
</resources>
diff --git a/core/src/main/res/values-v26/styles.xml b/core/src/main/res/values-v26/styles.xml
new file mode 100644
index 000000000..87453eb5e
--- /dev/null
+++ b/core/src/main/res/values-v26/styles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.AntennaPod.Splash" parent="Theme.Base.AntennaPod.Splash">
+ <item name="android:windowSplashscreenContent">@drawable/bg_splash</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-zh-rCN/strings.xml b/core/src/main/res/values-zh-rCN/strings.xml
index 123c1cc0d..758e7de78 100644
--- a/core/src/main/res/values-zh-rCN/strings.xml
+++ b/core/src/main/res/values-zh-rCN/strings.xml
@@ -274,6 +274,7 @@
<!--Variable Speed-->
<string name="download_plugin_label">插件下载</string>
<string name="no_playback_plugin_title">插件没有安装</string>
+ <string name="no_playback_plugin_or_sonic_msg">为了使变速播放正常工作,我们建议启用内置的Sonic媒体播放器</string>
<string name="set_playback_speed_label">播放速度</string>
<string name="enable_sonic">允许声音</string>
<!--Empty list labels-->
@@ -712,6 +713,7 @@
<string name="notification_channel_error_description">发生错误时显示,比如下载或与gpodder的同步失败</string>
<string name="import_bad_file">无效/损坏文件</string>
<!--Widget settings-->
+ <string name="widget_settings">小部件设置</string>
<string name="widget_create_button">创建小部件</string>
<string name="widget_opacity">不透明度</string>
</resources>
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index facde2c59..dc79905cd 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -13,6 +13,18 @@
<item>never</item>
</string-array>
+ <string-array name="spnVolumeReductionItems">
+ <item>@string/feed_volume_reduction_off</item>
+ <item>@string/feed_volume_reduction_light</item>
+ <item>@string/feed_volume_reduction_heavy</item>
+ </string-array>
+
+ <string-array name="spnVolumeReductionValues">
+ <item>off</item>
+ <item>light</item>
+ <item>heavy</item>
+ </string-array>
+
<string-array name="smart_mark_as_played_values">
<item>0</item>
<item>15</item>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 755bbac58..aa93398f5 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -9,6 +9,7 @@
<attr name="av_fast_forward" format="reference"/>
<attr name="av_pause" format="reference"/>
<attr name="av_play" format="reference"/>
+ <attr name="av_skip" format="reference"/>
<attr name="av_speed" format="reference"/>
<attr name="av_rewind" format="reference"/>
<attr name="content_discard" format="reference"/>
@@ -26,18 +27,10 @@
<attr name="social_share" format="reference"/>
<attr name="stat_playlist" format="reference"/>
<attr name="ic_folder" format="reference"/>
- <attr name="type_audio" format="reference"/>
<attr name="type_video" format="reference"/>
- <attr name="overlay_drawable" format="reference"/>
<attr name="dragview_background" format="reference"/>
- <attr name="dragview_float_background" format="reference"/>
- <attr name="ic_new" format="reference"/>
<attr name="ic_history" format="reference"/>
- <attr name="av_play_big" format="reference"/>
- <attr name="av_pause_big" format="reference"/>
- <attr name="av_ff_big" format="reference"/>
- <attr name="av_rew_big" format="reference"/>
- <attr name="av_skip_big" format="reference"/>
+ <attr name="ic_settings_playback" format="reference"/>
<attr name="ic_settings" format="reference"/>
<attr name="ic_lock_open" format="reference"/>
<attr name="ic_lock_closed" format="reference"/>
@@ -52,6 +45,7 @@
<attr name="ic_select_none" format="reference"/>
<attr name="ic_sort" format="reference"/>
<attr name="ic_key" format="reference"/>
+ <attr name="ic_volume_adaption" format="reference"/>
<attr name="ic_sd_storage" format="reference"/>
<attr name="ic_create_new_folder" format="reference"/>
<attr name="ic_cast_disconnect" format="reference"/>
@@ -64,17 +58,11 @@
<attr name="currently_playing_background" format="color"/>
<attr name="ic_bookmark" format="reference"/>
<attr name="ic_settings_speed" format="reference" />
-
- <!-- Used in itemdescription -->
- <attr name="non_transparent_background" format="reference"/>
- <attr name="overlay_background" format="color"/>
-
<attr name="nav_drawer_background" format="color"/>
<attr name="drawer_activated_color" format="color"/>
-
- <attr name="about_screen_background" format="color"/>
- <attr name="about_screen_card_background" format="color"/>
- <attr name="about_screen_card_border" format="color"/>
- <attr name="about_screen_font_color" format="color"/>
<attr name="batch_edit_fab_icon" format="reference"/>
+
+ <declare-styleable name="SquareImageView">
+ <attr name="useMinimum" format="boolean" />
+ </declare-styleable>
</resources>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index fea7da4a4..b162038cf 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -7,8 +7,8 @@
<color name="black">#000000</color>
<color name="holo_blue_light">#33B5E5</color>
<color name="holo_blue_dark">#0099CC</color>
- <color name="download_success_green">#669900</color>
- <color name="download_failed_red">#CC0000</color>
+ <color name="download_success_green">#248800</color>
+ <color name="download_failed_red">#B00020</color>
<color name="status_progress">#E033B5E5</color>
<color name="overlay_dark">#2C2C2C</color>
<color name="overlay_light">#FFFFFF</color>
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index 02c398b62..bef2d311c 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -10,8 +10,8 @@
<dimen name="text_size_navdrawer">16sp</dimen>
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">22sp</dimen>
- <dimen name="thumbnail_length_itemlist">64dp</dimen>
- <dimen name="thumbnail_length_queue_item">64dp</dimen>
+ <dimen name="thumbnail_length_itemlist">56dp</dimen>
+ <dimen name="thumbnail_length_queue_item">56dp</dimen>
<dimen name="thumbnail_length_downloaded_item">64dp</dimen>
<dimen name="thumbnail_length_onlinefeedview">100dp</dimen>
<dimen name="feeditemlist_header_height">132dp</dimen>
@@ -23,7 +23,7 @@
<dimen name="listitem_threeline_textleftpadding">16dp</dimen>
<dimen name="listitem_threeline_textrightpadding">8dp</dimen>
- <dimen name="listitem_threeline_verticalpadding">16dp</dimen>
+ <dimen name="listitem_threeline_verticalpadding">8dp</dimen>
<dimen name="listitem_threeline_horizontalpadding">16dp</dimen>
<dimen name="list_vertical_padding">8dp</dimen>
@@ -31,12 +31,11 @@
<dimen name="listitem_icon_rightpadding">16dp</dimen>
<dimen name="audioplayer_playercontrols_length">48dp</dimen>
+ <dimen name="audioplayer_playercontrols_length_big">64dp</dimen>
<dimen name="media_router_controller_playback_control_vertical_padding">16dp</dimen>
<dimen name="media_router_controller_playback_control_horizontal_spacing">12dp</dimen>
<dimen name="media_router_controller_playback_control_start_padding">24dp</dimen>
<dimen name="media_router_controller_bottom_margin">8dp</dimen>
- <dimen name="scrubber_vertical_padding">0dp</dimen>
-
</resources>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 2b48cb5ee..8cb9f7b84 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -7,7 +7,7 @@
<string name="app_name" translate="false">AntennaPod</string>
<string name="provider_authority" translate="false">de.danoeh.antennapod.provider</string>
<string name="feed_update_receiver_name">Update Subscriptions</string>
- <string name="feeds_label">Feeds</string>
+ <string name="feeds_label">Podcasts</string>
<string name="statistics_label">Statistics</string>
<string name="add_feed_label">Add Podcast</string>
<string name="episodes_label">Episodes</string>
@@ -96,7 +96,13 @@
<string name="auto_download_apply_to_items_title">Apply to Previous Episodes</string>
<string name="auto_download_apply_to_items_message">The new <i>Auto Download</i> setting will automatically be applied to new episodes.\nDo you also want to apply it to previously published episodes?</string>
<string name="auto_delete_label">Auto Delete Episode</string>
+ <string name="feed_volume_reduction">Volume Reduction</string>
+ <string name="feed_volume_reduction_summary">Turn down volume for episodes of this feed: \%s</string>
+ <string name="feed_volume_reduction_off">Off</string>
+ <string name="feed_volume_reduction_light">Light</string>
+ <string name="feed_volume_reduction_heavy">Heavy</string>
<string name="parallel_downloads_suffix">\u0020parallel downloads</string>
+ <string name="download_queued">Download queued</string>
<string name="feed_auto_download_global">Global default</string>
<string name="feed_auto_download_always">Always</string>
<string name="feed_auto_download_never">Never</string>
@@ -116,7 +122,7 @@
<string name="loading_more">Loading more…</string>
<!-- 'Add Feed' Activity labels -->
- <string name="feedurl_label">Feed URL</string>
+ <string name="feedurl_label">Podcast feed URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Add Podcast by URL</string>
<string name="browse_gpoddernet_label">Browse gpodder.net</string>
@@ -142,7 +148,7 @@
<string name="share_link_with_position_label">Share Episode URL with Position</string>
<string name="share_file_label">Share File</string>
<string name="share_website_url_label">Share Website URL</string>
- <string name="share_feed_url_label">Share Feed URL</string>
+ <string name="share_feed_url_label">Share Podcast URL</string>
<string name="share_item_url_label">Share Media File URL</string>
<string name="share_item_url_with_position_label">Share Media File URL with Position</string>
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete the podcast \"%1$s\" and ALL its episodes (including downloaded episodes).</string>
@@ -187,6 +193,7 @@
<string name="marked_as_read_label">Marked as played</string>
<string name="mark_read_no_media_label">Mark as read</string>
<string name="marked_as_read_no_media_label">Marked as read</string>
+ <string name="play_this_to_seek_position">To jump to positions, you need to play the episode</string>
<plurals name="marked_read_batch_label">
<item quantity="one">%d episode marked as played.</item>
<item quantity="other">%d episodes marked as played.</item>
@@ -307,6 +314,7 @@
<string name="clear_queue_confirmation_msg">Please confirm that you want to clear the queue of ALL of the episodes in it</string>
<string name="sort_old_to_new">Old to new</string>
<string name="sort_new_to_old">New to old</string>
+ <string name="time_left_label">Time left:\u0020</string>
<!-- Variable Speed -->
<string name="download_plugin_label">Download Plugin</string>
@@ -380,7 +388,7 @@
<string name="network_pref">Network</string>
<string name="network_pref_sum">Update interval, Download controls, Mobile data</string>
<string name="pref_autoUpdateIntervallOrTime_title">Update Interval or Time of Day</string>
- <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the feeds automatically</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the podcasts automatically</string>
<string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string>
@@ -395,7 +403,7 @@
<string name="pref_stream_over_download_sum">Display stream button instead of download button in lists.</string>
<string name="pref_mobileUpdate_title">Mobile Updates</string>
<string name="pref_mobileUpdate_sum">Select what should be allowed over the mobile data connection</string>
- <string name="pref_mobileUpdate_refresh">Feed refresh</string>
+ <string name="pref_mobileUpdate_refresh">Podcast refresh</string>
<string name="pref_mobileUpdate_images">Cover images</string>
<string name="pref_mobileUpdate_auto_download">Auto download</string>
<string name="pref_mobileUpdate_episode_download">Episode download</string>
@@ -448,7 +456,7 @@
<string name="pref_gpodnet_notifications_sum">This setting does not apply to authentication errors.</string>
<string name="pref_playback_speed_title">Playback Speeds</string>
<string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string>
- <string name="pref_feed_playback_speed_sum">The speed to use when starting audio playback for episodes in this feed</string>
+ <string name="pref_feed_playback_speed_sum">The speed to use when starting audio playback for episodes in this podcast</string>
<string name="pref_playback_time_respects_speed_title">Adjust media info to playback speed</string>
<string name="pref_playback_time_respects_speed_sum">Displayed position and duration are adapted to playback speed</string>
<string name="pref_fast_forward">Fast Forward Skip Time</string>
@@ -529,45 +537,45 @@
<string name="licenses_summary">AntennaPod uses other great software</string>
<!-- Search -->
- <string name="search_hint">Search for episodes</string>
- <string name="found_in_shownotes_label">Found in show notes</string>
- <string name="found_in_chapters_label">Found in chapters</string>
- <string name="found_in_authors_label">Found in author(s)</string>
- <string name="found_in_feeds_label">Found in podcast</string>
<string name="search_status_no_results">No results were found</string>
<string name="search_label">Search</string>
- <string name="found_in_title_label">Found in title</string>
<string name="no_results_for_query">No results were found for \"%1$s\"</string>
- <!-- OPML import and export -->
- <string name="opml_import_option">Option %1$d</string>
- <string name="opml_import_explanation_1">Choose a specific file path from the local filesystem.</string>
- <string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string>
+ <!-- import and export -->
+ <string name="import_export_summary">Move subscriptions and queue to another device</string>
+ <string name="database">Database</string>
+ <string name="opml">OPML</string>
+ <string name="html">HTML</string>
+ <string name="html_export_summary">Show your subscriptions to a friend</string>
+ <string name="opml_export_summary">Transfer your subscriptions to another podcast app</string>
+ <string name="opml_import_summary">Import your subscriptions from another podcast app</string>
+ <string name="database_export_summary">Transfer subscriptions, listened episodes and queue to AntennaPod on another device</string>
+ <string name="database_import_summary">Import AntennaPod database from another device</string>
<string name="opml_import_label">OPML Import</string>
- <string name="reading_opml_label">Reading OPML file</string>
<string name="opml_reader_error">An error has occurred while reading the OPML document:</string>
<string name="opml_import_error_no_file">No file selected!</string>
<string name="select_all_label">Select all</string>
<string name="deselect_all_label">Deselect all</string>
- <string name="choose_file_from_filesystem">From local filesystem</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="database_export_label">Database export</string>
+ <string name="database_import_label">Database import</string>
+ <string name="please_wait">Please wait&#8230;</string>
<string name="export_error_label">Export error</string>
<string name="export_success_title">Export successful</string>
<string name="export_success_sum">The exported file was written to:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Access to external storage is required to read the OPML file</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_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>
<!-- Sleep timer -->
<string name="set_sleeptimer_label">Set sleep timer</string>
<string name="disable_sleeptimer_label">Disable sleep timer</string>
- <string name="enter_time_here_label">Enter time</string>
<string name="sleep_timer_label">Sleep timer</string>
- <string name="time_left_label">Time left:\u0020</string>
<string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string>
- <string name="timer_about_to_expire_label"><b>When timer is about to expire:</b></string>
- <string name="shake_to_reset_label">Shake to reset timer</string>
- <string name="timer_vibration_label">Vibrate</string>
+ <string name="shake_to_reset_label">Shake to reset</string>
+ <string name="timer_vibration_label">Vibrate shortly before end</string>
<string name="time_seconds">seconds</string>
<string name="time_minutes">minutes</string>
<string name="time_hours">hours</string>
@@ -585,7 +593,6 @@
</plurals>
<string name="auto_enable_label">Auto-enable</string>
<string name="sleep_timer_enabled_label">Sleep timer enabled</string>
- <string name="sleep_timer_disabled_label">Sleep timer disabled</string>
<!-- gpodder.net -->
<string name="gpodnet_taglist_header">CATEGORIES</string>
@@ -671,7 +678,7 @@
<string name="episode_filters_exclude">Exclude</string>
<string name="episode_filters_hint">Single words \n\"Multiple Words\"</string>
<string name="keep_updated">Keep Updated</string>
- <string name="keep_updated_summary">Include this feed when (auto-)refreshing all feeds</string>
+ <string name="keep_updated_summary">Include this podcast when (auto-)refreshing all podcasts</string>
<string name="auto_download_disabled_globally">Auto download is disabled in the main AntennaPod settings</string>
<!-- Progress information -->
@@ -750,15 +757,6 @@
<!-- Subscriptions fragment -->
<string name="subscription_num_columns">Number of columns</string>
- <!-- Database import/export -->
- <string name="import_export">Database import/export</string>
- <string name="import_export_warning">This experimental function can be used to transfer your subscriptions and played episodes to another device.\n\nExported databases can only be imported when using the same version of AntennaPod. Otherwise, this function will lead to unexpected behavior.\n\nAfter importing, episodes might be displayed as downloaded even though they are not. Just press the play button of the episodes to make AntennaPod detect this.</string>
- <string name="label_import">Import</string>
- <string name="label_export">Export</string>
- <string name="import_select_file">Select file to import</string>
- <string name="export_ok">Export successful.</string>
- <string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string>
-
<!-- Casting -->
<string name="cast_media_route_menu_title">Play on&#8230;</string>
<string name="cast_disconnect_label">Disconnect the cast session</string>
@@ -766,7 +764,6 @@
<string name="cast_failed_to_play">Failed to start the playback of media</string>
<string name="cast_failed_to_stop">Failed to stop the playback of media</string>
<string name="cast_failed_to_pause">Failed to pause the playback of media</string>
- <!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<string name="cast_failed_setting_volume">Failed to set the volume</string>
<string name="cast_failed_no_connection">No connection to the cast device is present</string>
<string name="cast_failed_no_connection_trans">Connection to the cast device has been lost. Application is trying to re-establish the connection, if possible. Please wait for a few seconds and try again.</string>
@@ -784,7 +781,6 @@
<string name="notification_channel_playing_description">Allows to control playback. This is the main notification you see while playing a podcast.</string>
<string name="notification_channel_error">Errors</string>
<string name="notification_channel_error_description">Shown if something went wrong, for example if download or gpodder sync fails.</string>
- <string name="import_bad_file">Invalid/corrupt file</string>
<!-- Widget settings -->
<string name="widget_settings">Widget settings</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 2ccd48353..9f72c9f6f 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -20,12 +20,13 @@
<item name="action_search">@drawable/ic_search_grey600_24dp</item>
<item name="action_stream">@drawable/ic_stream_grey600</item>
<item name="av_download">@drawable/ic_file_download_grey600_24dp</item>
- <item name="av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
- <item name="av_pause">@drawable/ic_pause_grey600_24dp</item>
- <item name="av_play">@drawable/ic_play_arrow_grey600_24dp</item>
+ <item name="av_pause">@drawable/ic_av_pause_dark_48dp</item>
+ <item name="av_play">@drawable/ic_av_play_dark_48dp</item>
+ <item name="av_rewind">@drawable/ic_av_fast_rewind_dark_48dp</item>
+ <item name="av_fast_forward">@drawable/ic_av_fast_forward_dark_48dp</item>
+ <item name="av_skip">@drawable/ic_av_skip_dark_48dp</item>
<item name="av_speed">@drawable/ic_playback_speed_dark_48dp</item>
<item name="ic_settings_speed">@drawable/ic_playback_speed_dark_24dp</item>
- <item name="av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
<item name="content_discard">@drawable/ic_delete_grey600_24dp</item>
<item name="content_new">@drawable/ic_add_grey600_24dp</item>
<item name="content_remove_from_queue">@drawable/ic_remove_grey600</item>
@@ -38,23 +39,13 @@
<item name="navigation_up">@drawable/navigation_up</item>
<item name="social_share">@drawable/ic_share_grey600_24dp</item>
<item name="stat_playlist">@drawable/ic_list_grey600_24dp</item>
- <item name="type_audio">@drawable/ic_hearing_grey600_18dp</item>
- <item name="type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
- <item name="non_transparent_background">@color/white</item>
- <item name="overlay_background">@color/overlay_light</item>
- <item name="overlay_drawable">@drawable/overlay_drawable</item>
+ <item name="type_video">@drawable/ic_videocam_grey600_24dp</item>
<item name="dragview_background">@drawable/ic_drag_vertical_grey600_48dp</item>
- <item name="dragview_float_background">@color/white</item>
<item name="nav_drawer_background">@color/white</item>
<item name="drawer_activated_color">@color/highlight_light</item>
- <item name="ic_new">@drawable/ic_new_releases_grey600_24dp</item>
<item name="ic_history">@drawable/ic_history_grey600_24dp</item>
<item name="ic_folder">@drawable/ic_folder_grey600_24dp</item>
- <item name="av_play_big">@drawable/ic_play_arrow_grey600_36dp</item>
- <item name="av_pause_big">@drawable/ic_pause_grey600_36dp</item>
- <item name="av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
- <item name="av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
- <item name="av_skip_big">@drawable/ic_skip_grey600_36dp</item>
+ <item name="ic_settings_playback">@drawable/ic_av_play_dark_24dp</item>
<item name="ic_fav">@drawable/ic_star_border_grey600_24dp</item>
<item name="ic_unfav">@drawable/ic_star_grey600_24dp</item>
<item name="ic_settings">@drawable/ic_settings_grey600_24dp</item>
@@ -76,15 +67,11 @@
<item name="ic_bookmark">@drawable/ic_bookmark_grey600_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="ic_key">@drawable/ic_key_grey600</item>
+ <item name="ic_volume_adaption">@drawable/ic_volume_adaption_grey</item>
<item name="master_switch_background">@color/master_switch_background_light</item>
<item name="currently_playing_background">@color/highlight_light</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
-
- <item name="about_screen_background">#e5e5e5</item>
- <item name="about_screen_card_background">#ffffff</item>
- <item name="about_screen_card_border">#d2d2d2</item>
- <item name="about_screen_font_color">#000000</item>
</style>
<style name="Theme.AntennaPod.Dark" parent="Theme.Base.AntennaPod.Dark">
@@ -108,12 +95,13 @@
<item name="action_search">@drawable/ic_search_white_24dp</item>
<item name="action_stream">@drawable/ic_stream_white</item>
<item name="av_download">@drawable/ic_file_download_white_24dp</item>
- <item name="av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
- <item name="av_pause">@drawable/ic_pause_white_24dp</item>
- <item name="av_play">@drawable/ic_play_arrow_white_24dp</item>
+ <item name="av_rewind">@drawable/ic_av_fast_rewind_white_48dp</item>
+ <item name="av_fast_forward">@drawable/ic_av_fast_forward_white_48dp</item>
+ <item name="av_pause">@drawable/ic_av_pause_white_48dp</item>
+ <item name="av_play">@drawable/ic_av_play_white_48dp</item>
+ <item name="av_skip">@drawable/ic_av_skip_white_48dp</item>
<item name="av_speed">@drawable/ic_playback_speed_white_48dp</item>
<item name="ic_settings_speed">@drawable/ic_playback_speed_white_24dp</item>
- <item name="av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
<item name="content_discard">@drawable/ic_delete_white_24dp</item>
<item name="content_new">@drawable/ic_add_white_24dp</item>
<item name="content_remove_from_queue">@drawable/ic_remove_white</item>
@@ -126,23 +114,13 @@
<item name="navigation_up">@drawable/navigation_up_dark</item>
<item name="social_share">@drawable/ic_share_white_24dp</item>
<item name="stat_playlist">@drawable/ic_list_white_24dp</item>
- <item name="type_audio">@drawable/ic_hearing_white_18dp</item>
- <item name="type_video">@drawable/ic_remove_red_eye_white_18dp</item>
- <item name="non_transparent_background">@color/black</item>
- <item name="overlay_background">@color/overlay_dark</item>
- <item name="overlay_drawable">@drawable/overlay_drawable_dark</item>
+ <item name="type_video">@drawable/ic_videocam_white_24dp</item>
<item name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
- <item name="dragview_float_background">@color/black</item>
<item name="nav_drawer_background">@color/nav_drawer_background_dark</item>
<item name="drawer_activated_color">@color/nav_drawer_highlighted_dark</item>
- <item name="ic_new">@drawable/ic_new_releases_white_24dp</item>
<item name="ic_history">@drawable/ic_history_white_24dp</item>
<item name="ic_folder">@drawable/ic_folder_white_24dp</item>
- <item name="av_play_big">@drawable/ic_play_arrow_white_36dp</item>
- <item name="av_pause_big">@drawable/ic_pause_white_36dp</item>
- <item name="av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
- <item name="av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
- <item name="av_skip_big">@drawable/ic_skip_white_36dp</item>
+ <item name="ic_settings_playback">@drawable/ic_av_play_white_24dp</item>
<item name="ic_fav">@drawable/ic_star_border_white_24dp</item>
<item name="ic_unfav">@drawable/ic_star_white_24dp</item>
<item name="ic_settings">@drawable/ic_settings_white_24dp</item>
@@ -164,14 +142,10 @@
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="ic_key">@drawable/ic_key_white</item>
+ <item name="ic_volume_adaption">@drawable/ic_volume_adaption_white</item>
<item name="master_switch_background">@color/master_switch_background_dark</item>
<item name="currently_playing_background">@color/highlight_dark</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
-
- <item name="about_screen_background">#303030</item>
- <item name="about_screen_card_background">#424242</item>
- <item name="about_screen_card_border">#262626</item>
- <item name="about_screen_font_color">#ffffff</item>
</style>
<style name="Theme.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.TrueBlack">
@@ -180,12 +154,8 @@
<style name="Theme.Base.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.Dark">
<item name="progressBarTheme">@style/ProgressBarTrueBlack</item>
- <item name="non_transparent_background">@color/black</item>
- <item name="overlay_background">@color/black</item>
- <item name="overlay_drawable">@drawable/overlay_drawable_dark_trueblack</item>
<item name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_black</item>
- <item name="dragview_float_background">@color/black</item>
<item name="nav_drawer_background">@color/black</item>
<item name="drawer_activated_color">@color/highlight_trueblack</item>
<item name="android:textColorPrimary">@color/white</item>
@@ -266,7 +236,11 @@
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>
- <style name="Theme.AntennaPod.Dark.Splash" parent="Theme.AppCompat.NoActionBar">
+ <style name="Theme.AntennaPod.Splash" parent="Theme.Base.AntennaPod.Splash">
+ <!-- Room for API dependent attributes -->
+ </style>
+
+ <style name="Theme.Base.AntennaPod.Splash" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/bg_splash</item>
<item name="colorPrimary">@color/ic_launcher_background</item>
<item name="colorPrimaryDark">@color/ic_launcher_background</item>
@@ -285,7 +259,7 @@
<style name="AntennaPod.TextView.ListItemPrimaryTitle" parent="@android:style/TextAppearance.Small">
<item name="android:textSize">16sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:lines">2</item>
+ <item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>