summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2020-02-21 19:02:53 +0100
committerByteHamster <info@bytehamster.com>2020-02-21 19:02:53 +0100
commitb3ea96e7b3d3409a02da64f9e93511cc3400709a (patch)
tree529b4b72831a8c9e2daac74839d71f70636e095c /core/src
parent7b5435082042dc77de6e3fb5f59bf55fc71d3aa8 (diff)
parent657d19ccc2c1e3de6555c5c220605bb59225e450 (diff)
downloadAntennaPod-b3ea96e7b3d3409a02da64f9e93511cc3400709a.zip
Merge branch 'develop' into speed-indicator-view
Diffstat (limited to 'core/src')
-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
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java13
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java64
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java225
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java4
207 files changed, 2402 insertions, 1586 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>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index bf9ef3f5b..5014a1fc5 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -1243,14 +1243,11 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
throw new NoConnectionException();
}
Log.d(TAG, "remoteMediaPlayer.seek() to position " + position);
- remoteMediaPlayer.seek(mApiClient,
- position,
- RESUME_STATE_UNCHANGED).
- setResultCallback(result -> {
- if (!result.getStatus().isSuccess()) {
- onFailed(R.string.cast_failed_seek, result.getStatus().getStatusCode());
- }
- });
+ remoteMediaPlayer.seek(mApiClient, position, RESUME_STATE_UNCHANGED).setResultCallback(result -> {
+ if (!result.getStatus().isSuccess()) {
+ onFailed(R.string.cast_failed_seek, result.getStatus().getStatusCode());
+ }
+ });
}
/**
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
index 63fc05cb8..7c84437e1 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
@@ -64,7 +64,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
remoteState = MediaStatus.PLAYER_STATE_UNKNOWN;
}
- public void init () {
+ public void init() {
try {
if (castMgr.isConnected() && castMgr.isRemoteMediaLoaded()) {
onRemoteMediaPlayerStatusUpdated();
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
new file mode 100644
index 000000000..45c86cb83
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.core.feed;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class VolumeAdaptionSettingTest {
+
+ @Test
+ public void mapOffToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.OFF;
+ assertThat(setting.toInteger(), is(equalTo(0)));
+ }
+
+ @Test
+ public void mapLightReductionToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.LIGHT_REDUCTION;
+
+ assertThat(setting.toInteger(), is(equalTo(1)));
+ }
+
+ @Test
+ public void mapHeavyReductionToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.HEAVY_REDUCTION;
+
+ assertThat(setting.toInteger(), is(equalTo(2)));
+ }
+
+ @Test
+ public void mapIntegerToVolumeAdaptionSetting() {
+ assertThat(VolumeAdaptionSetting.fromInteger(0), is(equalTo(VolumeAdaptionSetting.OFF)));
+ assertThat(VolumeAdaptionSetting.fromInteger(1), is(equalTo(VolumeAdaptionSetting.LIGHT_REDUCTION)));
+ assertThat(VolumeAdaptionSetting.fromInteger(2), is(equalTo(VolumeAdaptionSetting.HEAVY_REDUCTION)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotMapNegativeValues() {
+ VolumeAdaptionSetting.fromInteger(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotMapValuesOutOfRange() {
+ VolumeAdaptionSetting.fromInteger(3);
+ }
+
+ @Test
+ public void noAdaptionIfTurnedOff() {
+ float adaptionFactor = VolumeAdaptionSetting.OFF.getAdaptionFactor();
+ assertEquals(1.0f, adaptionFactor, 0.01f);
+ }
+
+ @Test
+ public void lightReductionYieldsHigherValueThanHeavyReduction() {
+ float lightReductionFactor = VolumeAdaptionSetting.LIGHT_REDUCTION.getAdaptionFactor();
+
+ float heavyReductionFactor = VolumeAdaptionSetting.HEAVY_REDUCTION.getAdaptionFactor();
+
+ assertTrue("Light reduction must have higher factor than heavy reduction", lightReductionFactor > heavyReductionFactor);
+ }
+} \ No newline at end of file
diff --git a/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
new file mode 100644
index 000000000..22f67933f
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
@@ -0,0 +1,225 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+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;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class PlaybackVolumeUpdaterTest {
+
+ private static final long FEED_ID = 42;
+
+ private PlaybackServiceMediaPlayer mediaPlayer;
+
+ @Before
+ public void setUp() {
+ mediaPlayer = mock(PlaybackServiceMediaPlayer.class);
+ }
+
+ @Test
+ public void noChangeIfNoFeedMediaPlaying() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PAUSED);
+
+ Playable noFeedMedia = mock(Playable.class);
+ when(mediaPlayer.getPlayable()).thenReturn(noFeedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsError() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.ERROR);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsIndeterminate() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.INDETERMINATE);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsStopped() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.STOPPED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayableIsNoItemOfAffectedFeed() {
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PLAYING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ when(feedMedia.getItem().getFeed().getId()).thenReturn(FEED_ID + 1);
+
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPaused() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PAUSED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPrepared() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PREPARED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsInitializing() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.INITIALIZING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPreparing() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PREPARING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsSeeking() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.SEEKING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesAndForcesVolumeChangeForLoadedFeedMediaIfPlayerStatusIsPlaying() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PLAYING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.HEAVY_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
+
+ verify(mediaPlayer, times(1)).pause(false, false);
+ verify(mediaPlayer, times(1)).resume();
+ }
+
+ private FeedMedia mockFeedMedia() {
+ FeedMedia feedMedia = mock(FeedMedia.class);
+ FeedItem feedItem = mock(FeedItem.class);
+ Feed feed = mock(Feed.class);
+ FeedPreferences feedPreferences = mock(FeedPreferences.class);
+
+ when(feedMedia.getItem()).thenReturn(feedItem);
+ when(feedItem.getFeed()).thenReturn(feed);
+ when(feed.getId()).thenReturn(FEED_ID);
+ when(feed.getPreferences()).thenReturn(feedPreferences);
+ return feedMedia;
+ }
+}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
index 17b88bdd2..275b104ea 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
@@ -226,9 +226,7 @@ public class ItemEnqueuePositionCalculatorTest {
private static FeedItem setAsDownloading(int id, DownloadStateProvider stubDownloadStateProvider,
boolean isDownloading) {
FeedItem item = createFeedItem(id);
- FeedMedia media =
- new FeedMedia(item, "http://download.url.net/" + id
- , 100000 + id, "audio/mp3");
+ FeedMedia media = new FeedMedia(item, "http://download.url.net/" + id, 100000 + id, "audio/mp3");
media.setId(item.getId());
item.setMedia(media);
return setAsDownloading(item, stubDownloadStateProvider, isDownloading);