summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bechtold <max.bechtold@andrena.de>2019-12-21 11:12:39 +0100
committerMax Bechtold <max.bechtold@andrena.de>2019-12-21 11:52:01 +0100
commit9ca5cab24678873881709c5c554c1ce54c5db220 (patch)
treef97dd7474a03a5ad6155aa95400b9e152f6d9d09
parent87cca61dcd8337facf695f03f1330c55bfa85168 (diff)
parent46731178b4f0590edf7bcb14e5dcec02f2e98f57 (diff)
downloadAntennaPod-9ca5cab24678873881709c5c554c1ce54c5db220.zip
Merge remote-tracking branch 'upstream/develop' into feat/simple-adjust-volume-per-feed
-rw-r--r--.github/workflows/android-emulator.yml29
-rw-r--r--.gitignore4
-rw-r--r--.tx/config1
-rw-r--r--CHANGELOG.md10
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--CONTRIBUTORS297
-rw-r--r--app/build.gradle28
-rw-r--r--app/proguard.cfg9
-rw-r--r--app/src/androidTest/assets/3sec.mp3bin49043 -> 0 bytes
-rw-r--r--app/src/androidTest/assets/testfile.mp3bin20606 -> 0 bytes
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java73
-rw-r--r--app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java15
-rw-r--r--app/src/androidTest/java/de/test/antennapod/NthMatcher.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java308
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java104
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java29
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java36
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java11
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java164
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java95
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java30
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java17
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java39
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java277
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java375
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java351
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java10
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java120
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java71
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java47
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java128
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java73
-rw-r--r--app/src/main/AndroidManifest.xml20
-rw-r--r--app/src/main/assets/30sec.mp3bin0 -> 120017 bytes
-rw-r--r--app/src/main/assets/developers.csv119
-rw-r--r--app/src/main/assets/licenses.xml105
-rw-r--r--app/src/main/assets/testfile.mp3bin20606 -> 0 bytes
-rw-r--r--app/src/main/assets/translators.csv46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java158
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java170
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java139
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java131
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java34
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java120
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java70
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java237
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java196
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java101
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java60
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java65
-rw-r--r--app/src/main/play/listings/de-DE/full-description.txt65
-rw-r--r--app/src/main/play/listings/en-US/full-description.txt63
-rw-r--r--app/src/main/play/listings/es-ES/full-description.txt53
-rw-r--r--app/src/main/play/listings/fr-FR/full-description.txt65
-rw-r--r--app/src/main/play/release-notes/en-US/default.txt15
-rw-r--r--app/src/main/res/layout/about_teaser.xml7
-rw-r--r--app/src/main/res/layout/activity_widget_config.xml80
-rw-r--r--app/src/main/res/layout/addfeed.xml9
-rw-r--r--app/src/main/res/layout/audio_controls.xml3
-rw-r--r--app/src/main/res/layout/authentication_dialog.xml109
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog.xml12
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog_entry.xml7
-rw-r--r--app/src/main/res/layout/edit_text_dialog.xml15
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml41
-rw-r--r--app/src/main/res/layout/feeditem_pager_fragment.xml6
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml2
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml2
-rw-r--r--app/src/main/res/layout/numberpicker.xml2
-rw-r--r--app/src/main/res/layout/onlinefeedview_activity.xml63
-rw-r--r--app/src/main/res/layout/onlinefeedview_header.xml24
-rw-r--r--app/src/main/res/layout/proxy_settings.xml6
-rw-r--r--app/src/main/res/layout/quick_feed_discovery.xml35
-rw-r--r--app/src/main/res/layout/quick_feed_discovery_item.xml6
-rw-r--r--app/src/main/res/layout/simple_icon_list_item.xml41
-rw-r--r--app/src/main/res/layout/time_dialog.xml9
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml13
-rw-r--r--app/src/main/res/menu/episodes.xml4
-rw-r--r--app/src/main/res/menu/feedlist.xml19
-rw-r--r--app/src/main/res/xml/feed_settings.xml4
-rw-r--r--app/src/main/res/xml/player_widget_info.xml3
-rw-r--r--app/src/main/res/xml/preferences_about.xml33
-rw-r--r--app/src/main/res/xml/preferences_autodownload.xml6
-rw-r--r--app/src/main/res/xml/preferences_playback.xml30
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml18
-rw-r--r--app/src/main/templates/about.html183
-rw-r--r--build.gradle9
-rw-r--r--config/checkstyle/checkstyle-best-practice.xml15
-rw-r--r--config/checkstyle/checkstyle.xml22
-rwxr-xr-xcontributers.template.py471
-rw-r--r--core/build.gradle4
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java122
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomTextTest.java49
-rw-r--r--core/src/debug/res/drawable/ic_launcher_background_debug.xml179
-rw-r--r--core/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml5
-rw-r--r--core/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml5
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloadLogEvent.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java112
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java45
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java (renamed from core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java)12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java93
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java53
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java1050
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java162
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java130
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java57
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java112
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java56
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java73
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java185
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java154
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java97
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java80
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java (renamed from core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java)90
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java75
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java63
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java330
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java30
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java40
-rw-r--r--core/src/main/res/drawable-hdpi/ic_filter_grey600_24dp.pngbin111 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_filter_white_24dp.pngbin110 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_settings_input_antenna_grey600_24dp.pngbin569 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_settings_input_antenna_white_24dp.pngbin553 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_filter_grey600_24dp.pngbin91 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_filter_white_24dp.pngbin90 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_settings_input_antenna_grey600_24dp.pngbin388 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_settings_input_antenna_white_24dp.pngbin371 -> 0 bytes
-rw-r--r--[-rwxr-xr-x]core/src/main/res/drawable-nodpi/teaser.png (renamed from app/src/main/assets/logo.png)bin60183 -> 60183 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_filter_grey600_24dp.pngbin106 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_filter_white_24dp.pngbin103 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_grey600_24dp.pngbin698 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_white_24dp.pngbin675 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_filter_grey600_24dp.pngbin114 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_filter_white_24dp.pngbin107 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_grey600_24dp.pngbin1063 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_white_24dp.pngbin1019 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_filter_grey600_24dp.pngbin123 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_filter_white_24dp.pngbin106 -> 0 bytes
-rw-r--r--core/src/main/res/drawable/borderless_button_dark.xml19
-rw-r--r--core/src/main/res/drawable/ic_filter_grey600_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_filter_white_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_key_grey600.xml6
-rw-r--r--core/src/main/res/drawable/ic_key_white.xml (renamed from core/src/main/res/drawable/ic_notification_key.xml)0
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_dark_24dp.xml8
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_dark_48dp.xml (renamed from core/src/main/res/drawable/ic_playback_speed_dark.xml)4
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_white_24dp.xml8
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_white_48dp.xml (renamed from core/src/main/res/drawable/ic_playback_speed_white.xml)0
-rw-r--r--core/src/main/res/drawable/ic_stream_grey600.xml14
-rw-r--r--core/src/main/res/drawable/ic_stream_white.xml17
-rw-r--r--core/src/main/res/layout/player_widget.xml7
-rw-r--r--core/src/main/res/values-ca/strings.xml50
-rw-r--r--core/src/main/res/values-cs-rCZ/strings.xml76
-rw-r--r--core/src/main/res/values-da/strings.xml50
-rw-r--r--core/src/main/res/values-de/strings.xml87
-rw-r--r--core/src/main/res/values-es/strings.xml87
-rw-r--r--core/src/main/res/values-et/strings.xml390
-rw-r--r--core/src/main/res/values-eu/strings.xml732
-rw-r--r--core/src/main/res/values-fi/strings.xml50
-rw-r--r--core/src/main/res/values-fr/strings.xml103
-rw-r--r--core/src/main/res/values-gl-rES/strings.xml96
-rw-r--r--core/src/main/res/values-hi-rIN/strings.xml32
-rw-r--r--core/src/main/res/values-hu/strings.xml624
-rw-r--r--core/src/main/res/values-it/strings.xml89
-rw-r--r--core/src/main/res/values-iw-rIL/strings.xml87
-rw-r--r--core/src/main/res/values-ja/strings.xml87
-rw-r--r--core/src/main/res/values-lt/strings.xml81
-rw-r--r--core/src/main/res/values-nb/strings.xml46
-rw-r--r--core/src/main/res/values-nl/strings.xml50
-rw-r--r--core/src/main/res/values-pl-rPL/strings.xml235
-rw-r--r--core/src/main/res/values-pt-rBR/strings.xml50
-rw-r--r--core/src/main/res/values-pt/strings.xml149
-rw-r--r--core/src/main/res/values-ru/strings.xml54
-rw-r--r--core/src/main/res/values-sv-rSE/strings.xml65
-rw-r--r--core/src/main/res/values-tr/strings.xml45
-rw-r--r--core/src/main/res/values-uk-rUA/strings.xml50
-rw-r--r--core/src/main/res/values-v16/colors.xml (renamed from core/src/main/res/values-v19/colors.xml)0
-rw-r--r--core/src/main/res/values-v21/styles.xml3
-rw-r--r--core/src/main/res/values-zh-rCN/strings.xml105
-rw-r--r--core/src/main/res/values/arrays.xml42
-rw-r--r--core/src/main/res/values/attrs.xml2
-rw-r--r--core/src/main/res/values/strings.xml35
-rw-r--r--core/src/main/res/values/styles.xml54
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java13
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java24
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java293
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/CollectionTestUtil.java30
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java (renamed from core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java)78
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java73
-rw-r--r--createDevelopersList.py30
-rw-r--r--createTranslatorsList.py39
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xmakeRelease.sh7
291 files changed, 9616 insertions, 6466 deletions
diff --git a/.github/workflows/android-emulator.yml b/.github/workflows/android-emulator.yml
new file mode 100644
index 000000000..d42b4504e
--- /dev/null
+++ b/.github/workflows/android-emulator.yml
@@ -0,0 +1,29 @@
+name: Android Emulator test
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+jobs:
+ build:
+ runs-on: macOS-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Gradle
+ run: ./gradlew assemblePlayDebugAndroidTest
+ - name: Android Emulator test
+ uses: ReactiveCircus/android-emulator-runner@v1.0.1
+ with:
+ api-level: 28
+ headless: true
+ disable-animations: true
+ script: ./gradlew connectedPlayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.notAnnotation=de.test.antennapod.IgnoreOnCi
+ - uses: actions/upload-artifact@v1
+ if: failure()
+ with:
+ name: test-report
+ path: app/build/reports/androidTests/connected/flavors/PLAY/
diff --git a/.gitignore b/.gitignore
index b10f948bb..d4be5aef9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ bin/
gen/
target/
build/
+**/*.project
+**/*.classpath
# Local configuration file (sdk path, etc)
local.properties
@@ -33,6 +35,8 @@ out
#transifex downloads
contributers.py
+# View hierarchy captures
+captures
# other
*.odg#
proguard
diff --git a/.tx/config b/.tx/config
index ea1c0ccef..e82a784f7 100644
--- a/.tx/config
+++ b/.tx/config
@@ -17,6 +17,7 @@ trans.el = core/src/main/res/values-el/strings.xml
trans.es = core/src/main/res/values-es/strings.xml
trans.es_ES = core/src/main/res/values-es-rES/strings.xml
trans.et = core/src/main/res/values-et/strings.xml
+trans.eu = core/src/main/res/values-eu/strings.xml
trans.fa = core/src/main/res/values-fa/strings.xml
trans.fi = core/src/main/res/values-fi/strings.xml
trans.fr = core/src/main/res/values-fr/strings.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb429f026..b6357a0de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
Change Log
==========
+Version 1.8.0
+-------------
+* Added per-feed playback speed setting (by @spacecowboy)
+* Support sorting in Podcast screen (by @orionlee)
+* Option to show stream button rather than download in lists (by @dsmith47)
+* Option to replace Episode cover with Podcast cover (by @xgouchet)
+* Transparent widget (by @M-arcel)
+* User interface tweaks (by @ByteHamster)
+* Tons of bug fixes and improvements
+
Version 1.7.3
-------------
* Display episode image on widget (by @brad)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7f224dfb2..47a7336cb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,7 +27,7 @@ How to report a bug
1. Do this
1. Do that
-**Environment**: Settings you have changed, e.g. Auto Download. "Unusual" devices you use, e.g. Bluetooth headphones. Do you still use Prestissimo?
+**Environment**: Settings you have changed, e.g. Auto Download. "Unusual" devices you use, e.g. Bluetooth headphones.
**Stacktrace/Logcat**: [if available]
```
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c333a44b1..240be593b 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,194 +1,165 @@
-DEVELOPERS
-==========
-
-Alan Orth
-Alexander Terczka
-Alexei Bendebury
-Ali
-alifeflow
-alimemonzx
-amhokies
-Anders Bo Rasmussen
-Anderson Mesquita
-Andrew Gaul
-Andrey Krutov
-Anthony Lieuallen
-axq
-bhlee
-Borjan Tchakaloff
-brad
-Brad Pitcher
-Burt Wiley Snyder
+danieloeh
+mfietz
ByteHamster
-Cameron Banga
-Christian Ludwig
-Christopher Szucko
-Cj Malone
-Colin Willson
-Cédric Cabessa
+TomHennen
+orionlee
+domingos86
+andersonvom
+patheticpat
+spacecowboy
+brad
+Cj-Malone
+gaul
+qkolj
+pachecosf
+ahangarha
+rharriso
+xgouchet
damoasda
-Danial Klimkin
-Daniel Oeh
-David Carver
-David Reiss
-Dean Brettle
+sevenmaster
+TheRealFalcon
+keunes
+shortspider
+hannesa2
+jas14
+udif
+dirkmueller
+jatinkumarg
+peschmae0
+orelogo
+ydinath
+CedricCabessa
+mchelen
+johnjohndoe
dethstar
-Dirk Mueller
-Domingos Lopes
drabux
+saqura
+bibz
+hzulla
+deandreamatias
+MeirAtIMDDE
egsavage
-EirikV
-Eoin Mcloughlin
-eraymond
-Ercan Erden
-falko
-Falko Lehmann
-Hannes Achleitner
-hannesa2
-Hanno Zulla
-heckler01
-Holger Jeromin
-Humberto Fraga
-InsidE
-James Falcon
-Jan Niehusmann
-Jan-Peter von Hunnius
-Jatin Kumar
-Jens Klingenberg
-Jens Müller
-Joe Stein
-Johan Liesén
-Kaligule
-Katrin Leinweber
-Kevin Dalley
-Koen Glotzbach
-kroegerama
-Kurian Vithayathil
-LatinSuD
-Lee Yeong Khang
-lightonflux
ligi
-Luis Cruz
-Marc Lasson
-Martin Fietz
-Martin Olsson
-mat tso
-mateoeh
-Matias de Andrea
-Matthew Gaffen
-Matthias Schütz
-Matthieu De Beule
-Maurice Gilden
-Meir Schwarz
-Michael Kaiser
-Michael Scarito
-Mike Chelen
-minusf
-MolarAmbiguity
-Mostafa Ahangarha
-Mounir Lamouri
-mr-intj
-Nathan Mascitelli
-Nis Wechselberg
-Oliver Crow
-orelogo
-pachecosf
-Paul Ortyl
-Petar Kukolj
-qkolj
-Raghul
-Raghul Jagannathan
-recalculated
+dreiss
+liesen
+nereocystis
rezanejati
-Ross Harrison
-Sam Lee
-Sam Whited
-saqura
-Selivanov Pavel
-Serge
-Seth Golub
-sevenmaster
-Shantana Hardy
-Shinichiro Fujiwara
-Simon Danner
-Simon Rutishauser
-Simon Schubert
-Soso Tughushi
-Spencer Visick
-Stefan Mitrik
-Stephen Kitt
-Terence Eden
-Tim Butram
-Tobias Preuss
-Tom Hennen
-Tom Tom
-tommy watson
-tuxayo
twiceyuan
-Udi Finkelstein
-VarunBarad
+HaBaLeS
volhol
-Volker Hollich
-Wagubi Brian
-WangYun
-William Seemann
-ydinath
-
-
-TRANSLATORS
-===========
-
+CameronBanga
+HolgerJeromin
+xisberto
+jmue
+katrinleinweber
+LatinSuD
+24hours
+SosoTughushi
+fabolhak
+archibishop
+alifeflow
+toggles
+matdb
+kingargyle
+dsmith47
+hannesaa2
+jhunnius
+ShadowIce
+raghulj
+raghulrm
+stevomit
+skitt
+mr-intj
+tuxayo
+alimemonzx
+alanorth
+alexte
+andrey-krutov
+arantius
+bws9000
+chrissicool
+cszucko
+CWftw
+danielm5
+ariedov
+brettle
+eirikv
+eerden
+jannic
+Foso
+Kaligule
+kvithayathil
+luiscruz
+mlasson
+M-arcel
+mo
+mdeveloper20
+mschuetz
+MolarAmbiguity
+mounirlamouri
+ortylp
+PtilopsisLeucotis
+SamWhited
+selivan
+sonnayasomnambula
+sethoscope
+shantanahardy
+mamehacker
+danners
+corecode
+vimsick
+edent
+atrus6
+waylife
+wseemann
+amhokies
+axq
+fossterer
+lightonflux
+minusf
Arabic: abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
-Asturian (Spain): enolp
+Asturian (ast_ES): enolp
Azerbaijani: danieloeh, kotfenix
+Basque: gaztainalde, pospolos, zakurranputza
Bulgarian: solusitor
Catalan: dvd1985, exort12, javiercoll, lambdani, marcmetallextrem, xc70
-Catalan (Spain): 00c0c0, javiercoll
-Chinese: dillonbecker, RainSlide, xukeek, yangyang
-Chinese (China): bebeauties38, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, JayYoung, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, Sak94664, spice2wolf, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
-Chinese (Taiwan): gugod, nigelinux, pggdt, ymhuang0808
-Czech (Czech Republic): elich, Hanzmeister, mcepl, petnek, svetlemodry
+Chinese (zh_CN): bebeauties38, cyril3, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, JayYoung, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, Sak94664, spice2wolf, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
+Chinese (zh_TW): gugod, nigelinux, pggdt, Solomon, ymhuang0808
+Czech (cs_CZ): anotheranonymoususer, elich, Hanzmeister, mcepl, petnek, svetlemodry
Danish: CasperHN, jhertel
Dutch: e2jk, glotzbach, rwv, Vistaus
English: mfietz, sterylmreep
Estonian: Eraser
Finnish: danieloeh, elguitar, Sahtor
-French: cactux, ChaoticMind, clombion, e2jk, lacouture, LouFex, Matth78, mfietz, Poussinou, PRIMOKORN, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+French: cactux, ChaoticMind, clombion, e2jk, edewaele, lacouture, LouFex, Matth78, mfietz, Poussinou, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
Galician: antiparvos, pikamoku, Raichely
-German: 112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, moasda, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
-Greek: antonist, danieloeh, hua2016s, MSavoritias, pavlosv
-Hebrew (Israel): amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
-Hindi (India): ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
-Hungarian: glatz.balazs, hurrikan, lna91, marthynw, naren93, tszauer, ttyborg42
+German: 112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ByteHamster, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, moasda, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
+Modern Greek (1453-): antonist, danieloeh, hua2016s, jack.ath92, MSavoritias, pavlosv
+Hebrew (he_IL): amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
+Hindi (hi_IN): ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
+Hungarian: glatz.balazs, hurrikan, lna91, marthynw, meskobalazs, naren93, tszauer, ttyborg42
Icelandic: marthjod
Indonesian: jff, levirs565, luke137, rezafaiza, silvanael16
-Italian: aalex70, allin, apanontin, Bonnee, giuseppep, Guybrush88, marco_pag, neonsoftware, sevenmaster, theloca95
-Italian (Italy): aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, m.chinni, neonsoftware, niccord, nixxo, sevenmaster, theloca95
+Italian (it_IT): aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, ilmanzo, m.chinni, neonsoftware, niccord, nixxo, sevenmaster, theloca95
Japanese: Naofumi, RACER1, sh3llc4t, TranslatorG
-Kannada (India): thejeshgn
+Kannada (kn_IN): chiraag.nataraj, thejeshgn
Korean: changwoo, libliboom, seungrye, skcha
-Korean (South Korea): changwoo, seungrye
Lithuanian: naglis
Macedonian: krisfremen
-Malayalam: rashivkp, rubenroy
+Malayalam: joice, rashivkp, rubenroy
Norwegian: timbast
-Norwegian Bokmål: corkie, danieloeh, heraldo
-Norwegian Bokmål (Norway): corkie, heraldo, kongk, timbast
+Norwegian Bokmål (nb_NO): corkie, heraldo, kongk, timbast
Persian: ahangarha, F7D, sinamoghaddas
-Polish: Iwangelion, maniexx, mateossh, mfloryan
-Polish (Poland): d6210809, hiro2020, Iwangelion, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Polish (pl_PL): d6210809, hiro2020, Iwangelion, kRkk, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
Portuguese: andersonvom, domingos86, emansije, smarquespt
-Portuguese (Brazil): alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
-Romanian (Romania): corneliu.e, fuzzmz, ralienpp
-Russian (Russia): astra1, btimofeev, Duke_Raven, gammja, GaynullinDima, MegMasters98, mercutiy, null, overmind88, PtilopsisLeucotis, s.chebotar, shams4real, skvheadless, un_logic, Vladryyu, whereisthetea, zhenya97
-Slovenian (Slovenia): panter23
-Spanish: AleksSyntek, andersonvom, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
-Spanish (Spain): andersonvom, dvd1985, e2jk, frandavid100, hard_ware, palopezv, Raichely, TacoTheDank
-Swahili: kmtra
-Swahili (Kenya): BonfaceKilz
-Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TiloWiklund, TwoD
+Portuguese (pt_BR): alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
+Romanian (ro_RO): corneliu.e, fuzzmz, ralienpp
+Russian (ru_RU): astra1, btimofeev, Duke_Raven, gammja, GaynullinDima, MegMasters98, mercutiy, null, overmind88, PtilopsisLeucotis, s.chebotar, shams4real, skvheadless, un_logic, Vladryyu, whereisthetea, zhenya97
+Slovenian (sl_SI): panter23
+Spanish: AleksSyntek, andersonvom, Atreyu94, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Swahili (macrolanguage): kmtra
+Swedish (sv_SE): albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TiloWiklund, TwoD
Telugu: Isaasu, veeven
Turkish: basarancaner, brsata, Erdy, golcuk, overbite, Slsdem
-Ukrainian (Ukraine): older, paul_sm, sergiyr, zhenya97
+Ukrainian (uk_UA): older, paul_sm, sergiyr, zhenya97
Vietnamese: abnvolk, nguyenvui, ppanhh, vietnamesel10n
-Vietnamese (Vietnam): bizover
diff --git a/app/build.gradle b/app/build.gradle
index cadf8a447..adf816e5b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,12 +18,18 @@ android {
// "1.2.3-SNAPSHOT" -> 1020300
// "1.2.3-RC4" -> 1020304
// "1.2.3" -> 1020395
- versionCode 1070396
- versionName "1.7.3b"
+ versionCode 1080004
+ versionName "1.8.0-RC4"
testApplicationId "de.test.antennapod"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
generatedDensities = []
+ def commit = "Unknown commit"
+ try {
+ commit = "git rev-parse --short HEAD".execute().text.trim()
+ } catch (Exception ignore) { }
+ buildConfigField "String", "COMMIT_HASH", ('"' + commit + '"')
+
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'de.danoeh.antennapod.ApEventBusIndex' ]
@@ -154,9 +160,6 @@ dependencies {
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
- implementation("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
- transitive = true
- }
implementation "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
implementation "com.githang:viewpagerindicator:2.5.1@aar"
implementation "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
@@ -184,18 +187,7 @@ if (project.hasProperty("antennaPodServiceAccountEmail")) {
}
}
-task filterAbout(type: Copy) {
- from "src/main/templates/about.html"
- into "src/main/assets"
- filter(ReplaceTokens, tokens: [
- versionname: android.defaultConfig.versionName,
- commit : "git rev-parse --short HEAD".execute().text,
- year : new Date().format('yyyy')])
- outputs.upToDateWhen { false }
-}
-
-task copyTextFiles(type: Copy) {
- from "../CONTRIBUTORS"
+task copyLicense(type: Copy) {
from "../LICENSE"
into "src/main/assets/"
rename { String fileName ->
@@ -204,5 +196,5 @@ task copyTextFiles(type: Copy) {
outputs.upToDateWhen { false }
}
-preBuild.dependsOn filterAbout, copyTextFiles
+preBuild.dependsOn copyLicense
diff --git a/app/proguard.cfg b/app/proguard.cfg
index 5309e833d..2551988fd 100644
--- a/app/proguard.cfg
+++ b/app/proguard.cfg
@@ -9,7 +9,6 @@
-allowaccessmodification
-keepattributes *Annotation*
-#-injars libs/presto_client-0.8.5.jar
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
@@ -80,10 +79,11 @@
-dontskipnonpubliclibraryclassmembers
# greenrobot EventBus
--keepclassmembers class ** {
- public void onEvent*(**);
+-keepattributes *Annotation*
+-keepclassmembers class * {
+ @org.greenrobot.eventbus.Subscribe <methods>;
}
--keep class de.danoeh.antennapod.core.event.*
+-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# android-iconify
-keep class com.joanzapata.** { *; }
@@ -126,4 +126,3 @@
-dontwarn java.lang.management.ManagementFactory
-dontwarn java.lang.management.ThreadInfo
-dontwarn java.lang.management.ThreadMXBean
-
diff --git a/app/src/androidTest/assets/3sec.mp3 b/app/src/androidTest/assets/3sec.mp3
deleted file mode 100644
index 8ae450d01..000000000
--- a/app/src/androidTest/assets/3sec.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/androidTest/assets/testfile.mp3 b/app/src/androidTest/assets/testfile.mp3
deleted file mode 100644
index f15faadf3..000000000
--- a/app/src/androidTest/assets/testfile.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index f895b4d5e..bbd8a705c 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -1,11 +1,14 @@
package de.test.antennapod;
import android.content.Context;
+import android.content.Intent;
+import androidx.annotation.IdRes;
import androidx.annotation.StringRes;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.util.HumanReadables;
@@ -13,11 +16,16 @@ import androidx.test.espresso.util.TreeIterables;
import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.dialog.RatingDialog;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
import org.hamcrest.Matcher;
import java.io.File;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static androidx.test.espresso.Espresso.onView;
@@ -26,6 +34,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
public class EspressoTestUtils {
/**
@@ -75,6 +84,31 @@ public class EspressoTestUtils {
}
/**
+ * Perform action of waiting for a specific view id.
+ * https://stackoverflow.com/a/30338665/
+ * @param id The id of the child to click.
+ */
+ public static ViewAction clickChildViewWithId(final @IdRes int id) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Click on a child view with specified id.";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ View v = view.findViewById(id);
+ v.performClick();
+ }
+ };
+ }
+
+ /**
* Clear all app databases
*/
public static void clearPreferences() {
@@ -114,7 +148,9 @@ public class EspressoTestUtils {
public static void clickPreference(@StringRes int title) {
onView(withId(R.id.recycler_view)).perform(
- RecyclerViewActions.actionOnItem(hasDescendant(withText(title)),
+ RecyclerViewActions.actionOnItem(
+ allOf(hasDescendant(withText(title)),
+ hasDescendant(withId(android.R.id.widget_frame))),
click()));
}
@@ -123,8 +159,37 @@ public class EspressoTestUtils {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
- public static void closeNavDrawer() {
- onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
- onView(withId(R.id.drawer_layout)).perform(DrawerActions.close());
+ public static ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
+ return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
+ }
+
+ public static void tryKillPlaybackService() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ context.stopService(new Intent(context, PlaybackService.class));
+ try {
+ // Android has no reliable way to stop a service instantly.
+ // Calling stopSelf marks allows the system to destroy the service but the actual call
+ // to onDestroy takes until the next GC of the system, which we can not influence.
+ // Try to wait for the service at least a bit.
+ Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !PlaybackService.isRunning);
+ } catch (ConditionTimeoutException e) {
+ e.printStackTrace();
+ }
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ public static void tryKillDownloadService() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ context.stopService(new Intent(context, DownloadService.class));
+ try {
+ // Android has no reliable way to stop a service instantly.
+ // Calling stopSelf marks allows the system to destroy the service but the actual call
+ // to onDestroy takes until the next GC of the system, which we can not influence.
+ // Try to wait for the service at least a bit.
+ Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !DownloadService.isRunning);
+ } catch (ConditionTimeoutException e) {
+ e.printStackTrace();
+ }
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java b/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java
new file mode 100644
index 000000000..41ab15b9b
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java
@@ -0,0 +1,15 @@
+package de.test.antennapod;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Tests with this annotation are ignored on CI. This could be reasonable
+ * if the performance of the CI server is not enough to provide a reliable result.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface IgnoreOnCi {
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
index f9ecacda5..85eabcc63 100644
--- a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
+++ b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
@@ -11,11 +11,7 @@ public class NthMatcher {
return nth(matcher, 1);
}
- public static <T> Matcher<T> second(final Matcher<T> matcher) {
- return nth(matcher, 2);
- }
-
- private static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
+ public static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
return new BaseMatcher<T>() {
AtomicInteger count = new AtomicInteger(0);
@@ -31,7 +27,8 @@ public class NthMatcher {
@Override
public void describeTo(final Description description) {
- description.appendText("should return first matching item");
+ description.appendText("Item #" + index + " ");
+ description.appendDescriptionOf(matcher);
}
};
}
diff --git a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
index cd3847f17..161393b8a 100644
--- a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
@@ -29,7 +29,7 @@ public class GPodnetServiceTest {
private static final String PW = "";
@Before
- protected void setUp() {
+ public void setUp() {
service = new GpodnetService();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
new file mode 100644
index 000000000..cc380813e
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
@@ -0,0 +1,308 @@
+package de.test.antennapod.playback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.view.KeyEvent;
+import android.view.View;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.LongList;
+import de.test.antennapod.EspressoTestUtils;
+import de.test.antennapod.IgnoreOnCi;
+import de.test.antennapod.ui.UITestUtils;
+import org.awaitility.Awaitility;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
+import static androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.clickChildViewWithId;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity.
+ */
+@LargeTest
+@IgnoreOnCi
+@RunWith(Parameterized.class)
+public class PlaybackTest {
+ @Rule
+ public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
+
+ @Parameterized.Parameter(value = 0)
+ public String playerToUse;
+ private UITestUtils uiTestUtils;
+ protected Context context;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> initParameters() {
+ return Arrays.asList(new Object[][] { { "exoplayer" }, { "builtin" }, { "sonic" } });
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.makeNotFirstRun();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER, playerToUse).apply();
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ activityTestRule.finishActivity();
+ EspressoTestUtils.tryKillPlaybackService();
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ playFromQueue(0);
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(
+ () -> uiTestUtils.getPlaybackController(getActivity()).getStatus() == PlayerStatus.INITIALIZED);
+ }
+
+ @Test
+ public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(true);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+ final FeedItem second = queue.get(1);
+
+ playFromQueue(0);
+ Awaitility.await().atMost(2, TimeUnit.SECONDS).until(
+ () -> first.getMedia().equals(uiTestUtils.getCurrentMedia()));
+ Awaitility.await().atMost(6, TimeUnit.SECONDS).until(
+ () -> second.getMedia().equals(uiTestUtils.getCurrentMedia()));
+ }
+
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
+ replayEpisodeCheck(true);
+ }
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
+ replayEpisodeCheck(false);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_Average() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(0);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_LastEpisodeInQueue() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(-1);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Pause_WontAffectItem() throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ final int fiIdx = 0;
+ final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
+
+ playFromQueue(fiIdx);
+
+ // let playback run a bit then pause
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PLAYING == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+ pauseEpisode();
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PAUSED == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+
+ assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
+ DBReader.getQueue(), hasItems(feedItem));
+ assertThat("Ensure even with smart mark as play, after pause, the item played status remains false.",
+ DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(false));
+ }
+
+ @Test
+ public void testStartLocal() throws Exception {
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ @Test
+ public void testContinousPlaybackOffSingleEpisode() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ protected MainActivity getActivity() {
+ return activityTestRule.getActivity();
+ }
+
+ protected void setContinuousPlaybackPreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
+ }
+
+ protected void setSkipKeepsEpisodePreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit();
+ }
+
+ protected void setSmartMarkAsPlayedPreference(int smartMarkAsPlayedSecs) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS,
+ Integer.toString(smartMarkAsPlayedSecs, 10))
+ .commit();
+ }
+
+ private void skipEpisode() {
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
+ }
+
+ protected void pauseEpisode() {
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ }
+
+ protected void startLocalPlayback() {
+ openNavDrawer();
+ onDrawerItem(withText(R.string.episodes_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(R.string.all_episodes_short_label), 1000));
+ onView(withText(R.string.all_episodes_short_label)).perform(click());
+
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+ Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
+ onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
+ onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.butSecondaryAction)));
+
+ FeedMedia media = episodes.get(0).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+ }
+
+ /**
+ *
+ * @param itemIdx The 0-based index of the episode to be played in the queue.
+ */
+ protected void playFromQueue(int itemIdx) {
+ final List<FeedItem> queue = DBReader.getQueue();
+
+ Matcher<View> queueMatcher = allOf(withId(R.id.recyclerView), isDisplayed(), hasMinimumChildCount(2));
+ onView(isRoot()).perform(waitForView(queueMatcher, 1000));
+ onView(queueMatcher).perform(actionOnItemAtPosition(itemIdx, clickChildViewWithId(R.id.butSecondaryAction)));
+
+ FeedMedia media = queue.get(itemIdx).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+
+ }
+
+ /**
+ * Check if an episode can be played twice without problems.
+ */
+ protected void replayEpisodeCheck(boolean followQueue) throws Exception {
+ setContinuousPlaybackPreference(followQueue);
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ activityTestRule.launchActivity(new Intent());
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+
+ startLocalPlayback();
+ FeedMedia media = episodes.get(0).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(
+ () -> !media.equals(uiTestUtils.getCurrentMedia()));
+
+ startLocalPlayback();
+
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+ }
+
+ protected void doTestSmartMarkAsPlayed_Skip_ForEpisode(int itemIdxNegAllowed) throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+ // ensure when an episode is skipped, it is removed due to smart as played
+ setSkipKeepsEpisodePreference(false);
+ uiTestUtils.setMediaFileName("30sec.mp3");
+ uiTestUtils.addLocalFeedData(true);
+
+ LongList queue = DBReader.getQueueIDList();
+ int fiIdx;
+ if (itemIdxNegAllowed >= 0) {
+ fiIdx = itemIdxNegAllowed;
+ } else { // negative index: count from the end, with -1 being the last one, etc.
+ fiIdx = queue.size() + itemIdxNegAllowed;
+ }
+ final long feedItemId = queue.get(fiIdx);
+ queue.removeIndex(fiIdx);
+ assertFalse(queue.contains(feedItemId)); // Verify that episode is in queue only once
+
+ activityTestRule.launchActivity(new Intent());
+ playFromQueue(fiIdx);
+
+ skipEpisode();
+
+ // assert item no longer in queue (needs to wait till skip is asynchronously processed)
+ Awaitility.await()
+ .atMost(5000, MILLISECONDS)
+ .until(() -> !DBReader.getQueueIDList().contains(feedItemId));
+ assertTrue(DBReader.getFeedItem(feedItemId).isPlayed());
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
index cb099cc85..1ca4de1ad 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
@@ -1,10 +1,13 @@
package de.test.antennapod.service.download;
+import android.content.Context;
+import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import de.test.antennapod.EspressoTestUtils;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.junit.After;
@@ -21,10 +24,12 @@ import java.util.concurrent.TimeUnit;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.service.download.DownloaderFactory;
import de.danoeh.antennapod.core.service.download.StubDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -47,17 +52,21 @@ public class DownloadServiceTest {
private Feed testFeed = null;
private FeedMedia testMedia11 = null;
- private DownloadService.DownloaderFactory origFactory = null;
+ private DownloaderFactory origFactory = null;
@Before
public void setUp() throws Exception {
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.clearPreferences();
origFactory = DownloadService.getDownloaderFactory();
testFeed = setUpTestFeeds();
testMedia11 = testFeed.getItemAtIndex(0).getMedia();
}
private Feed setUpTestFeeds() throws Exception {
- Feed feed = new Feed("url", null, "Test Feed title 1");
+ // To avoid complication in case of test failures, leaving behind orphaned
+ // media files: add a timestamp so that each test run will have its own directory for media files.
+ Feed feed = new Feed("url", null, "Test Feed title 1 " + System.currentTimeMillis());
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
@@ -73,32 +82,51 @@ public class DownloadServiceTest {
@After
public void tearDown() throws Exception {
DownloadService.setDownloaderFactory(origFactory);
+ Context context = InstrumentationRegistry.getTargetContext();
+ DownloadRequester.getInstance().cancelAllDownloads(context);
+ context.stopService(new Intent(context, DownloadService.class));
+ EspressoTestUtils.tryKillDownloadService();
}
@Test
- public void testEventsGeneratedCaseMediaDownloadSuccess() throws Exception {
+ public void testEventsGeneratedCaseMediaDownloadSuccess_noEnqueue() throws Exception {
+ doTestEventsGeneratedCaseMediaDownloadSuccess(false, 1);
+ }
+
+ @Test
+ public void testEventsGeneratedCaseMediaDownloadSuccess_withEnqueue() throws Exception {
+ // enqueue itself generates additional FeedItem event
+ doTestEventsGeneratedCaseMediaDownloadSuccess(true, 2);
+ }
+
+ private void doTestEventsGeneratedCaseMediaDownloadSuccess(boolean enqueueDownloaded,
+ int numEventsExpected)
+ throws Exception {
// create a stub download that returns successful
//
// OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
// are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
// to do so
DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
- downloadStatus.setSuccessful();
+ downloadStatus.setSuccessful();
}));
+ UserPreferences.setEnqueueDownloadedEpisodes(enqueueDownloaded);
withFeedItemEventListener(feedItemEventListener -> {
try {
assertEquals(0, feedItemEventListener.getEvents().size());
assertFalse("The media in test should not yet been downloaded",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
- DownloadRequester.getInstance().downloadMedia(InstrumentationRegistry.getTargetContext(),
- testMedia11);
- Awaitility.await()
+ DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(),
+ testMedia11.getItem());Awaitility.await()
.atMost(1000, TimeUnit.MILLISECONDS)
- .until(() -> feedItemEventListener.getEvents().size() > 0);
+ .until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ assertEquals("The FeedItem should have been " + (enqueueDownloaded ? "" : "not ") + "enqueued",
+ enqueueDownloaded,
+ DBReader.getQueueIDList().contains(testMedia11.getItem().getId()));
} catch (ConditionTimeoutException cte) {
fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ cte.getMessage());
@@ -106,7 +134,65 @@ public class DownloadServiceTest {
});
}
- private static class StubDownloaderFactory implements DownloadService.DownloaderFactory {
+ @Test
+ public void testCancelDownload_UndoEnqueue_Normal() throws Exception {
+ doTestCancelDownload_UndoEnqueue(false);
+ }
+
+ @Test
+ public void testCancelDownload_UndoEnqueue_AlreadyInQueue() throws Exception {
+ doTestCancelDownload_UndoEnqueue(true);
+ }
+
+ private void doTestCancelDownload_UndoEnqueue(boolean itemAlreadyInQueue) throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+ // let download takes longer to ensure the test can cancel the download in time
+ DownloadService.setDownloaderFactory(new StubDownloaderFactory(10000, downloadStatus -> {
+ downloadStatus.setSuccessful();
+ }));
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnableAutodownload(false);
+
+ final long item1Id = testMedia11.getItem().getId();
+ if (itemAlreadyInQueue) {
+ // simulate item already in queue condition
+ DBWriter.addQueueItem(context, false, item1Id).get();
+ assertTrue(DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse(DBReader.getQueueIDList().contains(item1Id));
+ }
+
+ withFeedItemEventListener(feedItemEventListener -> {
+ DownloadRequester.getInstance().downloadMedia(false, context, testMedia11.getItem());
+ if (itemAlreadyInQueue) {
+ Awaitility.await("download service receives the request - "
+ + "no event is expected before cancel is issued")
+ .atLeast(100, TimeUnit.MILLISECONDS)
+ .until(() -> true);
+ } else {
+ Awaitility.await("item enqueue event")
+ .atMost(2000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= 1);
+ }
+ DownloadRequester.getInstance().cancelDownload(context, testMedia11);
+ final int totalNumEventsExpected = itemAlreadyInQueue ? 1 : 3;
+ Awaitility.await("item dequeue event + download termination event")
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= totalNumEventsExpected);
+ assertFalse("The download should have been canceled",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ if (itemAlreadyInQueue) {
+ assertTrue("The FeedItem should still be in the queue after the download is cancelled."
+ + " It's there before download.",
+ DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse("The FeedItem should not be in the queue after the download is cancelled.",
+ DBReader.getQueueIDList().contains(item1Id));
+ }
+ });
+ }
+
+ private static class StubDownloaderFactory implements DownloaderFactory {
private final long downloadTime;
@NonNull
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 4530b7679..7d9ec7c98 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -29,10 +29,9 @@ public class HttpDownloaderTest {
private static final String TAG = "HttpDownloaderTest";
private static final String DOWNLOAD_DIR = "testdownloads";
- private static boolean successful = true;
-
+ private String url404;
+ private String urlAuth;
private File destDir;
-
private HTTPBin httpServer;
public HttpDownloaderTest() {
@@ -57,6 +56,8 @@ public class HttpDownloaderTest {
assertTrue(destDir.exists());
httpServer = new HTTPBin();
httpServer.start();
+ url404 = httpServer.getBaseUrl() + "/status/404";
+ urlAuth = httpServer.getBaseUrl() + "/basic-auth/user/passwd";
}
private FeedFileImpl setupFeedFile(String downloadUrl, String title, boolean deleteExisting) {
@@ -88,33 +89,29 @@ public class HttpDownloaderTest {
return downloader;
}
-
- private static final String URL_404 = HTTPBin.BASE_URL + "/status/404";
- private static final String URL_AUTH = HTTPBin.BASE_URL + "/basic-auth/user/passwd";
-
@Test
public void testPassingHttp() {
- download(HTTPBin.BASE_URL + "/status/200", "test200", true);
+ download(httpServer.getBaseUrl() + "/status/200", "test200", true);
}
@Test
public void testRedirect() {
- download(HTTPBin.BASE_URL + "/redirect/4", "testRedirect", true);
+ download(httpServer.getBaseUrl() + "/redirect/4", "testRedirect", true);
}
@Test
public void testGzip() {
- download(HTTPBin.BASE_URL + "/gzip/100", "testGzip", true);
+ download(httpServer.getBaseUrl() + "/gzip/100", "testGzip", true);
}
@Test
public void test404() {
- download(URL_404, "test404", false);
+ download(url404, "test404", false);
}
@Test
public void testCancel() {
- final String url = HTTPBin.BASE_URL + "/delay/3";
+ final String url = httpServer.getBaseUrl() + "/delay/3";
FeedFileImpl feedFile = setupFeedFile(url, "delay", true);
final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt()));
Thread t = new Thread() {
@@ -139,7 +136,7 @@ public class HttpDownloaderTest {
@Test
public void testDeleteOnFailShouldDelete() {
- Downloader downloader = download(URL_404, "testDeleteOnFailShouldDelete", false, true, null, null, true);
+ Downloader downloader = download(url404, "testDeleteOnFailShouldDelete", false, true, null, null, true);
assertFalse(new File(downloader.getDownloadRequest().getDestination()).exists());
}
@@ -149,18 +146,18 @@ public class HttpDownloaderTest {
File dest = new File(destDir, filename);
dest.delete();
assertTrue(dest.createNewFile());
- Downloader downloader = download(URL_404, filename, false, false, null, null, false);
+ Downloader downloader = download(url404, filename, false, false, null, null, false);
assertTrue(new File(downloader.getDownloadRequest().getDestination()).exists());
}
@Test
public void testAuthenticationShouldSucceed() throws InterruptedException {
- download(URL_AUTH, "testAuthSuccess", true, true, "user", "passwd", true);
+ download(urlAuth, "testAuthSuccess", true, true, "user", "passwd", true);
}
@Test
public void testAuthenticationShouldFail() {
- Downloader downloader = download(URL_AUTH, "testAuthSuccess", false, true, "user", "Wrong passwd", true);
+ Downloader downloader = download(urlAuth, "testAuthSuccess", false, true, "user", "Wrong passwd", true);
assertEquals(DownloadError.ERROR_UNAUTHORIZED, downloader.getResult().getReason());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
index e4081a55a..f7f3c1aa7 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
@@ -2,8 +2,8 @@ package de.test.antennapod.service.playback;
import android.content.Context;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import de.test.antennapod.EspressoTestUtils;
import junit.framework.AssertionFailedError;
@@ -33,7 +33,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -46,13 +46,12 @@ import static org.junit.Assert.fail;
*/
@MediumTest
public class PlaybackServiceMediaPlayerTest {
- private static final String PLAYABLE_FILE_URL = "http://127.0.0.1:" + HTTPBin.PORT + "/files/0";
private static final String PLAYABLE_DEST_URL = "psmptestfile.mp3";
private String PLAYABLE_LOCAL_URL = null;
private static final int LATCH_TIMEOUT_SECONDS = 3;
private HTTPBin httpServer;
-
+ private String playableFileUrl;
private volatile AssertionFailedError assertionError;
@After
@@ -68,10 +67,11 @@ public class PlaybackServiceMediaPlayerTest {
EspressoTestUtils.makeNotFirstRun();
EspressoTestUtils.clearDatabase();
- final Context context = InstrumentationRegistry.getTargetContext();
+ final Context context = getInstrumentation().getTargetContext();
httpServer = new HTTPBin();
httpServer.start();
+ playableFileUrl = httpServer.getBaseUrl() + "/files/0";
File cacheDir = context.getExternalFilesDir("testFiles");
if (cacheDir == null)
@@ -82,7 +82,7 @@ public class PlaybackServiceMediaPlayerTest {
assertTrue(cacheDir.canWrite());
assertTrue(cacheDir.canRead());
if (!dest.exists()) {
- InputStream i = InstrumentationRegistry.getContext().getAssets().open("testfile.mp3");
+ InputStream i = getInstrumentation().getTargetContext().getAssets().open("3sec.mp3");
OutputStream o = new FileOutputStream(new File(cacheDir, PLAYABLE_DEST_URL));
IOUtils.copy(i, o);
o.flush();
@@ -168,7 +168,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, false, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -208,7 +208,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, true, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -252,7 +252,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, false, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -297,7 +297,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, true, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -335,7 +335,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -374,7 +374,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -416,7 +416,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -461,7 +461,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -524,7 +524,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.PLAYING) {
psmp.playMediaObject(p, stream, true, true);
}
@@ -617,7 +617,7 @@ public class PlaybackServiceMediaPlayerTest {
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
boolean startWhenPrepared = (initialState != PlayerStatus.PREPARED);
- psmp.playMediaObject(writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL), false, startWhenPrepared, true);
+ psmp.playMediaObject(writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL), false, startWhenPrepared, true);
}
if (initialState == PlayerStatus.PAUSED) {
psmp.pause(false, false);
@@ -674,7 +674,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.INITIALIZED
|| initialState == PlayerStatus.PLAYING
|| initialState == PlayerStatus.PREPARED
@@ -748,7 +748,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
boolean prepareImmediately = initialState != PlayerStatus.INITIALIZED;
boolean startImmediately = initialState != PlayerStatus.PREPARED;
psmp.playMediaObject(p, false, startImmediately, prepareImmediately);
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index 415ee9b68..dbc86a228 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -18,7 +18,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -106,18 +105,8 @@ public class PlaybackServiceTaskManagerTest {
assertNotNull(testQueue);
assertTrue(testQueue.isEmpty());
-
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- EventDistributor.EventListener queueListener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- countDownLatch.countDown();
- }
- };
- EventDistributor.getInstance().register(queueListener);
List<FeedItem> queue = writeTestQueue("a");
EventBus.getDefault().post(QueueEvent.setQueue(queue));
- countDownLatch.await(5000, TimeUnit.MILLISECONDS);
assertNotNull(queue);
testQueue = pstm.getQueue();
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
new file mode 100644
index 000000000..b89f1b9bc
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
@@ -0,0 +1,164 @@
+package de.test.antennapod.storage;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import de.test.antennapod.EspressoTestUtils;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.DBTasksCallbacks;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.test.antennapod.ui.UITestUtils;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AutoDownloadTest {
+
+ private Context context;
+ private UITestUtils stubFeedsServer;
+
+ private DBTasksCallbacks dbTasksCallbacksOrig;
+
+ @Before
+ public void setUp() throws Exception {
+ context = ApplicationProvider.getApplicationContext();
+
+ stubFeedsServer = new UITestUtils(context);
+ stubFeedsServer.setup();
+
+ dbTasksCallbacksOrig = ClientConfig.dbTasksCallbacks;
+
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.clearDatabase();
+ UserPreferences.setAllowMobileStreaming(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ ClientConfig.dbTasksCallbacks = dbTasksCallbacksOrig;
+ EspressoTestUtils.tryKillPlaybackService();
+ stubFeedsServer.tearDown();
+ }
+
+ /**
+ * A cross-functional test, ensuring playback's behavior works with Auto Download in boundary condition.
+ *
+ * Scenario:
+ * - For setting enqueue location AFTER_CURRENTLY_PLAYING
+ * - when playback of an episode is complete and the app advances to the next episode (continuous playback on)
+ * - when automatic download kicks in,
+ * - ensure the next episode is the current playing one, needed for AFTER_CURRENTLY_PLAYING enqueue location.
+ */
+ @Test
+ public void downloadsEnqueuedToAfterCurrent_CurrentAdvancedToNextOnPlaybackComplete() throws Exception {
+ UserPreferences.setFollowQueue(true); // continuous playback
+
+ // Setup: feeds and queue
+ // downloads 3 of them, leave some in new state (auto-downloadable)
+ stubFeedsServer.addLocalFeedData(false);
+ List<FeedItem> queue = DBReader.getQueue();
+ assertTrue(queue.size() > 1);
+ FeedItem item0 = queue.get(0);
+ FeedItem item1 = queue.get(1);
+
+ // Setup: enable automatic download
+ // it is not needed, as the actual automatic download is stubbed.
+ StubDownloadAlgorithm stubDownloadAlgorithm = new StubDownloadAlgorithm();
+ useDownloadAlgorithm(stubDownloadAlgorithm);
+
+ // Actual test
+ // Play the first one in the queue
+ playEpisode(item0);
+
+ try {
+ // when playback is complete, advances to the next one, and auto download kicks in,
+ // ensure that currently playing has been advanced to the next one by this point.
+ Awaitility.await("advanced to the next episode")
+ .atMost(6000, MILLISECONDS) // the test mp3 media is 3-second long. twice should be enough
+ .until(() -> item1.equals(stubDownloadAlgorithm.getCurrentlyPlayingAtDownload()));
+ } catch (ConditionTimeoutException cte) {
+ FeedItem actual = stubDownloadAlgorithm.getCurrentlyPlayingAtDownload();
+ fail("when auto download is triggered, the next episode should be playing: ("
+ + item1.getId() + ", " + item1.getTitle() + ") . "
+ + "Actual playing: ("
+ + (actual == null ? "" : actual.getId() + ", " + actual.getTitle()) + ")"
+ );
+ }
+
+ }
+
+ private void playEpisode(@NonNull FeedItem item) {
+ FeedMedia media = item.getMedia();
+ DBTasks.playMedia(context, media, false, true, true);
+ Awaitility.await("episode is playing")
+ .atMost(2000, MILLISECONDS)
+ .until(() -> item.equals(getCurrentlyPlaying()));
+ }
+
+ private FeedItem getCurrentlyPlaying() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ if (playable == null) {
+ return null;
+ }
+ return ((FeedMedia) playable).getItem();
+ }
+
+ private void useDownloadAlgorithm(final AutomaticDownloadAlgorithm downloadAlgorithm) {
+ ClientConfig.dbTasksCallbacks = new DBTasksCallbacks() {
+ @Override
+ public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm() {
+ return downloadAlgorithm;
+ }
+
+ @Override
+ public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
+ return dbTasksCallbacksOrig.getEpisodeCacheCleanupAlgorithm();
+ }
+ };
+ }
+
+ private class StubDownloadAlgorithm implements AutomaticDownloadAlgorithm {
+ @Nullable
+ private FeedItem currentlyPlaying;
+
+ @Override
+ public Runnable autoDownloadUndownloadedItems(Context context) {
+ return () -> {
+ if (currentlyPlaying == null) {
+ currentlyPlaying = getCurrentlyPlaying();
+ } else {
+ throw new AssertionError("Stub automatic download should be invoked once and only once");
+ }
+ };
+ }
+
+ @Nullable
+ FeedItem getCurrentlyPlayingAtDownload() {
+ return currentlyPlaying;
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
index 1acb96d01..88d78fd14 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -82,6 +82,7 @@ public class DBCleanupTests {
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
prefEdit.putString(UserPreferences.PREF_EPISODE_CLEANUP, Integer.toString(cleanupAlgorithm));
+ prefEdit.putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true);
prefEdit.commit();
UserPreferences.init(context);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
index 55ea16f13..090cd2213 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -2,26 +2,31 @@ package de.test.antennapod.storage;
import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.filters.SmallTest;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -179,4 +184,82 @@ public class DBTasksTest {
lastDate = item.getPubDate();
}
}
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ // the first item fis1.get(0) is already in the queue
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Arrays.asList(fis1.get(1), fis2.get(2), fis2.get(1));
+ List<FeedItem> expectedQueue = new ArrayList<>();
+ expectedQueue.addAll(DBReader.getQueue());
+ expectedQueue.addAll(expectedEnqueued);
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("Only items not in the queue are enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue has new items appended", expectedQueue, DBReader.getQueue());
+ }
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueDisabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(false);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Collections.emptyList();
+ List<FeedItem> expectedQueue = DBReader.getQueue();
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("No item is enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue is unchanged", expectedQueue, DBReader.getQueue());
+ }
+
+ private void assertEqualsByIds(String msg, List<? extends FeedItem> expected, List<? extends FeedItem> actual) {
+ // assert only the IDs, so that any differences are easily to spot.
+ List<Long> expectedIds = getIdList(expected);
+ List<Long> actualIds = getIdList(actual);
+ assertEquals(msg, expectedIds, actualIds);
+ }
+
+ private Feed createSavedFeed(String title, int numFeedItems) {
+ final Feed feed = new Feed("url", null, title);
+
+ if (numFeedItems > 0) {
+ List<FeedItem> items = new ArrayList<>(numFeedItems);
+ for (int i = 1; i <= numFeedItems; i++) {
+ FeedItem item = new FeedItem(0, "item " + i + " of " + title, "id", "link",
+ new Date(), FeedItem.UNPLAYED, feed);
+ items.add(item);
+ }
+ feed.setItems(items);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ adapter.close();
+ return feed;
+ }
+
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index 09a8466e6..ebf59fea7 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -44,8 +44,8 @@ class DBTestUtils {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < numFeeds; i++) {
- Feed f = new Feed(0, null, "feed " + i, null, "link" + i, "descr", null, null,
- null, null, "id" + i, null, null, "url" + i, false, false, null, null, false);
+ Feed f = new Feed(0, null, "feed " + i, "link" + i, "descr", null, null,
+ null, null, "id" + i, null, null, "url" + i, false);
f.setItems(new ArrayList<>());
for (int j = 0; j < numItems; j++) {
FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(),
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
index 42f4413d3..10ca281cb 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -4,12 +4,15 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
+import android.util.Log;
+
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
-import android.util.Log;
import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.io.File;
import java.io.IOException;
@@ -29,9 +32,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.Consumer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@@ -321,15 +322,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -386,15 +383,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -475,11 +468,12 @@ public class DBWriterTest {
assertFalse(OLD_DATE == media.getPlaybackCompletionDate().getTime());
}
- private Feed queueTestSetupMultipleItems(final int NUM_ITEMS) throws InterruptedException, ExecutionException, TimeoutException {
+ private Feed queueTestSetupMultipleItems(final int numItems) throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- for (int i = 0; i < NUM_ITEMS; i++) {
+ for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -573,12 +567,16 @@ public class DBWriterTest {
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getCount() == NUM_ITEMS);
+ List<Long> expectedIds = FeedItemUtil.getIdList(feed.getItems());
+ List<Long> actualIds = new ArrayList<>();
for (int i = 0; i < NUM_ITEMS; i++) {
assertTrue(cursor.moveToPosition(i));
- assertTrue(cursor.getLong(0) == feed.getItems().get(i).getId());
+ actualIds.add(cursor.getLong(0));
}
cursor.close();
adapter.close();
+ assertEquals("Bulk add to queue: result order should be the same as the order given",
+ expectedIds, actualIds);
}
@Test
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 9f16888fa..0784cb078 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -3,10 +3,10 @@ package de.test.antennapod.ui;
import android.app.Activity;
import android.content.Intent;
import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.Espresso;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
@@ -23,10 +23,18 @@ import java.io.IOException;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.Intents.times;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -69,10 +77,11 @@ public class MainActivityTest {
final Feed feed = uiTestUtils.hostedFeeds.get(0);
openNavDrawer();
onView(withText(R.string.add_feed_label)).perform(click());
- solo.enterText(1, feed.getDownload_url());
- onView(withText(R.string.confirm_label)).perform(click());
+ onView(withId(R.id.etxtFeedurl)).perform(scrollTo(), typeText(feed.getDownload_url()));
+ onView(withText(R.string.confirm_label)).perform(scrollTo(), click());
+ Espresso.closeSoftKeyboard();
onView(withText(R.string.subscribe_label)).perform(click());
- assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
+ onView(isRoot()).perform(waitForView(withId(R.id.butShowSettings), 5000));
}
private String getActionbarTitle() {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
index 360b55ce6..aa4389057 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
@@ -2,11 +2,9 @@ package de.test.antennapod.ui;
import android.content.Intent;
import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;
-import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -17,7 +15,6 @@ import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.test.antennapod.EspressoTestUtils;
-import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -29,15 +26,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static de.test.antennapod.NthMatcher.first;
import static junit.framework.TestCase.assertTrue;
@@ -45,7 +45,7 @@ import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
/**
- * User interface tests for MainActivity drawer
+ * User interface tests for MainActivity drawer.
*/
@RunWith(AndroidJUnit4.class)
public class NavigationDrawerTest {
@@ -53,7 +53,7 @@ public class NavigationDrawerTest {
private UITestUtils uiTestUtils;
@Rule
- public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() throws IOException {
@@ -75,16 +75,12 @@ public class NavigationDrawerTest {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
- private ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
- return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
- }
-
@Test
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
- MainActivity activity = mActivityRule.getActivity();
+ activityRule.launchActivity(new Intent());
+ MainActivity activity = activityRule.getActivity();
// queue
openNavDrawer();
@@ -118,6 +114,7 @@ public class NavigationDrawerTest {
// add podcast
openNavDrawer();
+ onView(withId(R.id.nav_list)).perform(swipeUp());
onDrawerItem(withText(R.string.add_feed_label)).perform(click());
onView(isRoot()).perform(waitForView(withId(R.id.txtvFeedurl), 1000));
assertEquals(activity.getString(R.string.add_feed_label), activity.getSupportActionBar().getTitle());
@@ -134,7 +131,7 @@ public class NavigationDrawerTest {
@Test
public void testGoToPreferences() {
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.settings_label)).perform(click());
intended(hasComponent(PreferenceActivity.class.getName()));
@@ -143,7 +140,7 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onDrawerItem(withText(R.string.queue_label)).perform(longClick());
onView(withText(R.string.episodes_label)).perform(click());
@@ -160,7 +157,7 @@ public class NavigationDrawerTest {
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
UserPreferences.setHiddenDrawerItems(hidden);
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
@@ -178,14 +175,20 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
- String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
+ activityRule.launchActivity(new Intent());
+ String[] titles = activityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
- for (String title : titles) {
+ for (int i = 0; i < titles.length; i++) {
+ String title = titles[i];
onView(first(withText(title))).perform(click());
+
+ if (i == 3) {
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
+ }
}
+
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
@@ -198,7 +201,7 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
deleted file mode 100644
index 5b3530ea8..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackSonicTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- private Solo solo;
- private UITestUtils uiTestUtils;
-
- private Context context;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
-
- @Before
- public void setUp() throws Exception {
- EspressoTestUtils.clearPreferences();
- EspressoTestUtils.makeNotFirstRun();
- EspressoTestUtils.clearDatabase();
- context = InstrumentationRegistry.getTargetContext();
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit()
- .clear()
- .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
- .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .putString(UserPreferences.PREF_MEDIA_PLAYER, "sonic")
- .commit();
-
- activityTestRule.launchActivity(new Intent());
- solo = new Solo(getInstrumentation(), activityTestRule.getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void startLocalPlayback() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Episodes'
- View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- getInstrumentation().waitForIdleSync();
- solo.waitForText(solo.getString(R.string.all_episodes_short_label));
- solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
- getInstrumentation().waitForIdleSync();
-
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- solo.clickOnView(solo.getView(R.id.butSecondaryAction));
- long mediaId = episodes.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- private void startLocalPlaybackFromQueue() {
- openNavDrawer();
-
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Queue'
- View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- getInstrumentation().waitForIdleSync();
- solo.clickOnView(targetView);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- final List<FeedItem> queue = DBReader.getQueue();
- solo.clickOnImageButton(1);
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- long mediaId = queue.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
-
- startLocalPlaybackFromQueue();
- boolean stopped = solo.waitForCondition(() -> {
- if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
- != PlayerStatus.PLAYING) {
- return true;
- } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- != first.getMedia().getId();
- } else {
- return true;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(stopped);
- Thread.sleep(1000);
- PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
- assertFalse(status.equals(PlayerStatus.PLAYING));
- }
-
- @Test
- public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(true);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- final FeedItem second = queue.get(1);
-
- startLocalPlaybackFromQueue();
- boolean firstPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == first.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(firstPlaying);
- boolean secondPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == second.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(secondPlaying);
- }
-
- /**
- * Check if an episode can be played twice without problems.
- */
- private void replayEpisodeCheck(boolean followQueue) throws Exception {
- setContinuousPlaybackPreference(followQueue);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
-
- startLocalPlayback();
- long mediaId = episodes.get(0).getMedia().getId();
- boolean startedPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(startedPlaying);
-
- boolean stoppedPlaying = solo.waitForCondition(() ->
- uiTestUtils.getCurrentMedia(getActivity()) == null
- || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId
- , Timeout.getLargeTimeout());
- assertTrue(stoppedPlaying);
-
- startLocalPlayback();
- boolean startedReplay = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(startedReplay);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
- replayEpisodeCheck(false);
- }
-
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
deleted file mode 100644
index ec5dc804e..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ /dev/null
@@ -1,375 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import org.awaitility.Awaitility;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
-
- private Solo solo;
- private UITestUtils uiTestUtils;
- private Context context;
-
- @Before
- public void setUp() throws Exception {
- context = InstrumentationRegistry.getTargetContext();
-
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit()
- .clear()
- .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
- .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .commit();
-
- solo = new Solo(InstrumentationRegistry.getInstrumentation(), getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
-
- // create database
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void setSkipKeepsEpisodePreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit();
- }
-
- private void setSmartMarkAsPlayedPreference(int smartMarkAsPlayedSecs) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS,
- Integer.toString(smartMarkAsPlayedSecs, 10))
- .commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void pauseEpisode() {
- Intent pauseIntent = new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
- context.sendBroadcast(pauseIntent);
- }
-
- private void startLocalPlayback() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Episodes'
- View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- solo.waitForText(solo.getString(R.string.all_episodes_short_label));
- solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
-
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- solo.clickOnView(solo.getView(R.id.butSecondaryAction));
- long mediaId = episodes.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- private void startLocalPlaybackFromQueue() {
- gotoQueueScreen();
- playFromQueue(0);
- }
-
- private void gotoQueueScreen() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Queue'
- View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
- }
-
- /**
- *
- * @param itemIdx The 0-based index of the episode to be played in the queue.
- */
- private void playFromQueue(int itemIdx) {
- final List<FeedItem> queue = DBReader.getQueue();
- solo.clickOnImageButton(itemIdx + 1);
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- long mediaId = queue.get(itemIdx).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
-
- assertTrue(playing);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- startLocalPlaybackFromQueue();
- boolean stopped = solo.waitForCondition(() -> {
- if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
- != PlayerStatus.PLAYING) {
- return true;
- } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- != first.getMedia().getId();
- } else {
- return true;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(stopped);
- Thread.sleep(1000);
- PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
- assertFalse(status.equals(PlayerStatus.PLAYING));
- }
-
- @Test
- public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(true);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- final FeedItem second = queue.get(1);
-
- startLocalPlaybackFromQueue();
- boolean firstPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == first.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(firstPlaying);
- boolean secondPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == second.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(secondPlaying);
- }
-
- /**
- * Check if an episode can be played twice without problems.
- */
- private void replayEpisodeCheck(boolean followQueue) throws Exception {
- setContinuousPlaybackPreference(followQueue);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
-
- startLocalPlayback();
- long mediaId = episodes.get(0).getMedia().getId();
- boolean startedPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(startedPlaying);
-
- boolean stoppedPlaying = solo.waitForCondition(() ->
- uiTestUtils.getCurrentMedia(getActivity()) == null
- || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId, Timeout.getLargeTimeout());
- assertTrue(stoppedPlaying);
-
- startLocalPlayback();
- boolean startedReplay = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(startedReplay);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
- replayEpisodeCheck(false);
- }
-
- @Test
- public void testSmartMarkAsPlayed_Skip_Average() throws Exception {
- doTestSmartMarkAsPlayed_Skip_ForEpisode(0);
- }
-
- @Test
- public void testSmartMarkAsPlayed_Skip_LastEpisodeInQueue() throws Exception {
- doTestSmartMarkAsPlayed_Skip_ForEpisode(-1);
- }
-
- private void doTestSmartMarkAsPlayed_Skip_ForEpisode(int itemIdxNegAllowed) throws Exception {
- setSmartMarkAsPlayedPreference(60);
- // ensure when an episode is skipped, it is removed due to smart as played
- setSkipKeepsEpisodePreference(false);
-
- uiTestUtils.addLocalFeedData(true);
-
- int fiIdx;
- if (itemIdxNegAllowed >= 0) {
- fiIdx = itemIdxNegAllowed;
- } else { // negative index: count from the end, with -1 being the last one, etc.
- fiIdx = DBReader.getQueue().size() + itemIdxNegAllowed;
- }
- final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
-
- gotoQueueScreen();
- playFromQueue(fiIdx);
-
- skipEpisode();
-
- // assert item no longer in queue (needs to wait till skip is asynchronously processed)
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .untilAsserted(() -> {
- assertThat("Ensure smart mark as play will lead to the item removed from the queue",
- DBReader.getQueue(), not(hasItems(feedItem)));
- });
- assertThat(DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(true));
- }
-
- @Test
- public void testSmartMarkAsPlayed_Pause_WontAffectItem() throws Exception {
- setSmartMarkAsPlayedPreference(60);
-
- uiTestUtils.addLocalFeedData(true);
-
- final int fiIdx = 0;
- final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
-
- gotoQueueScreen();
- playFromQueue(fiIdx);
-
- // let playback run a bit then pause
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .until(() -> PlayerStatus.PLAYING == uiTestUtils.getPlaybackController(getActivity()).getStatus());
- pauseEpisode();
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .until(() -> PlayerStatus.PAUSED == uiTestUtils.getPlaybackController(getActivity()).getStatus());
-
- assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
- DBReader.getQueue(), hasItems(feedItem));
- assertThat("Ensure even with smart mark as play, after pause, the item played status remains false.",
- DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(false));
- }
-
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index b46af83c8..a68afbc2e 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -4,23 +4,14 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
+import androidx.annotation.StringRes;
import androidx.test.filters.LargeTest;
-
import androidx.test.rule.ActivityTestRule;
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
-
+import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
@@ -28,20 +19,39 @@ import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
+import de.test.antennapod.EspressoTestUtils;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.swipeDown;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
-import static junit.framework.TestCase.assertEquals;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.not;
@LargeTest
public class PreferencesTest {
- private Solo solo;
private Resources res;
@Rule
@@ -49,14 +59,12 @@ public class PreferencesTest {
@Before
public void setUp() {
+ EspressoTestUtils.clearDatabase();
EspressoTestUtils.clearPreferences();
mActivityRule.launchActivity(new Intent());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
- solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
- Timeout.setSmallTimeout(500);
- Timeout.setLargeTimeout(1000);
res = mActivityRule.getActivity().getResources();
UserPreferences.init(mActivityRule.getActivity());
}
@@ -65,7 +73,7 @@ public class PreferencesTest {
public void testSwitchTheme() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -73,14 +81,15 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
public void testSwitchThemeBack() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -88,7 +97,8 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
@@ -96,18 +106,18 @@ public class PreferencesTest {
final boolean persistNotify = UserPreferences.isPersistNotify();
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify != UserPreferences.isPersistNotify());
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify == UserPreferences.isPersistNotify());
}
@Test
public void testSetLockscreenButtons() {
- onView(withText(R.string.user_interface_label)).perform(click());
- solo.scrollDown();
+ clickPreference(R.string.user_interface_label);
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
- onView(withText(R.string.pref_compact_notification_buttons_title)).perform(click());
- solo.waitForDialogToOpen(1000);
+ clickPreference(R.string.pref_compact_notification_buttons_title);
// First uncheck checkbox
onView(withText(buttons[2])).perform(click());
@@ -117,24 +127,39 @@ public class PreferencesTest {
onView(withText(buttons[2])).perform(click());
// Make sure that the third checkbox is unchecked
- assertTrue(!solo.isTextChecked(buttons[2]));
+ onView(withText(buttons[2])).check(matches(not(isChecked())));
+
+ String snackBarText = String.format(res.getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2);
+ Awaitility.await().ignoreExceptions().atMost(4000, MILLISECONDS)
+ .until(() -> {
+ onView(withText(snackBarText)).check(doesNotExist());
+ return true;
+ });
+
onView(withText(R.string.confirm_label)).perform(click());
- solo.waitForDialogToClose(1000);
- assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
+
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showRewindOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showFastForwardOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> !UserPreferences.showSkipOnCompactNotification());
}
@Test
- public void testEnqueueAtFront() {
- onView(withText(R.string.playback_pref)).perform(click());
- final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
+ public void testEnqueueLocation() {
+ clickPreference(R.string.playback_pref);
+ doTestEnqueueLocation(R.string.enqueue_location_after_current, EnqueueLocation.AFTER_CURRENTLY_PLAYING);
+ doTestEnqueueLocation(R.string.enqueue_location_front, EnqueueLocation.FRONT);
+ doTestEnqueueLocation(R.string.enqueue_location_back, EnqueueLocation.BACK);
+ }
+
+ private void doTestEnqueueLocation(@StringRes int optionResId, EnqueueLocation expected) {
+ clickPreference(R.string.pref_enqueue_location_title);
+ onView(withText(optionResId)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> expected == UserPreferences.getEnqueueLocation());
}
@Test
@@ -142,49 +167,57 @@ public class PreferencesTest {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect());
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect());
}
@Test
public void testHeadPhonesReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect());
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect());
}
@Test
public void testBluetoothReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect());
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect());
}
@Test
public void testContinuousPlayback() {
- onView(withText(R.string.playback_pref)).perform(click());
+ clickPreference(R.string.playback_pref);
final boolean continuousPlayback = UserPreferences.isFollowQueue();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback != UserPreferences.isFollowQueue());
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback == UserPreferences.isFollowQueue());
}
@Test
@@ -192,9 +225,11 @@ public class PreferencesTest {
onView(withText(R.string.storage_pref)).perform(click());
final boolean autoDelete = UserPreferences.isAutoDelete();
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete != UserPreferences.isAutoDelete());
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete == UserPreferences.isAutoDelete());
}
@Test
@@ -203,7 +238,7 @@ public class PreferencesTest {
clickPreference(R.string.media_player);
onView(withText(R.string.media_player_exoplayer)).perform(click());
clickPreference(R.string.pref_playback_speed_title);
- solo.waitForDialogToOpen();
+ onView(isRoot()).perform(waitForView(withText("0.50"), 1000));
onView(withText("0.50")).check(matches(isDisplayed()));
onView(withText(R.string.cancel_label)).perform(click());
}
@@ -212,10 +247,12 @@ public class PreferencesTest {
public void testPauseForInterruptions() {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
- onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_pausePlaybackForFocusLoss_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss());
+ clickPreference(R.string.pref_pausePlaybackForFocusLoss_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss());
}
@Test
@@ -223,7 +260,8 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_title)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Disable)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == 0);
}
@Test
@@ -231,59 +269,58 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_autoUpdateIntervallOrTime_title);
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Interval)).perform(click());
- String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
+ String search = "12 " + res.getString(R.string.pref_update_interval_hours_plural);
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
- TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == TimeUnit.HOURS.toMillis(12));
}
@Test
public void testSetSequentialDownload() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "1");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("1"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 1);
}
@Test
public void testSetParallelDownloads() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "10");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("10"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 10);
}
@Test
public void testSetParallelDownloadsInvalidInput() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "0");
- assertEquals("", solo.getEditText(0).getText().toString());
- solo.clearEditText(0);
- solo.enterText(0, "100");
- assertEquals("", solo.getEditText(0).getText().toString());
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("0"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("100"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
}
@Test
public void testSetEpisodeCache() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String entry = entries[entries.length/2];
- final int value = Integer.valueOf(values[values.length/2]);
+ String entry = entries[entries.length / 2];
+ final int value = Integer.parseInt(values[values.length / 2]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.waitForDialogToOpen();
- solo.clickOnText(entry);
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
+ onView(isRoot()).perform(waitForView(withText(entry), 1000));
+ onView(withText(entry)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == value);
}
@Test
@@ -291,27 +328,30 @@ public class PreferencesTest {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String minEntry = entries[0];
- final int minValue = Integer.valueOf(values[0]);
+ final int minValue = Integer.parseInt(values[0]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.scrollUp();
+ onView(withId(R.id.select_dialog_listview)).perform(swipeDown());
onView(withText(minEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == minValue);
}
@Test
public void testSetEpisodeCacheMax() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String maxEntry = entries[entries.length-1];
- final int maxValue = Integer.valueOf(values[values.length-1]);
+ String maxEntry = entries[entries.length - 1];
+ final int maxValue = Integer.parseInt(values[values.length - 1]);
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cache_title)).perform(click());
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
onView(withText(maxEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == maxValue);
}
@Test
@@ -320,22 +360,27 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_automatic_download_title);
-
- assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
- if(UserPreferences.isEnableAutodownload() == false) {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> automaticDownload != UserPreferences.isEnableAutodownload());
+ if (!UserPreferences.isEnableAutodownload()) {
clickPreference(R.string.pref_automatic_download_title);
}
- assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isEnableAutodownload);
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
- onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_automatic_download_on_battery_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery());
+ clickPreference(R.string.pref_automatic_download_on_battery_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
final boolean enableWifiFilter = UserPreferences.isEnableAutodownloadWifiFilter();
- onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_autodl_wifi_filter_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter());
+ clickPreference(R.string.pref_autodl_wifi_filter_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter());
}
@Test
@@ -343,13 +388,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_queue_removal), 1000));
onView(withText(R.string.episode_cleanup_queue_removal)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APQueueCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APQueueCleanupAlgorithm);
}
@Test
@@ -357,13 +399,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_never));
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
onView(withText(R.string.episode_cleanup_never)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APNullCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APNullCleanupAlgorithm);
}
@Test
@@ -371,17 +410,17 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_after_listening), 1000));
onView(withText(R.string.episode_cleanup_after_listening)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
@@ -389,24 +428,24 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cleanup_title);
- solo.waitForDialogToOpen();
- String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
+ String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 3, 3);
+ onView(isRoot()).perform(waitForView(withText(search), 1000));
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
- return cleanupAlg.getNumberOfHoursAfterPlayback() == 120; // 5 days
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
+ return cleanupAlg.getNumberOfHoursAfterPlayback() == 72; // 5 days
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
public void testRewindChange() {
int seconds = UserPreferences.getRewindSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.playback_pref);
clickPreference(R.string.pref_rewind);
@@ -416,11 +455,11 @@ public class PreferencesTest {
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
- onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getRewindSecs() == deltas[newIndex]);
}
@Test
@@ -428,7 +467,7 @@ public class PreferencesTest {
clickPreference(R.string.playback_pref);
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.pref_fast_forward);
@@ -441,9 +480,8 @@ public class PreferencesTest {
onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- solo.waitForDialogToClose();
- assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex]);
}
}
@@ -454,26 +492,26 @@ public class PreferencesTest {
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.queue_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.episodes_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.subscriptions_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG));
}
@Test
@@ -481,12 +519,15 @@ public class PreferencesTest {
clickPreference(R.string.storage_pref);
if (!UserPreferences.shouldDeleteRemoveFromQueue()) {
clickPreference(R.string.pref_delete_removes_from_queue_title);
- assertTrue(solo.waitForCondition(UserPreferences::shouldDeleteRemoveFromQueue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::shouldDeleteRemoveFromQueue);
}
final boolean deleteRemovesFromQueue = UserPreferences.shouldDeleteRemoveFromQueue();
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue());
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue());
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
index 7f0bf8fa2..37d76bb6d 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
@@ -15,17 +15,20 @@ import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.endsWith;
/**
- * User interface tests for queue fragment
+ * User interface tests for queue fragment.
*/
@RunWith(AndroidJUnit4.class)
public class QueueFragmentTest {
@Rule
- public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() {
@@ -33,12 +36,13 @@ public class QueueFragmentTest {
EspressoTestUtils.makeNotFirstRun();
EspressoTestUtils.clearDatabase();
EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
}
@Test
public void testLockEmptyQueue() {
onView(withContentDescription(R.string.lock_queue)).perform(click());
+ onView(allOf(withClassName(endsWith("Button")), withText(R.string.lock_queue))).perform(click());
onView(withContentDescription(R.string.unlock_queue)).perform(click());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
new file mode 100644
index 000000000..df32ed3c0
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
@@ -0,0 +1,120 @@
+package de.test.antennapod.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AudioplayerActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.fragment.QueueFragment;
+import de.test.antennapod.EspressoTestUtils;
+import de.test.antennapod.IgnoreOnCi;
+import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+
+/**
+ * User interface tests for changing the playback speed.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreOnCi
+public class SpeedChangeTest {
+
+ @Rule
+ public ActivityTestRule<AudioplayerActivity> activityRule
+ = new ActivityTestRule<>(AudioplayerActivity.class, false, false);
+ private UITestUtils uiTestUtils;
+ private String[] availableSpeeds;
+
+ @Before
+ public void setUp() throws Exception {
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
+
+ Context context = getInstrumentation().getTargetContext();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, true).commit();
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+ uiTestUtils.setMediaFileName("30sec.mp3");
+ uiTestUtils.addLocalFeedData(true);
+
+ List<FeedItem> queue = DBReader.getQueue();
+ PlaybackPreferences.writeMediaPlaying(queue.get(0).getMedia(), PlayerStatus.PAUSED, false);
+ UserPreferences.setPlaybackSpeedArray(new String[] {"1.00", "2.00", "3.00"});
+ availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+
+ EspressoTestUtils.tryKillPlaybackService();
+ activityRule.launchActivity(new Intent());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testChangeSpeedServiceNotRunning() {
+ clickThroughSpeeds();
+ }
+
+ @Test
+ public void testChangeSpeedPlaying() {
+ onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
+ clickThroughSpeeds();
+ }
+
+ @Test
+ public void testChangeSpeedPaused() {
+ onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PAUSED);
+ clickThroughSpeeds();
+ }
+
+ private void clickThroughSpeeds() {
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[0]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[0])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[1]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[1])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[2]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[2])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[0]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[0])));
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java b/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java
new file mode 100644
index 000000000..7ba7a57f2
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java
@@ -0,0 +1,71 @@
+package de.test.antennapod.ui;
+
+import android.content.Intent;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.test.antennapod.EspressoTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+
+/**
+ * Test UI for feeds that do not have media files
+ */
+@RunWith(AndroidJUnit4.class)
+public class TextOnlyFeedsTest {
+
+ private UITestUtils uiTestUtils;
+
+ @Rule
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+
+ @Before
+ public void setUp() throws IOException {
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
+
+ uiTestUtils = new UITestUtils(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ uiTestUtils.setHostTextOnlyFeeds(true);
+ uiTestUtils.setup();
+
+ activityRule.launchActivity(new Intent());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testMarkAsPlayedList() throws Exception {
+ uiTestUtils.addLocalFeedData(false);
+ final Feed feed = uiTestUtils.hostedFeeds.get(0);
+ openNavDrawer();
+ onDrawerItem(withText(feed.getTitle())).perform(scrollTo(), click());
+ onView(withText(feed.getItemAtIndex(0).getTitle())).perform(click());
+ onView(withText(R.string.mark_read_label)).perform(click());
+ onView(isRoot()).perform(waitForView(allOf(withText(R.string.mark_read_label), not(isDisplayed())), 3000));
+ }
+
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 905c65c34..8e16d1a3b 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -4,6 +4,8 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.util.playback.Playable;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -18,7 +20,6 @@ import java.util.List;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -34,16 +35,15 @@ import org.junit.Assert;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
-class UITestUtils {
+public class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
private static final int NUM_FEEDS = 5;
private static final int NUM_ITEMS_PER_FEED = 10;
- private static final String TEST_FILE_NAME = "3sec.mp3";
-
-
+ private String testFileName = "3sec.mp3";
+ private boolean hostTextOnlyFeeds = false;
private final Context context;
private final HTTPBin server = new HTTPBin();
private File destDir;
@@ -89,13 +89,13 @@ class UITestUtils {
out.close();
int id = server.serveFile(feedFile);
Assert.assertTrue(id != -1);
- return String.format("%s/files/%d", HTTPBin.BASE_URL, id);
+ return String.format("%s/files/%d", server.getBaseUrl(), id);
}
private String hostFile(File file) {
int id = server.serveFile(file);
Assert.assertTrue(id != -1);
- return String.format("%s/files/%d", HTTPBin.BASE_URL, id);
+ return String.format("%s/files/%d", server.getBaseUrl(), id);
}
private File newBitmapFile(String name) throws IOException {
@@ -109,12 +109,12 @@ class UITestUtils {
private File newMediaFile(String name) throws IOException {
File mediaFile = new File(hostedMediaDir, name);
- if(mediaFile.exists()) {
+ if (mediaFile.exists()) {
mediaFile.delete();
}
Assert.assertFalse(mediaFile.exists());
- InputStream in = context.getAssets().open(TEST_FILE_NAME);
+ InputStream in = context.getAssets().open(testFileName);
Assert.assertNotNull(in);
FileOutputStream out = new FileOutputStream(mediaFile);
@@ -143,9 +143,10 @@ class UITestUtils {
"http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
items.add(item);
- File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
- item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0, 0));
-
+ if (!hostTextOnlyFeeds) {
+ File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
+ item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0, 0));
+ }
}
feed.setItems(items);
feed.setDownload_url(hostFeed(feed));
@@ -192,7 +193,9 @@ class UITestUtils {
}
queue.add(feed.getItems().get(0));
- feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date());
+ if (feed.getItems().get(1).hasMedia()) {
+ feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date());
+ }
}
localFeedDataAdded = true;
@@ -201,16 +204,26 @@ class UITestUtils {
adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()]));
adapter.setQueue(queue);
adapter.close();
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(hostedFeeds));
EventBus.getDefault().post(QueueEvent.setQueue(queue));
}
public PlaybackController getPlaybackController(MainActivity mainActivity) {
- ExternalPlayerFragment fragment = (ExternalPlayerFragment)mainActivity.getSupportFragmentManager().findFragmentByTag(ExternalPlayerFragment.TAG);
+ ExternalPlayerFragment fragment = (ExternalPlayerFragment) mainActivity.getSupportFragmentManager()
+ .findFragmentByTag(ExternalPlayerFragment.TAG);
return fragment.getPlaybackControllerTestingOnly();
}
- public FeedMedia getCurrentMedia(MainActivity mainActivity) {
- return (FeedMedia)getPlaybackController(mainActivity).getMedia();
+ public FeedMedia getCurrentMedia() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ return (FeedMedia) playable;
+ }
+
+ public void setMediaFileName(String filename) {
+ testFileName = filename;
+ }
+
+ public void setHostTextOnlyFeeds(boolean hostTextOnlyFeeds) {
+ this.hostTextOnlyFeeds = hostTextOnlyFeeds;
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
index f2dfca92e..d33eb55b8 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
@@ -1,7 +1,7 @@
package de.test.antennapod.util;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
+import android.content.Context;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import android.text.TextUtils;
@@ -13,77 +13,79 @@ import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@SmallTest
public class FilenameGeneratorTest {
- private static final String VALID1 = "abc abc";
- private static final String INVALID1 = "ab/c: <abc";
- private static final String INVALID2 = "abc abc ";
+ private static final String VALID1 = "abc abc";
+ private static final String INVALID1 = "ab/c: <abc";
+ private static final String INVALID2 = "abc abc ";
public FilenameGeneratorTest() {
super();
}
@Test
- public void testGenerateFileName() throws IOException {
- String result = FileNameGenerator.generateFileName(VALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName1() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName2() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID2);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testFeedTitleContainsApostrophe() {
- String result = FileNameGenerator.generateFileName("Feed's Title ...");
- assertEquals("Feeds Title", result);
- }
-
- @Test
- public void testFeedTitleContainsDash() {
- String result = FileNameGenerator.generateFileName("Left - Right");
- assertEquals("Left - Right", result);
- }
-
- @Test
- public void testInvalidInput() {
- String result = FileNameGenerator.generateFileName("???");
- assertTrue(!TextUtils.isEmpty(result));
- }
-
- /**
- * Tests if files can be created.
- *
- * @throws IOException
- */
- private void createFiles(String name) throws IOException {
- File cache = InstrumentationRegistry.getContext().getExternalCacheDir();
- File testFile = new File(cache, name);
- testFile.mkdir();
- assertTrue(testFile.exists());
- testFile.delete();
- assertTrue(testFile.createNewFile());
-
- }
-
- @After
- public void tearDown() throws Exception {
- File f = new File(InstrumentationRegistry.getContext().getExternalCacheDir(), VALID1);
- f.delete();
- }
+ public void testGenerateFileName() throws IOException {
+ String result = FileNameGenerator.generateFileName(VALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName1() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName2() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID2);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testFeedTitleContainsApostrophe() {
+ String result = FileNameGenerator.generateFileName("Feed's Title ...");
+ assertEquals("Feeds Title", result);
+ }
+
+ @Test
+ public void testFeedTitleContainsDash() {
+ String result = FileNameGenerator.generateFileName("Left - Right");
+ assertEquals("Left - Right", result);
+ }
+
+ @Test
+ public void testInvalidInput() {
+ String result = FileNameGenerator.generateFileName("???");
+ assertFalse(TextUtils.isEmpty(result));
+ }
+
+ /**
+ * Tests if files can be created.
+ *
+ * @throws IOException
+ */
+ private void createFiles(String name) throws IOException {
+ File cache = InstrumentationRegistry.getInstrumentation().getTargetContext().getExternalCacheDir();
+ File testFile = new File(cache, name);
+ testFile.mkdir();
+ assertTrue(testFile.exists());
+ testFile.delete();
+ assertTrue(testFile.createNewFile());
+
+ }
+
+ @After
+ public void tearDown() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ File f = new File(context.getExternalCacheDir(), VALID1);
+ f.delete();
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
index 4158fd31c..c588cdbc2 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
@@ -39,9 +39,6 @@ import de.danoeh.antennapod.BuildConfig;
*/
public class HTTPBin extends NanoHTTPD {
private static final String TAG = "HTTPBin";
- public static final int PORT = 8124;
- public static final String BASE_URL = "http://127.0.0.1:" + HTTPBin.PORT;
-
private static final String MIME_HTML = "text/html";
private static final String MIME_PLAIN = "text/plain";
@@ -49,10 +46,14 @@ public class HTTPBin extends NanoHTTPD {
private final List<File> servedFiles;
public HTTPBin() {
- super(PORT);
+ super(0); // Let system pick a free port
this.servedFiles = new ArrayList<>();
}
+ public String getBaseUrl() {
+ return "http://127.0.0.1:" + getListeningPort();
+ }
+
/**
* Adds the given file to the server.
*
@@ -98,7 +99,7 @@ public class HTTPBin extends NanoHTTPD {
if (func.equalsIgnoreCase("status")) {
try {
int code = Integer.parseInt(param);
- return getStatus(code);
+ return new Response(getStatus(code), MIME_HTML, "");
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
@@ -284,31 +285,45 @@ public class HTTPBin extends NanoHTTPD {
return response;
}
- private Response getStatus(final int code) {
- Response.IStatus status = (code == 200) ? Response.Status.OK :
- (code == 201) ? Response.Status.CREATED :
- (code == 206) ? Response.Status.PARTIAL_CONTENT :
- (code == 301) ? Response.Status.REDIRECT :
- (code == 304) ? Response.Status.NOT_MODIFIED :
- (code == 400) ? Response.Status.BAD_REQUEST :
- (code == 401) ? Response.Status.UNAUTHORIZED :
- (code == 403) ? Response.Status.FORBIDDEN :
- (code == 404) ? Response.Status.NOT_FOUND :
- (code == 405) ? Response.Status.METHOD_NOT_ALLOWED :
- (code == 416) ? Response.Status.RANGE_NOT_SATISFIABLE :
- (code == 500) ? Response.Status.INTERNAL_ERROR : new Response.IStatus() {
- @Override
- public int getRequestStatus() {
- return code;
- }
-
- @Override
- public String getDescription() {
- return "Unknown";
- }
- };
- return new Response(status, MIME_HTML, "");
+ private Response.IStatus getStatus(final int code) {
+ switch (code) {
+ case 200:
+ return Response.Status.OK;
+ case 201:
+ return Response.Status.CREATED;
+ case 206:
+ return Response.Status.PARTIAL_CONTENT;
+ case 301:
+ return Response.Status.REDIRECT;
+ case 304:
+ return Response.Status.NOT_MODIFIED;
+ case 400:
+ return Response.Status.BAD_REQUEST;
+ case 401:
+ return Response.Status.UNAUTHORIZED;
+ case 403:
+ return Response.Status.FORBIDDEN;
+ case 404:
+ return Response.Status.NOT_FOUND;
+ case 405:
+ return Response.Status.METHOD_NOT_ALLOWED;
+ case 416:
+ return Response.Status.RANGE_NOT_SATISFIABLE;
+ case 500:
+ return Response.Status.INTERNAL_ERROR;
+ default:
+ return new Response.IStatus() {
+ @Override
+ public int getRequestStatus() {
+ return code;
+ }
+ @Override
+ public String getDescription() {
+ return "Unknown";
+ }
+ };
+ }
}
private Response getRedirectResponse(int times) {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 488d4e473..086206dcd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,6 +35,15 @@
android:restoreAnyVersion="true"
android:usesCleartextTraffic="true"
android:logo="@mipmap/ic_launcher">
+
+ <activity
+ android:name=".activity.WidgetConfigActivity"
+ android:label="@string/widget_settings">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_CONFIGUR"/>
+ </intent-filter>
+ </activity>
+
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_antenna" />
<meta-data
@@ -132,14 +141,6 @@
<activity android:name=".activity.StorageErrorActivity">
</activity>
<activity
- android:name=".activity.AboutActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:label="@string/about_pref">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
- </activity>
- <activity
android:name=".activity.ImportExportActivity"
android:label="@string/import_export">
<meta-data
@@ -228,6 +229,7 @@
<activity
android:name=".activity.OnlineFeedViewActivity"
android:configChanges="orientation|screenSize"
+ android:theme="@style/Theme.AntennaPod.Dark.Translucent"
android:label="@string/add_feed_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -336,7 +338,7 @@
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>
</intent-filter>
</receiver>
-
+
<provider
android:authorities="@string/provider_authority"
android:name="androidx.core.content.FileProvider"
diff --git a/app/src/main/assets/30sec.mp3 b/app/src/main/assets/30sec.mp3
new file mode 100644
index 000000000..48e398434
--- /dev/null
+++ b/app/src/main/assets/30sec.mp3
Binary files differ
diff --git a/app/src/main/assets/developers.csv b/app/src/main/assets/developers.csv
new file mode 100644
index 000000000..460e896f9
--- /dev/null
+++ b/app/src/main/assets/developers.csv
@@ -0,0 +1,119 @@
+danieloeh;968613;Original creator of AntennaPod (retired)
+mfietz;6860662;Maintainer
+ByteHamster;5811634;Maintainer
+TomHennen;5216560;Maintainer (retired)
+orionlee;250644;Contributor
+domingos86;9538859;Contributor
+andersonvom;69922;Contributor
+patheticpat;16046;Contributor
+spacecowboy;223655;Contributor
+brad;1614;Contributor
+Cj-Malone;10121513;Contributor
+gaul;848247;Contributor
+qkolj;6667105;Contributor
+pachecosf;46357909;Contributor
+ahangarha;11241315;Contributor
+rharriso;570910;Contributor
+xgouchet;818706;Contributor
+damoasda;46045854;Contributor
+sevenmaster;12869538;Contributor
+TheRealFalcon;153674;Contributor
+keunes;11229646;Contributor
+shortspider;5712543;Contributor
+hannesa2;3314607;Contributor
+jas14;569991;Contributor
+udif;809640;Contributor
+dirkmueller;1029152;Contributor
+jatinkumarg;20503830;Contributor
+peschmae0;4450993;Contributor
+orelogo;15976578;Contributor
+ydinath;4193331;Contributor
+CedricCabessa;365097;Contributor
+mchelen;30691;Contributor
+johnjohndoe;144518;Contributor
+dethstar;1239177;Contributor
+drabux;10663142;Contributor
+saqura;1935380;Contributor
+bibz;5141956;Contributor
+hzulla;1705654;Contributor
+deandreamatias;21011641;Contributor
+MeirAtIMDDE;4421079;Contributor
+egsavage;126165;Contributor
+ligi;111600;Contributor
+dreiss;4121;Contributor
+liesen;26872;Contributor
+nereocystis;2257107;Contributor
+rezanejati;16049370;Contributor
+twiceyuan;2619800;Contributor
+HaBaLeS;730902;Contributor
+volhol;11587858;Contributor
+CameronBanga;611354;Contributor
+HolgerJeromin;2410353;Contributor
+xisberto;1914956;Contributor
+jmue;898577;Contributor
+katrinleinweber;9948149;Contributor
+LatinSuD;451487;Contributor
+24hours;650407;Contributor
+SosoTughushi;19908097;Contributor
+fabolhak;20029691;Contributor
+archibishop;36948493;Contributor
+alifeflow;24603829;Contributor
+toggles;14695;Contributor
+matdb;48329535;Contributor
+kingargyle;177042;Contributor
+dsmith47;14109426;Contributor
+hannesaa2;18496079;Contributor
+jhunnius;9149031;Contributor
+ShadowIce;59123;Contributor
+raghulj;57007;Contributor
+raghulrm;5362986;Contributor
+stevomit;205959;Contributor
+skitt;2128935;Contributor
+mr-intj;6268767;Contributor
+tuxayo;2678215;Contributor
+alimemonzx;44647595;Contributor
+alanorth;191754;Contributor
+alexte;7724992;Contributor
+andrey-krutov;1488973;Contributor
+arantius;84729;Contributor
+bws9000;262625;Contributor
+chrissicool;232590;Contributor
+cszucko;1810383;Contributor
+CWftw;1498303;Contributor
+danielm5;66779;Contributor
+ariedov;958646;Contributor
+brettle;118192;Contributor
+eirikv;4076243;Contributor
+eerden;277513;Contributor
+jannic;232606;Contributor
+Foso;5015532;Contributor
+Kaligule;3586246;Contributor
+kvithayathil;1056073;Contributor
+luiscruz;1080714;Contributor
+mlasson;5814258;Contributor
+M-arcel;56698158;Contributor
+mo;7117;Contributor
+mdeveloper20;2319126;Contributor
+mschuetz;108637;Contributor
+MolarAmbiguity;10541979;Contributor
+mounirlamouri;573590;Contributor
+ortylp;470439;Contributor
+PtilopsisLeucotis;54054883;Contributor
+SamWhited;512573;Contributor
+selivan;1208989;Contributor
+sonnayasomnambula;7716779;Contributor
+sethoscope;534043;Contributor
+shantanahardy;26757164;Contributor
+mamehacker;16738348;Contributor
+danners;116551;Contributor
+corecode;177979;Contributor
+vimsick;20211590;Contributor
+edent;837136;Contributor
+atrus6;357881;Contributor
+waylife;3348620;Contributor
+wseemann;2296196;Contributor
+amhokies;3124968;Contributor
+axq;5077221;Contributor
+fossterer;4236021;Contributor
+lightonflux;1377943;Contributor
+minusf;3632883;Contributor
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
new file mode 100644
index 000000000..c69f692cf
--- /dev/null
+++ b/app/src/main/assets/licenses.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<libraries>
+ <library
+ name="AntennaPod"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod/"
+ license="MIT"
+ licenseText="LICENSE.txt" />
+ <library
+ name="AntennaPod-AudioPlayer"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod-AudioPlayer/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Apache Commons"
+ author="The Apache Software Foundation"
+ website="http://commons.apache.org/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="EventBus"
+ author="greenrobot"
+ website="https://github.com/greenrobot/EventBus"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="ExoPlayer"
+ author="Google"
+ website="https://github.com/google/ExoPlayer"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Floating Action Button Speed Dial"
+ author="Roberto Leinardi"
+ website="https://github.com/leinardi/FloatingActionButtonSpeedDial"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Glide"
+ author="bumptech"
+ website="https://github.com/bumptech/glide/"
+ license="Simplified BSD"
+ licenseText="LICENSE_GLIDE.txt" />
+ <library
+ name="Iconify"
+ author="Joan Zapata"
+ website="https://github.com/JoanZapata/android-iconify"
+ license="Apache 2.0"
+ licenseText="LICENSE_ANDROID_ICONIFY.txt" />
+ <library
+ name="jsoup"
+ author="Jonathan Hedley"
+ website="http://jsoup.org/"
+ license="MIT"
+ licenseText="LICENSE_JSOUP.txt" />
+ <library
+ name="Material Design Icons"
+ author="Google"
+ website="https://github.com/google/material-design-icons"
+ license="Attribution-ShareAlike 4.0 International"
+ licenseText="LICENSE_MATERIAL_DESIGN_ICONS.txt" />
+ <library
+ name="Material Design Icons"
+ author="Templarian"
+ website="https://github.com/Templarian/MaterialDesign"
+ license="SIL Open Font, Version 1.1"
+ licenseText="LICENSE_SIL.txt" />
+ <library
+ name="OkHttp"
+ author="Square"
+ website="https://github.com/square/okhttp"
+ license="Apache 2.0"
+ licenseText="LICENSE_OKHTTP.txt" />
+ <library
+ name="Okio"
+ author="Square"
+ website="https://github.com/square/okio"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RecyclerView-FlexibleDivider"
+ author="yqritc"
+ website="https://github.com/yqritc/RecyclerView-FlexibleDivider"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RxAndroid"
+ author="ReactiveX"
+ website="https://github.com/ReactiveX/RxAndroid"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="StackBlur"
+ author="Enrique López Mañas"
+ website="https://github.com/kikoso/android-stackblur"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Triangle Label View"
+ author="Shota Saito"
+ website="https://github.com/shts/TriangleLabelView"
+ license="Apache 2.0"
+ licenseText="LICENSE_TRIANGLE_LABEL_VIEW.txt" />
+</libraries>
diff --git a/app/src/main/assets/testfile.mp3 b/app/src/main/assets/testfile.mp3
deleted file mode 100644
index f15faadf3..000000000
--- a/app/src/main/assets/testfile.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/translators.csv b/app/src/main/assets/translators.csv
new file mode 100644
index 000000000..9670fb652
--- /dev/null
+++ b/app/src/main/assets/translators.csv
@@ -0,0 +1,46 @@
+Arabic;abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
+Asturian (ast_ES);enolp
+Azerbaijani;danieloeh, kotfenix
+Basque;gaztainalde, pospolos, zakurranputza
+Bulgarian;solusitor
+Catalan;dvd1985, exort12, javiercoll, lambdani, marcmetallextrem, xc70
+Chinese (zh_CN);bebeauties38, cyril3, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, JayYoung, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, Sak94664, spice2wolf, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
+Chinese (zh_TW);gugod, nigelinux, pggdt, Solomon, ymhuang0808
+Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, mcepl, petnek, svetlemodry
+Danish;CasperHN, jhertel
+Dutch;e2jk, glotzbach, rwv, Vistaus
+English;mfietz, sterylmreep
+Estonian;Eraser
+Finnish;danieloeh, elguitar, Sahtor
+French;cactux, ChaoticMind, clombion, e2jk, edewaele, lacouture, LouFex, Matth78, mfietz, Poussinou, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+Galician;antiparvos, pikamoku, Raichely
+German;112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ByteHamster, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, moasda, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
+Modern Greek (1453-);antonist, danieloeh, hua2016s, jack.ath92, MSavoritias, pavlosv
+Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
+Hindi (hi_IN);ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
+Hungarian;glatz.balazs, hurrikan, lna91, marthynw, meskobalazs, naren93, tszauer, ttyborg42
+Icelandic;marthjod
+Indonesian;jff, levirs565, luke137, rezafaiza, silvanael16
+Italian (it_IT);aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, ilmanzo, m.chinni, neonsoftware, niccord, nixxo, sevenmaster, theloca95
+Japanese;Naofumi, RACER1, sh3llc4t, TranslatorG
+Kannada (kn_IN);chiraag.nataraj, thejeshgn
+Korean;changwoo, libliboom, seungrye, skcha
+Lithuanian;naglis
+Macedonian;krisfremen
+Malayalam;joice, rashivkp, rubenroy
+Norwegian;timbast
+Norwegian Bokmål (nb_NO);corkie, heraldo, kongk, timbast
+Persian;ahangarha, F7D, sinamoghaddas
+Polish (pl_PL);d6210809, hiro2020, Iwangelion, kRkk, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Portuguese;andersonvom, domingos86, emansije, smarquespt
+Portuguese (pt_BR);alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
+Romanian (ro_RO);corneliu.e, fuzzmz, ralienpp
+Russian (ru_RU);astra1, btimofeev, Duke_Raven, gammja, GaynullinDima, MegMasters98, mercutiy, null, overmind88, PtilopsisLeucotis, s.chebotar, shams4real, skvheadless, un_logic, Vladryyu, whereisthetea, zhenya97
+Slovenian (sl_SI);panter23
+Spanish;AleksSyntek, andersonvom, Atreyu94, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Swahili (macrolanguage);kmtra
+Swedish (sv_SE);albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TiloWiklund, TwoD
+Telugu;Isaasu, veeven
+Turkish;basarancaner, brsata, Erdy, golcuk, overbite, Slsdem
+Ukrainian (uk_UA);older, paul_sm, sergiyr, zhenya97
+Vietnamese;abnvolk, nguyenvui, ppanhh, vietnamesel10n
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 94d281a45..4e6b8fa29 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod;
import android.app.Application;
-import android.os.Build;
import android.os.StrictMode;
import com.joanzapata.iconify.Iconify;
@@ -10,7 +9,6 @@ import com.joanzapata.iconify.fonts.MaterialModule;
import de.danoeh.antennapod.core.ApCoreEventBusIndex;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.spa.SPAUtil;
import org.greenrobot.eventbus.EventBus;
@@ -26,46 +24,43 @@ public class PodcastApp extends Application {
}
}
- private static PodcastApp singleton;
+ private static PodcastApp singleton;
- public static PodcastApp getInstance() {
- return singleton;
- }
+ public static PodcastApp getInstance() {
+ return singleton;
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
- if(BuildConfig.DEBUG) {
- StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDropBox();
- builder.detectActivityLeaks();
- builder.detectLeakedClosableObjects();
- if(Build.VERSION.SDK_INT >= 16) {
- builder.detectLeakedRegistrationObjects();
- }
- StrictMode.setVmPolicy(builder.build());
- }
+ if (BuildConfig.DEBUG) {
+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDropBox()
+ .detectActivityLeaks()
+ .detectLeakedClosableObjects()
+ .detectLeakedRegistrationObjects();
+ StrictMode.setVmPolicy(builder.build());
+ }
- singleton = this;
+ singleton = this;
- ClientConfig.initialize(this);
+ ClientConfig.initialize(this);
- EventDistributor.getInstance();
- Iconify.with(new FontAwesomeModule());
- Iconify.with(new MaterialModule());
+ Iconify.with(new FontAwesomeModule());
+ Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
- EventBus.builder()
- .addIndex(new ApEventBusIndex())
- .addIndex(new ApCoreEventBusIndex())
- .logNoSubscriberMessages(false)
- .sendNoSubscriberEvent(false)
- .installDefaultEventBus();
+ EventBus.builder()
+ .addIndex(new ApEventBusIndex())
+ .addIndex(new ApCoreEventBusIndex())
+ .logNoSubscriberMessages(false)
+ .sendNoSubscriberEvent(false)
+ .installDefaultEventBus();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
deleted file mode 100644
index ef7ea2b16..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.LinearLayout;
-
-import de.danoeh.antennapod.core.util.IntentUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import io.reactivex.Single;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Displays the 'about' screen
- */
-public class AboutActivity extends AppCompatActivity {
-
- private static final String TAG = AboutActivity.class.getSimpleName();
-
- private WebView webView;
- private LinearLayout webViewContainer;
- private Disposable disposable;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- setContentView(R.layout.about);
- webViewContainer = findViewById(R.id.webViewContainer);
- webView = findViewById(R.id.webViewAbout);
- webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webView.setBackgroundColor(Color.TRANSPARENT);
- webView.setWebViewClient(new WebViewClient() {
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (url.startsWith("http")) {
- IntentUtils.openInBrowser(AboutActivity.this, url);
- return true;
- } else {
- url = url.replace("file:///android_asset/", "");
- loadAsset(url);
- return true;
- }
- }
-
- });
- loadAsset("about.html");
- }
-
- private void loadAsset(String filename) {
- disposable = Single.create(subscriber -> {
- InputStream input = null;
- try {
- TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
- new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background,
- R.attr.about_screen_card_background, R.attr.about_screen_card_border});
- String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0));
- String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0));
- String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0));
- String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0));
- res.recycle();
- input = getAssets().open(filename);
- String webViewData = IOUtils.toString(input, Charset.defaultCharset());
- if (!webViewData.startsWith("<!DOCTYPE html>")) {
- webViewData = webViewData.replace("%", "&#37;");
- webViewData =
- "<!DOCTYPE html>" +
- "<html>" +
- "<head>" +
- " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" +
- " <style type=\"text/css\">" +
- " @font-face {" +
- " font-family: 'Roboto-Light';" +
- " src: url('file:///android_asset/Roboto-Light.ttf');" +
- " }" +
- " * {" +
- " color: @fontcolor@;" +
- " font-family: roboto-Light;" +
- " font-size: 8pt;" +
- " }" +
- " </style>" +
- "</head><body><p>" + webViewData + "</p></body></html>";
- webViewData = webViewData.replace("\n", "<br/>");
- }
- webViewData = webViewData.replace("@fontcolor@", fontColor);
- webViewData = webViewData.replace("@background@", backgroundColor);
- webViewData = webViewData.replace("@card_background@", cardBackground);
- webViewData = webViewData.replace("@card_border@", cardBorder);
- subscriber.onSuccess(webViewData);
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- subscriber.onError(e);
- } finally {
- IOUtils.closeQuietly(input);
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- webViewData ->
- webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()),
- error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- }
-
- @Override
- public void onBackPressed() {
- if (webView.canGoBack()) {
- webView.goBack();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (disposable != null) {
- disposable.dispose();
- }
- if (webViewContainer != null && webView != null) {
- webViewContainer.removeAllViews();
- webView.destroy();
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 8eb0b1e0b..7f8c14b03 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
@@ -83,7 +83,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
float speed = 1.0f;
if(controller.canSetPlaybackSpeed()) {
- speed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(controller.getMedia());
+ speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
}
String speedStr = new DecimalFormat("0.00").format(speed);
txtvPlaybackSpeed.setText(speedStr);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 6a38f8f0a..0023e6d7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -34,6 +34,8 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
@@ -48,7 +50,6 @@ import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -84,9 +85,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private static final String TAG = "MainActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
-
public static final String PREF_NAME = "MainActivityPrefs";
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
@@ -319,8 +317,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
fragment = new AddFeedFragment();
break;
case SubscriptionFragment.TAG:
- SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
- fragment = subscriptionFragment;
+ fragment = new SubscriptionFragment();
break;
default:
// default to the queue
@@ -490,7 +487,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
RatingDialog.init(this);
}
@@ -518,7 +514,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
protected void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
@@ -817,16 +812,16 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadData();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private void handleNavIntent() {
Log.d(TAG, "handleNavIntent()");
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index a060e258a..538ed1231 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -28,7 +28,6 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
@@ -64,6 +63,7 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -391,10 +391,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
| Intent.FLAG_ACTIVITY_NEW_TASK);
View cover = findViewById(R.id.imgvCover);
- if (cover != null && Build.VERSION.SDK_INT >= 16) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(MediaplayerActivity.this,
- cover, "coverTransition");
+ if (cover != null) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -426,19 +425,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
case R.id.disable_sleeptimer_item:
if (controller.serviceAvailable()) {
- MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
- stDialog.title(R.string.sleep_timer_label);
- stDialog.content(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()));
- stDialog.positiveText(R.string.disable_sleeptimer_label);
- stDialog.negativeText(R.string.cancel_label);
- stDialog.onPositive((dialog, which) -> {
- dialog.dismiss();
- controller.disableSleepTimer();
- });
- stDialog.onNegative((dialog, which) -> dialog.dismiss());
- stDialog.build().show();
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.sleep_timer_label)
+ .setMessage(getString(R.string.time_left_label)
+ + Converter.getDurationStringLong((int) controller
+ .getSleepTimerTimeLeft()))
+ .setPositiveButton(R.string.disable_sleeptimer_label, (dialog, which)
+ -> controller.disableSleepTimer())
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
}
break;
case R.id.set_sleeptimer_item:
@@ -504,7 +499,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static String getWebsiteLinkWithFallback(Playable media) {
if (media == null) {
return null;
- } else if (media.getWebsiteLink() != null) {
+ } else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
return media.getWebsiteLink();
} else if (media instanceof FeedMedia) {
return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 016168b45..21f4ad5be 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -36,8 +36,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -119,7 +119,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if (disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
saveCurrentFragment();
}
@@ -171,7 +170,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadData();
}
@@ -445,16 +443,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index ede6e6962..af11e9219 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -10,7 +10,6 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import androidx.core.app.NavUtils;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
@@ -22,22 +21,19 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
@@ -49,7 +45,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -95,60 +90,31 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_TITLE = "title";
private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds;
private Feed feed;
private String selectedDownloadUrl;
private Downloader downloader;
private boolean isPaused;
+ private boolean didPressSubscribe = false;
private Dialog dialog;
-
+ private ListView listView;
private Button subscribeButton;
+ private ProgressBar progressBar;
private Disposable download;
private Disposable parser;
private Disposable updater;
- private final EventDistributor.EventListener listener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- updater = Observable.fromCallable(DBReader::getFeedList)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- feeds -> {
- OnlineFeedViewActivity.this.feeds = feeds;
- setSubscribeButtonState(feed);
- }, error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- } else if ((arg & EVENTS) != 0) {
- setSubscribeButtonState(feed);
- }
- }
- };
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- setSubscribeButtonState(feed);
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
+ setTheme(UserPreferences.getTranslucentTheme());
super.onCreate(savedInstanceState);
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
-
- if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
- actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE));
- }
-
StorageUtils.checkStorageAvailability(this);
+ setContentView(R.layout.onlinefeedview_activity);
+ listView = findViewById(R.id.listview);
+ progressBar = findViewById(R.id.progressBar);
String feedUrl = null;
if (getIntent().hasExtra(ARG_FEEDURL)) {
@@ -157,9 +123,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
- if (actionBar != null) {
- actionBar.setTitle(R.string.add_feed_label);
- }
}
if (feedUrl == null) {
@@ -184,26 +147,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* Displays a progress indicator.
*/
private void setLoadingLayout() {
- RelativeLayout rl = new RelativeLayout(this);
- RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
-
- ProgressBar pb = new ProgressBar(this);
- pb.setIndeterminate(true);
- RelativeLayout.LayoutParams pbLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT);
- pbLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
- rl.addView(pb, pbLayoutParams);
- addContentView(rl, rlLayoutParams);
+ progressBar.setVisibility(View.VISIBLE);
+ findViewById(R.id.feedDisplay).setVisibility(View.GONE);
}
@Override
protected void onStart() {
super.onStart();
isPaused = false;
- EventDistributor.getInstance().register(listener);
EventBus.getDefault().register(this);
}
@@ -211,7 +162,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
protected void onStop() {
super.onStop();
isPaused = true;
- EventDistributor.getInstance().unregister(listener);
EventBus.getDefault().unregister(this);
if (downloader != null && !downloader.isFinished()) {
downloader.cancel();
@@ -252,6 +202,12 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
@Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@@ -301,7 +257,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
- R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
+ R.string.authentication_notification_title,
+ downloader.getDownloadRequest().getSource()).create();
dialog.show();
}
} else {
@@ -313,6 +270,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updater = Observable.fromCallable(DBReader::getFeedList)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ feeds -> {
+ OnlineFeedViewActivity.this.feeds = feeds;
+ handleUpdatedFeedStatus(feed);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error))
+ );
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ handleUpdatedFeedStatus(feed);
+ }
+
private void parseFeed() {
if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) {
throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called");
@@ -368,20 +344,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on a background thread
*/
private void beforeShowFeedInformation(Feed feed) {
- final HtmlToPlainText formatter = new HtmlToPlainText();
- if(Feed.TYPE_ATOM1.equals(feed.getType()) && feed.getDescription() != null) {
- // remove HTML tags from descriptions
- Log.d(TAG, "Removing HTML from feed description");
- Document feedDescription = Jsoup.parse(feed.getDescription());
- feed.setDescription(StringUtils.trim(formatter.getPlainText(feedDescription)));
- }
+ Log.d(TAG, "Removing HTML from feed description");
+
+ feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
+
Log.d(TAG, "Removing HTML from shownotes");
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
- if (item.getDescription() != null) {
- Document itemDescription = Jsoup.parse(item.getDescription());
- item.setDescription(StringUtils.trim(formatter.getPlainText(itemDescription)));
- }
+ item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
}
}
}
@@ -391,30 +361,27 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on the GUI thread.
*/
private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) {
- setContentView(R.layout.listview_activity);
-
+ progressBar.setVisibility(View.GONE);
+ findViewById(R.id.feedDisplay).setVisibility(View.VISIBLE);
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
- EventDistributor.getInstance().register(listener);
- ListView listView = findViewById(R.id.listview);
listView.setSelector(android.R.color.transparent);
- LayoutInflater inflater = LayoutInflater.from(this);
- View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
- listView.addHeaderView(header);
-
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
- ImageView cover = header.findViewById(R.id.imgvCover);
- ImageView headerBackground = header.findViewById(R.id.imgvBackground);
- header.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
- header.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ ImageView cover = findViewById(R.id.imgvCover);
+ ImageView headerBackground = findViewById(R.id.imgvBackground);
+ findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
headerBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
- TextView title = header.findViewById(R.id.txtvTitle);
- TextView author = header.findViewById(R.id.txtvAuthor);
+ TextView title = findViewById(R.id.txtvTitle);
+ TextView author = findViewById(R.id.txtvAuthor);
+ Spinner spAlternateUrls = findViewById(R.id.spinnerAlternateUrls);
+
+ View header = View.inflate(this, R.layout.onlinefeedview_header, null);
+ listView.addHeaderView(header);
TextView description = header.findViewById(R.id.txtvDescription);
- Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls);
- subscribeButton = header.findViewById(R.id.butSubscribe);
+ subscribeButton = findViewById(R.id.butSubscribe);
if (StringUtils.isNotBlank(feed.getImageUrl())) {
Glide.with(this)
@@ -442,11 +409,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
description.setText(feed.getDescription());
subscribeButton.setOnClickListener(v -> {
- if(feedInFeedlist(feed)) {
- // feed.getId() is always 0, we have to retrieve the id from the feed list from
- // the database
- Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
- startActivity(intent);
+ if (feedInFeedlist(feed)) {
+ openFeed();
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
f.setPreferences(feed.getPreferences());
@@ -457,7 +421,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
Log.e(TAG, Log.getStackTraceString(e));
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage());
}
- setSubscribeButtonState(feed);
+ didPressSubscribe = true;
+ handleUpdatedFeedStatus(feed);
}
});
@@ -503,17 +468,28 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
});
}
- setSubscribeButtonState(feed);
+ handleUpdatedFeedStatus(feed);
+ }
+
+ private void openFeed() {
+ // feed.getId() is always 0, we have to retrieve the id from the feed list from
+ // the database
+ Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
+ finish();
+ startActivity(intent);
}
- private void setSubscribeButtonState(Feed feed) {
+ private void handleUpdatedFeedStatus(Feed feed) {
if (subscribeButton != null && feed != null) {
if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
subscribeButton.setEnabled(false);
- subscribeButton.setText(R.string.downloading_label);
+ subscribeButton.setText(R.string.subscribing_label);
} else if (feedInFeedlist(feed)) {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.open_podcast);
+ if (didPressSubscribe) {
+ openFeed();
+ }
} else {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.subscribe_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
index 9caff0fc0..d7a4b9517 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
@@ -6,12 +6,11 @@ import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import org.apache.commons.lang3.ArrayUtils;
import java.io.InputStreamReader;
@@ -30,55 +29,55 @@ import de.danoeh.antennapod.core.util.LangUtils;
public class OpmlImportBaseActivity extends AppCompatActivity {
private static final String TAG = "OpmlImportBaseActivity";
- private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
+ private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
private OpmlImportWorker importWorker;
- @Nullable private Uri uri;
-
- /**
- * Handles the choices made by the user in the OpmlFeedChooserActivity and
- * starts the OpmlFeedQueuer if necessary.
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "Received result");
- if (resultCode == RESULT_CANCELED) {
- Log.d(TAG, "Activity was cancelled");
+ @Nullable private Uri uri;
+
+ /**
+ * Handles the choices made by the user in the OpmlFeedChooserActivity and
+ * starts the OpmlFeedQueuer if necessary.
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "Received result");
+ if (resultCode == RESULT_CANCELED) {
+ Log.d(TAG, "Activity was cancelled");
if (finishWhenCanceled()) {
- finish();
- }
- } else {
- int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
- if (selected != null && selected.length > 0) {
- OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
-
- };
- queuer.executeAsync();
- } else {
- Log.d(TAG, "No items were selected");
- }
- }
- }
-
- void importUri(@Nullable Uri uri) {
+ finish();
+ }
+ } else {
+ int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
+ if (selected != null && selected.length > 0) {
+ OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ };
+ queuer.executeAsync();
+ } else {
+ Log.d(TAG, "No items were selected");
+ }
+ }
+ }
+
+ void importUri(@Nullable Uri uri) {
if(uri == null) {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_error_no_file)
- .positiveText(android.R.string.ok)
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_error_no_file)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- this.uri = uri;
+ this.uri = uri;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
+ uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
requestPermission();
@@ -88,30 +87,28 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
startImport();
}
- private void requestPermission() {
- String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
- ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- String[] permissions,
- int[] grantResults) {
- if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
- return;
- }
- if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
- startImport();
- } else {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_ask_read_permission)
- .positiveText(android.R.string.ok)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ private void requestPermission() {
+ String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
+ ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
+ return;
+ }
+ if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
+ startImport();
+ } else {
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_ask_read_permission)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(R.string.cancel_label, (dialog, which) -> finish())
.show();
- }
- }
+ }
+ }
/** Starts the import process. */
private void startImport() {
@@ -136,10 +133,10 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
importWorker.executeAsync();
} catch (Exception e) {
Log.d(TAG, Log.getStackTraceString(e));
- String message = getString(R.string.opml_reader_error);
- new MaterialDialog.Builder(this)
- .content(message + " " + e.getMessage())
- .positiveText(android.R.string.ok)
+ String message = getString(R.string.opml_reader_error);
+ new AlertDialog.Builder(this)
+ .setMessage(message + " " + e.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
index c9c9a0e2c..8527949b0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -16,8 +16,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.widget.Button;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.io.File;
import de.danoeh.antennapod.R;
@@ -28,22 +26,22 @@ import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
/** Is show if there is now external storage available. */
public class StorageErrorActivity extends AppCompatActivity {
- private static final String TAG = "StorageErrorActivity";
+ private static final String TAG = "StorageErrorActivity";
private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 42;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
- setContentView(R.layout.storage_error);
+ setContentView(R.layout.storage_error);
- Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
- btnChooseDataFolder.setOnClickListener(v -> {
+ Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
+ btnChooseDataFolder.setOnClickListener(v -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
showChooseDataFolderDialog();
@@ -82,11 +80,10 @@ public class StorageErrorActivity extends AppCompatActivity {
}
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED) {
- new MaterialDialog.Builder(this)
- .content(R.string.choose_data_directory_permission_rationale)
- .positiveText(android.R.string.ok)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.choose_data_directory_permission_rationale)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.show();
}
}
@@ -101,15 +98,15 @@ public class StorageErrorActivity extends AppCompatActivity {
}
}
- @Override
- protected void onPause() {
- super.onPause();
- try {
- unregisterReceiver(mediaUpdate);
- } catch (IllegalArgumentException e) {
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ unregisterReceiver(mediaUpdate);
+ } catch (IllegalArgumentException e) {
Log.e(TAG, Log.getStackTraceString(e));
- }
- }
+ }
+ }
// see PreferenceController.showChooseDataFolderDialog()
private void showChooseDataFolderDialog() {
@@ -123,9 +120,9 @@ public class StorageErrorActivity extends AppCompatActivity {
});
}
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_OK &&
- requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
@@ -138,46 +135,46 @@ public class StorageErrorActivity extends AppCompatActivity {
return;
}
String message = null;
- if(!path.exists()) {
- message = String.format(getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
- message = String.format(getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
- message = String.format(getString(R.string.folder_not_writable_error), dir);
- }
-
- if(message == null) {
- Log.d(TAG, "Setting data folder: " + dir);
- UserPreferences.setDataFolder(dir);
- leaveErrorState();
- } else {
- AlertDialog.Builder ab = new AlertDialog.Builder(this);
- ab.setMessage(message);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
- }
-
- private void leaveErrorState() {
- finish();
- startActivity(new Intent(this, MainActivity.class));
- }
-
- private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
- if (intent.getBooleanExtra("read-only", true)) {
- Log.d(TAG, "Media was mounted; Finishing activity");
- leaveErrorState();
- } else {
- Log.d(TAG, "Media seemed to have been mounted read only");
- }
- }
- }
-
- };
+ if(!path.exists()) {
+ message = String.format(getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ leaveErrorState();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+ private void leaveErrorState() {
+ finish();
+ startActivity(new Intent(this, MainActivity.class));
+ }
+
+ private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
+ if (intent.getBooleanExtra("read-only", true)) {
+ Log.d(TAG, "Media was mounted; Finishing activity");
+ leaveErrorState();
+ } else {
+ Log.d(TAG, "Media seemed to have been mounted read only");
+ }
+ }
+ }
+
+ };
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 2d28ea561..0c6ae2645 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -151,10 +151,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoview.getHolder().addCallback(surfaceHolderCallback);
videoframe.setOnTouchListener(onVideoviewTouched);
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
-
- if (Build.VERSION.SDK_INT >= 16) {
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- }
+ videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
setupVideoControlsToggler();
@@ -351,9 +348,9 @@ public class VideoplayerActivity extends MediaplayerActivity {
controls.startAnimation(animation);
}
}
- int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
videoOverlay.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
new file mode 100644
index 000000000..474b96c38
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.activity;
+
+import android.Manifest;
+import android.app.WallpaperManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.PlayerWidget;
+import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
+
+public class WidgetConfigActivity extends AppCompatActivity {
+ private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private SeekBar opacitySeekBar;
+ private TextView opacityTextView;
+ private RelativeLayout widgetPreview;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_widget_config);
+
+ Intent configIntent = getIntent();
+ Bundle extras = configIntent.getExtras();
+ if (extras != null) {
+ appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_CANCELED, resultValue);
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ }
+
+ displayDeviceBackground();
+ opacityTextView = findViewById(R.id.widget_opacity_textView);
+ opacitySeekBar = findViewById(R.id.widget_opacity_seekBar);
+ widgetPreview = findViewById(R.id.widgetLayout);
+ findViewById(R.id.butConfirm).setOnClickListener(this::confirmCreateWidget);
+ opacitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+ opacityTextView.setText(seekBar.getProgress() + "%");
+ int color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+ widgetPreview.setBackgroundColor(color);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ });
+ }
+
+ private void displayDeviceBackground() {
+ int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (Build.VERSION.SDK_INT < 27 || permission == PackageManager.PERMISSION_GRANTED) {
+ final WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+ final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
+ ImageView background = findViewById(R.id.widget_config_background);
+ background.setImageDrawable(wallpaperDrawable);
+ }
+ }
+
+ private void confirmCreateWidget(View v) {
+ int backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+
+ SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
+ editor.apply();
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_OK, resultValue);
+ finish();
+ PlayerWidgetJobService.updateWidget(this);
+ }
+
+ private int getColorWithAlpha(int color, int opacity) {
+ return (int) Math.round(0xFF * (0.01 * opacity)) * 0x1000000 + color;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 5b735cd1f..9cd5cc3ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -35,10 +35,12 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -53,7 +55,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final boolean showOnlyNewEpisodes;
private FeedItem selectedItem;
- private Holder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -171,7 +172,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
- currentlyPlayingItem = holder;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -194,29 +194,13 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setTag(item);
new CoverLoader(mainActivityRef.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(holder.placeholder)
.withCoverView(holder.cover)
.load();
}
- @Override
- public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) {
- onBindViewHolder(holder, pos);
-
- if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
- PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
- holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
- }
- }
-
- public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
- if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
- notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
- }
- }
-
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -262,7 +246,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
- mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
+ mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, getAdapterPosition()));
}
}
@@ -301,6 +285,14 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
+
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ }
+
}
public interface ItemAccess {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
index 9014de525..e3ca5b5a5 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.adapter;
import android.app.Dialog;
import android.content.Context;
+import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -20,7 +21,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
-import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.ViewHolder> {
@@ -105,7 +105,7 @@ public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.Vi
private TextView path;
private TextView size;
private RadioButton radioButton;
- private MaterialProgressBar progressBar;
+ private ProgressBar progressBar;
ViewHolder(View itemView) {
super(itemView);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index b8764c2ae..4d66fd486 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.os.Build;
-import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.text.format.DateUtils;
import android.util.Log;
@@ -13,6 +12,8 @@ import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.core.content.ContextCompat;
+
import com.joanzapata.iconify.widget.IconButton;
import com.joanzapata.iconify.widget.IconTextView;
@@ -24,6 +25,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
/** Displays a list of DownloadStatus entries. */
public class DownloadLogAdapter extends BaseAdapter {
@@ -132,7 +134,7 @@ public class DownloadLogAdapter extends BaseAdapter {
FeedMedia media = DBReader.getFeedMedia(holder.id);
if (media != null) {
try {
- DBTasks.downloadFeedItems(context, media.getItem());
+ DownloadRequester.getInstance().downloadMedia(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 98d55dd97..b083908a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -17,9 +17,9 @@ import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
/**
* Shows a list of downloaded episodes
@@ -79,7 +79,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
}
Glide.with(context)
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
index df7ec46e0..66fa79a4e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
@@ -59,6 +59,8 @@ public class FeedDiscoverAdapter extends BaseAdapter {
final PodcastSearchResult podcast = getItem(position);
+ holder.imageView.setContentDescription(podcast.title);
+
Glide.with(mainActivityRef.get())
.load(podcast.imageUrl)
.apply(new RequestOptions()
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index fcdcb4ba6..5ccec0ade 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.view.MotionEventCompat;
@@ -26,10 +25,10 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
-import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -40,10 +39,10 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
-import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -60,7 +59,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private boolean locked;
private FeedItem selectedItem;
- private ViewHolder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -83,11 +81,13 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
notifyDataSetChanged();
}
+ @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
return new ViewHolder(view);
}
+ @Override
public void onBindViewHolder(ViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
@@ -97,18 +97,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
});
}
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) {
- onBindViewHolder(holder, pos);
-
- if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
- PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
- holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
- holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
- holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
- }
- }
-
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -124,12 +112,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
return itemAccess.getCount();
}
- public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
- if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
- notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
- }
- }
-
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
@@ -181,7 +163,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
}
@@ -309,7 +291,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
- currentlyPlayingItem = this;
} else {
container.setBackgroundColor(normalBackGroundColor);
}
@@ -322,13 +303,22 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setTag(item);
new CoverLoader(mainActivity.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
+
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
+ progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
+ }
}
public interface ItemAccess {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
new file mode 100644
index 000000000..10bda4efa
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
@@ -0,0 +1,59 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.R;
+
+import java.util.List;
+
+/**
+ * Displays a list of items that have a subtitle and an icon.
+ */
+public class SimpleIconListAdapter<T extends SimpleIconListAdapter.ListItem> extends ArrayAdapter<T> {
+ private final Context context;
+ private final List<T> listItems;
+
+ public SimpleIconListAdapter(Context context, List<T> listItems) {
+ super(context, R.layout.simple_icon_list_item, listItems);
+ this.context = context;
+ this.listItems = listItems;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = View.inflate(context, R.layout.simple_icon_list_item, null);
+ }
+
+ ListItem item = listItems.get(position);
+ ((TextView) view.findViewById(R.id.title)).setText(item.title);
+ ((TextView) view.findViewById(R.id.subtitle)).setText(item.subtitle);
+ Glide.with(context)
+ .load(item.imageUrl)
+ .apply(new RequestOptions()
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate())
+ .into(((ImageView) view.findViewById(R.id.icon)));
+ return view;
+ }
+
+ public static class ListItem {
+ public final String title;
+ public final String subtitle;
+ public final String imageUrl;
+
+ public ListItem(String title, String subtitle, String imageUrl) {
+ this.title = title;
+ this.subtitle = subtitle;
+ this.imageUrl = imageUrl;
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index 5d52eb263..3141c6046 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -84,6 +84,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
if (feed == null) return null;
holder.feedTitle.setText(feed.getTitle());
+ holder.imageView.setContentDescription(feed.getTitle());
holder.feedTitle.setVisibility(View.VISIBLE);
int count = itemAccess.getFeedCounter(feed.getId());
if(count > 0) {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
index 55ca5471b..026386cf9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -1,16 +1,16 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
+import android.widget.Toast;
+
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
-import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -64,7 +64,7 @@ class DownloadActionButton extends ItemActionButton {
private void downloadEpisode(Context context) {
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index 31e9fccb5..861c6a4be 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -10,6 +10,7 @@ import android.widget.ImageButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
public abstract class ItemActionButton {
@@ -43,6 +44,8 @@ public abstract class ItemActionButton {
return new PlayActionButton(item);
} else if (isDownloadingMedia) {
return new CancelDownloadActionButton(item);
+ } else if (UserPreferences.streamOverDownload()) {
+ return new StreamActionButton(item);
} else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
return new DownloadActionButton(item, isInQueue);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
index f8d2a139e..77efd9023 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
@@ -3,15 +3,14 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
class MobileDownloadHelper {
private static long addToQueueTimestamp;
@@ -27,16 +26,15 @@ class MobileDownloadHelper {
}
static void confirmMobileDownload(final Context context, final FeedItem item) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context)
- .title(R.string.confirm_mobile_download_dialog_title)
- .content(R.string.confirm_mobile_download_dialog_message)
- .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
- .onPositive((dialog, which) -> downloadFeedItems(context, item));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(R.string.confirm_mobile_download_dialog_title)
+ .setMessage(R.string.confirm_mobile_download_dialog_message)
+ .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily),
+ (dialog, which) -> downloadFeedItems(context, item));
if (!DBReader.getQueueIDList().contains(item.getId())) {
- builder
- .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
- .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
- .onNeutral((dialog, which) -> addToQueue(context, item));
+ builder.setMessage(R.string.confirm_mobile_download_dialog_message_not_in_queue)
+ .setNeutralButton(R.string.confirm_mobile_download_dialog_only_add_to_queue,
+ (dialog, which) -> addToQueue(context, item));
}
builder.show();
}
@@ -50,7 +48,7 @@ class MobileDownloadHelper {
private static void downloadFeedItems(Context context, FeedItem item) {
allowMobileDownloadTimestamp = System.currentTimeMillis();
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
index 23a7e03ad..0d314b5eb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
@@ -12,7 +12,6 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
-import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE;
class PlayActionButton extends ItemActionButton {
@@ -52,12 +51,14 @@ class PlayActionButton extends ItemActionButton {
}
private void togglePlayPause(Context context, FeedMedia media) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
-
- String pauseOrResume = media.isCurrentlyPlaying() ? ACTION_PAUSE_PLAY_CURRENT_EPISODE : ACTION_RESUME_PLAY_CURRENT_EPISODE;
- IntentUtils.sendLocalBroadcast(context, pauseOrResume);
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
new file mode 100644
index 000000000..c1e619fdf
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+
+import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
+
+public class StreamActionButton extends ItemActionButton {
+
+ StreamActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.stream_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isCurrentlyPlaying()) {
+ return R.attr.av_pause;
+ }
+ return R.attr.action_stream;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ final FeedMedia media = item.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ if (media.isPlaying()) {
+ togglePlayPause(context, media);
+ } else {
+ DBTasks.playMedia(context, media, false, true, true);
+ }
+ }
+
+ private void togglePlayPause(Context context, FeedMedia media) {
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(true)
+ .start();
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
index 2c41b8cb8..073f7b550 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
@@ -1,51 +1,29 @@
package de.danoeh.antennapod.dialog;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.content.Context;
-import android.os.Bundle;
import android.view.View;
-import android.view.Window;
-import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
-
import de.danoeh.antennapod.R;
/**
* Displays a dialog with a username and password text field and an optional checkbox to save username and preferences.
*/
-public abstract class AuthenticationDialog extends Dialog {
-
- private final int titleRes;
- private final boolean enableUsernameField;
- private final boolean showSaveCredentialsCheckbox;
- private final String usernameInitialValue;
- private final String passwordInitialValue;
+public abstract class AuthenticationDialog extends AlertDialog.Builder {
- public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField, boolean showSaveCredentialsCheckbox, String usernameInitialValue, String passwordInitialValue) {
+ public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField,
+ boolean showSaveCredentialsCheckbox, String usernameInitialValue,
+ String passwordInitialValue) {
super(context);
- this.titleRes = titleRes;
- this.enableUsernameField = enableUsernameField;
- this.showSaveCredentialsCheckbox = showSaveCredentialsCheckbox;
- this.usernameInitialValue = usernameInitialValue;
- this.passwordInitialValue = passwordInitialValue;
- }
+ setTitle(titleRes);
+ View rootView = View.inflate(context, R.layout.authentication_dialog, null);
+ setView(rootView);
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.authentication_dialog);
- final EditText etxtUsername = findViewById(R.id.etxtUsername);
- final EditText etxtPassword = findViewById(R.id.etxtPassword);
- final CheckBox saveUsernamePassword = findViewById(R.id.chkSaveUsernamePassword);
- final Button butConfirm = findViewById(R.id.butConfirm);
- final Button butCancel = findViewById(R.id.butCancel);
+ final EditText etxtUsername = rootView.findViewById(R.id.etxtUsername);
+ final EditText etxtPassword = rootView.findViewById(R.id.etxtPassword);
+ final CheckBox saveUsernamePassword = rootView.findViewById(R.id.chkSaveUsernamePassword);
- if (titleRes != 0) {
- setTitle(titleRes);
- } else {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- }
etxtUsername.setEnabled(enableUsernameField);
if (showSaveCredentialsCheckbox) {
saveUsernamePassword.setVisibility(View.VISIBLE);
@@ -59,13 +37,10 @@ public abstract class AuthenticationDialog extends Dialog {
etxtPassword.setText(passwordInitialValue);
}
setOnCancelListener(dialog -> onCancelled());
- butCancel.setOnClickListener(v -> cancel());
- butConfirm.setOnClickListener(v -> {
- onConfirmed(etxtUsername.getText().toString(),
- etxtPassword.getText().toString(),
- showSaveCredentialsCheckbox && saveUsernamePassword.isChecked());
- dismiss();
- });
+ setNegativeButton(R.string.cancel_label, null);
+ setPositiveButton(R.string.confirm_label, (dialog, which)
+ -> onConfirmed(etxtUsername.getText().toString(), etxtPassword.getText().toString(),
+ showSaveCredentialsCheckbox && saveUsernamePassword.isChecked()));
}
protected void onCancelled() {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index 4cfa7e870..ec285a8f6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -2,8 +2,10 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import android.view.View;
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DataFolderAdapter;
@@ -25,21 +27,23 @@ public class ChooseDataFolderDialog {
DataFolderAdapter adapter = new DataFolderAdapter(context, handlerFunc);
if (adapter.getItemCount() == 0) {
- new MaterialDialog.Builder(context)
- .title(R.string.error_label)
- .content(R.string.external_storage_error_msg)
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.error_label)
+ .setMessage(R.string.external_storage_error_msg)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- MaterialDialog dialog = new MaterialDialog.Builder(context)
- .title(R.string.choose_data_directory)
- .content(R.string.choose_data_directory_message)
- .adapter(adapter, null)
- .negativeText(R.string.cancel_label)
- .cancelable(true)
- .build();
+ View content = View.inflate(context, R.layout.choose_data_folder_dialog, null);
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setView(content)
+ .setTitle(R.string.choose_data_directory)
+ .setMessage(R.string.choose_data_directory_message)
+ .setNegativeButton(R.string.cancel_label, null)
+ .create();
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setLayoutManager(new LinearLayoutManager(context));
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setAdapter(adapter);
adapter.setDialog(dialog);
dialog.show();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index f6d08b7bf..ff131aeba 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -4,16 +4,6 @@ import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.PluralsRes;
-import androidx.annotation.StringRes;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.core.app.ActivityCompat;
-import androidx.fragment.app.Fragment;
-import androidx.collection.ArrayMap;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -24,6 +14,17 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.PluralsRes;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.collection.ArrayMap;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
@@ -35,10 +36,12 @@ import java.util.Map;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBTasks;
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.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.SortOrder;
public class EpisodesApplyActionFragment extends Fragment {
@@ -263,6 +266,18 @@ public class EpisodesApplyActionFragment extends Fragment {
mSelectToggle.setTitle(titleResId);
}
+ private static final Map<Integer, SortOrder> menuItemIdToSortOrder;
+ static {
+ Map<Integer, SortOrder> map = new ArrayMap<>();
+ map.put(R.id.sort_title_a_z, SortOrder.EPISODE_TITLE_A_Z);
+ map.put(R.id.sort_title_z_a, SortOrder.EPISODE_TITLE_Z_A);
+ map.put(R.id.sort_date_new_old, SortOrder.DATE_NEW_OLD);
+ map.put(R.id.sort_date_old_new, SortOrder.DATE_OLD_NEW);
+ map.put(R.id.sort_duration_long_short, SortOrder.DURATION_LONG_SHORT);
+ map.put(R.id.sort_duration_short_long, SortOrder.DURATION_SHORT_LONG);
+ menuItemIdToSortOrder = Collections.unmodifiableMap(map);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@StringRes int resId = 0;
@@ -305,24 +320,12 @@ public class EpisodesApplyActionFragment extends Fragment {
checkWithMedia();
resId = R.string.selected_has_media_label;
break;
- case R.id.sort_title_a_z:
- sortByTitle(false);
- return true;
- case R.id.sort_title_z_a:
- sortByTitle(true);
- return true;
- case R.id.sort_date_new_old:
- sortByDate(true);
- return true;
- case R.id.sort_date_old_new:
- sortByDate(false);
- return true;
- case R.id.sort_duration_long_short:
- sortByDuration(true);
- return true;
- case R.id.sort_duration_short_long:
- sortByDuration(false);
- return true;
+ default: // handle various sort options
+ SortOrder sortOrder = menuItemIdToSortOrder.get(item.getItemId());
+ if (sortOrder != null) {
+ sort(sortOrder);
+ return true;
+ }
}
if(resId != 0) {
Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
@@ -333,52 +336,9 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
- private void sortByTitle(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (reverse) {
- return -1 * lhs.getTitle().compareTo(rhs.getTitle());
- } else {
- return lhs.getTitle().compareTo(rhs.getTitle());
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDate(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (lhs.getPubDate() == null) {
- return -1;
- } else if (rhs.getPubDate() == null) {
- return 1;
- }
- int code = lhs.getPubDate().compareTo(rhs.getPubDate());
- if (reverse) {
- return -1 * code;
- } else {
- return code;
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDuration(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- int ordering;
- if (!lhs.hasMedia()) {
- ordering = 1;
- } else if (!rhs.hasMedia()) {
- ordering = -1;
- } else {
- ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
- }
- if(reverse) {
- return -1 * ordering;
- } else {
- return ordering;
- }
- });
+ private void sort(@NonNull SortOrder sortOrder) {
+ FeedItemPermutors.getPermutor(sortOrder)
+ .reorder(episodes);
refreshTitles();
refreshCheckboxes();
}
@@ -525,7 +485,7 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
try {
- DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
+ DownloadRequester.getInstance().downloadMedia(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
new file mode 100644
index 000000000..2ee716c7c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
@@ -0,0 +1,51 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.SortOrder;
+
+public abstract class IntraFeedSortDialog {
+
+ @Nullable
+ protected SortOrder currentSortOrder;
+ @NonNull
+ protected Context context;
+
+ public IntraFeedSortDialog(@NonNull Context context, @Nullable SortOrder sortOrder) {
+ this.context = context;
+ this.currentSortOrder = sortOrder;
+ }
+
+ public void openDialog() {
+ final String[] items = context.getResources().getStringArray(R.array.feed_episodes_sort_options);
+ final String[] valueStrs = context.getResources().getStringArray(R.array.feed_episodes_sort_values);
+ final SortOrder[] values = new SortOrder[valueStrs.length];
+ for (int i = 0; i < valueStrs.length; i++) {
+ values[i] = SortOrder.valueOf(valueStrs[i]);
+ }
+
+ int idxCurrentSort = -1;
+ for (int i = 0; i < values.length; i++) {
+ if (currentSortOrder == values[i]) {
+ idxCurrentSort = i;
+ break;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.sort);
+ builder.setSingleChoiceItems(items, idxCurrentSort, (dialog, idxNewSort) -> {
+ updateSort(values[idxNewSort]);
+ dialog.dismiss();
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ protected abstract void updateSort(@NonNull SortOrder sortOrder);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
index f53dbe57a..3e4e40a5b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -3,15 +3,16 @@ package de.danoeh.antennapod.dialog;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import java.util.Locale;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -24,7 +25,7 @@ public class PlaybackControlsDialog extends DialogFragment {
private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo";
private PlaybackController controller;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private boolean isPlayingVideo;
public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) {
@@ -42,7 +43,12 @@ public class PlaybackControlsDialog extends DialogFragment {
@Override
public void onStart() {
super.onStart();
- controller = new PlaybackController(getActivity(), false);
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public void setupGUI() {
+ setupUi();
+ }
+ };
controller.init();
setupUi();
}
@@ -59,15 +65,14 @@ public class PlaybackControlsDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO);
- dialog = new MaterialDialog.Builder(getContext())
- .title(R.string.audio_controls)
- .customView(R.layout.audio_controls, true)
- .neutralText(R.string.close_label)
- .onNeutral((dialog1, which) -> {
- final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
- final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ dialog = new AlertDialog.Builder(getContext())
+ .setTitle(R.string.audio_controls)
+ .setView(R.layout.audio_controls)
+ .setPositiveButton(R.string.close_label, (dialog1, which) -> {
+ final SeekBar left = dialog.findViewById(R.id.volume_left);
+ final SeekBar right = dialog.findViewById(R.id.volume_right);
UserPreferences.setVolume(left.getProgress(), right.getProgress());
- }).build();
+ }).create();
return dialog;
}
@@ -110,6 +115,7 @@ public class PlaybackControlsDialog extends DialogFragment {
controller.setPlaybackSpeed(playbackSpeed);
String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed);
if (isPlayingVideo) {
UserPreferences.setVideoPlaybackSpeed(speedPref);
} else {
@@ -136,7 +142,7 @@ public class PlaybackControlsDialog extends DialogFragment {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
+ barPlaybackSpeed.setProgress(Math.round((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
@@ -212,6 +218,6 @@ public class PlaybackControlsDialog extends DialogFragment {
media = controller.getMedia();
}
- return PlaybackSpeedHelper.getCurrentPlaybackSpeed(media);
+ return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
index 0499d02f1..11256f2de 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -4,6 +4,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import android.text.Editable;
import android.text.TextUtils;
@@ -16,10 +17,6 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-import com.afollestad.materialdialogs.internal.MDButton;
-
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
@@ -48,7 +45,7 @@ public class ProxyDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private Spinner spType;
private EditText etHost;
@@ -64,54 +61,53 @@ public class ProxyDialog {
this.context = context;
}
- public Dialog createDialog() {
- dialog = new MaterialDialog.Builder(context)
- .title(R.string.pref_proxy_title)
- .customView(R.layout.proxy_settings, true)
- .positiveText(R.string.proxy_test_label)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog1, which) -> {
- if(!testSuccessful) {
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
- test();
- return;
- }
- String type = (String) ((Spinner) dialog1.findViewById(R.id.spType)).getSelectedItem();
- ProxyConfig proxy;
- if(Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
- proxy = ProxyConfig.direct();
- } else {
- String host = etHost.getText().toString();
- String port = etPort.getText().toString();
- String username = etUsername.getText().toString();
- if(TextUtils.isEmpty(username)) {
- username = null;
- }
- String password = etPassword.getText().toString();
- if(TextUtils.isEmpty(password)) {
- password = null;
- }
- int portValue = 0;
- if(!TextUtils.isEmpty(port)) {
- portValue = Integer.valueOf(port);
- }
- if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
- proxy = ProxyConfig.socks(host, portValue, username, password);
- } else {
- proxy = ProxyConfig.http(host, portValue, username, password);
- }
- }
- UserPreferences.setProxyConfig(proxy);
- AntennapodHttpClient.reinit();
- dialog.dismiss();
- })
- .onNegative((dialog1, which) -> dialog1.dismiss())
- .autoDismiss(false)
- .build();
- View view = dialog.getCustomView();
- spType = view.findViewById(R.id.spType);
+ public Dialog show() {
+ View content = View.inflate(context, R.layout.proxy_settings, null);
+ dialog = new AlertDialog.Builder(context)
+ .setTitle(R.string.pref_proxy_title)
+ .setView(content)
+ .setNegativeButton(R.string.cancel_label, null)
+ .setPositiveButton(R.string.proxy_test_label, null)
+ .show();
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> {
+ if (!testSuccessful) {
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ test();
+ return;
+ }
+ String type = (String) ((Spinner) content.findViewById(R.id.spType)).getSelectedItem();
+ ProxyConfig proxy;
+ if (Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
+ proxy = ProxyConfig.direct();
+ } else {
+ String host = etHost.getText().toString();
+ String port = etPort.getText().toString();
+ String username = etUsername.getText().toString();
+ if(TextUtils.isEmpty(username)) {
+ username = null;
+ }
+ String password = etPassword.getText().toString();
+ if(TextUtils.isEmpty(password)) {
+ password = null;
+ }
+ int portValue = 0;
+ if(!TextUtils.isEmpty(port)) {
+ portValue = Integer.valueOf(port);
+ }
+ if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
+ proxy = ProxyConfig.socks(host, portValue, username, password);
+ } else {
+ proxy = ProxyConfig.http(host, portValue, username, password);
+ }
+ }
+ UserPreferences.setProxyConfig(proxy);
+ AntennapodHttpClient.reinit();
+ dialog.dismiss();
+ });
- List<String> types= new ArrayList<>();
+ spType = content.findViewById(R.id.spType);
+ List<String> types = new ArrayList<>();
types.add(Proxy.Type.DIRECT.name());
types.add(Proxy.Type.HTTP.name());
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -123,22 +119,22 @@ public class ProxyDialog {
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
- etHost = view.findViewById(R.id.etHost);
+ etHost = content.findViewById(R.id.etHost);
if(!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
- etPort = view.findViewById(R.id.etPort);
+ etPort = content.findViewById(R.id.etPort);
if(proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
- etUsername = view.findViewById(R.id.etUsername);
+ etUsername = content.findViewById(R.id.etUsername);
if(!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
- etPassword = view.findViewById(R.id.etPassword);
+ etPassword = content.findViewById(R.id.etPassword);
if(!TextUtils.isEmpty(proxyConfig.password)) {
etPassword.setText(proxyConfig.username);
}
@@ -159,7 +155,7 @@ public class ProxyDialog {
enableSettings(false);
}
});
- txtvMessage = view.findViewById(R.id.txtvMessage);
+ txtvMessage = content.findViewById(R.id.txtvMessage);
checkValidity();
return dialog;
}
@@ -230,14 +226,12 @@ public class ProxyDialog {
private void setTestRequired(boolean required) {
if(required) {
testSuccessful = false;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(R.string.proxy_test_label));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
} else {
testSuccessful = true;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(android.R.string.ok));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index c49e9153e..7cb274708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -7,11 +7,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -97,21 +96,18 @@ public class RatingDialog {
}
@Nullable
- private static MaterialDialog createDialog() {
+ private static AlertDialog createDialog() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return null;
}
- return new MaterialDialog.Builder(context)
- .title(R.string.rating_title)
- .content(R.string.rating_message)
- .positiveText(R.string.rating_now_label)
- .negativeText(R.string.rating_never_label)
- .neutralText(R.string.rating_later_label)
- .onPositive((dialog, which) -> rateNow())
- .onNegative((dialog, which) -> saveRated())
- .onNeutral((dialog, which) -> resetStartDate())
- .cancelListener(dialog1 -> resetStartDate())
- .build();
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.rating_title)
+ .setMessage(R.string.rating_message)
+ .setPositiveButton(R.string.rating_now_label, (dialog, which) -> rateNow())
+ .setNegativeButton(R.string.rating_never_label, (dialog, which) -> saveRated())
+ .setNeutralButton(R.string.rating_later_label, (dialog, which) -> resetStartDate())
+ .setOnCancelListener(dialog1 -> resetStartDate())
+ .create();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
index 31a544582..699c6f492 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
@@ -3,10 +3,12 @@ package de.danoeh.antennapod.dialog;
import android.app.Activity;
import android.text.InputType;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
+import android.view.View;
+import android.widget.EditText;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -25,20 +27,24 @@ public class RenameFeedDialog {
if(activity == null) {
return;
}
- new MaterialDialog.Builder(activity)
- .title(de.danoeh.antennapod.core.R.string.rename_feed_label)
- .inputType(InputType.TYPE_CLASS_TEXT)
- .input(feed.getTitle(), feed.getTitle(), true, (dialog, input) -> {
- feed.setCustomTitle(input.toString());
+
+ View content = View.inflate(activity, R.layout.edit_text_dialog, null);
+ EditText editText = content.findViewById(R.id.text);
+ editText.setText(feed.getTitle());
+ AlertDialog dialog = new AlertDialog.Builder(activity)
+ .setView(content)
+ .setTitle(de.danoeh.antennapod.core.R.string.rename_feed_label)
+ .setPositiveButton(android.R.string.ok, (d, input) -> {
+ feed.setCustomTitle(editText.getText().toString());
DBWriter.setFeedCustomTitle(feed);
- dialog.dismiss();
})
- .neutralText(de.danoeh.antennapod.core.R.string.reset)
- .onNeutral((dialog, which) -> dialog.getInputEditText().setText(feed.getFeedTitle()))
- .negativeText(de.danoeh.antennapod.core.R.string.cancel_label)
- .onNegative((dialog, which) -> dialog.dismiss())
- .autoDismiss(false)
+ .setNeutralButton(de.danoeh.antennapod.core.R.string.reset, null)
+ .setNegativeButton(de.danoeh.antennapod.core.R.string.cancel_label, null)
.show();
+
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(
+ (view) -> editText.setText(feed.getFeedTitle()));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
index dc056a3f9..8d176c708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
@@ -12,9 +11,7 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -26,7 +23,7 @@ public abstract class SleepTimerDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private EditText etxtTime;
private Spinner spTimeUnit;
private CheckBox cbShakeToReset;
@@ -38,40 +35,38 @@ public abstract class SleepTimerDialog {
this.context = context;
}
- public MaterialDialog createNewDialog() {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.set_sleeptimer_label);
- builder.customView(R.layout.time_dialog, false);
- builder.positiveText(R.string.set_sleeptimer_label);
- builder.negativeText(R.string.cancel_label);
- builder.onNegative((dialog, which) -> dialog.dismiss());
- builder.onPositive((dialog, which) -> {
- try {
- savePreferences();
- long input = SleepTimerPreferences.timerMillis();
- onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
- dialog.dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- });
- dialog = builder.build();
-
- View view = dialog.getView();
- etxtTime = view.findViewById(R.id.etxtTime);
- spTimeUnit = view.findViewById(R.id.spTimeUnit);
- cbShakeToReset = view.findViewById(R.id.cbShakeToReset);
- cbVibrate = view.findViewById(R.id.cbVibrate);
- chAutoEnable = view.findViewById(R.id.chAutoEnable);
+ public AlertDialog createNewDialog() {
+ View content = View.inflate(context, R.layout.time_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.set_sleeptimer_label);
+ builder.setView(content);
+ builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> dialog.dismiss());
+ builder.setPositiveButton(R.string.set_sleeptimer_label, (dialog, which) -> {
+ try {
+ savePreferences();
+ long input = SleepTimerPreferences.timerMillis();
+ onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
+ dialog.dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ });
+ dialog = builder.create();
+
+ etxtTime = content.findViewById(R.id.etxtTime);
+ spTimeUnit = content.findViewById(R.id.spTimeUnit);
+ cbShakeToReset = content.findViewById(R.id.cbShakeToReset);
+ cbVibrate = content.findViewById(R.id.cbVibrate);
+ chAutoEnable = content.findViewById(R.id.chAutoEnable);
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0);
}
@Override
@@ -109,16 +104,6 @@ public abstract class SleepTimerDialog {
return dialog;
}
- private void checkInputLength(int length) {
- if (length > 0) {
- Log.d(TAG, "Length is larger than 0, enabling confirm button");
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
- } else {
- Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
- }
- }
-
public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate);
private void savePreferences() {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index 1cf34b2b3..ef624ebe6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,37 +1,21 @@
package de.danoeh.antennapod.dialog;
-import android.content.ActivityNotFoundException;
import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Build;
import androidx.appcompat.app.AlertDialog;
-import android.util.Log;
-import android.view.View;
-
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.IntentUtils;
-
public class VariableSpeedDialog {
- private static final String TAG = VariableSpeedDialog.class.getSimpleName();
-
- private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse("market://details?id=com.falconware.prestissimo"));
-
private VariableSpeedDialog() {
}
public static void showDialog(final Context context) {
- if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
- || UserPreferences.useSonic()
+ if (UserPreferences.useSonic()
|| UserPreferences.useExoplayer()
|| Build.VERSION.SDK_INT >= 23) {
showSpeedSelectorDialog(context);
@@ -45,38 +29,17 @@ public class VariableSpeedDialog {
}
private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.no_playback_plugin_title);
- builder.content(R.string.no_playback_plugin_or_sonic_msg);
- builder.positiveText(R.string.enable_sonic);
- builder.negativeText(R.string.download_plugin_label);
- builder.neutralText(R.string.close_label);
- builder.onPositive((dialog, which) -> {
- if (Build.VERSION.SDK_INT >= 16) { // just to be safe
- UserPreferences.enableSonic();
- if(showSpeedSelector) {
- showSpeedSelectorDialog(context);
- }
- }
- });
- builder.onNegative((dialog, which) -> {
- try {
- context.startActivity(playStoreIntent);
- } catch (ActivityNotFoundException e) {
- // this is usually thrown on an emulator if the Android market is not installed
- Log.e(TAG, Log.getStackTraceString(e));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.no_playback_plugin_title);
+ builder.setMessage(R.string.no_playback_plugin_or_sonic_msg);
+ builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> {
+ UserPreferences.enableSonic();
+ if (showSpeedSelector) {
+ showSpeedSelectorDialog(context);
}
});
- builder.forceStacking(true);
- MaterialDialog dialog = builder.show();
- if (Build.VERSION.SDK_INT < 16) {
- View pos = dialog.getActionButton(DialogAction.POSITIVE);
- pos.setEnabled(false);
- }
- if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
- View pos = dialog.getActionButton(DialogAction.NEGATIVE);
- pos.setEnabled(false);
- }
+ builder.setNeutralButton(R.string.close_label, null);
+ builder.show();
}
private static void showSpeedSelectorDialog(final Context context) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index b7bfe3438..2cfe7c1e8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -46,7 +46,7 @@ public class AddFeedFragment extends Fragment {
View butOpmlImport = root.findViewById(R.id.btn_opml_import);
butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
OpmlImportFromPathActivity.class)));
-
+ root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch());
return root;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
index 5ab781fe3..47d7a0b86 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
@@ -101,7 +101,6 @@ public class CombinedSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 1917e4c75..7f70daaec 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -16,7 +16,8 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -27,6 +28,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
@@ -38,10 +41,6 @@ public class CompletedDownloadsFragment extends ListFragment {
private static final String TAG = CompletedDownloadsFragment.class.getSimpleName();
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOADLOG_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE;
-
private List<FeedItem> items = new ArrayList<>();
private DownloadedEpisodesListAdapter listAdapter;
private Disposable disposable;
@@ -56,19 +55,24 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
setListShown(false);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -79,7 +83,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
- ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) requireActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -135,14 +139,15 @@ public class CompletedDownloadsFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadItems();
+ }
private void loadItems() {
if (disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index cf9ee6c41..5467d71a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -15,6 +15,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
@@ -70,7 +71,7 @@ public class CoverFragment extends Fragment {
txtvPodcastTitle.setText(media.getFeedTitle());
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 528c50747..16337b00d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -19,7 +19,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Shows the download log
@@ -45,14 +47,12 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -77,6 +77,13 @@ public class DownloadLogFragment extends ListFragment {
adapter = new DownloadLogAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
private void onFragmentLoaded() {
@@ -133,15 +140,10 @@ public class DownloadLogFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 3fc67f795..5dbb703b7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -24,7 +24,10 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -39,7 +42,6 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -64,11 +66,6 @@ import io.reactivex.schedulers.Schedulers;
public abstract class EpisodesListFragment extends Fragment {
public static final String TAG = "EpisodesListFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
@@ -102,7 +99,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStart() {
super.onStart();
setHasOptionsMenu(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -124,7 +120,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -174,7 +169,6 @@ public abstract class EpisodesListFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -388,7 +382,14 @@ public abstract class EpisodesListFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
if (listAdapter != null) {
- listAdapter.notifyCurrentlyPlayingItemChanged(event);
+ for (int i = 0; i < listAdapter.getItemCount(); i++) {
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
}
}
@@ -414,17 +415,27 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- requireActivity().invalidateOptionsMenu();
- }
- }
+ private void updateUi() {
+ loadItems();
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ requireActivity().invalidateOptionsMenu();
}
- };
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updateUi();
+ }
void loadItems() {
if (disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index bbc33c6ca..ce2232a55 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -22,6 +22,7 @@ import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
@@ -70,9 +71,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null && controller.getMedia() != null) {
Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
- if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
+ if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -227,7 +228,7 @@ public class ExternalPlayerFragment extends Fragment {
onPositionObserverUpdate();
Glide.with(getActivity())
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 5282a6bb2..f73735658 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -54,7 +54,8 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_fav_episodes_head_label);
emptyView.setMessage(R.string.no_fav_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.LEFT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
index 6b270e220..79637d79a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -167,16 +167,8 @@ public class FeedInfoFragment extends Fragment {
txtvTitle.setText(feed.getTitle());
- String description = feed.getDescription();
- if(description != null) {
- if(Feed.TYPE_ATOM1.equals(feed.getType())) {
- HtmlToPlainText formatter = new HtmlToPlainText();
- Document feedDescription = Jsoup.parse(feed.getDescription());
- description = StringUtils.trim(formatter.getPlainText(feedDescription));
- }
- } else {
- description = "";
- }
+ String description = HtmlToPlainText.getPlainText(feed.getDescription());
+
txtvDescription.setText(description);
if (!TextUtils.isEmpty(feed.getAuthor())) {
@@ -222,6 +214,10 @@ public class FeedInfoFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (feed == null) {
+ Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show();
+ return super.onOptionsItemSelected(item);
+ }
boolean handled = false;
try {
handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 0c33dce5a..33343948f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -5,10 +5,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.ListFragment;
-import androidx.core.view.MenuItemCompat;
-import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -23,12 +19,17 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.ListFragment;
+
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -45,7 +46,12 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -59,6 +65,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Optional;
@@ -79,12 +86,6 @@ import io.reactivex.schedulers.Schedulers;
@SuppressLint("ValidFragment")
public class FeedItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.PLAYER_STATUS_UPDATE;
-
- public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
private FeedItemlistAdapter adapter;
@@ -151,7 +152,6 @@ public class FeedItemlistFragment extends ListFragment {
registerForContextMenu(getListView());
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -160,7 +160,6 @@ public class FeedItemlistFragment extends ListFragment {
public void onDestroyView() {
super.onDestroyView();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
@@ -187,11 +186,11 @@ public class FeedItemlistFragment extends ListFragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
+ menu.findItem(R.id.sort_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.filter_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.episode_actions).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.refresh_item).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
@@ -237,6 +236,10 @@ public class FeedItemlistFragment extends ListFragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!super.onOptionsItemSelected(item)) {
+ if (feed == null) {
+ Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show();
+ return true;
+ }
try {
if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) {
switch (item.getItemId()) {
@@ -345,7 +348,7 @@ public class FeedItemlistFragment extends ListFragment {
position -= l.getHeaderViewsCount();
MainActivity activity = (MainActivity) getActivity();
long[] ids = FeedItemUtil.getIds(feed.getItems());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
activity.getSupportActionBar().setTitle(feed.getTitle());
}
@@ -392,18 +395,28 @@ public class FeedItemlistFragment extends ListFragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private void updateUi() {
+ refreshHeaderView();
+ loadItems();
+ updateProgressBarVisibility();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
- refreshHeaderView();
- loadItems();
- updateProgressBarVisibility();
- }
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ if (event.contains(feed)) {
+ updateUi();
}
- };
+ }
private void updateProgressBarVisibility() {
if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) {
@@ -629,6 +642,11 @@ public class FeedItemlistFragment extends ListFragment {
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
+ if (feed != null && feed.getSortOrder() != null) {
+ List<FeedItem> feedItems = feed.getItems();
+ FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
+ feed.setItems(feedItems);
+ }
return Optional.ofNullable(feed);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
index a3c3df340..aa26610aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -100,7 +100,6 @@ public class FyydSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_fyyd_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index a97d60099..85978b761 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -144,7 +144,6 @@ public class ItemDescriptionFragment extends Fragment {
}
};
- @SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public boolean onContextItemSelected(MenuItem item) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index f17f8c645..2806f48ba 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -3,55 +3,49 @@ package de.danoeh.antennapod.fragment;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.core.content.ContextCompat;
-import androidx.core.view.GestureDetectorCompat;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-
+import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.List;
-
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -61,63 +55,45 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
-import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.view.OnSwipeGesture;
-import de.danoeh.antennapod.view.SwipeGestureDetector;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.ArrayUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
/**
* Displays information about a FeedItem and actions.
*/
-public class ItemFragment extends Fragment implements OnSwipeGesture {
+public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
-
- private static final String ARG_FEEDITEMS = "feeditems";
- private static final String ARG_FEEDITEM_POS = "feeditem_pos";
-
- private GestureDetectorCompat headerGestureDetector;
- private GestureDetectorCompat webviewGestureDetector;
+ private static final String ARG_FEEDITEM = "feeditem";
/**
* Creates a new instance of an ItemFragment
*
- * @param feeditem The ID of the FeedItem that should be displayed.
+ * @param feeditem The ID of the FeedItem to show
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long feeditem) {
- return newInstance(new long[] { feeditem }, 0);
- }
-
- /**
- * Creates a new instance of an ItemFragment
- *
- * @param feeditems The IDs of the FeedItems that belong to the same list
- * @param feedItemPos The position of the FeedItem that is currently shown
- * @return The ItemFragment instance
- */
- public static ItemFragment newInstance(long[] feeditems, int feedItemPos) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
- args.putLongArray(ARG_FEEDITEMS, feeditems);
- args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ args.putLong(ARG_FEEDITEM, feeditem);
fragment.setArguments(args);
return fragment;
}
private boolean itemsLoaded = false;
- private long[] feedItems;
- private int feedItemPos;
+ private long itemId;
private FeedItem item;
private String webviewData;
private List<Downloader> downloaderList;
@@ -131,9 +107,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
- private IconButton butAction1;
- private IconButton butAction2;
- private Menu popupMenu;
+ private Button butAction1;
+ private Button butAction2;
private Disposable disposable;
@@ -145,20 +120,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
- setHasOptionsMenu(true);
-
- feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
- feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
- headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this));
- webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) {
- // necessary for the longclick context menu to work properly
- @Override
- public boolean onDown(MotionEvent e) {
- return false;
- }
- });
+ itemId = getArguments().getLong(ARG_FEEDITEM);
}
@Override
@@ -168,11 +131,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
root = layout.findViewById(R.id.content_root);
- LinearLayout header = root.findViewById(R.id.header);
- if(feedItems.length > 0) {
- header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
- }
-
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = layout.findViewById(R.id.txtvTitle);
@@ -203,10 +161,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
- if(feedItems.length > 0) {
webvDescription.setOnLongClickListener(webViewLongClickListener);
- }
- webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event));
+
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
@@ -260,6 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
@Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ load();
+ }
+
+ @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@@ -267,15 +229,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
- load();
}
@Override
public void onResume() {
super.onResume();
- if(itemsLoaded) {
+ if (itemsLoaded) {
progbarLoading.setVisibility(View.GONE);
updateAppearance();
}
@@ -284,7 +244,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
}
@@ -300,68 +259,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
- @Override
- public boolean onSwipeLeftToRight() {
- return swipeFeedItem(-1);
- }
-
- @Override
- public boolean onSwipeRightToLeft() {
- return swipeFeedItem(+1);
- }
-
- private boolean swipeFeedItem(int position) {
- Log.d(TAG, String.format("onSwipe() shift: %s", position));
- feedItemPos = (feedItemPos + position) % feedItems.length;
- if (feedItemPos < 0) {
- feedItemPos = feedItems.length - 1;
- }
- load();
- return true;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded() || item == null) {
- return;
- }
- super.onCreateOptionsMenu(menu, inflater);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- inflater.inflate(R.menu.feeditem_options, menu);
- popupMenu = menu;
- if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- switch(menuItem.getItemId()) {
- case R.id.open_podcast:
- openPodcast();
- return true;
- default:
- return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
- }
- }
-
- private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
- @Override
- public void setItemVisibility(int id, boolean visible) {
- MenuItem item = popupMenu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
- };
-
-
private void onFragmentLoaded() {
if (webviewData != null) {
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
@@ -374,7 +271,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Log.d(TAG, "updateAppearance item is null");
return;
}
- getActivity().supportInvalidateOptionsMenu();
txtvPodcast.setText(item.getFeed().getTitle());
txtvTitle.setText(item.getTitle());
@@ -384,7 +280,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
Glide.with(getActivity())
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
@@ -405,53 +301,59 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
FeedMedia media = item.getMedia();
- String butAction1Icon = null;
- int butAction1Text = 0;
- String butAction2Icon = null;
- int butAction2Text = 0;
+ @AttrRes int butAction1Icon = 0;
+ @StringRes int butAction1Text = 0;
+ @AttrRes int butAction2Icon = 0;
+ @StringRes int butAction2Text = 0;
if (media == null) {
if (!item.isPlayed()) {
- butAction1Icon = "{fa-check 24sp}";
+ butAction1Icon = R.attr.navigation_accept;
butAction1Text = R.string.mark_read_label;
}
if (item.getLink() != null) {
- butAction2Icon = "{md-web 24sp}";
+ butAction2Icon = R.attr.location_web_site;
butAction2Text = R.string.visit_website_label;
}
} else {
- if(media.getDuration() > 0) {
+ if (media.getDuration() > 0) {
txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
}
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!media.isDownloaded()) {
- butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Icon = R.attr.action_stream;
butAction2Text = R.string.stream_label;
} else {
- butAction2Icon = "{md-delete 24sp}";
+ butAction2Icon = R.attr.content_discard;
butAction2Text = R.string.delete_label;
}
if (isDownloading) {
- butAction1Icon = "{md-cancel 24sp}";
+ butAction1Icon = R.attr.navigation_cancel;
butAction1Text = R.string.cancel_label;
} else if (media.isDownloaded()) {
- butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Icon = R.attr.av_play;
butAction1Text = R.string.play_label;
} else {
- butAction1Icon = "{md-file-download 24sp}";
+ butAction1Icon = R.attr.av_download;
butAction1Text = R.string.download_label;
}
}
- if(butAction1Icon != null && butAction1Text != 0) {
- butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
- Iconify.addIcons(butAction1);
+ if (butAction1Icon != 0 && butAction1Text != 0) {
+ butAction1.setText(butAction1Text);
+ butAction1.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction1Icon, typedValue, true);
+ butAction1.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
- if(butAction2Icon != null && butAction2Text != 0) {
- butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
- Iconify.addIcons(butAction2);
+ if (butAction2Icon != 0 && butAction2Text != 0) {
+ butAction2.setText(butAction2Text);
+ butAction2.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction2Icon, typedValue, true);
+ butAction2.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction2.setVisibility(View.VISIBLE);
} else {
butAction2.setVisibility(View.INVISIBLE);
@@ -533,8 +435,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- for(FeedItem item : event.items) {
- if(feedItems[feedItemPos] == item.getId()) {
+ for (FeedItem item : event.items) {
+ if (this.item.getId() == item.getId()) {
load();
return;
}
@@ -557,18 +459,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
-
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- load();
- }
- }
- };
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ load();
+ }
private void load() {
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
@@ -585,7 +482,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Nullable
private FeedItem loadInBackground() {
- FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
+ FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
Timeline t = new Timeline(context, feedItem);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
new file mode 100644
index 000000000..fdac649d1
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -0,0 +1,196 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.CastEnabledActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Displays information about a list of FeedItems.
+ */
+public class ItemPagerFragment extends Fragment {
+ private static final String ARG_FEEDITEMS = "feeditems";
+ private static final String ARG_FEEDITEM_POS = "feeditem_pos";
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditem The ID of the FeedItem that should be displayed.
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long feeditem) {
+ return newInstance(new long[] { feeditem }, 0);
+ }
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditems The IDs of the FeedItems that belong to the same list
+ * @param feedItemPos The position of the FeedItem that is currently shown
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long[] feeditems, int feedItemPos) {
+ if (feeditems.length <= feedItemPos) {
+ throw new IllegalArgumentException("Trying to show a feed item that is out of the list");
+ }
+ ItemPagerFragment fragment = new ItemPagerFragment();
+ Bundle args = new Bundle();
+ args.putLongArray(ARG_FEEDITEMS, feeditems);
+ args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private long[] feedItems;
+ private int feedItemPos;
+ private FeedItem item;
+ private Disposable disposable;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
+ feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View layout = inflater.inflate(R.layout.feeditem_pager_fragment, container, false);
+
+ ViewPager pager = layout.findViewById(R.id.pager);
+ // FragmentStatePagerAdapter documentation:
+ // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set.
+ // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc,
+ // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages.
+ int newId = ViewCompat.generateViewId();
+ pager.setId(newId);
+ pager.setAdapter(new ItemPagerAdapter());
+ pager.setCurrentItem(feedItemPos);
+ loadItem(feedItems[feedItemPos]);
+ pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ loadItem(feedItems[position]);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ return layout;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void loadItem(long itemId) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ disposable = Observable.fromCallable(() -> DBReader.getFeedItem(itemId))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ item = result;
+ getActivity().invalidateOptionsMenu();
+ }, Throwable::printStackTrace);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (!isAdded() || item == null) {
+ return;
+ }
+ super.onCreateOptionsMenu(menu, inflater);
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
+ inflater.inflate(R.menu.feeditem_options, menu);
+
+ FeedItemMenuHandler.MenuInterface popupMenuInterface = (id, visible) -> {
+ MenuItem item = menu.findItem(id);
+ if (item != null) {
+ item.setVisible(visible);
+ }
+ };
+
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case R.id.open_podcast:
+ openPodcast();
+ return true;
+ default:
+ return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
+ }
+ }
+
+ private void openPodcast() {
+ Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
+ ((MainActivity) getActivity()).loadChildFragment(fragment);
+ }
+
+ private class ItemPagerAdapter extends FragmentStatePagerAdapter {
+
+ ItemPagerAdapter() {
+ super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ return ItemFragment.newInstance(feedItems[position]);
+ }
+
+ @Override
+ public int getCount() {
+ return feedItems.length;
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index 673b58901..4b1544e47 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.core.view.MenuItemCompat;
import androidx.appcompat.widget.SearchView;
@@ -18,8 +19,6 @@ import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
import de.danoeh.antennapod.discovery.ItunesTopListLoader;
import de.danoeh.antennapod.discovery.PodcastSearchResult;
@@ -120,9 +119,9 @@ public class ItunesSearchFragment extends Fragment {
progressBar.setVisibility(View.GONE);
gridView.setVisibility(View.VISIBLE);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
});
@@ -151,7 +150,6 @@ public class ItunesSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_itunes_label));
sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index 5dbb84bc7..2bfdd040b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -54,7 +54,8 @@ public class NewEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_new_episodes_head_label);
emptyView.setMessage(R.string.no_new_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index f9fca87fc..a97e3dae8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -12,6 +12,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -24,7 +26,6 @@ import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -39,12 +40,8 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class PlaybackHistoryFragment extends ListFragment {
-
public static final String TAG = "PlaybackHistoryFragment";
- private static final int EVENTS = EventDistributor.PLAYBACK_HISTORY_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private List<FeedItem> playbackHistory;
private FeedItemlistAdapter adapter;
private List<Downloader> downloaderList;
@@ -83,7 +80,6 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -92,7 +88,6 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -111,7 +106,7 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(playbackHistory);
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -166,16 +161,17 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onHistoryUpdated(PlaybackHistoryEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- };
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
private void onFragmentLoaded() {
adapter.notifyDataSetChanged();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index b550669f3..b36ce6145 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,15 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.fragment.app.Fragment;
-import androidx.core.view.MenuItemCompat;
-import androidx.appcompat.app.AlertDialog;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.appcompat.widget.SearchView;
-import androidx.recyclerview.widget.SimpleItemAnimator;
-import androidx.recyclerview.widget.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -24,9 +15,18 @@ import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.google.android.material.snackbar.Snackbar;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -40,11 +40,13 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -54,7 +56,6 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.SortOrder;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
@@ -73,13 +74,8 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM
* Shows all items in the queue
*/
public class QueueFragment extends Fragment {
-
public static final String TAG = "QueueFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
@@ -117,7 +113,6 @@ public class QueueFragment extends Fragment {
onFragmentLoaded(true);
}
loadItems(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
}
@@ -130,9 +125,8 @@ public class QueueFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
@@ -218,7 +212,31 @@ public class QueueFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
if (recyclerAdapter != null) {
- recyclerAdapter.notifyCurrentlyPlayingItemChanged(event);
+ for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
+ QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ // Sent when playback position is reset
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
}
}
@@ -270,7 +288,6 @@ public class QueueFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -331,7 +348,7 @@ public class QueueFragment extends Fragment {
conDialog.createNewDialog().show();
return true;
case R.id.episode_actions:
- ((MainActivity) requireActivity()) .loadChildFragment(
+ ((MainActivity) requireActivity()).loadChildFragment(
EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
return true;
case R.id.queue_sort_episode_title_asc:
@@ -373,7 +390,7 @@ public class QueueFragment extends Fragment {
UserPreferences.setQueueKeepSorted(keepSortedNew);
if (keepSortedNew) {
SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
if (recyclerAdapter != null) {
recyclerAdapter.setLocked(true);
}
@@ -439,7 +456,7 @@ public class QueueFragment extends Fragment {
*/
private void setSortOrder(SortOrder sortOrder) {
UserPreferences.setQueueKeepSortedOrder(sortOrder);
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
}
@Override
@@ -492,7 +509,8 @@ public class QueueFragment extends Fragment {
registerForContextMenu(recyclerView);
itemTouchHelper = new ItemTouchHelper(
- new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
+ new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+ ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
// Position tracking whilst dragging
int dragFrom = -1;
@@ -634,14 +652,16 @@ public class QueueFragment extends Fragment {
private void refreshInfoBar() {
String info = queue.size() + getString(R.string.episodes_suffix);
- if(queue.size() > 0) {
+ if (queue.size() > 0) {
long timeLeft = 0;
- for(FeedItem item : queue) {
- float playbackSpeed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(item.getMedia());
- if(item.getMedia() != null) {
- timeLeft +=
- (long) ((item.getMedia().getDuration() - item.getMedia().getPosition())
- / playbackSpeed);
+ for (FeedItem item : queue) {
+ float playbackSpeed = 1;
+ if (UserPreferences.timeRespectsSpeed()) {
+ playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia());
+ }
+ if (item.getMedia() != null) {
+ long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition();
+ timeLeft += itemTimeLeft / playbackSpeed;
}
}
info += " • ";
@@ -711,19 +731,6 @@ public class QueueFragment extends Fragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- Log.d(TAG, "arg: " + arg);
- loadItems(false);
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- }
- };
-
private void loadItems(final boolean restoreScrollPosition) {
Log.d(TAG, "loadItems()");
if(disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
index 226209740..7e217cde4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -11,7 +12,6 @@ import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -110,9 +110,9 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.
Log.e(TAG, Log.getStackTraceString(error));
view.setAlpha(1f);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 528fa7c32..7e8823c27 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -25,6 +25,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.view.EmptyViewHandler;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Displays all running downloads and provides actions to cancel them
@@ -75,7 +76,7 @@ public class RunningDownloadsFragment extends ListFragment {
setListAdapter(null);
}
- @Subscribe(sticky = true)
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 7f3c60e5d..d124d6aa2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -20,7 +20,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SearchlistAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@@ -76,7 +78,6 @@ public class SearchFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
search();
}
@@ -86,7 +87,6 @@ public class SearchFragment extends ListFragment {
if(disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
@@ -103,6 +103,13 @@ public class SearchFragment extends ListFragment {
searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
setListAdapter(searchAdapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ EventBus.getDefault().unregister(this);
}
@Override
@@ -145,15 +152,10 @@ public class SearchFragment extends ListFragment {
MenuItemCompat.setActionView(item, sv);
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.DOWNLOAD_HANDLED)) != 0) {
- search();
- }
- }
- };
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ search();
+ }
private void onSearchResults(List<SearchResult> results) {
searchResults = results;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 253c99c4e..af3649ed0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -28,7 +28,8 @@ import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
@@ -41,6 +42,7 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -55,9 +57,6 @@ import org.greenrobot.eventbus.ThreadMode;
public class SubscriptionFragment extends Fragment {
public static final String TAG = "SubscriptionFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String PREFS = "SubscriptionFragment";
private static final String PREF_NUM_COLUMNS = "columns";
@@ -65,6 +64,7 @@ public class SubscriptionFragment extends Fragment {
private DBReader.NavDrawerData navDrawerData;
private SubscriptionsAdapter subscriptionAdapter;
private FloatingActionButton subscriptionAddButton;
+ private EmptyViewHandler emptyView;
private int mPosition = -1;
private boolean isUpdatingFeeds = false;
@@ -138,12 +138,22 @@ public class SubscriptionFragment extends Fragment {
getActivity().invalidateOptionsMenu();
}
+ private void setupEmptyView() {
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.setIcon(R.attr.ic_folder);
+ emptyView.setTitle(R.string.no_subscriptions_head_label);
+ emptyView.setMessage(R.string.no_subscriptions_label);
+ emptyView.attachToListView(subscriptionGridLayout);
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+
subscriptionAdapter = new SubscriptionsAdapter((MainActivity) getActivity(), itemAccess);
subscriptionGridLayout.setAdapter(subscriptionAdapter);
subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter);
+ setupEmptyView();
subscriptionAddButton.setOnClickListener(view -> {
if (getActivity() instanceof MainActivity) {
@@ -159,7 +169,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadSubscriptions();
}
@@ -167,7 +176,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
disposable.dispose();
@@ -178,12 +186,14 @@ public class SubscriptionFragment extends Fragment {
if(disposable != null) {
disposable.dispose();
}
+ emptyView.hide();
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
subscriptionAdapter.notifyDataSetChanged();
+ emptyView.updateVisibility();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@@ -294,15 +304,15 @@ public class SubscriptionFragment extends Fragment {
dialog.createNewDialog().show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadSubscriptions();
- }
- }
- };
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadSubscriptions();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadSubscriptions();
+ }
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 1d6debbfe..4baa74df6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -54,7 +54,6 @@ public abstract class PodcastListFragment extends Fragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
index 60fc1f446..ffe69aa9a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
@@ -50,7 +50,6 @@ public class SearchListFragment extends PodcastListFragment {
// parent already inflated menu
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setQuery(query, false);
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index b5a95bc33..cde8fb3df 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -40,7 +40,6 @@ public class TagListFragment extends ListFragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
new file mode 100644
index 000000000..62a5eb306
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
@@ -0,0 +1,65 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutDevelopersFragment extends ListFragment {
+ private Disposable developersLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ developersLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> developers = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("developers.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ developers.add(new SimpleIconListAdapter.ListItem(info[0], info[2],
+ "https://avatars2.githubusercontent.com/u/" + info[1] + "?s=60&v=4"));
+ }
+ emitter.onSuccess(developers);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading developers", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (developersLoader != null) {
+ developersLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.developers);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
new file mode 100644
index 000000000..0fa7bd4bb
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
@@ -0,0 +1,56 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import androidx.preference.PreferenceFragmentCompat;
+import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.util.IntentUtils;
+
+public class AboutFragment extends PreferenceFragmentCompat {
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_about);
+
+ findPreference("about_version").setSummary(String.format(
+ "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH));
+ findPreference("about_version").setOnPreferenceClickListener((preference) -> {
+ ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title),
+ findPreference("about_version").getSummary());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(getView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ return true;
+ });
+ findPreference("about_developers").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment())
+ .addToBackStack(getString(R.string.developers)).commit();
+ return true;
+ });
+ findPreference("about_translators").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ findPreference("about_privacy_policy").setOnPreferenceClickListener((preference) -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/privacy.html");
+ return true;
+ });
+ findPreference("about_licenses").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.about_pref);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
new file mode 100644
index 000000000..536d11e01
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
@@ -0,0 +1,126 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutLicensesFragment extends ListFragment {
+ private Disposable licensesLoader;
+ private final ArrayList<LicenseItem> licenses = new ArrayList<>();
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+
+ licensesLoader = Single.create((SingleOnSubscribe<ArrayList<LicenseItem>>) emitter -> {
+ licenses.clear();
+ InputStream stream = getContext().getAssets().open("licenses.xml");
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ NodeList libraryList = docBuilder.parse(stream).getElementsByTagName("library");
+ for (int i = 0; i < libraryList.getLength(); i++) {
+ NamedNodeMap lib = libraryList.item(i).getAttributes();
+ licenses.add(new LicenseItem(
+ lib.getNamedItem("name").getTextContent(),
+ String.format("By %s, %s license",
+ lib.getNamedItem("author").getTextContent(),
+ lib.getNamedItem("license").getTextContent()),
+ null,
+ lib.getNamedItem("website").getTextContent(),
+ lib.getNamedItem("licenseText").getTextContent()));
+ }
+ emitter.onSuccess(licenses);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<LicenseItem>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading licenses", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ private static class LicenseItem extends SimpleIconListAdapter.ListItem {
+ final String licenseUrl;
+ final String licenseTextFile;
+
+ LicenseItem(String title, String subtitle, String imageUrl, String licenseUrl, String licenseTextFile) {
+ super(title, subtitle, imageUrl);
+ this.licenseUrl = licenseUrl;
+ this.licenseTextFile = licenseTextFile;
+ }
+ }
+
+ @Override
+ public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ LicenseItem item = licenses.get(position);
+ CharSequence[] items = {"View website", "View license"};
+ new AlertDialog.Builder(getContext())
+ .setTitle(item.title)
+ .setItems(items, (dialog, which) -> {
+ if (which == 0) {
+ IntentUtils.openInBrowser(getContext(), item.licenseUrl);
+ } else if (which == 1) {
+ showLicenseText(item.licenseTextFile);
+ }
+ }).show();
+ }
+
+ private void showLicenseText(String licenseTextFile) {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open(licenseTextFile)));
+ StringBuilder licenseText = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ licenseText.append(line).append("\n");
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setMessage(licenseText)
+ .show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (licensesLoader != null) {
+ licensesLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.licenses);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
new file mode 100644
index 000000000..914dbb9a2
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutTranslatorsFragment extends ListFragment {
+ private Disposable translatorsLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("translators.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ translators.add(new SimpleIconListAdapter.ListItem(info[0], info[1], null));
+ }
+ emitter.onSuccess(translators);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)),
+ error -> Toast.makeText(getContext(), "Error while loading translators", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (translatorsLoader != null) {
+ translatorsLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.translators);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index 121b7fef8..fa17fed0a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -50,7 +50,7 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
@Override
public void onStart() {
super.onStart();
- ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label);
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_automatic_download_title);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index 00e69f1db..5fd38d663 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -7,7 +7,6 @@ import androidx.preference.PreferenceFragmentCompat;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -63,7 +62,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
findPreference(PREF_ABOUT).setOnPreferenceClickListener(
preference -> {
- startActivity(new Intent(getActivity(), AboutActivity.class));
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment())
+ .addToBackStack(getString(R.string.about_pref)).commit();
return true;
}
);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
index 440660942..1ca8f63aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -5,11 +5,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import android.text.format.DateFormat;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -65,7 +62,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
ProxyDialog dialog = new ProxyDialog(getActivity());
- dialog.createDialog().show();
+ dialog.show();
return true;
});
}
@@ -107,13 +104,10 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = getActivity();
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
- builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
- builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_Interval, (dialog, which) -> {
AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
@@ -133,8 +127,9 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
builder1.show();
});
- builder.onNegative((dialog, which) -> {
- int hourOfDay = 7, minute = 0;
+ builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, (dialog, which) -> {
+ int hourOfDay = 7;
+ int minute = 0;
int[] updateTime = UserPreferences.getUpdateTimeOfDay();
if (updateTime.length == 2) {
hourOfDay = updateTime[0];
@@ -151,7 +146,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
timePickerDialog.show();
});
- builder.onNeutral((dialog, which) -> {
+ builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Disable, (dialog, which) -> {
UserPreferences.disableAutoUpdate();
setUpdateIntervalText();
});
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
index 1795dfc29..b82bba89b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -4,8 +4,15 @@ import android.app.Activity;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.collection.ArrayMap;
import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+
+import java.util.Map;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -34,12 +41,6 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref);
}
- @Override
- public void onResume() {
- super.onResume();
- checkSonicItemVisibility();
- }
-
private void setupPlaybackScreen() {
final Activity activity = getActivity();
@@ -63,6 +64,43 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
}
+
+ buildEnqueueLocationPreference();
+ }
+
+ private void buildEnqueueLocationPreference() {
+ final Resources res = requireActivity().getResources();
+ final Map<String, String> options = new ArrayMap<>();
+ {
+ String[] keys = res.getStringArray(R.array.enqueue_location_values);
+ String[] values = res.getStringArray(R.array.enqueue_location_options);
+ for (int i = 0; i < keys.length; i++) {
+ options.put(keys[i], values[i]);
+ }
+ }
+
+ ListPreference pref = requirePreference(UserPreferences.PREF_ENQUEUE_LOCATION);
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(pref.getValue())));
+
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ if (!(newValue instanceof String)) {
+ return false;
+ }
+ String newValStr = (String)newValue;
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(newValStr)));
+ return true;
+ });
+ }
+
+ @NonNull
+ private <T extends Preference> T requirePreference(@NonNull CharSequence key) {
+ // Possibly put it to a common method in abstract base class
+ T result = findPreference(key);
+ if (result == null) {
+ throw new IllegalArgumentException("Preference with key '" + key + "' is not found");
+
+ }
+ return result;
}
private void buildSmartMarkAsPlayedPreference() {
@@ -86,14 +124,4 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
}
pref.setEntries(entries);
}
-
-
-
- private void checkSonicItemVisibility() {
- if (Build.VERSION.SDK_INT < 16) {
- ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER);
- p.setEntries(R.array.media_player_options_no_sonic);
- p.setEntryValues(R.array.media_player_values_no_sonic);
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 60eaf14a5..2f3459beb 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -13,6 +13,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -200,6 +201,10 @@ public class FeedItemMenuHandler {
break;
case R.id.reset_position:
selectedItem.getMedia().setPosition(0);
+ if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == selectedItem.getMedia().getId()) {
+ PlaybackPreferences.writeNoMediaPlaying();
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
+ }
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, true);
break;
case R.id.activate_auto_download:
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 5d012168e..e32deba27 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -2,12 +2,15 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.DialogInterface;
-import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import androidx.annotation.NonNull;
+
+import org.apache.commons.lang3.StringUtils;
+
import java.util.Set;
import de.danoeh.antennapod.R;
@@ -18,7 +21,9 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.SortOrder;
import de.danoeh.antennapod.dialog.FilterDialog;
+import de.danoeh.antennapod.dialog.IntraFeedSortDialog;
/**
* Handles interactions with the FeedItemMenu.
@@ -42,6 +47,10 @@ public class FeedMenuHandler {
Log.d(TAG, "Preparing options menu");
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
+ if (StringUtils.isBlank(selectedFeed.getLink())) {
+ menu.findItem(R.id.visit_website_item).setVisible(false);
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ }
return true;
}
@@ -60,6 +69,9 @@ public class FeedMenuHandler {
case R.id.refresh_complete_item:
DBTasks.forceRefreshCompleteFeed(context, selectedFeed);
break;
+ case R.id.sort_items:
+ showSortDialog(context, selectedFeed);
+ break;
case R.id.filter_items:
showFilterDialog(context, selectedFeed);
break;
@@ -103,4 +115,17 @@ public class FeedMenuHandler {
filterDialog.openDialog();
}
+
+
+ private static void showSortDialog(Context context, Feed selectedFeed) {
+ IntraFeedSortDialog sortDialog = new IntraFeedSortDialog(context, selectedFeed.getSortOrder()) {
+ @Override
+ protected void updateSort(@NonNull SortOrder sortOrder) {
+ selectedFeed.setSortOrder(sortOrder);
+ DBWriter.setFeedItemSortOrder(selectedFeed.getId(), sortOrder);
+ }
+ };
+ sortDialog.openDialog();
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index 4a57726b1..64eb72ee3 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -2,14 +2,8 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Build;
-import androidx.appcompat.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
-import android.widget.EditText;
-
-import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
@@ -17,18 +11,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
*/
public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuItemUtils {
- public static void adjustTextColor(Context context, SearchView sv) {
- if(Build.VERSION.SDK_INT < 14) {
- EditText searchEditText = sv.findViewById(R.id.search_src_text);
- if (UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark
- || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
- searchEditText.setTextColor(Color.WHITE);
- } else {
- searchEditText.setTextColor(Color.BLACK);
- }
- }
- }
-
@SuppressWarnings("ResourceType")
public static void refreshLockItem(Context context, Menu menu) {
final MenuItem queueLock = menu.findItem(de.danoeh.antennapod.R.id.queue_lock);
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 6392d0535..4686f0303 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -7,6 +7,7 @@ import android.preference.PreferenceManager;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
@@ -23,7 +24,6 @@ public class PreferenceUpgrader {
int newVersion = BuildConfig.VERSION_CODE;
if (oldVersion != newVersion) {
- NotificationUtils.createChannels(context);
AutoUpdateManager.restartUpdateAlarm();
upgrade(oldVersion);
@@ -32,6 +32,9 @@ public class PreferenceUpgrader {
}
private static void upgrade(int oldVersion) {
+ if (oldVersion == -1) {
+ return;
+ }
if (oldVersion < 1070196) {
// migrate episode cleanup value (unit changed from days to hours)
int oldValueInDays = UserPreferences.getEpisodeCleanupValue();
@@ -72,6 +75,14 @@ public class PreferenceUpgrader {
}
UserPreferences.setQueueLocked(false);
+ prefs.edit().putBoolean(UserPreferences.PREF_STREAM_OVER_DOWNLOAD, false).apply();
+
+ if (!prefs.contains(UserPreferences.PREF_ENQUEUE_LOCATION)) {
+ final String keyOldPrefEnqueueFront = "prefQueueAddToFront";
+ boolean enqueueAtFront = prefs.getBoolean(keyOldPrefEnqueueFront, false);
+ EnqueueLocation enqueueLocation = enqueueAtFront ? EnqueueLocation.FRONT : EnqueueLocation.BACK;
+ UserPreferences.setEnqueueLocation(enqueueLocation);
+ }
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
index e79389fb3..4c116fa2d 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
@@ -88,7 +88,7 @@ public class AspectRatioVideoView extends VideoView {
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
- /**
+ /*
* If this isn't set the video is stretched across the
* SurfaceHolders display surface (i.e. the SurfaceHolder
* as the same size and the video is drawn to fit this
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
index 1516c4eb6..a2d8ec091 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
import androidx.annotation.AttrRes;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -9,7 +12,6 @@ import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -17,8 +19,9 @@ import de.danoeh.antennapod.R;
public class EmptyViewHandler {
private boolean layoutAdded = false;
- private RecyclerView recyclerView;
- private RecyclerView.Adapter adapter;
+ private View list;
+ private ListAdapter listAdapter;
+ private RecyclerView.Adapter recyclerAdapter;
private final Context context;
private final View emptyView;
@@ -54,44 +57,60 @@ public class EmptyViewHandler {
emptyView.setVisibility(View.GONE);
}
- public void attachToListView(ListView listView) {
+ public void attachToListView(AbsListView listView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(listView);
layoutAdded = true;
- ((ViewGroup) listView.getParent()).addView(emptyView);
+ this.list = listView;
listView.setEmptyView(emptyView);
+ updateAdapter(listView.getAdapter());
}
public void attachToRecyclerView(RecyclerView recyclerView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(recyclerView);
layoutAdded = true;
- this.recyclerView = recyclerView;
- ViewGroup parent = ((ViewGroup) recyclerView.getParent());
- parent.addView(emptyView);
+ this.list = recyclerView;
updateAdapter(recyclerView.getAdapter());
+ }
+ private void addToParentView(View view) {
+ ViewGroup parent = ((ViewGroup) view.getParent());
+ parent.addView(emptyView);
if (parent instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ (RelativeLayout.LayoutParams) emptyView.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
emptyView.setLayoutParams(layoutParams);
}
}
public void updateAdapter(RecyclerView.Adapter adapter) {
- if (this.adapter != null) {
- this.adapter.unregisterAdapterDataObserver(adapterObserver);
+ if (this.recyclerAdapter != null) {
+ this.recyclerAdapter.unregisterAdapterDataObserver(adapterObserver);
}
- this.adapter = adapter;
+ this.recyclerAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(adapterObserver);
}
updateVisibility();
}
+ private void updateAdapter(ListAdapter adapter) {
+ if (this.listAdapter != null) {
+ this.listAdapter.unregisterDataSetObserver(listAdapterObserver);
+ }
+ this.listAdapter = adapter;
+ if (adapter != null) {
+ adapter.registerDataSetObserver(listAdapterObserver);
+ }
+ updateVisibility();
+ }
+
private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
@Override
public void anythingChanged() {
@@ -99,14 +118,22 @@ public class EmptyViewHandler {
}
};
- private void updateVisibility() {
+ private final DataSetObserver listAdapterObserver = new DataSetObserver() {
+ public void onChanged() {
+ updateVisibility();
+ }
+ };
+
+ public void updateVisibility() {
boolean empty;
- if (adapter == null) {
- empty = true;
+ if (recyclerAdapter != null) {
+ empty = recyclerAdapter.getItemCount() == 0;
+ } else if (listAdapter != null) {
+ empty = listAdapter.isEmpty();
} else {
- empty = adapter.getItemCount() == 0;
+ empty = true;
}
- recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
+ list.setVisibility(empty ? View.GONE : View.VISIBLE);
emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
}
}
diff --git a/app/src/main/play/listings/de-DE/full-description.txt b/app/src/main/play/listings/de-DE/full-description.txt
index c49417c3e..5365983bb 100644
--- a/app/src/main/play/listings/de-DE/full-description.txt
+++ b/app/src/main/play/listings/de-DE/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht, angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs. Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch die Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
-Aber am wichtigsten: Downloade, streame oder füge Episoden zur Abspielliste hinzu und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
-
-AntennaPod ist, von Podcast-Enthusiasten gemacht, frei im Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
-
-<b>Alle Funktionen:</b><br>
-IMPORTIERE, ORGANISIERE UND HÖRE<br>
-&#8226; Importiere oder füge Feeds über das iTunes und gPodder.net Verzeichnis, OMPL Dateien und RSS oder Atom Links hinzu.
-&#8226; Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente<br>
-&#8226; Genieße das Zuhören auf deine Art mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln (MP3, OGG, Podlove) und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern und Geschwindigkeit verlangsamen)
-&#8226; Greife auf Passwort-geschützte Feeds und Episoden zu<br>
-&#8226; Nutze den Vorteil von Paged Feeds (http://www.podlove.org/paged-feeds)
-
-ORDNE, TEILE & GENIEßE
-&#8226; Bleib an den Besten der Besten dran, indem Du Episoden als Favoriten markierst<br>
-&#8226; Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
-&#8226; Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
-
-STEUERE DAS SYSTEM<br>
-&#8226; Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest<br>
-&#8226; Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes<br>
-&#8226; Benutze AntennaPod in deiner Sprache (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Passe das Aussehen mit dem hellen oder dunklen Theme an<br>
-&#8226; Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
-
-<b>Tritt der AntennaPod-Community bei!</b><br>
+AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht. Angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs.
+Downloade, streame oder sortiere Episoden in der Abspielliste und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
+Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch leistungsfähige Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
+
+AntennaPod ist von Podcast-Enthusiasten gemacht und frei im wahrsten Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
+
+<b>Importiere, organisiere und spiele ab</b>
+• Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente
+• Füge Feeds über iTunes und gPodder.net oder über OMPL Dateien und RSS oder Atom Links hinzu.
+• Genieße das Zuhören genau wie du willst - mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern)
+• Greife auf Passwort-geschützte Feeds und Episoden zu
+
+<b>Behalte den Überblick</b>
+• Behalte den Überblick über die Besten der Besten, indem du Episoden als Favoriten markierst
+• Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
+• Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
+
+<b>Behalte die Kontrolle</b>
+• Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest
+• Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes
+• Passe das Aussehen mit dem hellen oder dunklen Theme an deine Wünsche an
+• Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
+
+<b>Tritt der AntennaPod-Community bei!</b>
AntennaPod wird aktiv von Freiwilligen weiterentwickelt. Auch du kannst bei der Entwicklung mit Quellcode oder Kommentaren mitwirken!
-Wir verwenden GitHub für Funktionswünsche (Feature Requests), Fehlerberichte (Bug Reports) und zum Beisteuern von Code (Code Contributions).
+Wir verwenden GitHub für Funktionswünsche, Fehlerberichte und zur Beteiligung an der Entwicklung:
https://www.github.com/AntennaPod/AntennaPod
-Teile deine Idee und Lieblingspodcastmomente und äußere Deine Dankbarkeit gegenüber allen Freiwilligen in unserer Google Group:<br>
-https://groups.google.com/forum/#!forum/antennapod (Englisch)
-
-Du hast eine Frage oder willst uns Feedback geben?
-https://twitter.com/@AntennaPod
-
-Mit Transifex kannst du uns beim Übersetzen helfen:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Probiere unser Beta-Testing-Programm aus, um die neusten Funktionen als Erster zu erhalten:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Mit Transifex kannst du uns beim Übersetzen helfen:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/en-US/full-description.txt b/app/src/main/play/listings/en-US/full-description.txt
index c562387af..e0042e07f 100644
--- a/app/src/main/play/listings/en-US/full-description.txt
+++ b/app/src/main/play/listings/en-US/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).<br>
-But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
-
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-
-<b>All features:</b><br>
-IMPORT, ORGANIZE AND PLAY<br>
-&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
-&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
-&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
-&#8226; Access password-protected feeds and episodes<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
-
-KEEP TRACK, SHARE & APPRECIATE<br>
-&#8226; Keep track of the best of the best by marking episodes as favourites<br>
-&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
-&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
-
-CONTROL THE SYSTEM<br>
-&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
-&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
-&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adapt to your environment using the light and dark theme<br>
-&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
-
-<b>Join the AntennaPod community!</b><br>
+AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs.
+Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
+Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).
+
+Made by podcast-enthusiasts, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+
+<b>Import, organize and play</b>
+• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls
+• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links
+• Enjoy listening your way with adjustable playback speed, chapter support, remembered playback position and an advanced sleep timer (shake to reset, lower volume)
+• Access password-protected feeds and episodes
+
+<b>Keep track, share & appreciate</b>
+• Keep track of the best of the best by marking episodes as favourites
+• Find that one episode through the playback history or by searching titles and shownotes
+• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export
+
+<b>Control the system</b>
+• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals
+• Manage storage by setting the amount of cached episodes, smart deletion and selecting your preferred location
+• Adapt to your environment using the light and dark theme
+• Back-up your subscriptions with the gPodder.net integration and OPML export
+
+<b>Join the AntennaPod community!</b>
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+GitHub is the place to go for feature requests, bug reports and code contributions:
https://www.github.com/AntennaPod/AntennaPod
-Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Have a question or want to give us feedback?
-https://twitter.com/@AntennaPod
-
-Transifex is the place to help with translations:<br>
+Transifex is the place to help with translations:
https://www.transifex.com/antennapod/antennapod
-
-Check out our Beta Testing programme to get the latest features first:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
diff --git a/app/src/main/play/listings/es-ES/full-description.txt b/app/src/main/play/listings/es-ES/full-description.txt
index 8c5cba745..b98757cee 100644
--- a/app/src/main/play/listings/es-ES/full-description.txt
+++ b/app/src/main/play/listings/es-ES/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod es un reproductor y administrador de pódcast que te da acceso instantáneo a millones de pódcast gratuitos y de pago, desde editores independientes a grandes editoriales como la BBC, NPR y CNN. Añade, importa y exporta tus fuentes sin complicaciones usando la base de datos de pódcast de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, energía de la batería y uso de datos móviles con potentes controles de automatización para descargar episodios (especifica horarios, intervalos y redes wifi) y elimina episodios (según tus favoritos y la configuración de retardo).<br>
-Y lo que es más importante: Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+AntennaPod is un gestor y reproductor de podcast que te da acceso instantáneo a millones de podcast gratuitos y de pago, desde podcasters independientes a grandes estaciones como la BBC, NPR y CNN. Agrega, importa y exporta las fuentes de manera sencilla usando el listado de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, batería y datos con los controles de descarga y de borrado de episodios (basado en favoritos y ajustes de tiempo).
+Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+Ahorra esuferzo, batería y consumo de datos con los potentes controles de automatización para descarga (especifica a qué horas y desde qué redes Wi-Fi) y borrado de episodios antiguos (basado en tus preferencias).
Creado por entusiastas del pódcast, AntennaPod es libre en todos los sentidos: código abierto, gratuito y sin publicidad.
-<b>Todas las características:</b><br>
-IMPORTAR, ORGANIZAR Y REPRODUCIR<br>
-&#8226; Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom<br>
-&#8226; Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth<br>
-&#8226; Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)<br>
-&#8226; Accede a fuentes y episodios protegidos con contraseña<br>
-&#8226; Aprovecha las fuentes paginadas (www.podlove.org/paged-feeds)
+<b>Importar, organizar y reproducir</b>
+• Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth
+• Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom
+• Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)
+• Accede a fuentes y episodios protegidos con contraseña
-MANTÉN UN SEGUIMIENTO, COMPARTE Y APRECIA
-&#8226; Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos<br>
-&#8226; Encuentra ese episodio a través del historial de reproducción o por búsqueda (títulos y notas de episodios)<br>
-&#8226; Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML<br>
+<b>Seguir, valorar y compartir </b>
+• Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos
+• Busca ese episodio en el historial o busca por título y notas
+• Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML
-CONTROLA EL SISTEMA<br>
-&#8226; Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos<br>
-&#8226; Administra el almacenamiento configurando la cantidad de episodios almacenados, el borrado inteligente (según tus favoritos y el estado de reproducción) y selecciona tu ubicación preferida<br>
-&#8226; Usa AntennaPod en tu idioma (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adáptate a tu entorno usando el tema claro u oscuro<br>
-&#8226; Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
+<b>Control del sistema</b>
+• Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos
+• Gestiona el almacenamiento configurando la cantidad de episodios en caché, configura borrado inteligente y eligiendo tu ubicación favorita
+• Adáptate a tu entorno usando el tema claro u oscuro
+• Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
-<b>¡Únete a la comunidad AntennaPod!</b><br>
+<b>¡Únete a la comunidad AntennaPod!</b>
AntennaPod está en continuo desarrollo por voluntarios. ¡Tú también puedes contribuir, con tu código o con tus comentarios!
-GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código<br>
+GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código:
https://www.github.com/AntennaPod/AntennaPod
-Nuestro Grupo de Google es el sitio para compartir tus ideas, momentos favoritos de tus pódcast y tu gratitud a los voluntarios:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-¿Tienes una pregunta o quieres darnos tu opinión?
-https://twitter.com/@AntennaPod
-
-Transifex es el sitio para ayudar con las traducciones:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Echa un vistazo a nuestro programa de pruebas Beta y ser el primero en usar las nuevas características:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Para ayudar con las traducciones en Transifex:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/full-description.txt b/app/src/main/play/listings/fr-FR/full-description.txt
index 5c69f3892..fe8734be4 100644
--- a/app/src/main/play/listings/fr-FR/full-description.txt
+++ b/app/src/main/play/listings/fr-FR/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit aussi bien par des podcasters indépendants que des éditeurs professionnels comme la BBC, NPR ou CNN. Ajoutez, importez et exportez leurs flux facilement à partir d'ITunes, de fichiers OPML ou simplement à partir de liens RSS. Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi, etc...) et de la suppression des épisodes (selon vos critères)<br>
-Avant tout : téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous voulez grâce au réglage de vitesse de lecture, au support des chapitres et au minuteur d'arrêt.
-
-Conçu par des fans de podcast, AntennaPod est gratuit dans tous les sens du terme : open source, gratuit et sans publicité.
-
-<b>Caractéristiques complètes :</b><br>
-IMPORTER, GÉRER ET ÉCOUTER<br>
-&#8226; Ajouter et importer à partir d'iTunes, gPodder.net, fichiers OPML, liens RSS ou Atom<br>
-&#8226; Gérez la lecture de n'importe où : widget sur l'écran d'accueil, notification système, commande casque ou Bluetooth<br>
-&#8226; Écoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres (MP3, VorbisComment et Podlove), à l'enregistrement de la position de lecture et à une mise en veille automatique puissante (secouez pour prolonger le minuteur, baisser le volume et ralentir la lecture)<br>
-&#8226; Accès aux flux et épisodes protégés par mot de passe<br>
-&#8226; Tirez profit des flux paginés (www.podlove.org/paged-feeds)
-
-SUIVEZ, PARTAGEZ & PROFITEZ<br>
-&#8226; Marquer les meilleurs épisodes en tant que favoris<br>
-&#8226; Retrouvez un épisode à partir de l'historique de lecture ou en recherchant parmi les titres et commentaires des épisodes précédents<br>
-&#8226; Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML<br>
-
-CONTRÔLER LE SYSTÈME<br>
-&#8226; Prenez le contrôle en automatisant vos téléchargements : choix des flux, restriction de la connexion mobile, sélection du réseau WIFI à utiliser, uniquement durant la recharge et spécifiez la fréquence de mise à jour vous-même<br>
-&#8226; Gérez l'espace de stockage en paramétrant le nombre d'épisodes à garder, leur suppression automatique (en fonction de vos favoris et de leur statut) et leur emplacement sur le disque<br>
-&#8226; Utilisez AntennaPod dans votre langue (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Choix d'un thème clair ou sombre selon vos préférences<br>
-&#8226; Sauvegardez vos abonnements avec l’intégration à gPodder.net et les exports OPML
-
-<b>Rejoignez la communauté d'AntennaPod !</b><br>
+AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit par des podcasters indépendants ou de gros éditeurs comme la BBC, NPR, CNN. Ajoutez, importez et exportez facilement des podcasts à partir d'ITunes, de fichiers OPML ou de liens RSS.
+Téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous aimez avec une vitesse de lecture réglable, le support des chapitres et un minuteur d'arrêt automatique.
+Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi) et la suppression des épisodes (selon vos critères)
+
+Conçu par des fans de podcast, AntennaPod est gratuit, open source et sans publicité.
+
+<b>Importez, organisez et écoutez</b>
+• Gérez la lecture à l'aide : de widget, de notification système, de commande casque ou bluetooth
+• Ajouter et importer à partir d'iTunes, gPodder.net, de fichiers OPML, de liens RSS ou Atom
+• Ecoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres, à l'enregistrement de la position de lecture et à un minuteur d'arrêt automatique puissant (secouez pour réinitialiser, baisse du volume)
+• Accès possible aux flux et épisodes nécessitant un mot de passe
+
+<b>Suivez, partagez et appréciez</b>
+• Enregistrez les meilleurs épisodes en tant que favoris
+• Retrouvez un épisode à partir de l'historique de lecture ou en recherchant les noms et commentaires des épisodes
+• Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML
+
+<b>Contrôlez</b>
+• Prenez le contrôle en automatisant vos téléchargements : flux spécifiques, restriction de la connexion mobile, réseaux WIFI autorisés, seulement pendant la recharge, fréquence de mise à jour
+• Gérez l'espace de stockage en réglant le nombre d'épisodes à garder, quand les supprimer et où les enregistrer
+• Choisissez le thème de l'interface
+• Sauvegardez vos abonnements avec gPodder.net ou des exports OPML
+
+<b>Rejoignez la communauté d'AntennaPod !</b>
AntennaPod est développé activement par des volontaires. Vous pouvez aussi contribuer avec du code, des traductions ou des commentaires !
-GitHub est l'endroit où aller pour demander de nouvelles options, faire part de bug ou pour contribuer au code :<br>
+Allez sur GitHub pour demander de nouvelles options, signaler des bugs ou pour contribuer au développement :
https://www.github.com/AntennaPod/AntennaPod
-Rejoignez notre Google Group pour partager vos idées, podcast préférés et vos remerciements à tous les bénévoles :<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Vous avez une question ou des suggestions ?
-https://twitter.com/@AntennaPod
-
-Retrouvez nous sur Transifex pour contribuer à la traduction de cette app :<br>
-https://www.transifex.com/antennapod/antennapod
-
-Jetez un coup d’œil à notre programme de version Beta pour bénéficier des dernières options :<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Allez sur Transifex pour aider la traduction :
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/release-notes/en-US/default.txt b/app/src/main/play/release-notes/en-US/default.txt
index 11371852d..20f9bf802 100644
--- a/app/src/main/play/release-notes/en-US/default.txt
+++ b/app/src/main/play/release-notes/en-US/default.txt
@@ -1,8 +1,7 @@
-- Display episode image on widget (by @brad)
-- Added checkbox to keep queue sorted (by @damoasda)
-- New UI for "Add podcast" screen (by @ByteHamster)
-- Added batch editing to the queue (by @ByteHamster)
-- Added option to adapt remaining time to playback speed (by @CedricCabessa)
-- Removed broken Flattr integration (by @ByteHamster)
-- Added filter to "All episodes" list (by @jhunnius)
-- Tons of bug fixes and performance improvements
+- Added per-feed playback speed setting (by @spacecowboy)
+- Support sorting in Podcast screen (by @orionlee)
+- Option to show stream button rather than download in lists (by @dsmith47)
+- Option to replace Episode cover with Podcast cover (by @xgouchet)
+- Transparent widget (by @M-arcel)
+- User interface tweaks (by @ByteHamster)
+- Tons of bug fixes and improvements
diff --git a/app/src/main/res/layout/about_teaser.xml b/app/src/main/res/layout/about_teaser.xml
new file mode 100644
index 000000000..a9e50f6da
--- /dev/null
+++ b/app/src/main/res/layout/about_teaser.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:adjustViewBounds="true"
+ android:layout_height="wrap_content" app:srcCompat="@drawable/teaser" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml
new file mode 100644
index 000000000..ca8aba52d
--- /dev/null
+++ b/app/src/main/res/layout/activity_widget_config.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="de.danoeh.antennapod.activity.WidgetConfigActivity">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/widget_config_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/teaser"
+ android:scaleType="centerCrop" />
+
+ <include
+ android:id="@+id/widget_config_preview"
+ layout="@layout/player_widget"
+ android:layout_width="match_parent"
+ android:layout_height="80dp"
+ android:layout_gravity="center"
+ android:layout_margin="16dp" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/widget_opacity"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/widget_opacity_textView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="end"
+ android:text="100%"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <SeekBar
+ android:id="@+id/widget_opacity_seekBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:max="100"
+ android:progress="100" />
+
+ <Button
+ android:id="@+id/butConfirm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/widget_create_button" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index 99fa1c3f9..9e57e0743 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -32,6 +32,7 @@
android:layout_marginRight="8dp"
android:contentDescription="@string/search_podcast_hint"
app:srcCompat="?attr/action_search"
+ android:id="@+id/search_icon"
android:scaleType="center"/>
<EditText
@@ -72,7 +73,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
<TextView
@@ -82,6 +83,8 @@
android:text="@string/txtvfeedurl_label"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
<EditText
@@ -96,8 +99,10 @@
<Button
android:id="@+id/butConfirm"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ style="?android:attr/buttonBarButtonStyle"
android:text="@string/confirm_label"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
index 090aec47f..5049db215 100644
--- a/app/src/main/res/layout/audio_controls.xml
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -3,7 +3,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:padding="16dp">
<LinearLayout
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/authentication_dialog.xml b/app/src/main/res/layout/authentication_dialog.xml
index 187045c63..1661eaea1 100644
--- a/app/src/main/res/layout/authentication_dialog.xml
+++ b/app/src/main/res/layout/authentication_dialog.xml
@@ -4,88 +4,33 @@
android:layout_height="match_parent"
android:orientation="vertical" >
- <LinearLayout
+ <EditText
+ android:id="@+id/etxtUsername"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_margin="16dp"
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <EditText
+ android:id="@+id/etxtPassword"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_margin="16dp"
+ android:inputType="textPassword"
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <CheckBox
+ android:id="@+id/chkSaveUsernamePassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <EditText
- android:id="@+id/etxtUsername"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_margin="16dp"
- android:hint="@string/username_label"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <EditText
- android:id="@+id/etxtPassword"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_margin="16dp"
- android:inputType="textPassword"
- android:hint="@string/password_label"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <CheckBox
- android:id="@+id/chkSaveUsernamePassword"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/save_username_password_label"/>
- </LinearLayout>
-
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="?android:attr/dividerVertical" />
-
- <View
- android:id="@+id/horizontal_divider"
- android:layout_width="1dip"
- android:layout_height="fill_parent"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:background="?android:attr/dividerVertical" />
-
- <Button
- android:id="@+id/butCancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/horizontal_divider"
- android:layout_toStartOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/cancel_label" />
-
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:layout_toEndOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
+ android:layout_margin="16dp"
+ android:text="@string/save_username_password_label"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/choose_data_folder_dialog.xml b/app/src/main/res/layout/choose_data_folder_dialog.xml
new file mode 100644
index 000000000..bac14a108
--- /dev/null
+++ b/app/src/main/res/layout/choose_data_folder_dialog.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/recyclerView" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
index f4a2ff703..ae59c0614 100644
--- a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
+++ b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
@@ -37,12 +37,11 @@
android:layout_height="wrap_content"
tools:text="2 GB" />
- <me.zhanghai.android.materialprogressbar.MaterialProgressBar
+ <ProgressBar
android:id="@+id/used_space"
- style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
+ style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:mpb_progressStyle="horizontal" />
+ android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
diff --git a/app/src/main/res/layout/edit_text_dialog.xml b/app/src/main/res/layout/edit_text_dialog.xml
new file mode 100644
index 000000000..6bf0bc6cb
--- /dev/null
+++ b/app/src/main/res/layout/edit_text_dialog.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:ems="10"
+ android:id="@+id/text" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index b047b3da0..3352fdf19 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -112,38 +112,43 @@
android:orientation="horizontal"
tools:background="@android:color/holo_blue_bright">
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction1"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction1"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="8dp"
- android:layout_marginEnd="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
android:ellipsize="end"
- android:gravity="center"
+ android:drawablePadding="8dp"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 1"
tools:background="@android:color/holo_red_light" />
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction2"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction2"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
+ android:drawablePadding="8dp"
android:ellipsize="end"
- android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 2"
tools:background="@android:color/holo_orange_dark" />
+ <View
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
</LinearLayout>
<View
diff --git a/app/src/main/res/layout/feeditem_pager_fragment.xml b/app/src/main/res/layout/feeditem_pager_fragment.xml
new file mode 100644
index 000000000..8ea5bf4f9
--- /dev/null
+++ b/app/src/main/res/layout/feeditem_pager_fragment.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.viewpager.widget.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index 0cc13f74c..211f6c9ef 100644
--- a/app/src/main/res/layout/fragment_itunes_search.xml
+++ b/app/src/main/res/layout/fragment_itunes_search.xml
@@ -20,7 +20,7 @@
tools:listitem="@layout/gpodnet_podcast_listitem" />
<TextView
- android:id="@id/android:empty"
+ android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index b759b0092..636cbb015 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -169,7 +169,7 @@
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_speed"
android:scaleType="fitCenter"
- tools:src="@drawable/ic_playback_speed_white"
+ tools:src="@drawable/ic_playback_speed_white_48dp"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
diff --git a/app/src/main/res/layout/numberpicker.xml b/app/src/main/res/layout/numberpicker.xml
index 813326bd6..d493f2e6c 100644
--- a/app/src/main/res/layout/numberpicker.xml
+++ b/app/src/main/res/layout/numberpicker.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="32dp">
+ android:padding="16dp">
<EditText
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/onlinefeedview_activity.xml b/app/src/main/res/layout/onlinefeedview_activity.xml
new file mode 100644
index 000000000..4ed8a0341
--- /dev/null
+++ b/app/src/main/res/layout/onlinefeedview_activity.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:cardCornerRadius="4dp"
+ android:layout_margin="32dp"
+ android:elevation="16dp">
+
+ <FrameLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center"/>
+
+ <LinearLayout
+ android:id="@+id/feedDisplay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/feeditemlist_header"/>
+
+ <Spinner
+ android:id="@+id/spinnerAlternateUrls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginTop="8dp"
+ android:padding="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_micro"/>
+
+ <Button
+ android:id="@+id/butSubscribe"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:focusable="false"
+ android:text="@string/subscribe_label"/>
+
+ <ListView
+ android:id="@+id/listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+ </androidx.cardview.widget.CardView>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/onlinefeedview_header.xml b/app/src/main/res/layout/onlinefeedview_header.xml
index 057bfb379..aa690e142 100644
--- a/app/src/main/res/layout/onlinefeedview_header.xml
+++ b/app/src/main/res/layout/onlinefeedview_header.xml
@@ -6,27 +6,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent">
- <include layout="@layout/feeditemlist_header"/>
-
- <Spinner
- android:id="@+id/spinnerAlternateUrls"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:padding="8dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_micro"/>
-
- <Button
- android:id="@+id/butSubscribe"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:focusable="false"
- android:text="@string/subscribe_label"/>
-
<TextView
android:id="@+id/txtvDescription"
android:layout_width="wrap_content"
@@ -50,8 +29,7 @@
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
- android:textColor="?attr/colorAccent"
- android:textSize="@dimen/text_size_small"
+ android:textSize="@dimen/text_size_medium"
android:text="@string/episodes_label"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/proxy_settings.xml b/app/src/main/res/layout/proxy_settings.xml
index 983325030..e4e57cc92 100644
--- a/app/src/main/res/layout/proxy_settings.xml
+++ b/app/src/main/res/layout/proxy_settings.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
<TextView
android:id="@+id/txtvType"
diff --git a/app/src/main/res/layout/quick_feed_discovery.xml b/app/src/main/res/layout/quick_feed_discovery.xml
index e791d19d7..75a35a130 100644
--- a/app/src/main/res/layout/quick_feed_discovery.xml
+++ b/app/src/main/res/layout/quick_feed_discovery.xml
@@ -10,35 +10,32 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
- <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
- android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
<TextView
- android:layout_width="wrap_content"
+ android:layout_width="0dip"
android:layout_height="wrap_content"
android:text="@string/discover"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
- <TextView
- android:layout_width="0dp"
+ <Button
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="0dp"
+ android:minWidth="0dp"
android:text="@string/discover_more"
- android:gravity="end"
- android:textAlignment="viewEnd"
- android:textSize="18sp"
- android:background="?android:attr/selectableItemBackground"
- android:layout_marginBottom="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_weight="1"
android:id="@+id/discover_more"
- android:textColor="@color/antennapod_blue"/>
+ style="?android:attr/buttonBarButtonStyle"/>
</LinearLayout>
<RelativeLayout
@@ -52,8 +49,6 @@
android:numColumns="4"
app:layout_columnWeight="1"
app:layout_rowWeight="1"
- android:horizontalSpacing="4dp"
- android:verticalSpacing="4dp"
android:scrollbars="none"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
@@ -70,7 +65,7 @@
<TextView
android:id="@+id/discover_error"
- android:textColor="@color/md_edittext_error"
+ android:textColor="@color/download_failed_red"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content" />
diff --git a/app/src/main/res/layout/quick_feed_discovery_item.xml b/app/src/main/res/layout/quick_feed_discovery_item.xml
index 6b0c98013..3a0fe0b4b 100644
--- a/app/src/main/res/layout/quick_feed_discovery_item.xml
+++ b/app/src/main/res/layout/quick_feed_discovery_item.xml
@@ -2,12 +2,16 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:padding="4dp"
+ android:clipToPadding="false">
<de.danoeh.antennapod.view.SquareImageView
android:id="@+id/discovery_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:elevation="4dp"
+ android:outlineProvider="bounds"
android:foreground="?android:attr/selectableItemBackground"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/simple_icon_list_item.xml b/app/src/main/res/layout/simple_icon_list_item.xml
new file mode 100644
index 000000000..7ed129204
--- /dev/null
+++ b/app/src/main/res/layout/simple_icon_list_item.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:id="@+id/icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginLeft="16dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp">
+
+ <TextView
+ tools:text="Title"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/title"/>
+
+ <TextView
+ tools:text="Subtitle"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/subtitle"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index ba4249268..b3742c20c 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:gravity="center">
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -33,7 +34,7 @@
</LinearLayout>
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/app/src/main/res/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index 4de46810e..b26ef304c 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
@@ -33,27 +34,27 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/rewind_label"
- android:src="@drawable/ic_av_fast_rewind_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_rewind_white_80dp" />
<ImageButton
android:id="@+id/butPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/pause_label"
- android:src="@drawable/ic_av_pause_white_80dp" />
+ app:srcCompat="@drawable/ic_av_pause_white_80dp" />
<ImageButton
android:id="@+id/butFF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/fast_forward_label"
- android:src="@drawable/ic_av_fast_forward_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_forward_white_80dp" />
</LinearLayout>
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 7398754e5..2fac77375 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -6,7 +6,7 @@
<item
android:id="@+id/action_search"
android:icon="?attr/action_search"
- custom:showAsAction="collapseActionView|ifRoom"
+ custom:showAsAction="collapseActionView|always"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
@@ -14,7 +14,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="ifRoom"
+ custom:showAsAction="always"
android:icon="?attr/navigation_refresh"/>
<item
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index fdd0e01bc..13c019b65 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -3,17 +3,17 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/filter_items"
- android:icon="?attr/ic_filter"
+ android:id="@+id/sort_items"
+ android:icon="?attr/ic_sort"
android:menuCategory="container"
- android:title="@string/filter"
+ android:title="@string/sort"
custom:showAsAction="always">
</item>
<item
- android:id="@+id/episode_actions"
+ android:id="@+id/filter_items"
+ android:icon="?attr/ic_filter"
android:menuCategory="container"
- android:icon="?attr/checkbox_multiple"
- android:title="@string/batch_edit"
+ android:title="@string/filter"
custom:showAsAction="always">
</item>
<item
@@ -38,6 +38,13 @@
android:title="@string/search_label"/>
<item
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:icon="?attr/checkbox_multiple"
+ android:title="@string/batch_edit"
+ custom:showAsAction="collapseActionView">
+ </item>
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
android:menuCategory="container"
diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml
index 2e58d3445..9f2a0ba82 100644
--- a/app/src/main/res/xml/feed_settings.xml
+++ b/app/src/main/res/xml/feed_settings.xml
@@ -4,22 +4,26 @@
<SwitchPreference
android:key="keepUpdated"
+ android:icon="?attr/navigation_refresh"
android:title="@string/keep_updated"
android:summary="@string/keep_updated_summary"/>
<Preference
android:key="authentication"
+ android:icon="?attr/ic_key"
android:title="@string/authentication_label"
android:summary="@string/authentication_descr"/>
<ListPreference
android:key="feedPlaybackSpeed"
+ android:icon="?attr/ic_settings_speed"
android:title="@string/playback_speed"
android:summary="@string/pref_feed_playback_speed_sum"/>
<ListPreference
android:entries="@array/spnAutoDeleteItems"
android:entryValues="@array/spnAutoDeleteValues"
+ android:icon="?attr/content_discard"
android:title="@string/auto_delete_label"
android:key="autoDelete"/>
diff --git a/app/src/main/res/xml/player_widget_info.xml b/app/src/main/res/xml/player_widget_info.xml
index 1dbfabcd6..79cdd4a69 100644
--- a/app/src/main/res/xml/player_widget_info.xml
+++ b/app/src/main/res/xml/player_widget_info.xml
@@ -6,6 +6,7 @@
android:previewImage="@drawable/ic_widget_preview"
android:minHeight="40dp"
android:minWidth="250dp"
- android:minResizeWidth="40dp">
+ android:minResizeWidth="40dp"
+ android:configure="de.danoeh.antennapod.activity.WidgetConfigActivity">
</appwidget-provider> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_about.xml b/app/src/main/res/xml/preferences_about.xml
new file mode 100644
index 000000000..9b8d744e1
--- /dev/null
+++ b/app/src/main/res/xml/preferences_about.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <Preference
+ android:layout="@layout/about_teaser"/>
+ <Preference
+ android:key="about_version"
+ android:title="@string/antennapod_version"
+ android:icon="?attr/ic_unfav"
+ android:summary="1.7.2 (asd8qs)"/>
+ <Preference
+ android:key="about_developers"
+ android:icon="?attr/ic_settings"
+ android:summary="@string/developers_summary"
+ android:title="@string/developers"/>
+ <Preference
+ android:key="about_translators"
+ android:icon="?attr/ic_chat"
+ android:summary="@string/translators_summary"
+ android:title="@string/translators"/>
+ <Preference
+ android:key="about_privacy_policy"
+ android:icon="?attr/ic_questionmark"
+ android:summary="https://antennapod.org/privacy.html"
+ android:title="@string/privacy_policy"/>
+ <Preference
+ android:key="about_licenses"
+ android:icon="?attr/action_about"
+ android:summary="@string/licenses_summary"
+ android:title="@string/licenses"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_autodownload.xml b/app/src/main/res/xml/preferences_autodownload.xml
index a4967c839..333224aa0 100644
--- a/app/src/main/res/xml/preferences_autodownload.xml
+++ b/app/src/main/res/xml/preferences_autodownload.xml
@@ -15,16 +15,14 @@
android:key="prefEpisodeCacheSize"
android:title="@string/pref_episode_cache_title"
android:summary="@string/pref_episode_cache_summary"
- android:entryValues="@array/episode_cache_size_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cache_size_values"/>
<ListPreference
android:defaultValue="-1"
android:entries="@array/episode_cleanup_entries"
android:key="prefEpisodeCleanup"
android:title="@string/pref_episode_cleanup_title"
android:summary="@string/pref_episode_cleanup_summary"
- android:entryValues="@array/episode_cleanup_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cleanup_values"/>
<SwitchPreference
android:key="prefEnableAutoDownloadOnBattery"
android:title="@string/pref_automatic_download_on_battery_title"
diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml
index d609d3daa..7a7535dda 100644
--- a/app/src/main/res/xml/preferences_playback.xml
+++ b/app/src/main/res/xml/preferences_playback.xml
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/interruptions">
<SwitchPreference
@@ -42,8 +40,7 @@
android:entryValues="@array/video_background_behavior_values"
android:key="prefVideoBehavior"
android:summary="@string/pref_videoBehavior_sum"
- android:title="@string/pref_videoBehavior_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_videoBehavior_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/playback_control">
@@ -76,6 +73,11 @@
android:key="prefPlaybackTimeRespectsSpeed"
android:summary="@string/pref_playback_time_respects_speed_sum"
android:title="@string/pref_playback_time_respects_speed_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="prefStreamOverDownload"
+ android:summary="@string/pref_stream_over_download_sum"
+ android:title="@string/pref_stream_over_download_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
@@ -85,12 +87,12 @@
android:key="prefEnqueueDownloaded"
android:summary="@string/pref_enqueue_downloaded_summary"
android:title="@string/pref_enqueue_downloaded_title" />
- <SwitchPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefQueueAddToFront"
- android:summary="@string/pref_queueAddToFront_sum"
- android:title="@string/pref_queueAddToFront_title"/>
+ <ListPreference
+ android:defaultValue="BACK"
+ android:entries="@array/enqueue_location_options"
+ android:entryValues="@array/enqueue_location_values"
+ android:key="prefEnqueueLocation"
+ android:title="@string/pref_enqueue_location_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -103,8 +105,7 @@
android:entryValues="@array/smart_mark_as_played_values"
android:key="prefSmartMarkAsPlayedSecs"
android:summary="@string/pref_smart_mark_as_played_sum"
- android:title="@string/pref_smart_mark_as_played_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_smart_mark_as_played_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -120,8 +121,7 @@
android:key="prefMediaPlayer"
android:title="@string/media_player"
android:summary="@string/pref_media_player_message"
- android:entryValues="@array/media_player_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/media_player_values"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/experimental_pref">
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index c48e9adc8..20a06c4c4 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -10,8 +10,7 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="system"
- app:useStockLayout="true"/>
+ android:defaultValue="system"/>
<Preference
android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum"
@@ -22,16 +21,20 @@
android:title="@string/pref_nav_drawer_feed_order_title"
android:key="prefDrawerFeedOrder"
android:summary="@string/pref_nav_drawer_feed_order_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
<ListPreference
android:entryValues="@array/nav_drawer_feed_counter_values"
android:entries="@array/nav_drawer_feed_counter_options"
android:title="@string/pref_nav_drawer_feed_counter_title"
android:key="prefDrawerFeedIndicator"
android:summary="@string/pref_nav_drawer_feed_counter_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
+ <SwitchPreference
+ android:title="@string/pref_episode_cover_title"
+ android:key="prefEpisodeCover"
+ android:summary="@string/pref_episode_cover_summary"
+ android:defaultValue="true"
+ android:enabled="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/external_elements">
<SwitchPreference
@@ -64,7 +67,6 @@
android:key="prefBackButtonBehavior"
android:title="@string/pref_back_button_behavior_title"
android:summary="@string/pref_back_button_behavior_sum"
- android:defaultValue="default"
- app:useStockLayout="true"/>
+ android:defaultValue="default"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
deleted file mode 100644
index fd70ab549..000000000
--- a/app/src/main/templates/about.html
+++ /dev/null
@@ -1,183 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
- <style type="text/css">
-
- @font-face {
- font-family: 'Roboto-Light';
- src: url('file:///android_asset/Roboto-Light.ttf');
- }
-
- html, body {
- background: @background@;
- margin: 0;
- padding: 0;
- }
-
- * {
- color: @fontcolor@;
- font-family: roboto-Light;
- font-size: 12pt;
- }
-
- img#logo {
- display: block;
- margin-left: auto;
- margin-right: auto;
- max-height: 200px;
- max-height: 50vh;
- max-width: 100%;
- height: auto;
- width: auto;
- }
-
- div#logobackground{
- width: 100%;
- background: #42a5f5;
- }
-
- .card {
- background: @card_background@;
- margin: 10px;
- padding: 10px;
- border: 1px solid @card_border@;
- border-top-width: 0;
- border-bottom-width: 2px;
- }
-
- h1 {
- font-size: 15pt;
- margin-left: 20px;
- }
-
- h2 {
- font-size: 13pt;
- margin-top: 0px;
- }
-
- a {
- font-size: 14px;
- color: #00A8DF;
- text-decoration: none;
- }
-
- </style>
- <title>About AntennaPod</title>
-</head>
-<body>
-<div id="logobackground">
-<img id="logo" src="file:///android_asset/logo.png" alt="Logo"/>
-</div>
-
-<h1>AntennaPod</h1>
-
-<div class="card">
-<table>
-<tr><td>Version:</td><td><b>@versionname@</b></td></tr>
-<tr><td>Commit:</td><td><b>@commit@</b></td></tr>
-</table>
-</div>
-
-<div class="card">
-Created by Daniel Oeh<br />
-Copyright &copy; 2012-@year@<br />
-AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a><br />
-Licensed under the MIT License <a href="LICENSE.txt">(View)</a><br />
-Privacy Policy <a href="https://antennapod.org/privacy.html">(View)</a>
-</div>
-
-<h1>Used libraries</h1>
-
-<div class="card">
-<h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2>
-by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
-by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>ExoPlayer <a href="https://github.com/google/ExoPlayer">(Link)</a></h2>
-by Google, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
- <h2>Floating Action Button Speed Dial <a href="https://github.com/leinardi/FloatingActionButtonSpeedDial">(Link)</a></h2>
- by Roberto Leinardi, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
-licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Iconify <a href="https://github.com/JoanZapata/android-iconify">(Link)</a></h2>
-by Joan Zapata, licensed under the Apache 2.0 license <a href="LICENSE_ANDROID_ICONIFY.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
-licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Design Icons <a href="https://github.com/google/material-design-icons">(Link)</a></h2>
-by Google, licensed under an Attribution-ShareAlike 4.0 International license <a href="LICENSE_MATERIAL_DESIGN_ICONS.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Design Icons <a href="https://github.com/Templarian/MaterialDesign">(Link)</a></h2>
-by Templarian, licensed under the SIL Open Font License, Version 1.1 <a href="LICENSE_SIL.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Dialogs <a href="https://github.com/afollestad/material-dialogs">(Link)</a></h2>
-by Aidan Michael Follestad, licensed under the MIT License <a href="LICENSE_MATERIAL_DIALOGS.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>OkHttp <a href="https://github.com/square/okhttp">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
-by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Triangle Label View <a href="https://github.com/shts/TriangleLabelView">(Link)</a></h2>
-by Shota Saito, licensed under the Apache 2.0 license <a href="LICENSE_TRIANGLE_LABEL_VIEW.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
-by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-</body>
-</html>
diff --git a/build.gradle b/build.gradle
index d8e1a6c32..88273960d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,21 +41,19 @@ subprojects {
project.ext {
compileSdkVersion = 28
- minSdkVersion = 14
+ minSdkVersion = 16
targetSdkVersion = 28
workManagerVersion = "1.0.1"
espressoVersion = "3.2.0"
- awaitilityVersion = "3.1.2"
+ awaitilityVersion = "3.1.6"
commonsioVersion = "2.5"
commonslangVersion = "3.6"
- commonstextVersion = "1.3"
eventbusVersion = "3.1.1"
glideVersion = "4.8.0"
glideOkhttpIntegrationVersion = "4.8.0"
iconifyVersion = "2.2.2"
jsoupVersion = "1.11.2"
- materialDialogsVersion = "0.9.0.2"
okhttpVersion = "3.12.5"
okioVersion = "1.17.4"
recyclerviewFlexibledividerVersion = "1.4.0"
@@ -65,7 +63,7 @@ project.ext {
rxJavaRulesVersion = "1.3.3.0"
triangleLabelViewVersion = "1.1.2"
- exoPlayerVersion = "2.9.3"
+ exoPlayerVersion = "2.10.8"
audioPlayerVersion = "v1.0.17"
castCompanionLibVer = "2.9.1"
@@ -91,4 +89,5 @@ task checkstyle(type: Checkstyle) {
classpath = files()
source "${project.rootDir}"
exclude("**/gen/**")
+ exclude("**/generated/**")
}
diff --git a/config/checkstyle/checkstyle-best-practice.xml b/config/checkstyle/checkstyle-best-practice.xml
index 66ce0dcc4..410251d6c 100644
--- a/config/checkstyle/checkstyle-best-practice.xml
+++ b/config/checkstyle/checkstyle-best-practice.xml
@@ -187,12 +187,8 @@
</module>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
- <module name="CustomImportOrder">
- <property name="sortImportsInGroupAlphabetically" value="true"/>
- <property name="separateLineBetweenGroups" value="true"/>
- <property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
- </module>
<module name="MethodParamPad"/>
+ <module name="WhitespaceAfter"/>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
@@ -248,5 +244,14 @@
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
+ <module name="NestedIfDepth">
+ <property name="max" value="4"/>
+ </module>
+ <module name="NestedTryDepth">
+ <property name="max" value="2"/>
+ </module>
+ <module name="NestedForDepth">
+ <property name="max" value="2"/>
+ </module>
</module>
</module>
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 0a5b47eb8..02a370b7e 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -11,6 +11,13 @@
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
+ <module name="IllegalTokenText">
+ <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
+ <property name="format"
+ value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
+ <property name="message"
+ value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
+ </module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
@@ -25,6 +32,7 @@
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="OneStatementPerLine"/>
+ <module name="MultipleVariableDeclarations"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
@@ -68,6 +76,11 @@
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
+ <module name="NoWhitespaceBefore">
+ <property name="tokens"
+ value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
+ <property name="allowLineBreaks" value="true"/>
+ </module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
@@ -78,5 +91,14 @@
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
+ <module name="NestedIfDepth">
+ <property name="max" value="4"/>
+ </module>
+ <module name="NestedTryDepth">
+ <property name="max" value="2"/>
+ </module>
+ <module name="NestedForDepth">
+ <property name="max" value="2"/>
+ </module>
</module>
</module>
diff --git a/contributers.template.py b/contributers.template.py
deleted file mode 100755
index 4492fae79..000000000
--- a/contributers.template.py
+++ /dev/null
@@ -1,471 +0,0 @@
-#!/usr/bin/env python3
-import requests
-import subprocess
-import configparser
-import os
-
-config = configparser.ConfigParser()
-config.read(os.path.expanduser("~") + '/.transifexrc')
-if 'https://www.transifex.com' in config:
- TRANSIFEX_USER = config['https://www.transifex.com']['username']
- TRANSIFEX_PW = config['https://www.transifex.com']['password']
-else:
- TRANSIFEX_USER = ""
- TRANSIFEX_PW = ""
-
-print('DEVELOPERS\n==========\n')
-p = subprocess.Popen("git log --format='%aN' --no-merges "
- +"| grep -v '@' " # No email adresses
- +"| grep -v 'no.reply' " # no.reply
- +"| sed -e 's/^\(Daniel\|daniel oeh\|danieloeh\)$/Daniel Oeh/I'" # Duplicate name
- +"| sed -e 's/^keunes$/Koen Glotzbach/'" # Duplicate name
- +"| sed -e 's/^H. Lehmann$/ByteHamster/'" # Duplicate name
- +"| sed -e 's/^domingos86$/Domingos Lopes/'" # Duplicate name
- +"| sed -e 's/^orionlee$/Sam Lee/'" # Duplicate name
- +"| sort -fu", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-for line in p.stdout.readlines():
- output = line.decode()
- print(output, end='')
-
-language_codes = {
- "af_NA": "Afrikaans (Namibia)",
- "af_ZA": "Afrikaans (South Africa)",
- "af": "Afrikaans",
- "ak_GH": "Akan (Ghana)",
- "ak": "Akan",
- "ast_ES": "Asturian (Spain)",
- "sq_AL": "Albanian (Albania)",
- "sq": "Albanian",
- "am_ET": "Amharic (Ethiopia)",
- "am": "Amharic",
- "ar_DZ": "Arabic (Algeria)",
- "ar_BH": "Arabic (Bahrain)",
- "ar_EG": "Arabic (Egypt)",
- "ar_IQ": "Arabic (Iraq)",
- "ar_JO": "Arabic (Jordan)",
- "ar_KW": "Arabic (Kuwait)",
- "ar_LB": "Arabic (Lebanon)",
- "ar_LY": "Arabic (Libya)",
- "ar_MA": "Arabic (Morocco)",
- "ar_OM": "Arabic (Oman)",
- "ar_QA": "Arabic (Qatar)",
- "ar_SA": "Arabic (Saudi Arabia)",
- "ar_SD": "Arabic (Sudan)",
- "ar_SY": "Arabic (Syria)",
- "ar_TN": "Arabic (Tunisia)",
- "ar_AE": "Arabic (United Arab Emirates)",
- "ar_YE": "Arabic (Yemen)",
- "ar": "Arabic",
- "hy_AM": "Armenian (Armenia)",
- "hy": "Armenian",
- "as_IN": "Assamese (India)",
- "as": "Assamese",
- "asa_TZ": "Asu (Tanzania)",
- "asa": "Asu",
- "az_Cyrl": "Azerbaijani (Cyrillic)",
- "az_Cyrl_AZ": "Azerbaijani (Cyrillic, Azerbaijan)",
- "az_Latn": "Azerbaijani (Latin)",
- "az_Latn_AZ": "Azerbaijani (Latin, Azerbaijan)",
- "az": "Azerbaijani",
- "bm_ML": "Bambara (Mali)",
- "bm": "Bambara",
- "eu_ES": "Basque (Spain)",
- "eu": "Basque",
- "be_BY": "Belarusian (Belarus)",
- "be": "Belarusian",
- "bem_ZM": "Bemba (Zambia)",
- "bem": "Bemba",
- "bez_TZ": "Bena (Tanzania)",
- "bez": "Bena",
- "bn_BD": "Bengali (Bangladesh)",
- "bn_IN": "Bengali (India)",
- "bn": "Bengali",
- "bs_BA": "Bosnian (Bosnia and Herzegovina)",
- "bs": "Bosnian",
- "bg_BG": "Bulgarian (Bulgaria)",
- "bg": "Bulgarian",
- "my_MM": "Burmese (Myanmar [Burma])",
- "my": "Burmese",
- "ca_ES": "Catalan (Spain)",
- "ca": "Catalan",
- "tzm_Latn": "Central Morocco Tamazight (Latin)",
- "tzm_Latn_MA": "Central Morocco Tamazight (Latin, Morocco)",
- "tzm": "Central Morocco Tamazight",
- "chr_US": "Cherokee (United States)",
- "chr": "Cherokee",
- "cgg_UG": "Chiga (Uganda)",
- "cgg": "Chiga",
- "zh": "Chinese (Simplified)",
- "zh_CN": "Chinese (China)",
- "zh_HK": "Chinese (Hong Kong SAR China)",
- "zh_MO": "Chinese (Macau SAR China)",
- "zh_MO_SG": "Chinese (Singapore)",
- "zh_TW": "Chinese (Taiwan)",
- "zh": "Chinese",
- "kw_GB": "Cornish (United Kingdom)",
- "kw": "Cornish",
- "hr_HR": "Croatian (Croatia)",
- "hr": "Croatian",
- "cs_CZ": "Czech (Czech Republic)",
- "cs": "Czech",
- "da_DK": "Danish (Denmark)",
- "da": "Danish",
- "nl_BE": "Dutch (Belgium)",
- "nl_NL": "Dutch (Netherlands)",
- "nl": "Dutch",
- "ebu_KE": "Embu (Kenya)",
- "ebu": "Embu",
- "en_AS": "English (American Samoa)",
- "en_AU": "English (Australia)",
- "en_BE": "English (Belgium)",
- "en_BZ": "English (Belize)",
- "en_BW": "English (Botswana)",
- "en_CA": "English (Canada)",
- "en_GU": "English (Guam)",
- "en_HK": "English (Hong Kong SAR China)",
- "en_IN": "English (India)",
- "en_IE": "English (Ireland)",
- "en_JM": "English (Jamaica)",
- "en_MT": "English (Malta)",
- "en_MH": "English (Marshall Islands)",
- "en_MU": "English (Mauritius)",
- "en_NA": "English (Namibia)",
- "en_NZ": "English (New Zealand)",
- "en_MP": "English (Northern Mariana Islands)",
- "en_PK": "English (Pakistan)",
- "en_PH": "English (Philippines)",
- "en_SG": "English (Singapore)",
- "en_ZA": "English (South Africa)",
- "en_TT": "English (Trinidad and Tobago)",
- "en_UM": "English (U.S. Minor Outlying Islands)",
- "en_VI": "English (U.S. Virgin Islands)",
- "en_GB": "English (United Kingdom)",
- "en_US": "English (United States)",
- "en_ZW": "English (Zimbabwe)",
- "en": "English",
- "eo": "Esperanto",
- "et_EE": "Estonian (Estonia)",
- "et": "Estonian",
- "ee_GH": "Ewe (Ghana)",
- "ee_TG": "Ewe (Togo)",
- "ee": "Ewe",
- "fo_FO": "Faroese (Faroe Islands)",
- "fo": "Faroese",
- "fil_PH": "Filipino (Philippines)",
- "fil": "Filipino",
- "fi_FI": "Finnish (Finland)",
- "fi": "Finnish",
- "fr_BE": "French (Belgium)",
- "fr_BJ": "French (Benin)",
- "fr_BF": "French (Burkina Faso)",
- "fr_BI": "French (Burundi)",
- "fr_CM": "French (Cameroon)",
- "fr_CA": "French (Canada)",
- "fr_CF": "French (Central African Republic)",
- "fr_TD": "French (Chad)",
- "fr_KM": "French (Comoros)",
- "fr_CG": "French (Congo - Brazzaville)",
- "fr_CD": "French (Congo - Kinshasa)",
- "fr_CI": "French (Côte d’Ivoire)",
- "fr_DJ": "French (Djibouti)",
- "fr_GQ": "French (Equatorial Guinea)",
- "fr_FR": "French (France)",
- "fr_GA": "French (Gabon)",
- "fr_GP": "French (Guadeloupe)",
- "fr_GN": "French (Guinea)",
- "fr_LU": "French (Luxembourg)",
- "fr_MG": "French (Madagascar)",
- "fr_ML": "French (Mali)",
- "fr_MQ": "French (Martinique)",
- "fr_MC": "French (Monaco)",
- "fr_NE": "French (Niger)",
- "fr_RW": "French (Rwanda)",
- "fr_RE": "French (Réunion)",
- "fr_BL": "French (Saint Barthélemy)",
- "fr_MF": "French (Saint Martin)",
- "fr_SN": "French (Senegal)",
- "fr_CH": "French (Switzerland)",
- "fr_TG": "French (Togo)",
- "fr": "French",
- "ff_SN": "Fulah (Senegal)",
- "ff": "Fulah",
- "gl_ES": "Galician (Spain)",
- "gl": "Galician",
- "lg_UG": "Ganda (Uganda)",
- "lg": "Ganda",
- "ka_GE": "Georgian (Georgia)",
- "ka": "Georgian",
- "de_AT": "German (Austria)",
- "de_BE": "German (Belgium)",
- "de_DE": "German (Germany)",
- "de_LI": "German (Liechtenstein)",
- "de_LU": "German (Luxembourg)",
- "de_CH": "German (Switzerland)",
- "de": "German",
- "el_CY": "Greek (Cyprus)",
- "el_GR": "Greek (Greece)",
- "el": "Greek",
- "gu_IN": "Gujarati (India)",
- "gu": "Gujarati",
- "guz_KE": "Gusii (Kenya)",
- "guz": "Gusii",
- "ha_Latn": "Hausa (Latin)",
- "ha_Latn_GH": "Hausa (Latin, Ghana)",
- "ha_Latn_NE": "Hausa (Latin, Niger)",
- "ha_Latn_NG": "Hausa (Latin, Nigeria)",
- "ha": "Hausa",
- "haw_US": "Hawaiian (United States)",
- "haw": "Hawaiian",
- "he_IL": "Hebrew (Israel)",
- "he": "Hebrew",
- "hi_IN": "Hindi (India)",
- "hi": "Hindi",
- "hu_HU": "Hungarian (Hungary)",
- "hu": "Hungarian",
- "is_IS": "Icelandic (Iceland)",
- "is": "Icelandic",
- "ig_NG": "Igbo (Nigeria)",
- "ig": "Igbo",
- "id_ID": "Indonesian (Indonesia)",
- "id": "Indonesian",
- "ga_IE": "Irish (Ireland)",
- "ga": "Irish",
- "it_IT": "Italian (Italy)",
- "it_CH": "Italian (Switzerland)",
- "it": "Italian",
- "ja_JP": "Japanese (Japan)",
- "ja": "Japanese",
- "kea_CV": "Kabuverdianu (Cape Verde)",
- "kea": "Kabuverdianu",
- "kab_DZ": "Kabyle (Algeria)",
- "kab": "Kabyle",
- "kl_GL": "Kalaallisut (Greenland)",
- "kl": "Kalaallisut",
- "kln_KE": "Kalenjin (Kenya)",
- "kln": "Kalenjin",
- "kam_KE": "Kamba (Kenya)",
- "kam": "Kamba",
- "kn_IN": "Kannada (India)",
- "kn": "Kannada",
- "kk_Cyrl": "Kazakh (Cyrillic)",
- "kk_Cyrl_KZ": "Kazakh (Cyrillic, Kazakhstan)",
- "kk": "Kazakh",
- "km_KH": "Khmer (Cambodia)",
- "km": "Khmer",
- "ki_KE": "Kikuyu (Kenya)",
- "ki": "Kikuyu",
- "rw_RW": "Kinyarwanda (Rwanda)",
- "rw": "Kinyarwanda",
- "kok_IN": "Konkani (India)",
- "kok": "Konkani",
- "ko_KR": "Korean (South Korea)",
- "ko": "Korean",
- "khq_ML": "Koyra Chiini (Mali)",
- "khq": "Koyra Chiini",
- "ses_ML": "Koyraboro Senni (Mali)",
- "ses": "Koyraboro Senni",
- "lag_TZ": "Langi (Tanzania)",
- "lag": "Langi",
- "lv_LV": "Latvian (Latvia)",
- "lv": "Latvian",
- "lt_LT": "Lithuanian (Lithuania)",
- "lt": "Lithuanian",
- "luo_KE": "Luo (Kenya)",
- "luo": "Luo",
- "luy_KE": "Luyia (Kenya)",
- "luy": "Luyia",
- "mk_MK": "Macedonian (Macedonia)",
- "mk": "Macedonian",
- "jmc_TZ": "Machame (Tanzania)",
- "jmc": "Machame",
- "kde_TZ": "Makonde (Tanzania)",
- "kde": "Makonde",
- "mg_MG": "Malagasy (Madagascar)",
- "mg": "Malagasy",
- "ms_BN": "Malay (Brunei)",
- "ms_MY": "Malay (Malaysia)",
- "ms": "Malay",
- "ml_IN": "Malayalam (India)",
- "ml": "Malayalam",
- "mt_MT": "Maltese (Malta)",
- "mt": "Maltese",
- "gv_GB": "Manx (United Kingdom)",
- "gv": "Manx",
- "mr_IN": "Marathi (India)",
- "mr": "Marathi",
- "mas_KE": "Masai (Kenya)",
- "mas_TZ": "Masai (Tanzania)",
- "mas": "Masai",
- "mer_KE": "Meru (Kenya)",
- "mer": "Meru",
- "mfe_MU": "Morisyen (Mauritius)",
- "mfe": "Morisyen",
- "naq_NA": "Nama (Namibia)",
- "naq": "Nama",
- "ne_IN": "Nepali (India)",
- "ne_NP": "Nepali (Nepal)",
- "ne": "Nepali",
- "nd_ZW": "North Ndebele (Zimbabwe)",
- "nd": "North Ndebele",
- "no": "Norwegian",
- "nb_NO": "Norwegian Bokmål (Norway)",
- "nb": "Norwegian Bokmål",
- "nn_NO": "Norwegian Nynorsk (Norway)",
- "nn": "Norwegian Nynorsk",
- "nyn_UG": "Nyankole (Uganda)",
- "nyn": "Nyankole",
- "or_IN": "Oriya (India)",
- "or": "Oriya",
- "om_ET": "Oromo (Ethiopia)",
- "m_KE": "Oromo (Kenya)",
- "om": "Oromo",
- "ps_AF": "Pashto (Afghanistan)",
- "ps": "Pashto",
- "fa_AF": "Persian (Afghanistan)",
- "fa_IR": "Persian (Iran)",
- "fa": "Persian",
- "pl_PL": "Polish (Poland)",
- "pl": "Polish",
- "pt_BR": "Portuguese (Brazil)",
- "pt_GW": "Portuguese (Guinea-Bissau)",
- "pt_MZ": "Portuguese (Mozambique)",
- "pt_PT": "Portuguese (Portugal)",
- "pt": "Portuguese",
- "pa_Arab": "Punjabi (Arabic)",
- "pa_Arab_PK": "Punjabi (Arabic, Pakistan)",
- "pa_Guru": "Punjabi (Gurmukhi)",
- "pa_Guru_IN": "Punjabi (Gurmukhi, India)",
- "pa": "Punjabi",
- "ro_MD": "Romanian (Moldova)",
- "ro_RO": "Romanian (Romania)",
- "ro": "Romanian",
- "rm_CH": "Romansh (Switzerland)",
- "rm": "Romansh",
- "rof_TZ": "Rombo (Tanzania)",
- "rof": "Rombo",
- "ru_MD": "Russian (Moldova)",
- "ru_RU": "Russian (Russia)",
- "ru_UA": "Russian (Ukraine)",
- "ru": "Russian",
- "rwk_TZ": "Rwa (Tanzania)",
- "rwk": "Rwa",
- "saq_KE": "Samburu (Kenya)",
- "saq": "Samburu",
- "sg_CF": "Sango (Central African Republic)",
- "sg": "Sango",
- "seh_MZ": "Sena (Mozambique)",
- "seh": "Sena",
- "sr_Cyrl": "Serbian (Cyrillic)",
- "sr_Cyrl_BA": "Serbian (Cyrillic, Bosnia and Herzegovina)",
- "sr_Cyrl_ME": "Serbian (Cyrillic, Montenegro)",
- "sr_Cyrl_RS": "Serbian (Cyrillic, Serbia)",
- "sr_Latn": "Serbian (Latin)",
- "sr_Latn_BA": "Serbian (Latin, Bosnia and Herzegovina)",
- "sr_Latn_ME": "Serbian (Latin, Montenegro)",
- "sr_Latn_RS": "Serbian (Latin, Serbia)",
- "sr": "Serbian",
- "sn_ZW": "Shona (Zimbabwe)",
- "sn": "Shona",
- "ii_CN": "Sichuan Yi (China)",
- "ii": "Sichuan Yi",
- "si_LK": "Sinhala (Sri Lanka)",
- "si": "Sinhala",
- "sk_SK": "Slovak (Slovakia)",
- "sk": "Slovak",
- "sl_SI": "Slovenian (Slovenia)",
- "sl": "Slovenian",
- "xog_UG": "Soga (Uganda)",
- "xog": "Soga",
- "so_DJ": "Somali (Djibouti)",
- "so_ET": "Somali (Ethiopia)",
- "so_KE": "Somali (Kenya)",
- "so_SO": "Somali (Somalia)",
- "so": "Somali",
- "es_AR": "Spanish (Argentina)",
- "es_BO": "Spanish (Bolivia)",
- "es_CL": "Spanish (Chile)",
- "es_CO": "Spanish (Colombia)",
- "es_CR": "Spanish (Costa Rica)",
- "es_DO": "Spanish (Dominican Republic)",
- "es_EC": "Spanish (Ecuador)",
- "es_SV": "Spanish (El Salvador)",
- "es_GQ": "Spanish (Equatorial Guinea)",
- "es_GT": "Spanish (Guatemala)",
- "es_HN": "Spanish (Honduras)",
- "es_419": "Spanish (Latin America)",
- "es_MX": "Spanish (Mexico)",
- "es_NI": "Spanish (Nicaragua)",
- "es_PA": "Spanish (Panama)",
- "es_PY": "Spanish (Paraguay)",
- "es_PE": "Spanish (Peru)",
- "es_PR": "Spanish (Puerto Rico)",
- "es_ES": "Spanish (Spain)",
- "es_US": "Spanish (United States)",
- "es_UY": "Spanish (Uruguay)",
- "es_VE": "Spanish (Venezuela)",
- "es": "Spanish",
- "sw_KE": "Swahili (Kenya)",
- "sw_TZ": "Swahili (Tanzania)",
- "sw": "Swahili",
- "sv_FI": "Swedish (Finland)",
- "sv_SE": "Swedish (Sweden)",
- "sv": "Swedish",
- "gsw_CH": "Swiss German (Switzerland)",
- "gsw": "Swiss German",
- "shi_Latn": "Tachelhit (Latin)",
- "shi_Latn_MA": "Tachelhit (Latin, Morocco)",
- "shi_Tfng": "Tachelhit (Tifinagh)",
- "shi_Tfng_MA": "Tachelhit (Tifinagh, Morocco)",
- "shi": "Tachelhit",
- "dav_KE": "Taita (Kenya)",
- "dav": "Taita",
- "ta_IN": "Tamil (India)",
- "ta_LK": "Tamil (Sri Lanka)",
- "ta": "Tamil",
- "te_IN": "Telugu (India)",
- "te": "Telugu",
- "teo_KE": "Teso (Kenya)",
- "teo_UG": "Teso (Uganda)",
- "teo": "Teso",
- "th_TH": "Thai (Thailand)",
- "th": "Thai",
- "bo_CN": "Tibetan (China)",
- "bo_IN": "Tibetan (India)",
- "bo": "Tibetan",
- "ti_ER": "Tigrinya (Eritrea)",
- "ti_ET": "Tigrinya (Ethiopia)",
- "ti": "Tigrinya",
- "to_TO": "Tonga (Tonga)",
- "to": "Tonga",
- "tr_TR": "Turkish (Turkey)",
- "tr": "Turkish",
- "uk_UA": "Ukrainian (Ukraine)",
- "uk": "Ukrainian",
- "ur_IN": "Urdu (India)",
- "ur_PK": "Urdu (Pakistan)",
- "ur": "Urdu",
- "uz_Arab": "Uzbek (Arabic)",
- "uz_Arab_AF": "Uzbek (Arabic, Afghanistan)",
- "uz_Cyrl": "Uzbek (Cyrillic)",
- "uz_Cyrl_UZ": "Uzbek (Cyrillic, Uzbekistan)",
- "uz_Latn": "Uzbek (Latin)",
- "uz_Latn_UZ": "Uzbek (Latin, Uzbekistan)",
- "uz": "Uzbek",
- "vi_VN": "Vietnamese (Vietnam)",
- "vi": "Vietnamese",
- "vun_TZ": "Vunjo (Tanzania)",
- "vun": "Vunjo",
- "cy_GB": "Welsh (United Kingdom)",
- "cy": "Welsh",
- "yo_NG": "Yoruba (Nigeria)",
- "yo": "Yoruba",
- "zu_ZA": "Zulu (South Africa)",
- "zu": "Zulu"
-}
-
-print('\n\nTRANSLATORS\n===========\n')
-r = requests.get('http://www.transifex.com/api/2/project/antennapod/languages/', auth=(TRANSIFEX_USER, TRANSIFEX_PW))
-for lang in r.json():
- lang_contributers = lang['coordinators'] + lang['reviewers'] + lang['translators']
- lang_contributers = sorted(lang_contributers, key=str.lower)
- print(language_codes[lang['language_code']], ": ", ', '.join(lang_contributers), sep="")
diff --git a/core/build.gradle b/core/build.gradle
index fe6ed824a..c3110060f 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -62,9 +62,7 @@ dependencies {
implementation "android.arch.work:work-runtime:$workManagerVersion"
implementation "androidx.media:media:1.1.0"
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
- implementation "org.apache.commons:commons-text:$commonstextVersion"
implementation "commons-io:commons-io:$commonsioVersion"
- implementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
implementation "org.jsoup:jsoup:$jsoupVersion"
implementation "com.github.bumptech.glide:glide:$glideVersion"
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
@@ -94,10 +92,10 @@ dependencies {
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
+ androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
-
}
tasks.withType(Test) {
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java
new file mode 100644
index 000000000..71212e6ec
--- /dev/null
+++ b/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java
@@ -0,0 +1,122 @@
+package de.danoeh.antennapod.core.service.download;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import de.danoeh.antennapod.core.feed.FeedFile;
+
+import static org.junit.Assert.assertEquals;
+
+@SmallTest
+public class DownloadRequestTest {
+
+ @Test
+ public void parcelInArrayListTest_WithAuth() {
+ doTestParcelInArrayList("case has authentication",
+ "usr1", "pass1", "usr2", "pass2");
+ }
+
+ @Test
+ public void parcelInArrayListTest_NoAuth() {
+ doTestParcelInArrayList("case no authentication",
+ null, null, null, null);
+ }
+
+ @Test
+ public void parcelInArrayListTest_MixAuth() {
+ doTestParcelInArrayList("case mixed authentication",
+ null, null, "usr2", "pass2");
+ }
+
+ // Test to ensure parcel using put/getParcelableArrayList() API work
+ // based on: https://stackoverflow.com/a/13507191
+ private void doTestParcelInArrayList(String message,
+ String username1, String password1,
+ String username2, String password2) {
+ ArrayList<DownloadRequest> toParcel;
+ { // test DownloadRequests to parcel
+ String destStr = "file://location/media.mp3";
+ FeedFile item1 = createFeedItem(1);
+ Bundle arg1 = new Bundle();
+ arg1.putString("arg1", "value1");
+ DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1)
+ .withAuthentication(username1, password1)
+ .withArguments(arg1)
+ .build();
+
+ FeedFile item2 = createFeedItem(2);
+ DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2)
+ .withAuthentication(username2, password2)
+ .build();
+
+ toParcel = new ArrayList<>();
+ toParcel.add(request1);
+ toParcel.add(request2);
+ }
+
+ // parcel the download requests
+ Bundle bundleIn = new Bundle();
+ bundleIn.putParcelableArrayList("r", toParcel);
+
+ Parcel parcel = Parcel.obtain();
+ bundleIn.writeToParcel(parcel, 0);
+
+ Bundle bundleOut = new Bundle();
+ bundleOut.setClassLoader(DownloadRequest.class.getClassLoader());
+ parcel.setDataPosition(0); // to read the parcel from the beginning.
+ bundleOut.readFromParcel(parcel);
+
+ ArrayList<DownloadRequest> fromParcel = bundleOut.getParcelableArrayList("r");
+
+ // spot-check contents to ensure they are the same
+ // DownloadRequest.equals() implementation doesn't quite work
+ // for DownloadRequest.argument (a Bundle)
+ assertEquals(message + " - size", toParcel.size(), fromParcel.size());
+ assertEquals(message + " - source", toParcel.get(1).getSource(), fromParcel.get(1).getSource());
+ assertEquals(message + " - password", toParcel.get(0).getPassword(), fromParcel.get(0).getPassword());
+ assertEquals(message + " - argument", toString(toParcel.get(0).getArguments()),
+ toString(fromParcel.get(0).getArguments()));
+ }
+
+ private static String toString(Bundle b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ for (String key: b.keySet()) {
+ Object val = b.get(key);
+ sb.append("(").append(key).append(":").append(val).append(") ");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private FeedFile createFeedItem(final int id) {
+ // Use mockito would be less verbose, but it'll take extra 1 second for this tiny test
+ return new FeedFile() {
+ @Override
+ public long getId() {
+ return id;
+ }
+
+ @Override
+ public String getDownload_url() {
+ return "http://example.com/episode" + id;
+ }
+
+ @Override
+ public int getTypeAsInt() {
+ return 0;
+ }
+
+ @Override
+ public String getHumanReadableIdentifier() {
+ return "human-id-" + id;
+ }
+ };
+ }
+}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomTextTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomTextTest.java
new file mode 100644
index 000000000..1ab194133
--- /dev/null
+++ b/core/src/androidTest/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomTextTest.java
@@ -0,0 +1,49 @@
+package de.danoeh.antennapod.core.syndication.namespace.atom;
+
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.runners.Parameterized.Parameter;
+import static org.junit.runners.Parameterized.Parameters;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit test for {@link AtomText}.
+ */
+@SmallTest
+@RunWith(Parameterized.class)
+public class AtomTextTest {
+
+ @Parameter(value = 0)
+ public String input;
+
+ @Parameter(value = 1)
+ public String expectedOutput;
+
+ @Parameters
+ public static Collection<Object[]> initParameters() {
+ return Arrays.asList(new Object[][] {
+ {"&gt;", ">"},
+ {">", ">"},
+ {"&lt;Fran&ccedil;ais&gt;", "<Français>"},
+ {"ßÄÖÜ", "ßÄÖÜ"},
+ {"&quot;", "\""},
+ {"&szlig;", "ß"},
+ {"&#8217;", "’"},
+ {"&#x2030;", "‰"},
+ {"&euro;", "€"},
+ });
+ }
+
+ @Test
+ public void testProcessingHtml() {
+ final AtomText atomText = new AtomText("", new NSAtom(), AtomText.TYPE_HTML);
+ atomText.setContent(input);
+ assertEquals(expectedOutput, atomText.getProcessedContent());
+ }
+}
diff --git a/core/src/debug/res/drawable/ic_launcher_background_debug.xml b/core/src/debug/res/drawable/ic_launcher_background_debug.xml
new file mode 100644
index 000000000..3bc445a92
--- /dev/null
+++ b/core/src/debug/res/drawable/ic_launcher_background_debug.xml
@@ -0,0 +1,179 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="19.242424"
+ android:viewportHeight="19.242424">
+ <group android:translateX="3.271212"
+ android:translateY="3.271212">
+ <path
+ android:pathData="M-5.2197,-5.2361h23.5693v23.3376h-23.5693z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.1750972"
+ android:fillColor="#008ab8"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:fillAlpha="1"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M19.0258,11.307l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18.8452,8.7757l0.0258,0.3616l-24.1082,1.7194l-0.0258,-0.3616z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.1376536"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18.6819,6.4854l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18.5099,4.0746l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18.3294,1.5432l0.0258,0.3616l-24.1082,1.7194l-0.0258,-0.3616z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.13765359"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18.166,-0.7471l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M19.1977,13.7179l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M17.9941,-3.1579l0.0086,0.1205l-24.1082,1.7194l-0.0086,-0.1205z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M11.6532,-6.2797l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M9.1218,-6.0991l0.3616,-0.0258l1.7194,24.1082l-0.3616,0.0258z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.1376536"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M6.8315,-5.9358l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M4.4207,-5.7638l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M1.8893,-5.5833l0.3616,-0.0258l1.7194,24.1082l-0.3616,0.0258z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.13765359"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M-0.4009,-5.42l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M14.064,-6.4516l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M-2.8118,-5.248l0.1205,-0.0086l1.7194,24.1082l-0.1205,0.0086z"
+ android:strokeAlpha="1"
+ android:strokeLineJoin="round"
+ android:strokeWidth="0.07947434"
+ android:fillColor="#0079a1"
+ android:fillAlpha="1"
+ android:strokeColor="#00000000"
+ android:fillType="nonZero"
+ android:strokeLineCap="round"/>
+ </group>
+</vector>
diff --git a/core/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/core/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..74ad49784
--- /dev/null
+++ b/core/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background_debug"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/core/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..74ad49784
--- /dev/null
+++ b/core/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background_debug"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
index 03ce82140..54cd84b43 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -8,6 +8,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.exception.RxJavaErrorHandlerSetup;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
* Stores callbacks for core classes like Services, DB classes etc. and other configuration variables.
@@ -44,6 +45,7 @@ public class ClientConfig {
NetworkUtils.init(context);
SleepTimerPreferences.init(context);
RxJavaErrorHandlerSetup.setupRxJavaErrorHandler();
+ NotificationUtils.createChannels(context);
initialized = true;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadLogEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadLogEvent.java
new file mode 100644
index 000000000..7428c5b00
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadLogEvent.java
@@ -0,0 +1,16 @@
+package de.danoeh.antennapod.core.event;
+
+public class DownloadLogEvent {
+
+ private DownloadLogEvent() {
+ }
+
+ public static DownloadLogEvent listUpdated() {
+ return new DownloadLogEvent();
+ }
+
+ @Override
+ public String toString() {
+ return "DownloadLogEvent";
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
index 9acd7728a..f549940b7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
@@ -30,7 +30,8 @@ public class DownloaderUpdate {
DownloaderUpdate(@NonNull List<Downloader> downloaders) {
this.downloaders = downloaders;
- LongList feedIds1 = new LongList(), mediaIds1 = new LongList();
+ LongList feedIds1 = new LongList();
+ LongList mediaIds1 = new LongList();
for(Downloader d1 : downloaders) {
int type = d1.getDownloadRequest().getFeedfileType();
long id = d1.getDownloadRequest().getFeedfileId();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
new file mode 100644
index 000000000..ca8db3cc9
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
@@ -0,0 +1,28 @@
+package de.danoeh.antennapod.core.event;
+
+import de.danoeh.antennapod.core.feed.Feed;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FeedListUpdateEvent {
+ private final List<Long> feeds = new ArrayList<>();
+
+ public FeedListUpdateEvent(List<Feed> feeds) {
+ for (Feed feed : feeds) {
+ this.feeds.add(feed.getId());
+ }
+ }
+
+ public FeedListUpdateEvent(Feed feed) {
+ feeds.add(feed.getId());
+ }
+
+ public FeedListUpdateEvent(long feedId) {
+ feeds.add(feedId);
+ }
+
+ public boolean contains(Feed feed) {
+ return feeds.contains(feed.getId());
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java
new file mode 100644
index 000000000..cd3f27bf5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java
@@ -0,0 +1,16 @@
+package de.danoeh.antennapod.core.event;
+
+public class PlaybackHistoryEvent {
+
+ private PlaybackHistoryEvent() {
+ }
+
+ public static PlaybackHistoryEvent listUpdated() {
+ return new PlaybackHistoryEvent();
+ }
+
+ @Override
+ public String toString() {
+ return "PlaybackHistoryEvent";
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java
new file mode 100644
index 000000000..fe7f17968
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java
@@ -0,0 +1,6 @@
+package de.danoeh.antennapod.core.event;
+
+public class PlayerStatusEvent {
+ public PlayerStatusEvent() {
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java
new file mode 100644
index 000000000..c3efbfe8b
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java
@@ -0,0 +1,6 @@
+package de.danoeh.antennapod.core.event;
+
+public class UnreadItemsUpdateEvent {
+ public UnreadItemsUpdateEvent() {
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java b/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
deleted file mode 100644
index b769eaf55..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.os.Handler;
-import android.util.Log;
-
-import java.util.AbstractQueue;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * Notifies its observers about changes in the feed database. Observers can
- * register by retrieving an instance of this class and registering an
- * EventListener. When new events arrive, the EventDistributor will process the
- * event queue in a handler that runs on the main thread. The observers will only
- * be notified once if the event queue contains multiple elements.
- *
- * Events can be sent with the send* methods.
- */
-public class EventDistributor extends Observable {
- private static final String TAG = "EventDistributor";
-
- public static final int FEED_LIST_UPDATE = 1;
- public static final int UNREAD_ITEMS_UPDATE = 2;
- public static final int DOWNLOADLOG_UPDATE = 8;
- public static final int PLAYBACK_HISTORY_UPDATE = 16;
- public static final int DOWNLOAD_HANDLED = 64;
- public static final int PLAYER_STATUS_UPDATE = 128;
-
- private final Handler handler;
- private final AbstractQueue<Integer> events;
-
- private static EventDistributor instance;
-
- private EventDistributor() {
- this.handler = new Handler();
- events = new ConcurrentLinkedQueue<>();
- }
-
- public static synchronized EventDistributor getInstance() {
- if (instance == null) {
- instance = new EventDistributor();
- }
- return instance;
- }
-
- public void register(EventListener el) {
- addObserver(el);
- }
-
- public void unregister(EventListener el) {
- deleteObserver(el);
- }
-
- private void addEvent(Integer i) {
- events.offer(i);
- handler.post(EventDistributor.this::processEventQueue);
- }
-
- private void processEventQueue() {
- Integer result = 0;
- Log.d(TAG, "Processing event queue. Number of events: " + events.size());
- for (Integer current = events.poll(); current != null; current = events
- .poll()) {
- result |= current;
- }
- if (result != 0) {
- Log.d(TAG, "Notifying observers. Data: " + result);
- setChanged();
- notifyObservers(result);
- } else {
- Log.d(TAG, "Event queue didn't contain any new events. Observers will not be notified.");
- }
- }
-
- @Override
- public void addObserver(Observer observer) {
- super.addObserver(observer);
- }
-
- public void sendUnreadItemsUpdateBroadcast() {
- addEvent(UNREAD_ITEMS_UPDATE);
- }
-
- public void sendFeedUpdateBroadcast() {
- addEvent(FEED_LIST_UPDATE);
- }
-
- public void sendPlaybackHistoryUpdateBroadcast() {
- addEvent(PLAYBACK_HISTORY_UPDATE);
- }
-
- public void sendDownloadLogUpdateBroadcast() {
- addEvent(DOWNLOADLOG_UPDATE);
- }
-
- public void sendPlayerStatusUpdateBroadcast() { addEvent(PLAYER_STATUS_UPDATE); }
-
- public abstract static class EventListener implements Observer {
-
- @Override
- public void update(Observable observable, Object data) {
- if (observable instanceof EventDistributor
- && data instanceof Integer) {
- update((EventDistributor) observable, (Integer) data);
- }
- }
-
- public abstract void update(EventDistributor eventDistributor,
- Integer arg);
- }
-}
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 61a09565f..58a21760b 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
@@ -1,9 +1,10 @@
package de.danoeh.antennapod.core.feed;
import android.database.Cursor;
-import androidx.annotation.Nullable;
import android.text.TextUtils;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -11,6 +12,8 @@ import java.util.List;
import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.SortOrder;
+
/**
* Data Object for a whole feed
*
@@ -89,12 +92,19 @@ public class Feed extends FeedFile implements ImageResource {
private FeedItemFilter itemfilter;
/**
+ * User-preferred sortOrder for display.
+ * Only those of scope {@link SortOrder.Scope#INTRA_FEED} is allowed.
+ */
+ @Nullable
+ private SortOrder sortOrder;
+
+ /**
* This constructor is used for restoring a feed from the database.
*/
public Feed(long id, String lastUpdate, String title, String customTitle, String link, String description, String paymentLink,
String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
String downloadUrl, boolean downloaded, boolean paged, String nextPageLink,
- String filter, boolean lastUpdateFailed) {
+ String filter, @Nullable SortOrder sortOrder, boolean lastUpdateFailed) {
super(fileUrl, downloadUrl, downloaded);
this.id = id;
this.feedTitle = title;
@@ -116,6 +126,7 @@ public class Feed extends FeedFile implements ImageResource {
} else {
this.itemfilter = new FeedItemFilter(new String[0]);
}
+ setSortOrder(sortOrder);
this.lastUpdateFailed = lastUpdateFailed;
}
@@ -126,7 +137,7 @@ public class Feed extends FeedFile implements ImageResource {
String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
String downloadUrl, boolean downloaded) {
this(id, lastUpdate, title, null, link, description, paymentLink, author, language, type, feedIdentifier, imageUrl,
- fileUrl, downloadUrl, downloaded, false, null, null, false);
+ fileUrl, downloadUrl, downloaded, false, null, null, null, false);
}
/**
@@ -181,6 +192,7 @@ public class Feed extends FeedFile implements ImageResource {
int indexIsPaged = cursor.getColumnIndex(PodDBAdapter.KEY_IS_PAGED);
int indexNextPageLink = cursor.getColumnIndex(PodDBAdapter.KEY_NEXT_PAGE_LINK);
int indexHide = cursor.getColumnIndex(PodDBAdapter.KEY_HIDE);
+ int indexSortOrder = cursor.getColumnIndex(PodDBAdapter.KEY_SORT_ORDER);
int indexLastUpdateFailed = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
int indexImageUrl = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
@@ -203,6 +215,7 @@ public class Feed extends FeedFile implements ImageResource {
cursor.getInt(indexIsPaged) > 0,
cursor.getString(indexNextPageLink),
cursor.getString(indexHide),
+ SortOrder.fromCodeString(cursor.getString(indexSortOrder)),
cursor.getInt(indexLastUpdateFailed) > 0
);
@@ -247,7 +260,9 @@ public class Feed extends FeedFile implements ImageResource {
@Override
public String getHumanReadableIdentifier() {
- if (feedTitle != null) {
+ if (!TextUtils.isEmpty(customTitle)) {
+ return customTitle;
+ } else if (!TextUtils.isEmpty(feedTitle)) {
return feedTitle;
} else {
return download_url;
@@ -523,6 +538,19 @@ public class Feed extends FeedFile implements ImageResource {
}
}
+ @Nullable
+ public SortOrder getSortOrder() {
+ return sortOrder;
+ }
+
+ public void setSortOrder(@Nullable SortOrder sortOrder) {
+ if (sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED) {
+ throw new IllegalArgumentException("The specified sortOrder " + sortOrder
+ + " is invalid. Only those with INTRA_FEED scope are allowed.");
+ }
+ this.sortOrder = sortOrder;
+ }
+
public boolean hasLastUpdateFailed() {
return this.lastUpdateFailed;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java
index b790faadf..15cdf92dc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java
@@ -6,7 +6,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
public class FeedEvent {
public enum Action {
- FILTER_CHANGED
+ FILTER_CHANGED,
+ SORT_ORDER_CHANGED
}
private final Action action;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
index ecd34acff..ca387925f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
@@ -185,7 +185,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, ImageR
if (other.link != null) {
link = other.link;
}
- if (other.pubDate != null && other.pubDate.equals(pubDate)) {
+ if (other.pubDate != null && !other.pubDate.equals(pubDate)) {
pubDate = other.pubDate;
}
if (other.media != null) {
@@ -375,10 +375,10 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, ImageR
@Override
public String getImageLocation() {
- if(media != null && media.hasEmbeddedPicture()) {
- return media.getImageLocation();
- } else if (imageUrl != null) {
- return imageUrl;
+ if (imageUrl != null) {
+ return imageUrl;
+ } else if (media != null && media.hasEmbeddedPicture()) {
+ return media.getLocalMediaUrl();
} else if (feed != null) {
return feed.getImageLocation();
} else {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
index b1c2ff15a..572f32e66 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
@@ -566,10 +566,10 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public String getImageLocation() {
- if (hasEmbeddedPicture()) {
- return getLocalMediaUrl();
- } else if(item != null) {
+ if (item != null) {
return item.getImageLocation();
+ } else if (hasEmbeddedPicture()) {
+ return getLocalMediaUrl();
} else {
return null;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java
new file mode 100644
index 000000000..674663a6d
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java
@@ -0,0 +1,45 @@
+package de.danoeh.antennapod.core.feed.util;
+
+import de.danoeh.antennapod.core.asynctask.ImageResource;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * Utility class to use the appropriate image resource based on {@link UserPreferences}
+ */
+public final class ImageResourceUtils {
+
+ private ImageResourceUtils() {
+ }
+
+ public static String getImageLocation(ImageResource resource) {
+ if (UserPreferences.getUseEpisodeCoverSetting()) {
+ return resource.getImageLocation();
+ } else {
+ return getShowImageLocation(resource);
+ }
+ }
+
+ private static String getShowImageLocation(ImageResource resource) {
+
+ if (resource instanceof FeedItem) {
+ FeedItem item = (FeedItem) resource;
+ if (item.getFeed() != null) {
+ return item.getFeed().getImageLocation();
+ } else {
+ return null;
+ }
+ } else if (resource instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) resource;
+ FeedItem item = media.getItem();
+ if (item != null && item.getFeed() != null) {
+ return item.getFeed().getImageLocation();
+ } else {
+ return null;
+ }
+ } else {
+ return resource.getImageLocation();
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
index d48e41c3b..0d5ecbb71 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
@@ -1,14 +1,22 @@
-package de.danoeh.antennapod.core.preferences;
+package de.danoeh.antennapod.core.feed.util;
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.MediaType;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.Playable;
import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
-public class PlaybackSpeedHelper {
+/**
+ * Utility class to use the appropriate playback speed based on {@link PlaybackPreferences}
+ */
+public final class PlaybackSpeedUtils {
+
+ private PlaybackSpeedUtils() {
+ }
/**
* Returns the currently configured playback speed for the specified media.
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 cbd22ceb0..3cc906b7f 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
@@ -83,7 +83,16 @@ public class FastBlurTransformation extends BitmapTransformation {
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
- int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+ int rsum;
+ int gsum;
+ int bsum;
+ int x;
+ int y;
+ int i;
+ int p;
+ int yp;
+ int yi;
+ int yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
@@ -101,8 +110,12 @@ public class FastBlurTransformation extends BitmapTransformation {
int[] sir;
int rbs;
int r1 = radius + 1;
- int routsum, goutsum, boutsum;
- int rinsum, ginsum, binsum;
+ int routsum;
+ int goutsum;
+ int boutsum;
+ int rinsum;
+ int ginsum;
+ int binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
index 192674f0d..4e2233407 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
@@ -5,11 +5,12 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.playback.Playable;
+import org.greenrobot.eventbus.EventBus;
import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
@@ -97,7 +98,7 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(PREF_CURRENT_PLAYER_STATUS)) {
- EventDistributor.getInstance().sendPlayerStatusUpdateBroadcast();
+ EventBus.getDefault().post(new PlayerStatusEvent());
}
}
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 ba02a9b8c..4f871e83b 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
@@ -4,11 +4,13 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import androidx.core.app.NotificationCompat;
-import android.text.TextUtils;
-import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
@@ -23,8 +25,8 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
@@ -53,6 +55,7 @@ public class UserPreferences {
private static final String PREF_DRAWER_FEED_ORDER = "prefDrawerFeedOrder";
private static final String PREF_DRAWER_FEED_COUNTER = "prefDrawerFeedIndicator";
public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
+ public static final String PREF_USE_EPISODE_COVER = "prefEpisodeCover";
private static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
public static final String PREF_COMPACT_NOTIFICATION_BUTTONS = "prefCompactNotificationButtons";
public static final String PREF_LOCKSCREEN_BACKGROUND = "prefLockscreenBackground";
@@ -60,8 +63,6 @@ public class UserPreferences {
public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior";
private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage";
- // Queue
- private static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted";
public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder";
@@ -81,9 +82,11 @@ public class UserPreferences {
private static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
public static final String PREF_VIDEO_BEHAVIOR = "prefVideoBehavior";
private static final String PREF_TIME_RESPECTS_SPEED = "prefPlaybackTimeRespectsSpeed";
+ public static final String PREF_STREAM_OVER_DOWNLOAD = "prefStreamOverDownload";
// Network
private static final String PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded";
+ public static final String PREF_ENQUEUE_LOCATION = "prefEnqueueLocation";
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
private static final String PREF_MOBILE_UPDATE = "prefMobileUpdateTypes";
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
@@ -180,6 +183,17 @@ public class UserPreferences {
}
}
+ public static int getTranslucentTheme() {
+ int theme = getTheme();
+ if (theme == R.style.Theme_AntennaPod_Dark) {
+ return R.style.Theme_AntennaPod_Dark_Translucent;
+ } else if (theme == R.style.Theme_AntennaPod_TrueBlack) {
+ return R.style.Theme_AntennaPod_TrueBlack_Translucent;
+ } else {
+ return R.style.Theme_AntennaPod_Light_Translucent;
+ }
+ }
+
public static List<String> getHiddenDrawerItems() {
String hiddenItems = prefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "");
return new ArrayList<>(Arrays.asList(TextUtils.split(hiddenItems, ",")));
@@ -232,6 +246,13 @@ public class UserPreferences {
}
/**
+ * @return {@code true} if episodes should use their own cover, {@code false} otherwise
+ */
+ public static boolean getUseEpisodeCoverSetting() {
+ return prefs.getBoolean(PREF_USE_EPISODE_COVER, true);
+ }
+
+ /**
* Returns notification priority.
*
* @return NotificationCompat.PRIORITY_MAX or NotificationCompat.PRIORITY_DEFAULT
@@ -275,8 +296,33 @@ public class UserPreferences {
return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true);
}
- public static boolean enqueueAtFront() {
- return prefs.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
+ @VisibleForTesting
+ public static void setEnqueueDownloadedEpisodes(boolean enqueueDownloadedEpisodes) {
+ prefs.edit()
+ .putBoolean(PREF_ENQUEUE_DOWNLOADED, enqueueDownloadedEpisodes)
+ .apply();
+ }
+
+ public enum EnqueueLocation {
+ BACK, FRONT, AFTER_CURRENTLY_PLAYING
+ }
+
+ @NonNull
+ public static EnqueueLocation getEnqueueLocation() {
+ String valStr = prefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name());
+ try {
+ return EnqueueLocation.valueOf(valStr);
+ } catch (Throwable t) {
+ // should never happen but just in case
+ Log.e(TAG, "getEnqueueLocation: invalid value '" + valStr + "' Use default.", t);
+ return EnqueueLocation.BACK;
+ }
+ }
+
+ public static void setEnqueueLocation(@NonNull EnqueueLocation location) {
+ prefs.edit()
+ .putString(PREF_ENQUEUE_LOCATION, location.name())
+ .apply();
}
public static boolean isPauseOnHeadsetDisconnect() {
@@ -304,6 +350,14 @@ public class UserPreferences {
return prefs.getBoolean(PREF_FOLLOW_QUEUE, true);
}
+ /**
+ * Set to true to enable Continuous Playback
+ */
+ @VisibleForTesting
+ public static void setFollowQueue(boolean value) {
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).apply();
+ }
+
public static boolean shouldSkipKeepEpisode() { return prefs.getBoolean(PREF_SKIP_KEEPS_EPISODE, true); }
public static boolean shouldFavoriteKeepEpisode() {
@@ -490,6 +544,11 @@ public class UserPreferences {
return prefs.getBoolean(PREF_ENABLE_AUTODL, false);
}
+ @VisibleForTesting
+ public static void setEnableAutodownload(boolean enabled) {
+ prefs.edit().putBoolean(PREF_ENABLE_AUTODL, enabled).apply();
+ }
+
public static boolean isEnableAutodownloadOnBattery() {
return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
}
@@ -507,8 +566,7 @@ public class UserPreferences {
prefs.edit().putString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE).apply();
cacheSizeInt = Integer.parseInt(IMAGE_CACHE_DEFAULT_VALUE);
}
- int cacheSizeMB = cacheSizeInt * 1024 * 1024;
- return cacheSizeMB;
+ return cacheSizeInt * 1024 * 1024;
}
public static int getFastForwardSecs() {
@@ -747,6 +805,14 @@ 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);
}
@@ -767,6 +833,9 @@ public class UserPreferences {
}
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
+ if (!isEnableAutodownload()) {
+ return new APNullCleanupAlgorithm();
+ }
int cleanupValue = getEpisodeCleanupValue();
if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
return new APQueueCleanupAlgorithm();
@@ -778,7 +847,7 @@ public class UserPreferences {
}
public static int getEpisodeCleanupValue() {
- return Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));
+ return Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "" + EPISODE_CLEANUP_NULL));
}
public static void setEpisodeCleanupValue(int episodeCleanupValue) {
@@ -931,6 +1000,10 @@ public class UserPreferences {
return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false);
}
+ public static boolean streamOverDownload() {
+ return prefs.getBoolean(PREF_STREAM_OVER_DOWNLOAD, false);
+ }
+
/**
* Returns if the queue is in keep sorted mode.
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
index 7663cdbe4..2e592bdf5 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
@@ -11,11 +11,12 @@ import java.util.Arrays;
import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
-
public class PlayerWidget extends AppWidgetProvider {
private static final String TAG = "PlayerWidget";
- private static final String PREFS_NAME = "PlayerWidgetPrefs";
+ public static final String PREFS_NAME = "PlayerWidgetPrefs";
private static final String KEY_ENABLED = "WidgetEnabled";
+ public static final String KEY_WIDGET_COLOR = "widget_color";
+ public static final int DEFAULT_COLOR = 0x00262C31;
@Override
public void onReceive(Context context, Intent intent) {
@@ -45,6 +46,16 @@ public class PlayerWidget extends AppWidgetProvider {
setEnabled(context, false);
}
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ Log.d(TAG, "OnDeleted");
+ for (int appWidgetId : appWidgetIds) {
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ prefs.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply();
+ }
+ super.onDeleted(context, appWidgetIds);
+ }
+
public static boolean isEnabled(Context context) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return prefs.getBoolean(KEY_ENABLED, false);
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 a34a1e2c3..2d9de1894 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
@@ -6,6 +6,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
@@ -24,12 +25,13 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.receiver.PlayerWidget;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -128,7 +130,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
int iconSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
icon = Glide.with(PlayerWidgetJobService.this)
.asBitmap()
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
.submit(iconSize, iconSize)
.get(500, TimeUnit.MILLISECONDS);
@@ -147,7 +149,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
progressString = getProgressString(playbackService.getCurrentPosition(),
playbackService.getDuration(), playbackService.getCurrentPlaybackSpeed());
} else {
- progressString = getProgressString(media.getPosition(), media.getDuration(), PlaybackSpeedHelper.getCurrentPlaybackSpeed(media));
+ progressString = getProgressString(media.getPosition(), media.getDuration(), PlaybackSpeedUtils.getCurrentPlaybackSpeed(media));
}
if (progressString != null) {
@@ -192,6 +194,11 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
} else {
views.setViewVisibility(R.id.layout_center, View.VISIBLE);
}
+
+ SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, Context.MODE_PRIVATE);
+ int backgroundColor = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + id, PlayerWidget.DEFAULT_COLOR);
+ views.setInt(R.id.widgetLayout, "setBackgroundColor", backgroundColor);
+
manager.updateAppWidget(id, views);
}
} else {
@@ -212,7 +219,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
}
private String getProgressString(int position, int duration, float speed) {
- if (position > 0 && duration > 0) {
+ if (position >= 0 && duration > 0) {
TimeSpeedConverter converter = new TimeSpeedConverter(speed);
position = converter.convert(position);
duration = converter.convert(duration);
@@ -241,6 +248,5 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
}
Log.d(TAG, "Disconnected from service");
}
-
};
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
index 2082b95d1..672cbe9e1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
@@ -49,7 +49,7 @@ public class AntennapodHttpClient {
private static final String TAG = "AntennapodHttpClient";
- private static final int CONNECTION_TIMEOUT = 30000;
+ private static final int CONNECTION_TIMEOUT = 10000;
private static final int READ_TIMEOUT = 30000;
private static final int MAX_CONNECTIONS = 8;
@@ -142,7 +142,7 @@ public class AntennapodHttpClient {
});
}
}
- if (16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) {
+ if (Build.VERSION.SDK_INT < 21) {
builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java
new file mode 100644
index 000000000..c0de6c825
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DefaultDownloaderFactory.java
@@ -0,0 +1,20 @@
+package de.danoeh.antennapod.core.service.download;
+
+import android.util.Log;
+import android.webkit.URLUtil;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class DefaultDownloaderFactory implements DownloaderFactory {
+ private static final String TAG = "DefaultDwnldrFactory";
+
+ @Nullable
+ @Override
+ public Downloader create(@NonNull DownloadRequest request) {
+ if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
+ Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
+ return null;
+ }
+ return new HttpDownloader(request);
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
index 60591899d..2dd46cf96 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
@@ -3,6 +3,8 @@ package de.danoeh.antennapod.core.service.download;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -26,6 +28,7 @@ public class DownloadRequest implements Parcelable {
private long soFar;
private long size;
private int statusMsg;
+ private boolean mediaEnqueued;
public DownloadRequest(@NonNull String destination,
@NonNull String source,
@@ -45,6 +48,7 @@ public class DownloadRequest implements Parcelable {
this.username = username;
this.password = password;
this.deleteOnFailure = deleteOnFailure;
+ this.mediaEnqueued = false;
this.arguments = (arguments != null) ? arguments : new Bundle();
}
@@ -74,17 +78,10 @@ public class DownloadRequest implements Parcelable {
feedfileType = in.readInt();
lastModified = in.readString();
deleteOnFailure = (in.readByte() > 0);
+ username = nullIfEmpty(in.readString());
+ password = nullIfEmpty(in.readString());
+ mediaEnqueued = (in.readByte() > 0);
arguments = in.readBundle();
- if (in.dataAvail() > 0) {
- username = in.readString();
- } else {
- username = null;
- }
- if (in.dataAvail() > 0) {
- password = in.readString();
- } else {
- password = null;
- }
}
@Override
@@ -101,13 +98,23 @@ public class DownloadRequest implements Parcelable {
dest.writeInt(feedfileType);
dest.writeString(lastModified);
dest.writeByte((deleteOnFailure) ? (byte) 1 : 0);
+ // in case of null username/password, still write an empty string
+ // (rather than skipping it). Otherwise, unmarshalling a collection
+ // of them from a Parcel (from an Intent extra to submit a request to DownloadService) will fail.
+ //
+ // see: https://stackoverflow.com/a/22926342
+ dest.writeString(nonNullString(username));
+ dest.writeString(nonNullString(password));
+ dest.writeByte((mediaEnqueued) ? (byte) 1 : 0);
dest.writeBundle(arguments);
- if (username != null) {
- dest.writeString(username);
- }
- if (password != null) {
- dest.writeString(password);
- }
+ }
+
+ private static String nonNullString(String str) {
+ return str != null ? str : "";
+ }
+
+ private static String nullIfEmpty(String str) {
+ return TextUtils.isEmpty(str) ? null : str;
}
public static final Parcelable.Creator<DownloadRequest> CREATOR = new Parcelable.Creator<DownloadRequest>() {
@@ -145,6 +152,7 @@ public class DownloadRequest implements Parcelable {
if (title != null ? !title.equals(that.title) : that.title != null) return false;
if (username != null ? !username.equals(that.username) : that.username != null)
return false;
+ if (mediaEnqueued != that.mediaEnqueued) return false;
return true;
}
@@ -164,6 +172,7 @@ public class DownloadRequest implements Parcelable {
result = 31 * result + (int) (soFar ^ (soFar >>> 32));
result = 31 * result + (int) (size ^ (size >>> 32));
result = 31 * result + statusMsg;
+ result = 31 * result + (mediaEnqueued ? 1 : 0);
return result;
}
@@ -245,6 +254,18 @@ public class DownloadRequest implements Parcelable {
return deleteOnFailure;
}
+ public boolean isMediaEnqueued() {
+ return mediaEnqueued;
+ }
+
+ /**
+ * Set to true if the media is enqueued because of this download.
+ * The state is helpful if the download is cancelled, and undoing the enqueue is needed.
+ */
+ public void setMediaEnqueued(boolean mediaEnqueued) {
+ this.mediaEnqueued = mediaEnqueued;
+ }
+
public Bundle getArguments() {
return arguments;
}
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 b6e5de254..72e080902 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
@@ -7,78 +7,58 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.MediaMetadataRetriever;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
+import android.text.TextUtils;
+import android.util.Log;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.core.app.NotificationCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.webkit.URLUtil;
import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import org.apache.commons.io.FileUtils;
import org.greenrobot.eventbus.EventBus;
-import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.xml.parsers.ParserConfigurationException;
-
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
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.gpoddernet.model.GpodnetEpisodeAction;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
+import de.danoeh.antennapod.core.service.download.handler.FailedDownloadHandler;
+import de.danoeh.antennapod.core.service.download.handler.FeedSyncTask;
+import de.danoeh.antennapod.core.service.download.handler.MediaDownloadedHandler;
+import de.danoeh.antennapod.core.service.download.handler.PostDownloaderTask;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
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.syndication.handler.FeedHandler;
-import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
-import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
-import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DownloadError;
-import de.danoeh.antennapod.core.util.InvalidFeedException;
-import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
* Manages the download of feedfiles in the app. Downloads can be enqueued via the startService intent.
- * The argument of the intent is an instance of DownloadRequest in the EXTRA_REQUEST field of
+ * The argument of the intent is an instance of DownloadRequest in the EXTRA_REQUESTS field of
* the intent.
* After the downloads have finished, the downloaded object will be passed on to a specific handler, depending on the
* type of the feedfile.
@@ -105,28 +85,25 @@ public class DownloadService extends Service {
/**
* Extra for ACTION_ENQUEUE_DOWNLOAD intent.
*/
- public static final String EXTRA_REQUEST = "request";
+ public static final String EXTRA_REQUESTS = "downloadRequests";
+
+ public static final String EXTRA_CLEANUP_MEDIA = "cleanupMedia";
+
+ public static final int NOTIFICATION_ID = 2;
/**
* Contains all completed downloads that have not been included in the report yet.
*/
- private List<DownloadStatus> reportQueue;
-
- private ExecutorService syncExecutor;
- private CompletionService<Downloader> downloadExecutor;
- private FeedSyncThread feedSyncThread;
-
- private DownloadRequester requester;
-
-
- private NotificationCompat.Builder notificationCompatBuilder;
- private static final int NOTIFICATION_ID = 2;
- private static final int REPORT_ID = 3;
+ private final List<DownloadStatus> reportQueue;
+ private final ExecutorService syncExecutor;
+ private final CompletionService<Downloader> downloadExecutor;
+ private final DownloadRequester requester;
+ private DownloadServiceNotification notificationManager;
/**
* Currently running downloads.
*/
- private List<Downloader> downloads;
+ private final List<Downloader> downloads;
/**
* Number of running downloads.
@@ -142,10 +119,10 @@ public class DownloadService extends Service {
private NotificationUpdater notificationUpdater;
private ScheduledFuture<?> notificationUpdaterFuture;
+ private ScheduledFuture<?> downloadPostFuture;
private static final int SCHED_EX_POOL_SIZE = 1;
private ScheduledThreadPoolExecutor schedExecutor;
-
- private final Handler postHandler = new Handler();
+ private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
private final IBinder mBinder = new LocalBinder();
@@ -155,110 +132,14 @@ public class DownloadService extends Service {
}
}
- private final Thread downloadCompletionThread = new Thread("DownloadCompletionThread") {
- private static final String TAG = "downloadCompletionThd";
-
- @Override
- public void run() {
- Log.d(TAG, "downloadCompletionThread was started");
- while (!isInterrupted()) {
- try {
- Downloader downloader = downloadExecutor.take().get();
- Log.d(TAG, "Received 'Download Complete' - message.");
- removeDownload(downloader);
- DownloadStatus status = downloader.getResult();
- boolean successful = status.isSuccessful();
-
- final int type = status.getFeedfileType();
- if (successful) {
- if (type == Feed.FEEDFILETYPE_FEED) {
- handleCompletedFeedDownload(downloader.getDownloadRequest());
- } else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- handleCompletedFeedMediaDownload(status, downloader.getDownloadRequest());
- }
- } else {
- numberOfDownloads.decrementAndGet();
- if (!status.isCancelled()) {
- if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
- postAuthenticationNotification(downloader.getDownloadRequest());
- } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && Integer.parseInt(status.getReasonDetailed()) == 416) {
-
- Log.d(TAG, "Requested invalid range, restarting download from the beginning");
- FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
- DownloadRequester.getInstance().download(DownloadService.this, downloader.getDownloadRequest());
- } else {
- Log.e(TAG, "Download failed");
- saveDownloadStatus(status);
- handleFailedDownload(status, downloader.getDownloadRequest());
-
- if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedItem item = getFeedItemFromId(status.getFeedfileId());
- if (item == null) {
- return;
- }
- boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && String.valueOf(HttpURLConnection.HTTP_NOT_FOUND).equals(status.getReasonDetailed());
- boolean forbidden = status.getReason() == DownloadError.ERROR_FORBIDDEN
- && 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) {
- DBWriter.saveFeedItemAutoDownloadFailed(item).get();
- }
- // to make lists reload the failed item, we fake an item update
- EventBus.getDefault().post(FeedItemEvent.updated(item));
- }
- }
- } else {
- // if FeedMedia download has been canceled, fake FeedItem update
- // so that lists reload that it
- if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedItem item = getFeedItemFromId(status.getFeedfileId());
- if (item == null) {
- return;
- }
- EventBus.getDefault().post(FeedItemEvent.updated(item));
- }
- }
- queryDownloadsAsync();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "DownloadCompletionThread was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in DownloadCompletionThread: " + e.getMessage());
- numberOfDownloads.decrementAndGet();
- }
- }
- Log.d(TAG, "End of downloadCompletionThread");
- }
- };
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent.getParcelableExtra(EXTRA_REQUEST) != null) {
- onDownloadQueued(intent);
- } else if (numberOfDownloads.get() == 0) {
- stopSelf();
- }
- return Service.START_NOT_STICKY;
- }
-
- @Override
- public void onCreate() {
- Log.d(TAG, "Service started");
- isRunning = true;
- handler = new Handler();
+ public DownloadService() {
reportQueue = Collections.synchronizedList(new ArrayList<>());
downloads = Collections.synchronizedList(new ArrayList<>());
numberOfDownloads = new AtomicInteger(0);
+ requester = DownloadRequester.getInstance();
- IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
- cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_ALL_DOWNLOADS);
- cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_DOWNLOAD);
- registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter);
syncExecutor = Executors.newSingleThreadExecutor(r -> {
- Thread t = new Thread(r);
+ Thread t = new Thread(r, "SyncThread");
t.setPriority(Thread.MIN_PRIORITY);
return t;
});
@@ -266,7 +147,7 @@ public class DownloadService extends Service {
downloadExecutor = new ExecutorCompletionService<>(
Executors.newFixedThreadPool(UserPreferences.getParallelDownloads(),
r -> {
- Thread t = new Thread(r);
+ Thread t = new Thread(r, "DownloadThread");
t.setPriority(Thread.MIN_PRIORITY);
return t;
}
@@ -274,18 +155,45 @@ public class DownloadService extends Service {
);
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE,
r -> {
- Thread t = new Thread(r);
+ Thread t = new Thread(r, "DownloadSchedExecutorThread");
t.setPriority(Thread.MIN_PRIORITY);
return t;
}, (r, executor) -> Log.w(TAG, "SchedEx rejected submission of new task")
);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null && intent.getParcelableArrayListExtra(EXTRA_REQUESTS) != null) {
+ Notification notification = notificationManager.updateNotifications(
+ requester.getNumberOfDownloads(), downloads);
+ startForeground(NOTIFICATION_ID, notification);
+ onDownloadQueued(intent);
+ } else if (numberOfDownloads.get() == 0) {
+ stopSelf();
+ } else {
+ Log.d(TAG, "onStartCommand: Unknown intent");
+ }
+ return Service.START_NOT_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "Service started");
+ isRunning = true;
+ handler = new Handler();
+ notificationManager = new DownloadServiceNotification(this);
+
+ IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
+ cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_ALL_DOWNLOADS);
+ cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_DOWNLOAD);
+ registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter);
+
downloadCompletionThread.start();
- feedSyncThread = new FeedSyncThread();
- feedSyncThread.start();
- setupNotificationBuilders();
- requester = DownloadRequester.getInstance();
- startForeground(NOTIFICATION_ID, updateNotifications());
+ Notification notification = notificationManager.updateNotifications(
+ requester.getNumberOfDownloads(), downloads);
+ startForeground(NOTIFICATION_ID, notification);
}
@Override
@@ -298,25 +206,32 @@ public class DownloadService extends Service {
Log.d(TAG, "Service shutting down");
isRunning = false;
- if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() &&
- UserPreferences.showDownloadReport()) {
- updateReport();
+ if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()
+ && UserPreferences.showDownloadReport()) {
+ notificationManager.updateReport(reportQueue);
+ reportQueue.clear();
}
- postHandler.removeCallbacks(postDownloaderTask);
EventBus.getDefault().postSticky(DownloadEvent.refresh(Collections.emptyList()));
- stopForeground(true);
- NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
-
downloadCompletionThread.interrupt();
+ try {
+ downloadCompletionThread.join(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
syncExecutor.shutdown();
schedExecutor.shutdown();
- feedSyncThread.shutdown();
cancelNotificationUpdater();
+ if (downloadPostFuture != null) {
+ downloadPostFuture.cancel(true);
+ }
unregisterReceiver(cancelDownloadReceiver);
+ stopForeground(true);
+ NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(NOTIFICATION_ID);
+
// if this was the initial gpodder sync, i.e. we just synced the feeds successfully,
// it is now time to sync the episode actions
if (GpodnetPreferences.loggedIn() &&
@@ -329,38 +244,122 @@ public class DownloadService extends Service {
DBTasks.autodownloadUndownloadedItems(getApplicationContext());
}
- private void setupNotificationBuilders() {
- notificationCompatBuilder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_DOWNLOADING)
- .setOngoing(true)
- .setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(this))
- .setSmallIcon(R.drawable.stat_notify_sync);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- notificationCompatBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
+ private final Thread downloadCompletionThread = new Thread("DownloadCompletionThread") {
+ private static final String TAG = "downloadCompletionThd";
+
+ @Override
+ public void run() {
+ Log.d(TAG, "downloadCompletionThread was started");
+ while (!isInterrupted()) {
+ try {
+ Downloader downloader = downloadExecutor.take().get();
+ Log.d(TAG, "Received 'Download Complete' - message.");
+
+ if (downloader.getResult().isSuccessful()) {
+ syncExecutor.execute(() -> {
+ handleSuccessfulDownload(downloader);
+ removeDownload(downloader);
+ numberOfDownloads.decrementAndGet();
+ queryDownloadsAsync();
+ });
+ } else {
+ handleFailedDownload(downloader);
+ removeDownload(downloader);
+ numberOfDownloads.decrementAndGet();
+ queryDownloadsAsync();
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "DownloadCompletionThread was interrupted");
+ return;
+ } catch (ExecutionException e) {
+ Log.e(TAG, "ExecutionException in DownloadCompletionThread: " + e.getMessage());
+ return;
+ }
+ }
+ Log.d(TAG, "End of downloadCompletionThread");
}
+ };
- Log.d(TAG, "Notification set up");
- }
+ private void handleSuccessfulDownload(Downloader downloader) {
+ DownloadRequest request = downloader.getDownloadRequest();
+ DownloadStatus status = downloader.getResult();
+ final int type = status.getFeedfileType();
- /**
- * Updates the contents of the service's notifications. Should be called
- * after setupNotificationBuilders.
- */
- private Notification updateNotifications() {
- if (notificationCompatBuilder == null) {
- return null;
+ if (type == Feed.FEEDFILETYPE_FEED) {
+ Log.d(TAG, "Handling completed Feed Download");
+ FeedSyncTask task = new FeedSyncTask(DownloadService.this, request);
+ boolean success = task.run();
+
+ if (success) {
+ // we create a 'successful' download log if the feed's last refresh failed
+ List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
+ if (log.size() > 0 && !log.get(0).isSuccessful()) {
+ saveDownloadStatus(task.getDownloadStatus());
+ }
+ } else {
+ DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
+ saveDownloadStatus(task.getDownloadStatus());
+ }
+ } else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ Log.d(TAG, "Handling completed FeedMedia Download");
+ MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
+ handler.run();
+ saveDownloadStatus(handler.getUpdatedStatus());
}
+ }
- String contentTitle = getString(R.string.download_notification_title);
- int numDownloads = requester.getNumberOfDownloads();
- String downloadsLeft = (numDownloads > 0) ?
- getResources().getQuantityString(R.plurals.downloads_left, numDownloads, numDownloads) :
- getString(R.string.downloads_processing);
- String bigText = compileNotificationString(downloads);
-
- notificationCompatBuilder.setContentTitle(contentTitle);
- notificationCompatBuilder.setContentText(downloadsLeft);
- notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
- return notificationCompatBuilder.build();
+ private void handleFailedDownload(Downloader downloader) {
+ DownloadStatus status = downloader.getResult();
+ final int type = status.getFeedfileType();
+
+ if (!status.isCancelled()) {
+ if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
+ notificationManager.postAuthenticationNotification(downloader.getDownloadRequest());
+ } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
+ && Integer.parseInt(status.getReasonDetailed()) == 416) {
+
+ Log.d(TAG, "Requested invalid range, restarting download from the beginning");
+ FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination()));
+ DownloadRequester.getInstance().download(DownloadService.this, downloader.getDownloadRequest());
+ } else {
+ Log.e(TAG, "Download failed");
+ saveDownloadStatus(status);
+ syncExecutor.execute(new FailedDownloadHandler(downloader.getDownloadRequest()));
+
+ if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
+ return;
+ }
+ boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
+ && String.valueOf(HttpURLConnection.HTTP_NOT_FOUND).equals(status.getReasonDetailed());
+ boolean forbidden = status.getReason() == DownloadError.ERROR_FORBIDDEN
+ && 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) {
+ try {
+ DBWriter.saveFeedItemAutoDownloadFailed(item).get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.d(TAG, "Ignoring exception while setting item download status");
+ e.printStackTrace();
+ }
+ }
+ // to make lists reload the failed item, we fake an item update
+ EventBus.getDefault().post(FeedItemEvent.updated(item));
+ }
+ }
+ } else {
+ // if FeedMedia download has been canceled, fake FeedItem update
+ // so that lists reload that it
+ if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedItem item = getFeedItemFromId(status.getFeedfileId());
+ if (item == null) {
+ return;
+ }
+ EventBus.getDefault().post(FeedItemEvent.updated(item));
+ }
+ }
}
private Downloader getDownloader(String downloadUrl) {
@@ -386,10 +385,20 @@ public class DownloadService extends Service {
Downloader d = getDownloader(url);
if (d != null) {
d.cancel();
- DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
+ DownloadRequest request = d.getDownloadRequest();
+ DownloadRequester.getInstance().removeDownload(request);
- FeedItem item = getFeedItemFromId(d.getDownloadRequest().getFeedfileId());
+ FeedItem item = getFeedItemFromId(request.getFeedfileId());
if (item != null) {
+ // undo enqueue upon cancel
+ if (request.isMediaEnqueued()) {
+ Log.v(TAG, "Undoing enqueue upon cancelling download");
+ try {
+ DBWriter.removeQueueItem(getApplicationContext(), false, item).get();
+ } catch (Throwable t) {
+ Log.e(TAG, "Unexpected exception during undoing enqueue upon cancel", t);
+ }
+ }
EventBus.getDefault().post(FeedItemEvent.updated(item));
}
} else {
@@ -410,24 +419,66 @@ public class DownloadService extends Service {
};
private void onDownloadQueued(Intent intent) {
- Log.d(TAG, "Received enqueue request");
- DownloadRequest request = intent.getParcelableExtra(EXTRA_REQUEST);
- if (request == null) {
+ List<DownloadRequest> requests = intent.getParcelableArrayListExtra(EXTRA_REQUESTS);
+ if (requests == null) {
throw new IllegalArgumentException(
"ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
}
+ boolean cleanupMedia = intent.getBooleanExtra(EXTRA_CLEANUP_MEDIA, false);
+ Log.d(TAG, "Received enqueue request. #requests=" + requests.size()
+ + ", cleanupMedia=" + cleanupMedia);
+
+ if (cleanupMedia) {
+ ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
+ .makeRoomForEpisodes(getApplicationContext(), requests.size());
+ }
+
+ // #2448: First, add to-download items to the queue before actual download
+ // so that the resulting queue order is the same as when download is clicked
+ List<? extends FeedItem> itemsEnqueued;
+ try {
+ itemsEnqueued = enqueueFeedItems(requests);
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected exception during enqueue before downloads. Abort download", e);
+ return;
+ }
+ for (DownloadRequest request : requests) {
+ onDownloadQueued(request, itemsEnqueued);
+ }
+ }
+
+ private List<? extends FeedItem> enqueueFeedItems(@NonNull List<? extends DownloadRequest> requests)
+ throws Exception {
+ List<FeedItem> feedItems = new ArrayList<>();
+ for (DownloadRequest request : requests) {
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ long mediaId = request.getFeedfileId();
+ FeedMedia media = DBReader.getFeedMedia(mediaId);
+ if (media == null) {
+ Log.w(TAG, "enqueueFeedItems() : FeedFile Id " + mediaId + " is not found. ignore it.");
+ continue;
+ }
+ feedItems.add(media.getItem());
+ }
+ }
+
+ return DBTasks.enqueueFeedItemsToDownload(getApplicationContext(), feedItems);
+ }
+
+ private void onDownloadQueued(@NonNull DownloadRequest request,
+ @NonNull List<? extends FeedItem> itemsEnqueued) {
writeFileUrl(request);
- Downloader downloader = getDownloader(request);
+ Downloader downloader = downloaderFactory.create(request);
if (downloader != null) {
numberOfDownloads.incrementAndGet();
- // smaller rss feeds before bigger media files
- if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- downloads.add(0, downloader);
- } else {
- downloads.add(downloader);
+
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && isEnqueued(request, itemsEnqueued)) {
+ request.setMediaEnqueued(true);
}
+ downloads.add(downloader);
downloadExecutor.submit(downloader);
postDownloaders();
@@ -436,26 +487,19 @@ public class DownloadService extends Service {
queryDownloads();
}
- @VisibleForTesting
- public interface DownloaderFactory {
- @Nullable
- Downloader create(@NonNull DownloadRequest request);
- }
-
- private static class DefaultDownloaderFactory implements DownloaderFactory {
- @Nullable
- @Override
- public Downloader create(@NonNull DownloadRequest request) {
- if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
- Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
- return null;
+ private static boolean isEnqueued(@NonNull DownloadRequest request,
+ @NonNull List<? extends FeedItem> itemsEnqueued) {
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ final long mediaId = request.getFeedfileId();
+ for (FeedItem item : itemsEnqueued) {
+ if (item.getMedia() != null && item.getMedia().getId() == mediaId) {
+ return true;
+ }
}
- return new HttpDownloader(request);
}
+ return false;
}
- private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
-
@VisibleForTesting
public static DownloaderFactory getDownloaderFactory() {
return downloaderFactory;
@@ -468,10 +512,6 @@ public class DownloadService extends Service {
DownloadService.downloaderFactory = downloaderFactory;
}
- private Downloader getDownloader(@NonNull DownloadRequest request) {
- return downloaderFactory.create(request);
- }
-
/**
* Remove download from the DownloadRequester list and from the
* DownloadService list.
@@ -498,55 +538,6 @@ public class DownloadService extends Service {
}
/**
- * Creates a notification at the end of the service lifecycle to notify the
- * user about the number of completed downloads. A report will only be
- * created if there is at least one failed download excluding images
- */
- private void updateReport() {
- // check if report should be created
- boolean createReport = false;
- int successfulDownloads = 0;
- int failedDownloads = 0;
-
- // a download report is created if at least one download has failed
- // (excluding failed image downloads)
- for (DownloadStatus status : reportQueue) {
- if (status.isSuccessful()) {
- successfulDownloads++;
- } else if (!status.isCancelled()) {
- createReport = true;
- failedDownloads++;
- }
- }
-
- if (createReport) {
- Log.d(TAG, "Creating report");
- // create notification object
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_ERROR)
- .setTicker(getString(R.string.download_report_title))
- .setContentTitle(getString(R.string.download_report_content_title))
- .setContentText(
- String.format(
- getString(R.string.download_report_content),
- successfulDownloads, failedDownloads)
- )
- .setSmallIcon(R.drawable.stat_notify_sync_error)
- .setContentIntent(
- ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(this)
- )
- .setAutoCancel(true);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- builder.setVisibility(Notification.VISIBILITY_PUBLIC);
- }
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(REPORT_ID, builder.build());
- } else {
- Log.d(TAG, "No report is created");
- }
- reportQueue.clear();
- }
-
- /**
* Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is
* used from a thread other than the main thread.
*/
@@ -565,53 +556,12 @@ public class DownloadService extends Service {
stopSelf();
} else {
setupNotificationUpdater();
- startForeground(NOTIFICATION_ID, updateNotifications());
+ Notification notification = notificationManager.updateNotifications(
+ requester.getNumberOfDownloads(), downloads);
+ startForeground(NOTIFICATION_ID, notification);
}
}
- private void postAuthenticationNotification(final DownloadRequest downloadRequest) {
- handler.post(() -> {
- final String resourceTitle = (downloadRequest.getTitle() != null) ?
- downloadRequest.getTitle() : downloadRequest.getSource();
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(DownloadService.this, NotificationUtils.CHANNEL_ID_USER_ACTION);
- builder.setTicker(getText(R.string.authentication_notification_title))
- .setContentTitle(getText(R.string.authentication_notification_title))
- .setContentText(getText(R.string.authentication_notification_msg))
- .setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg)
- + ": " + resourceTitle))
- .setSmallIcon(R.drawable.ic_notification_key)
- .setAutoCancel(true)
- .setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- builder.setVisibility(Notification.VISIBILITY_PUBLIC);
- }
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(downloadRequest.getSource().hashCode(), builder.build());
- });
- }
-
- /**
- * Is called whenever a Feed is downloaded
- */
- private void handleCompletedFeedDownload(DownloadRequest request) {
- Log.d(TAG, "Handling completed Feed Download");
- feedSyncThread.submitCompletedDownload(request);
- }
-
- /**
- * Is called whenever a FeedMedia is downloaded.
- */
- private void handleCompletedFeedMediaDownload(DownloadStatus status, DownloadRequest request) {
- Log.d(TAG, "Handling completed FeedMedia Download");
- syncExecutor.execute(new MediaHandlerThread(status, request));
- }
-
- private void handleFailedDownload(DownloadStatus status, DownloadRequest request) {
- Log.d(TAG, "Handling failed download");
- syncExecutor.execute(new FailedDownloadHandler(status, request));
- }
-
@Nullable
private FeedItem getFeedItemFromId(long id) {
FeedMedia media = DBReader.getFeedMedia(id);
@@ -623,300 +573,6 @@ public class DownloadService extends Service {
}
/**
- * Takes a single Feed, parses the corresponding file and refreshes
- * information in the manager
- */
- private class FeedSyncThread extends Thread {
- private static final String TAG = "FeedSyncThread";
-
- private final BlockingQueue<DownloadRequest> completedRequests = new LinkedBlockingDeque<>();
- private final CompletionService<Pair<DownloadRequest, FeedHandlerResult>> parserService = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor());
- private final ExecutorService dbService = Executors.newSingleThreadExecutor();
- private Future<?> dbUpdateFuture;
- private volatile boolean isActive = true;
- private volatile boolean isCollectingRequests = false;
-
- private static final long WAIT_TIMEOUT = 3000;
-
- FeedSyncThread() {
- super("FeedSyncThread");
- }
-
- /**
- * Waits for completed requests. Once the first request has been taken, the method will wait WAIT_TIMEOUT ms longer to
- * collect more completed requests.
- *
- * @return Collected feeds or null if the method has been interrupted during the first waiting period.
- */
- private List<Pair<DownloadRequest, FeedHandlerResult>> collectCompletedRequests() {
- List<Pair<DownloadRequest, FeedHandlerResult>> results = new LinkedList<>();
- DownloadRequester requester = DownloadRequester.getInstance();
- int tasks = 0;
-
- try {
- DownloadRequest request = completedRequests.take();
- parserService.submit(new FeedParserTask(request));
- tasks++;
- } catch (InterruptedException e) {
- Log.e(TAG, "FeedSyncThread was interrupted");
- return null;
- }
-
- tasks += pollCompletedDownloads();
-
- isCollectingRequests = true;
-
- if (requester.isDownloadingFeeds()) {
- // wait for completion of more downloads
- long startTime = System.currentTimeMillis();
- long currentTime = startTime;
- while (requester.isDownloadingFeeds() && (currentTime - startTime) < WAIT_TIMEOUT) {
- try {
- Log.d(TAG, "Waiting for " + (startTime + WAIT_TIMEOUT - currentTime) + " ms");
- sleep(startTime + WAIT_TIMEOUT - currentTime);
- } catch (InterruptedException e) {
- Log.d(TAG, "interrupted while waiting for more downloads");
- tasks += pollCompletedDownloads();
- } finally {
- currentTime = System.currentTimeMillis();
- }
- }
-
- tasks += pollCompletedDownloads();
-
- }
-
- isCollectingRequests = false;
-
- for (int i = 0; i < tasks; i++) {
- try {
- Pair<DownloadRequest, FeedHandlerResult> result = parserService.take().get();
- if (result != null) {
- results.add(result);
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "FeedSyncThread was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
- e.printStackTrace();
- }
- }
-
- return results;
- }
-
- private int pollCompletedDownloads() {
- int tasks = 0;
- while (!completedRequests.isEmpty()) {
- parserService.submit(new FeedParserTask(completedRequests.poll()));
- tasks++;
- }
- return tasks;
- }
-
- @Override
- public void run() {
- while (isActive) {
- final List<Pair<DownloadRequest, FeedHandlerResult>> results = collectCompletedRequests();
-
- if (results == null) {
- continue;
- }
-
- Log.d(TAG, "Bundling " + results.size() + " feeds");
-
- // Save information of feed in DB
- if (dbUpdateFuture != null) {
- try {
- dbUpdateFuture.get();
- } catch (InterruptedException e) {
- Log.e(TAG, "FeedSyncThread was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
- e.printStackTrace();
- }
- }
-
- dbUpdateFuture = dbService.submit(() -> {
- Feed[] savedFeeds = DBTasks.updateFeed(DownloadService.this, getFeeds(results));
-
- for (int i = 0; i < savedFeeds.length; i++) {
- Feed savedFeed = savedFeeds[i];
-
- // If loadAllPages=true, check if another page is available and queue it for download
- final boolean loadAllPages = results.get(i).first.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
- final Feed feed = results.get(i).second.feed;
- if (loadAllPages && feed.getNextPageLink() != null) {
- try {
- feed.setId(savedFeed.getId());
- DBTasks.loadNextPageOfFeed(DownloadService.this, savedFeed, true);
- } catch (DownloadRequestException e) {
- Log.e(TAG, "Error trying to load next page", e);
- }
- }
-
- ClientConfig.downloadServiceCallbacks.onFeedParsed(DownloadService.this,
- savedFeed);
-
- numberOfDownloads.decrementAndGet();
- }
-
- queryDownloadsAsync();
- });
-
- }
-
- if (dbUpdateFuture != null) {
- try {
- dbUpdateFuture.get();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted while updating the db");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException while updating the db: " + e.getMessage());
- }
- }
-
- Log.d(TAG, "Shutting down");
- }
-
- /**
- * Helper method
- */
- private Feed[] getFeeds(List<Pair<DownloadRequest, FeedHandlerResult>> results) {
- Feed[] feeds = new Feed[results.size()];
- for (int i = 0; i < results.size(); i++) {
- feeds[i] = results.get(i).second.feed;
- }
- return feeds;
- }
-
- private class FeedParserTask implements Callable<Pair<DownloadRequest, FeedHandlerResult>> {
-
- private final DownloadRequest request;
-
- private FeedParserTask(DownloadRequest request) {
- this.request = request;
- }
-
- @Override
- public Pair<DownloadRequest, FeedHandlerResult> call() throws Exception {
- return parseFeed(request);
- }
- }
-
- private Pair<DownloadRequest, FeedHandlerResult> parseFeed(DownloadRequest request) {
- Feed feed = new Feed(request.getSource(), request.getLastModified());
- feed.setFile_url(request.getDestination());
- feed.setId(request.getFeedfileId());
- feed.setDownloaded(true);
- feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
- VolumeReductionSetting.OFF, request.getUsername(), request.getPassword()));
- feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
-
- DownloadError reason = null;
- String reasonDetailed = null;
- boolean successful = true;
- FeedHandler feedHandler = new FeedHandler();
-
- FeedHandlerResult result = null;
- try {
- result = feedHandler.parseFeed(feed);
- Log.d(TAG, feed.getTitle() + " parsed");
- if (!checkFeedData(feed)) {
- throw new InvalidFeedException();
- }
-
- } catch (SAXException | IOException | ParserConfigurationException e) {
- successful = false;
- e.printStackTrace();
- reason = DownloadError.ERROR_PARSER_EXCEPTION;
- reasonDetailed = e.getMessage();
- } catch (UnsupportedFeedtypeException e) {
- e.printStackTrace();
- successful = false;
- reason = DownloadError.ERROR_UNSUPPORTED_TYPE;
- reasonDetailed = e.getMessage();
- } catch (InvalidFeedException e) {
- e.printStackTrace();
- successful = false;
- reason = DownloadError.ERROR_PARSER_EXCEPTION;
- reasonDetailed = e.getMessage();
- } finally {
- File feedFile = new File(request.getDestination());
- if (feedFile.exists()) {
- boolean deleted = feedFile.delete();
- Log.d(TAG, "Deletion of file '" + feedFile.getAbsolutePath() + "' " + (deleted ? "successful" : "FAILED"));
- }
- }
-
- if (successful) {
- // we create a 'successful' download log if the feed's last refresh failed
- List<DownloadStatus> log = DBReader.getFeedDownloadLog(feed);
- if (log.size() > 0 && !log.get(0).isSuccessful()) {
- saveDownloadStatus(
- new DownloadStatus(feed, feed.getHumanReadableIdentifier(),
- DownloadError.SUCCESS, successful, reasonDetailed));
- }
- return Pair.create(request, result);
- } else {
- numberOfDownloads.decrementAndGet();
- saveDownloadStatus(
- new DownloadStatus(feed, feed.getHumanReadableIdentifier(), reason,
- successful, reasonDetailed));
- return null;
- }
- }
-
-
- /**
- * Checks if the feed was parsed correctly.
- */
- private boolean checkFeedData(Feed feed) {
- if (feed.getTitle() == null) {
- Log.e(TAG, "Feed has no title.");
- return false;
- }
- if (!hasValidFeedItems(feed)) {
- Log.e(TAG, "Feed has invalid items");
- return false;
- }
- return true;
- }
-
- private boolean hasValidFeedItems(Feed feed) {
- for (FeedItem item : feed.getItems()) {
- if (item.getTitle() == null) {
- Log.e(TAG, "Item has no title");
- return false;
- }
- if (item.getPubDate() == null) {
- Log.e(TAG, "Item has no pubDate. Using current time as pubDate");
- if (item.getTitle() != null) {
- Log.e(TAG, "Title of invalid item: " + item.getTitle());
- }
- item.setPubDate(new Date());
- }
- }
- return true;
- }
-
- public void shutdown() {
- isActive = false;
- if (isCollectingRequests) {
- interrupt();
- }
- }
-
- void submitCompletedDownload(DownloadRequest request) {
- completedRequests.offer(request);
- if (isCollectingRequests) {
- interrupt();
- }
- }
-
- }
-
- /**
* Creates the destination file and writes FeedMedia File_url directly after starting download
* to make it possible to resume download after the service was killed by the system.
*/
@@ -953,128 +609,13 @@ public class DownloadService extends Service {
}
/**
- * Handles failed downloads.
- * <p/>
- * If the file has been partially downloaded, this handler will set the file_url of the FeedFile to the location
- * of the downloaded file.
- * <p/>
- * Currently, this handler only handles FeedMedia objects, because Feeds and FeedImages are deleted if the download fails.
- */
- private static class FailedDownloadHandler implements Runnable {
-
- private final DownloadRequest request;
- private final DownloadStatus status;
-
- FailedDownloadHandler(DownloadStatus status, DownloadRequest request) {
- this.request = request;
- this.status = status;
- }
-
- @Override
- public void run() {
- if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
- DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
- } else if (request.isDeleteOnFailure()) {
- Log.d(TAG, "Ignoring failed download, deleteOnFailure=true");
- }
- }
- }
-
- /**
- * Handles a completed media download.
- */
- private class MediaHandlerThread implements Runnable {
-
- private final DownloadRequest request;
- private DownloadStatus status;
-
- MediaHandlerThread(@NonNull DownloadStatus status,
- @NonNull DownloadRequest request) {
- this.status = status;
- this.request = request;
- }
-
- @Override
- public void run() {
- FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
- if (media == null) {
- Log.e(TAG, "Could not find downloaded media object in database");
- return;
- }
- media.setDownloaded(true);
- media.setFile_url(request.getDestination());
- media.checkEmbeddedPicture(); // enforce check
-
- // check if file has chapters
- if(media.getItem() != null && !media.getItem().hasChapters()) {
- ChapterUtils.loadChaptersFromFileUrl(media);
- }
-
- // Get duration
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- String durationStr = null;
- try {
- mmr.setDataSource(media.getFile_url());
- durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- media.setDuration(Integer.parseInt(durationStr));
- Log.d(TAG, "Duration of file is " + media.getDuration());
- } catch (NumberFormatException e) {
- Log.d(TAG, "Invalid file duration: " + durationStr);
- } catch (Exception e) {
- Log.e(TAG, "Get duration failed", e);
- } finally {
- mmr.release();
- }
-
- final FeedItem item = media.getItem();
-
- try {
- DBWriter.setFeedMedia(media).get();
-
- // we've received the media, we don't want to autodownload it again
- if (item != null) {
- item.setAutoDownload(false);
- // setFeedItem() signals (via EventBus) that the item has been updated,
- // so we do it after the enclosing media has been updated above,
- // to ensure subscribers will get the updated FeedMedia as well
- DBWriter.setFeedItem(item).get();
- }
-
- if (item != null && UserPreferences.enqueueDownloadedEpisodes() &&
- !DBTasks.isInQueue(DownloadService.this, item.getId())) {
- DBWriter.addQueueItem(DownloadService.this, item).get();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "MediaHandlerThread was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
- status = new DownloadStatus(media, media.getEpisodeTitle(), DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage());
- }
-
- saveDownloadStatus(status);
-
- if (GpodnetPreferences.loggedIn() && item != null) {
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.DOWNLOAD)
- .currentDeviceId()
- .currentTimestamp()
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
-
- numberOfDownloads.decrementAndGet();
- queryDownloadsAsync();
- }
- }
-
- /**
* 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) {
notificationUpdater = new NotificationUpdater();
- notificationUpdaterFuture = schedExecutor.scheduleAtFixedRate(
- notificationUpdater, 5L, 5L, TimeUnit.SECONDS);
+ notificationUpdaterFuture = schedExecutor.scheduleAtFixedRate(notificationUpdater, 1, 1, TimeUnit.SECONDS);
}
}
@@ -1090,71 +631,20 @@ public class DownloadService extends Service {
private class NotificationUpdater implements Runnable {
public void run() {
- handler.post(() -> {
- Notification n = updateNotifications();
- if (n != null) {
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(NOTIFICATION_ID, n);
- }
- });
- }
-
- }
-
-
- private long lastPost = 0;
-
- private final Runnable postDownloaderTask = new Runnable() {
- @Override
- public void run() {
- List<Downloader> runningDownloads = new ArrayList<>();
- for (Downloader downloader : downloads) {
- if (!downloader.cancelled) {
- runningDownloads.add(downloader);
- }
+ Notification n = notificationManager.updateNotifications(requester.getNumberOfDownloads(), downloads);
+ if (n != null) {
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(NOTIFICATION_ID, n);
}
- List<Downloader> list = Collections.unmodifiableList(runningDownloads);
- EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
- postHandler.postDelayed(postDownloaderTask, 1500);
}
- };
+ }
private void postDownloaders() {
- long now = System.currentTimeMillis();
- if (now - lastPost >= 250) {
- postHandler.removeCallbacks(postDownloaderTask);
- postDownloaderTask.run();
- lastPost = now;
- }
- }
+ new PostDownloaderTask(downloads).run();
- private static String compileNotificationString(List<Downloader> downloads) {
- List<String> lines = new ArrayList<>(downloads.size());
- for (Downloader downloader : downloads) {
- if (downloader.cancelled) {
- continue;
- }
- StringBuilder line = new StringBuilder("• ");
- DownloadRequest request = downloader.getDownloadRequest();
- switch (request.getFeedfileType()) {
- case Feed.FEEDFILETYPE_FEED:
- if (request.getTitle() != null) {
- line.append(request.getTitle());
- }
- break;
- case FeedMedia.FEEDFILETYPE_FEEDMEDIA:
- if (request.getTitle() != null) {
- line.append(request.getTitle())
- .append(" (")
- .append(request.getProgressPercent())
- .append("%)");
- }
- break;
- default:
- line.append("Unknown: ").append(request.getFeedfileType());
- }
- lines.add(line.toString());
+ if (downloadPostFuture == null) {
+ downloadPostFuture = schedExecutor.scheduleAtFixedRate(
+ new PostDownloaderTask(downloads), 1, 1, TimeUnit.SECONDS);
}
- return TextUtils.join("\n", lines);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
new file mode 100644
index 000000000..f49257174
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
@@ -0,0 +1,162 @@
+package de.danoeh.antennapod.core.service.download;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+import androidx.core.app.NotificationCompat;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DownloadServiceNotification {
+ private static final String TAG = "DownloadSvcNotification";
+ private static final int REPORT_ID = 3;
+
+ private final Context context;
+ private NotificationCompat.Builder notificationCompatBuilder;
+
+ public DownloadServiceNotification(Context context) {
+ this.context = context;
+ setupNotificationBuilders();
+ }
+
+ private void setupNotificationBuilders() {
+ notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
+ .setOngoing(true)
+ .setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(context))
+ .setSmallIcon(R.drawable.stat_notify_sync);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ notificationCompatBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ }
+
+ Log.d(TAG, "Notification set up");
+ }
+
+ /**
+ * Updates the contents of the service's notifications. Should be called
+ * after setupNotificationBuilders.
+ */
+ public Notification updateNotifications(int numDownloads, List<Downloader> downloads) {
+ if (notificationCompatBuilder == null) {
+ return null;
+ }
+
+ String contentTitle = context.getString(R.string.download_notification_title);
+ String downloadsLeft = (numDownloads > 0)
+ ? context.getResources().getQuantityString(R.plurals.downloads_left, numDownloads, numDownloads)
+ : context.getString(R.string.downloads_processing);
+ String bigText = compileNotificationString(downloads);
+
+ notificationCompatBuilder.setContentTitle(contentTitle);
+ notificationCompatBuilder.setContentText(downloadsLeft);
+ notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
+ return notificationCompatBuilder.build();
+ }
+
+ private static String compileNotificationString(List<Downloader> downloads) {
+ List<String> lines = new ArrayList<>(downloads.size());
+ for (Downloader downloader : downloads) {
+ if (downloader.cancelled) {
+ continue;
+ }
+ StringBuilder line = new StringBuilder("• ");
+ DownloadRequest request = downloader.getDownloadRequest();
+ switch (request.getFeedfileType()) {
+ case Feed.FEEDFILETYPE_FEED:
+ if (request.getTitle() != null) {
+ line.append(request.getTitle());
+ }
+ break;
+ case FeedMedia.FEEDFILETYPE_FEEDMEDIA:
+ if (request.getTitle() != null) {
+ line.append(request.getTitle())
+ .append(" (")
+ .append(request.getProgressPercent())
+ .append("%)");
+ }
+ break;
+ default:
+ line.append("Unknown: ").append(request.getFeedfileType());
+ }
+ lines.add(line.toString());
+ }
+ return TextUtils.join("\n", lines);
+ }
+
+ /**
+ * Creates a notification at the end of the service lifecycle to notify the
+ * user about the number of completed downloads. A report will only be
+ * created if there is at least one failed download excluding images
+ */
+ public void updateReport(List<DownloadStatus> reportQueue) {
+ // check if report should be created
+ boolean createReport = false;
+ int successfulDownloads = 0;
+ int failedDownloads = 0;
+
+ // a download report is created if at least one download has failed
+ // (excluding failed image downloads)
+ for (DownloadStatus status : reportQueue) {
+ if (status.isSuccessful()) {
+ successfulDownloads++;
+ } else if (!status.isCancelled()) {
+ createReport = true;
+ failedDownloads++;
+ }
+ }
+
+ if (createReport) {
+ Log.d(TAG, "Creating report");
+ // create notification object
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
+ NotificationUtils.CHANNEL_ID_ERROR)
+ .setTicker(context.getString(R.string.download_report_title))
+ .setContentTitle(context.getString(R.string.download_report_content_title))
+ .setContentText(
+ String.format(
+ context.getString(R.string.download_report_content),
+ successfulDownloads, failedDownloads)
+ )
+ .setSmallIcon(R.drawable.stat_notify_sync_error)
+ .setContentIntent(
+ ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context)
+ )
+ .setAutoCancel(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ }
+ NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(REPORT_ID, builder.build());
+ } else {
+ Log.d(TAG, "No report is created");
+ }
+ }
+
+ public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
+ final String resourceTitle = (downloadRequest.getTitle() != null) ?
+ downloadRequest.getTitle() : downloadRequest.getSource();
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_USER_ACTION);
+ builder.setTicker(context.getText(R.string.authentication_notification_title))
+ .setContentTitle(context.getText(R.string.authentication_notification_title))
+ .setContentText(context.getText(R.string.authentication_notification_msg))
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getText(R.string.authentication_notification_msg)
+ + ": " + resourceTitle))
+ .setSmallIcon(R.drawable.ic_key_white)
+ .setAutoCancel(true)
+ .setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(context, downloadRequest));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
+ }
+ NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify(downloadRequest.getSource().hashCode(), builder.build());
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
index 02dc17301..2a0989d23 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java
@@ -17,7 +17,7 @@ public abstract class Downloader implements Callable<Downloader> {
private volatile boolean finished;
- volatile boolean cancelled;
+ public volatile boolean cancelled;
@NonNull
final DownloadRequest request;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java
new file mode 100644
index 000000000..d96210a6e
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderFactory.java
@@ -0,0 +1,9 @@
+package de.danoeh.antennapod.core.service.download;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public interface DownloaderFactory {
+ @Nullable
+ Downloader create(@NonNull DownloadRequest request);
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
new file mode 100644
index 000000000..041d26bd4
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
@@ -0,0 +1,33 @@
+package de.danoeh.antennapod.core.service.download.handler;
+
+import android.util.Log;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+/**
+ * Handles failed downloads.
+ * <p/>
+ * If the file has been partially downloaded, this handler will set the file_url of the FeedFile to the location
+ * of the downloaded file.
+ * <p/>
+ * Currently, this handler only handles FeedMedia objects, because Feeds and FeedImages are deleted if the download fails.
+ */
+public class FailedDownloadHandler implements Runnable {
+ private static final String TAG = "FailedDownloadHandler";
+ private final DownloadRequest request;
+
+ public FailedDownloadHandler(DownloadRequest request) {
+ this.request = request;
+ }
+
+ @Override
+ public void run() {
+ if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
+ } else if (request.isDeleteOnFailure()) {
+ Log.d(TAG, "Ignoring failed download, deleteOnFailure=true");
+ }
+ }
+}
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
new file mode 100644
index 000000000..a7cbb4cfe
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
@@ -0,0 +1,130 @@
+package de.danoeh.antennapod.core.service.download.handler;
+
+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.VolumeReductionSetting;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
+import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
+import de.danoeh.antennapod.core.util.DownloadError;
+import de.danoeh.antennapod.core.util.InvalidFeedException;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.Callable;
+
+public class FeedParserTask implements Callable<FeedHandlerResult> {
+ private static final String TAG = "FeedParserTask";
+ private final DownloadRequest request;
+ private DownloadStatus downloadStatus;
+ private boolean successful = true;
+
+ public FeedParserTask(DownloadRequest request) {
+ this.request = request;
+ }
+
+ @Override
+ public FeedHandlerResult call() {
+ Feed feed = new Feed(request.getSource(), request.getLastModified());
+ feed.setFile_url(request.getDestination());
+ feed.setId(request.getFeedfileId());
+ feed.setDownloaded(true);
+ feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
+ VolumeReductionSetting.OFF, request.getUsername(), request.getPassword()));
+ feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
+
+ DownloadError reason = null;
+ String reasonDetailed = null;
+ FeedHandler feedHandler = new FeedHandler();
+
+ FeedHandlerResult result = null;
+ try {
+ result = feedHandler.parseFeed(feed);
+ Log.d(TAG, feed.getTitle() + " parsed");
+ if (!checkFeedData(feed)) {
+ throw new InvalidFeedException();
+ }
+
+ } catch (SAXException | IOException | ParserConfigurationException e) {
+ successful = false;
+ e.printStackTrace();
+ reason = DownloadError.ERROR_PARSER_EXCEPTION;
+ reasonDetailed = e.getMessage();
+ } catch (UnsupportedFeedtypeException e) {
+ e.printStackTrace();
+ successful = false;
+ reason = DownloadError.ERROR_UNSUPPORTED_TYPE;
+ reasonDetailed = e.getMessage();
+ } catch (InvalidFeedException e) {
+ e.printStackTrace();
+ successful = false;
+ reason = DownloadError.ERROR_PARSER_EXCEPTION;
+ reasonDetailed = e.getMessage();
+ } finally {
+ File feedFile = new File(request.getDestination());
+ if (feedFile.exists()) {
+ boolean deleted = feedFile.delete();
+ Log.d(TAG, "Deletion of file '" + feedFile.getAbsolutePath() + "' "
+ + (deleted ? "successful" : "FAILED"));
+ }
+ }
+
+ if (successful) {
+ downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(),
+ DownloadError.SUCCESS, successful, reasonDetailed);
+ return result;
+ } else {
+ downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(),
+ reason, successful, reasonDetailed);
+ return null;
+ }
+ }
+
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ /**
+ * Checks if the feed was parsed correctly.
+ */
+ private boolean checkFeedData(Feed feed) {
+ if (feed.getTitle() == null) {
+ Log.e(TAG, "Feed has no title.");
+ return false;
+ }
+ if (!hasValidFeedItems(feed)) {
+ Log.e(TAG, "Feed has invalid items");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hasValidFeedItems(Feed feed) {
+ for (FeedItem item : feed.getItems()) {
+ if (item.getTitle() == null) {
+ Log.e(TAG, "Item has no title");
+ return false;
+ }
+ if (item.getPubDate() == null) {
+ Log.e(TAG, "Item has no pubDate. Using current time as pubDate");
+ if (item.getTitle() != null) {
+ Log.e(TAG, "Title of invalid item: " + item.getTitle());
+ }
+ item.setPubDate(new Date());
+ }
+ }
+ return true;
+ }
+
+ public DownloadStatus getDownloadStatus() {
+ return downloadStatus;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
new file mode 100644
index 000000000..718faaa0a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
@@ -0,0 +1,57 @@
+package de.danoeh.antennapod.core.service.download.handler;
+
+import android.content.Context;
+import android.util.Log;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
+import java.util.List;
+
+public class FeedSyncTask {
+ private static final String TAG = "FeedParserTask";
+ private final DownloadRequest request;
+ private final Context context;
+ private DownloadStatus downloadStatus;
+
+ public FeedSyncTask(Context context, DownloadRequest request) {
+ this.request = request;
+ this.context = context;
+ }
+
+ public boolean run() {
+ FeedParserTask task = new FeedParserTask(request);
+ FeedHandlerResult result = task.call();
+ downloadStatus = task.getDownloadStatus();
+
+ if (!task.isSuccessful()) {
+ return false;
+ }
+
+ Feed[] savedFeeds = DBTasks.updateFeed(context, result.feed);
+ Feed savedFeed = savedFeeds[0];
+ // If loadAllPages=true, check if another page is available and queue it for download
+ final boolean loadAllPages = request.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
+ final Feed feed = result.feed;
+ if (loadAllPages && feed.getNextPageLink() != null) {
+ try {
+ feed.setId(savedFeed.getId());
+ DBTasks.loadNextPageOfFeed(context, savedFeed, true);
+ } catch (DownloadRequestException e) {
+ Log.e(TAG, "Error trying to load next page", e);
+ }
+ }
+
+ ClientConfig.downloadServiceCallbacks.onFeedParsed(context, savedFeed);
+ return true;
+ }
+
+ public DownloadStatus getDownloadStatus() {
+ return downloadStatus;
+ }
+}
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
new file mode 100644
index 000000000..40be2895c
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
@@ -0,0 +1,112 @@
+package de.danoeh.antennapod.core.service.download.handler;
+
+import android.content.Context;
+import android.media.MediaMetadataRetriever;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.ExecutionException;
+
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
+import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.ChapterUtils;
+import de.danoeh.antennapod.core.util.DownloadError;
+import org.greenrobot.eventbus.EventBus;
+
+/**
+ * Handles a completed media download.
+ */
+public class MediaDownloadedHandler implements Runnable {
+ private static final String TAG = "MediaDownloadedHandler";
+ private final DownloadRequest request;
+ private final DownloadStatus status;
+ private final Context context;
+ private DownloadStatus updatedStatus;
+
+ public MediaDownloadedHandler(@NonNull Context context, @NonNull DownloadStatus status,
+ @NonNull DownloadRequest request) {
+ this.status = status;
+ this.request = request;
+ this.context = context;
+ }
+
+ @Override
+ public void run() {
+ updatedStatus = status;
+ FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
+ if (media == null) {
+ Log.e(TAG, "Could not find downloaded media object in database");
+ return;
+ }
+ // media.setDownloaded modifies played state
+ boolean broadcastUnreadStateUpdate = media.getItem() != null && media.getItem().isNew();
+ media.setDownloaded(true);
+ media.setFile_url(request.getDestination());
+ media.checkEmbeddedPicture(); // enforce check
+
+ // check if file has chapters
+ if (media.getItem() != null && !media.getItem().hasChapters()) {
+ ChapterUtils.loadChaptersFromFileUrl(media);
+ }
+
+ // Get duration
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ String durationStr = null;
+ try {
+ mmr.setDataSource(media.getFile_url());
+ durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ media.setDuration(Integer.parseInt(durationStr));
+ Log.d(TAG, "Duration of file is " + media.getDuration());
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid file duration: " + durationStr);
+ } catch (Exception e) {
+ Log.e(TAG, "Get duration failed", e);
+ } finally {
+ mmr.release();
+ }
+
+ final FeedItem item = media.getItem();
+
+ try {
+ DBWriter.setFeedMedia(media).get();
+
+ // we've received the media, we don't want to autodownload it again
+ if (item != null) {
+ item.setAutoDownload(false);
+ // setFeedItem() signals (via EventBus) that the item has been updated,
+ // so we do it after the enclosing media has been updated above,
+ // to ensure subscribers will get the updated FeedMedia as well
+ DBWriter.setFeedItem(item).get();
+ if (broadcastUnreadStateUpdate) {
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "MediaHandlerThread was interrupted");
+ } catch (ExecutionException e) {
+ Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage());
+ updatedStatus = new DownloadStatus(media, media.getEpisodeTitle(),
+ DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage());
+ }
+
+ if (GpodnetPreferences.loggedIn() && item != null) {
+ GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.DOWNLOAD)
+ .currentDeviceId()
+ .currentTimestamp()
+ .build();
+ GpodnetPreferences.enqueueEpisodeAction(action);
+ }
+ }
+
+ public DownloadStatus getUpdatedStatus() {
+ return updatedStatus;
+ }
+}
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
new file mode 100644
index 000000000..5d2c48679
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/PostDownloaderTask.java
@@ -0,0 +1,29 @@
+package de.danoeh.antennapod.core.service.download.handler;
+
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PostDownloaderTask implements Runnable {
+ private List<Downloader> downloads;
+
+ public PostDownloaderTask(List<Downloader> downloads) {
+ this.downloads = downloads;
+ }
+
+ @Override
+ public void run() {
+ List<Downloader> runningDownloads = new ArrayList<>();
+ for (Downloader downloader : downloads) {
+ if (!downloader.cancelled) {
+ runningDownloads.add(downloader);
+ }
+ }
+ 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 c988ef70e..c250cd17f 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
@@ -46,11 +46,13 @@ public class ExoPlayerWrapper implements IPlayer {
private MediaPlayer.OnCompletionListener audioCompletionListener;
private MediaPlayer.OnErrorListener audioErrorListener;
private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
+ private PlaybackParameters playbackParameters;
ExoPlayerWrapper(Context context) {
mContext = context;
mExoPlayer = createPlayer();
+ playbackParameters = mExoPlayer.getPlaybackParameters();
bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
@@ -145,7 +147,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public float getCurrentSpeedMultiplier() {
- return mExoPlayer.getPlaybackParameters().speed;
+ return playbackParameters.speed;
}
@Override
@@ -225,8 +227,8 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void setPlaybackParams(float speed, boolean skipSilence) {
- PlaybackParameters params = mExoPlayer.getPlaybackParameters();
- mExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, params.pitch, skipSilence));
+ playbackParameters = new PlaybackParameters(speed, playbackParameters.pitch, skipSilence);
+ mExoPlayer.setPlaybackParameters(playbackParameters);
}
@Override
@@ -247,6 +249,8 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void start() {
mExoPlayer.setPlayWhenReady(true);
+ // Can't set params when paused - so always set it on start in case they changed
+ mExoPlayer.setPlaybackParameters(playbackParameters);
}
@Override
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 8ac2721b6..ead686ae4 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
@@ -29,7 +29,7 @@ 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.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
@@ -247,7 +247,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
try {
media.loadMetadata();
callback.onMediaChanged(false);
- setPlaybackParams(PlaybackSpeedHelper.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
+ setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
if (stream) {
mediaPlayer.setDataSource(media.getStreamUrl());
} else if (media.getLocalMediaUrl() != null && new File(media.getLocalMediaUrl()).canRead()) {
@@ -311,7 +311,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
Log.d(TAG, "Resuming/Starting playback");
acquireWifiLockIfNecessary();
- setPlaybackParams(PlaybackSpeedHelper.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
+ setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
float leftVolume = UserPreferences.getLeftVolume();
float rightVolume = UserPreferences.getRightVolume();
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 eb38a02d9..8f9aff8f8 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
@@ -69,6 +69,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
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;
@@ -85,7 +86,6 @@ import org.greenrobot.eventbus.EventBus;
* Controls the MediaPlayer that plays a FeedMedia-file
*/
public class PlaybackService extends MediaBrowserServiceCompat {
-
/**
* Logging tag
*/
@@ -146,11 +146,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final String EXTRA_VOLUME_REDUCTION_AFFECTED_FEED = "PlaybackService.VolumeReductionSettingAffectedFeed";
/**
- * If the PlaybackService receives this action, it will resume playback.
- */
- public static final String ACTION_RESUME_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.resumePlayCurrentEpisode";
-
- /**
* Custom action used by Android Wear
*/
private static final String CUSTOM_ACTION_FAST_FORWARD = "action.de.danoeh.antennapod.core.service.fastForward";
@@ -291,7 +286,6 @@ 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));
- registerReceiver(pauseResumeCurrentEpisodeReceiver, new IntentFilter(ACTION_RESUME_PLAY_CURRENT_EPISODE));
registerReceiver(volumeReductionChangedReceiver, new IntentFilter(ACTION_VOLUME_REDUCTION_CHANGED));
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
@@ -342,6 +336,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service is about to be destroyed");
+
+ if (notificationBuilder.getPlayerStatus() == PlayerStatus.PLAYING) {
+ notificationBuilder.setPlayerStatus(PlayerStatus.STOPPED);
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ }
stateManager.stopForeground(!UserPreferences.isPersistNotify());
isRunning = false;
currentMediaType = MediaType.UNKNOWN;
@@ -358,7 +358,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
unregisterReceiver(audioBecomingNoisy);
unregisterReceiver(skipCurrentEpisodeReceiver);
unregisterReceiver(pausePlayCurrentEpisodeReceiver);
- unregisterReceiver(pauseResumeCurrentEpisodeReceiver);
flavorHelper.removeCastConsumer();
flavorHelper.unregisterWifiBroadcastReceiver();
mediaPlayer.shutdown();
@@ -723,6 +722,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case INITIALIZED:
PlaybackPreferences.writeMediaPlaying(mediaPlayer.getPSMPInfo().playable,
mediaPlayer.getPSMPInfo().playerStatus, mediaPlayer.isStreaming());
+ setupNotification(newInfo);
break;
case PREPARED:
@@ -906,7 +906,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return null;
}
- if (!nextItem.getMedia().localFileAvailable() && !NetworkUtils.isStreamingAllowed()) {
+ if (!nextItem.getMedia().localFileAvailable() && !NetworkUtils.isStreamingAllowed()
+ && UserPreferences.isFollowQueue()) {
displayStreamingNotAllowedNotification(
new PlaybackServiceStarter(this, nextItem.getMedia())
.prepareImmediately(true)
@@ -994,23 +995,15 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (item != null) {
- if (ended || smartMarkAsPlayed ||
- (skipped && !UserPreferences.shouldSkipKeepEpisode())) {
+ if (ended || smartMarkAsPlayed
+ || (skipped && !UserPreferences.shouldSkipKeepEpisode())) {
// only mark the item as played if we're not keeping it anyways
DBWriter.markItemPlayed(item, FeedItem.PLAYED, ended);
- try {
- final List<FeedItem> queue = taskManager.getQueue();
- if (QueueAccess.ItemListAccess(queue).contains(item.getId())) {
- // don't know if it actually matters to not autodownload when smart mark as played is triggered
- DBWriter.removeQueueItem(PlaybackService.this, ended, item);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- // isInQueue remains false
- }
+ // don't know if it actually matters to not autodownload when smart mark as played is triggered
+ DBWriter.removeQueueItem(PlaybackService.this, ended, item);
// Delete episode if enabled
- if (item.getFeed().getPreferences().getCurrentAutoDelete() &&
- (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) {
+ if (item.getFeed().getPreferences().getCurrentAutoDelete()
+ && (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
Log.d(TAG, "Episode Deleted");
}
@@ -1148,7 +1141,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, p.getEpisodeTitle());
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, p.getFeedTitle());
- String imageLocation = p.getImageLocation();
+ String imageLocation = ImageResourceUtils.getImageLocation(p);
if (!TextUtils.isEmpty(imageLocation)) {
if (UserPreferences.setLockscreenBackground()) {
@@ -1214,7 +1207,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
- notificationBuilder.setMetadata(playable, mediaSession.getSessionToken(), playerStatus, isCasting);
+ notificationBuilder.setPlayable(playable);
+ notificationBuilder.setMediaSessionToken(mediaSession.getSessionToken());
+ notificationBuilder.setPlayerStatus(playerStatus);
+ notificationBuilder.setCasting(isCasting);
notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed());
Log.d(TAG, "setupNotification: startForeground" + playerStatus);
@@ -1440,16 +1436,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
};
- private final BroadcastReceiver pauseResumeCurrentEpisodeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), ACTION_RESUME_PLAY_CURRENT_EPISODE)) {
- Log.d(TAG, "Received RESUME_PLAY_CURRENT_EPISODE intent");
- mediaPlayer.resume();
- }
- }
- };
-
private final BroadcastReceiver pausePlayCurrentEpisodeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index b277a6bc2..6219fe78c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -24,6 +24,7 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.IntList;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
@@ -45,16 +46,11 @@ public class PlaybackServiceNotificationBuilder {
this.context = context;
}
- public void setMetadata(Playable playable, MediaSessionCompat.Token mediaSessionToken,
- PlayerStatus playerStatus, boolean isCasting) {
-
+ public void setPlayable(Playable playable) {
if (playable != this.playable) {
clearCache();
}
this.playable = playable;
- this.mediaSessionToken = mediaSessionToken;
- this.playerStatus = playerStatus;
- this.isCasting = isCasting;
}
private void clearCache() {
@@ -62,7 +58,7 @@ public class PlaybackServiceNotificationBuilder {
this.position = null;
}
- public void updatePosition(int position,float speed) {
+ public void updatePosition(int position, float speed) {
TimeSpeedConverter converter = new TimeSpeedConverter(speed);
this.position = Converter.getDurationStringLong(converter.convert(position));
}
@@ -76,7 +72,7 @@ public class PlaybackServiceNotificationBuilder {
try {
icon = Glide.with(context)
.asBitmap()
- .load(playable.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(playable))
.apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
.apply(new RequestOptions().centerCrop())
.submit(iconSize, iconSize)
@@ -238,4 +234,20 @@ public class PlaybackServiceNotificationBuilder {
return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
+
+ public void setMediaSessionToken(MediaSessionCompat.Token mediaSessionToken) {
+ this.mediaSessionToken = mediaSessionToken;
+ }
+
+ public void setPlayerStatus(PlayerStatus playerStatus) {
+ this.playerStatus = playerStatus;
+ }
+
+ public void setCasting(boolean casting) {
+ isCasting = casting;
+ }
+
+ public PlayerStatus getPlayerStatus() {
+ return playerStatus;
+ }
} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java
index 18c8fa51f..2f6989bb1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java
@@ -22,6 +22,7 @@ class PlaybackServiceStateManager {
void stopService() {
stopForeground(true);
playbackService.stopSelf();
+ hasReceivedValidStartCommand = false;
}
void stopForeground(boolean removeNotification) {
@@ -35,7 +36,6 @@ class PlaybackServiceStateManager {
}
}
isInForeground = false;
- hasReceivedValidStartCommand = false;
}
boolean isInForeground() {
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 5647590b1..736cf8cf2 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
@@ -11,6 +11,7 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.List;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
@@ -140,7 +141,7 @@ public class PlaybackServiceTaskManager {
if (queueFuture.isDone()) {
try {
return queueFuture.get();
- } catch (InterruptedException | ExecutionException e) {
+ } catch (InterruptedException | ExecutionException | CancellationException e) {
e.printStackTrace();
}
}
@@ -244,7 +245,7 @@ public class PlaybackServiceTaskManager {
public synchronized void disableSleepTimer() {
if (isSleepTimerActive()) {
Log.d(TAG, "Disabling sleep timer");
- sleepTimerFuture.cancel(true);
+ sleepTimer.cancel();
}
}
@@ -449,6 +450,13 @@ public class PlaybackServiceTaskManager {
}
}
+ public void cancel() {
+ sleepTimerFuture.cancel(true);
+ if (shakeListener != null) {
+ shakeListener.pause();
+ }
+ postCallback(callback::onSleepTimerReset);
+ }
}
public interface PSTMCallback {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
index aa97b321a..d029e7bfb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
@@ -53,19 +53,19 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
final List<FeedItem> newItems = DBReader.getNewItemsList();
candidates = new ArrayList<>(queue.size() + newItems.size());
candidates.addAll(queue);
- for(FeedItem newItem : newItems) {
+ for (FeedItem newItem : newItems) {
FeedPreferences feedPrefs = newItem.getFeed().getPreferences();
FeedFilter feedFilter = feedPrefs.getFilter();
- if(!candidates.contains(newItem) && feedFilter.shouldAutoDownload(newItem)) {
+ if (!candidates.contains(newItem) && feedFilter.shouldAutoDownload(newItem)) {
candidates.add(newItem);
}
}
// filter items that are not auto downloadable
Iterator<FeedItem> it = candidates.iterator();
- while(it.hasNext()) {
+ while (it.hasNext()) {
FeedItem item = it.next();
- if(!item.isAutoDownloadable()) {
+ if (!item.isAutoDownloadable()) {
it.remove();
}
}
@@ -74,13 +74,12 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
int downloadedEpisodes = DBReader.getNumberOfDownloadedEpisodes();
int deletedEpisodes = UserPreferences.getEpisodeCleanupAlgorithm()
.makeRoomForEpisodes(context, autoDownloadableEpisodes);
- boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences
- .getEpisodeCacheSizeUnlimited();
+ boolean cacheIsUnlimited =
+ UserPreferences.getEpisodeCacheSize() == UserPreferences.getEpisodeCacheSizeUnlimited();
int episodeCacheSize = UserPreferences.getEpisodeCacheSize();
int episodeSpaceLeft;
- if (cacheIsUnlimited ||
- episodeCacheSize >= downloadedEpisodes + autoDownloadableEpisodes) {
+ if (cacheIsUnlimited || episodeCacheSize >= downloadedEpisodes + autoDownloadableEpisodes) {
episodeSpaceLeft = autoDownloadableEpisodes;
} else {
episodeSpaceLeft = episodeCacheSize - (downloadedEpisodes - deletedEpisodes);
@@ -89,14 +88,15 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
FeedItem[] itemsToDownload = candidates.subList(0, episodeSpaceLeft)
.toArray(new FeedItem[episodeSpaceLeft]);
- Log.d(TAG, "Enqueueing " + itemsToDownload.length + " items for download");
+ if (itemsToDownload.length > 0) {
+ Log.d(TAG, "Enqueueing " + itemsToDownload.length + " items for download");
- try {
- DBTasks.downloadFeedItems(false, context, itemsToDownload);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
+ try {
+ DownloadRequester.getInstance().downloadMedia(false, context, itemsToDownload);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ }
}
-
}
};
}
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 948a21efc..8b87d7c54 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
@@ -31,7 +31,6 @@ import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparato
* Provides methods for reading data from the AntennaPod database.
* In general, all database calls in DBReader-methods are executed on the caller's thread.
* This means that the caller should make sure that DBReader-methods are not executed on the GUI-thread.
- * This class will use the {@link de.danoeh.antennapod.core.feed.EventDistributor} to notify listeners about changes in the database.
*/
public final class DBReader {
@@ -516,13 +515,24 @@ public final class DBReader {
* newest events first.
*/
public static List<DownloadStatus> getFeedDownloadLog(Feed feed) {
- Log.d(TAG, "getFeedDownloadLog() called with: " + "feed = [" + feed + "]");
+ return getFeedDownloadLog(feed.getId());
+ }
+
+ /**
+ * Loads the download log for a particular feed from the database.
+ *
+ * @param feedId Feed id for which the download log is loaded
+ * @return A list with DownloadStatus objects that represent the feed's download log,
+ * newest events first.
+ */
+ public static List<DownloadStatus> getFeedDownloadLog(long feedId) {
+ Log.d(TAG, "getFeedDownloadLog() called with: " + "feed = [" + feedId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor cursor = null;
try {
- cursor = adapter.getDownloadLog(Feed.FEEDFILETYPE_FEED, feed.getId());
+ cursor = adapter.getDownloadLog(Feed.FEEDFILETYPE_FEED, feedId);
List<DownloadStatus> downloadLog = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
downloadLog.add(DownloadStatus.fromCursor(cursor));
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 46fa4b99c..6e72a762a 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
@@ -7,6 +7,10 @@ import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
+import org.greenrobot.eventbus.EventBus;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -21,11 +25,12 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -298,59 +303,23 @@ public final class DBTasks {
media.setDownloaded(false);
media.setFile_url(null);
DBWriter.setFeedMedia(media);
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
- }
-
- /**
- * Requests the download of a list of FeedItem objects.
- *
- * @param context Used for requesting the download and accessing the DB.
- * @param items The FeedItem objects.
- */
- public static void downloadFeedItems(final Context context,
- FeedItem... items) throws DownloadRequestException {
- downloadFeedItems(true, context, items);
+ EventBus.getDefault().post(new FeedListUpdateEvent(media.getItem().getFeed()));
}
- static void downloadFeedItems(boolean performAutoCleanup,
- final Context context, final FeedItem... items)
- throws DownloadRequestException {
- final DownloadRequester requester = DownloadRequester.getInstance();
-
- if (performAutoCleanup) {
- new Thread() {
-
- @Override
- public void run() {
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
- .makeRoomForEpisodes(context, items.length);
- }
-
- }.start();
- }
- for (FeedItem item : items) {
- if (item.getMedia() != null
- && !requester.isDownloadingFile(item.getMedia())
- && !item.getMedia().isDownloaded()) {
- if (items.length > 1) {
- try {
- requester.downloadMedia(context, item.getMedia());
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DBWriter.addDownloadStatus(
- new DownloadStatus(item.getMedia(), item
- .getMedia()
- .getHumanReadableIdentifier(),
- DownloadError.ERROR_REQUEST_ERROR,
- false, e.getMessage()
- )
- );
- }
- } else {
- requester.downloadMedia(context, item.getMedia());
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static List<? extends FeedItem> enqueueFeedItemsToDownload(final Context context,
+ List<? extends FeedItem> items) throws InterruptedException, ExecutionException {
+ List<FeedItem> itemsToEnqueue = new ArrayList<>();
+ if (UserPreferences.enqueueDownloadedEpisodes()) {
+ LongList queueIDList = DBReader.getQueueIDList();
+ for (FeedItem item : items) {
+ if (!queueIDList.contains(item.getId())) {
+ itemsToEnqueue.add(item);
}
}
+ DBWriter.addQueueItem(context, false, itemsToEnqueue.toArray(new FeedItem[0])).get();
}
+ return itemsToEnqueue;
}
/**
@@ -557,13 +526,13 @@ public final class DBTasks {
adapter.close();
try {
- DBWriter.addNewFeed(context, newFeedsList.toArray(new Feed[newFeedsList.size()])).get();
- DBWriter.setCompleteFeed(updatedFeedsList.toArray(new Feed[updatedFeedsList.size()])).get();
+ DBWriter.addNewFeed(context, newFeedsList.toArray(new Feed[0])).get();
+ DBWriter.setCompleteFeed(updatedFeedsList.toArray(new Feed[0])).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(updatedFeedsList));
return resultFeeds;
}
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 148252902..aa1395d9c 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
@@ -293,6 +293,11 @@ class DBUpgrader {
if (oldVersion < 1070400) {
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");
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_REDUCTION + " INTEGER DEFAULT 0");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index e0e15c1ff..bbd7bbcf1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -5,6 +5,7 @@ import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus;
@@ -20,11 +21,14 @@ import java.util.concurrent.Future;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -36,19 +40,18 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Permutor;
-import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.core.util.playback.Playable;
/**
* Provides methods for writing data to AntennaPod's database.
* In general, DBWriter-methods will be executed on an internal ExecutorService.
* Some methods return a Future-object which the caller can use for waiting for the method's completion. The returned Future's
* will NOT contain any results.
- * The caller can also use the {@link EventDistributor} in order to be notified about the method's completion asynchronously.
- * This class will use the {@link EventDistributor} to notify listeners about changes in the database.
*/
public class DBWriter {
@@ -123,7 +126,7 @@ public class DBWriter {
}
}
EventBus.getDefault().post(FeedItemEvent.deletedMedia(Collections.singletonList(media.getItem())));
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
return true;
}
@@ -171,12 +174,12 @@ public class DBWriter {
if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
GpodnetPreferences.addRemovedFeed(feed.getDownload_url());
}
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(feed));
// we assume we also removed download log entries for the feed or its media files.
// especially important if download or refresh failed, as the user should not be able
// to retry these
- EventDistributor.getInstance().sendDownloadLogUpdateBroadcast();
+ EventBus.getDefault().post(DownloadLogEvent.listUpdated());
BackupManager backupManager = new BackupManager(context);
backupManager.dataChanged();
@@ -193,7 +196,7 @@ public class DBWriter {
adapter.open();
adapter.clearPlaybackHistory();
adapter.close();
- EventDistributor.getInstance().sendPlaybackHistoryUpdateBroadcast();
+ EventBus.getDefault().post(PlaybackHistoryEvent.listUpdated());
});
}
@@ -206,7 +209,7 @@ public class DBWriter {
adapter.open();
adapter.clearDownloadLog();
adapter.close();
- EventDistributor.getInstance().sendDownloadLogUpdateBroadcast();
+ EventBus.getDefault().post(DownloadLogEvent.listUpdated());
});
}
@@ -227,7 +230,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedMediaPlaybackCompletionDate(media);
adapter.close();
- EventDistributor.getInstance().sendPlaybackHistoryUpdateBroadcast();
+ EventBus.getDefault().post(PlaybackHistoryEvent.listUpdated());
});
}
@@ -243,7 +246,7 @@ public class DBWriter {
adapter.open();
adapter.setDownloadStatus(status);
adapter.close();
- EventDistributor.getInstance().sendDownloadLogUpdateBroadcast();
+ EventBus.getDefault().post(DownloadLogEvent.listUpdated());
});
}
@@ -291,14 +294,17 @@ public class DBWriter {
}
- public static Future<?> addQueueItem(final Context context,
- final FeedItem... items) {
+ public static Future<?> addQueueItem(final Context context, final FeedItem... items) {
+ return addQueueItem(context, true, items);
+ }
+
+ public static Future<?> addQueueItem(final Context context, boolean markAsUnplayed, final FeedItem... items) {
LongList itemIds = new LongList(items.length);
for (FeedItem item : items) {
itemIds.add(item.getId());
item.addTag(FeedItem.TAG_QUEUE);
}
- return addQueueItem(context, false, itemIds.toArray());
+ return addQueueItem(context, false, markAsUnplayed, itemIds.toArray());
}
/**
@@ -311,58 +317,69 @@ public class DBWriter {
*/
public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload,
final long... itemIds) {
+ return addQueueItem(context, performAutoDownload, true, itemIds);
+ }
+
+ /**
+ * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true.
+ * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @param performAutoDownload true if an auto-download process should be started after the operation.
+ * @param markAsUnplayed true if the items should be marked as unplayed when enqueueing
+ * @param itemIds IDs of the FeedItem objects that should be added to the queue.
+ */
+ public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload,
+ final boolean markAsUnplayed, final long... itemIds) {
return dbExec.submit(() -> {
- if (itemIds.length > 0) {
- final PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- final List<FeedItem> queue = DBReader.getQueue(adapter);
-
- if (queue != null) {
- boolean queueModified = false;
- LongList markAsUnplayedIds = new LongList();
- List<QueueEvent> events = new ArrayList<>();
- List<FeedItem> updatedItems = new ArrayList<>();
- for (int i = 0; i < itemIds.length; i++) {
- if (!itemListContains(queue, itemIds[i])) {
- final FeedItem item = DBReader.getFeedItem(itemIds[i]);
-
-
- if (item != null) {
- // add item to either front ot back of queue
- boolean addToFront = UserPreferences.enqueueAtFront();
- if (addToFront) {
- queue.add(i, item);
- events.add(QueueEvent.added(item, i));
- } else {
- queue.add(item);
- events.add(QueueEvent.added(item, queue.size() - 1));
- }
- item.addTag(FeedItem.TAG_QUEUE);
- updatedItems.add(item);
- queueModified = true;
- if (item.isNew()) {
- markAsUnplayedIds.add(item.getId());
- }
- }
- }
- }
- if (queueModified) {
- applySortOrder(queue, events);
- adapter.setQueue(queue);
- for (QueueEvent event : events) {
- EventBus.getDefault().post(event);
- }
- EventBus.getDefault().post(FeedItemEvent.updated(updatedItems));
- if (markAsUnplayedIds.size() > 0) {
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, markAsUnplayedIds.toArray());
+ if (itemIds.length < 1) {
+ return;
+ }
+
+ final PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ final List<FeedItem> queue = DBReader.getQueue(adapter);
+
+ boolean queueModified = false;
+ LongList markAsUnplayedIds = new LongList();
+ List<QueueEvent> events = new ArrayList<>();
+ List<FeedItem> updatedItems = new ArrayList<>();
+ ItemEnqueuePositionCalculator positionCalculator =
+ new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation());
+ Playable currentlyPlaying = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ int insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying);
+ for (long itemId : itemIds) {
+ if (!itemListContains(queue, itemId)) {
+ final FeedItem item = DBReader.getFeedItem(itemId);
+ if (item != null) {
+ queue.add(insertPosition, item);
+ events.add(QueueEvent.added(item, insertPosition));
+
+ item.addTag(FeedItem.TAG_QUEUE);
+ updatedItems.add(item);
+ queueModified = true;
+ if (item.isNew()) {
+ markAsUnplayedIds.add(item.getId());
}
+ insertPosition++;
}
}
- adapter.close();
- if (performAutoDownload) {
- DBTasks.autodownloadUndownloadedItems(context);
+ }
+ if (queueModified) {
+ applySortOrder(queue, events);
+ adapter.setQueue(queue);
+ for (QueueEvent event : events) {
+ EventBus.getDefault().post(event);
+ }
+ EventBus.getDefault().post(FeedItemEvent.updated(updatedItems));
+ if (markAsUnplayed && markAsUnplayedIds.size() > 0) {
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, markAsUnplayedIds.toArray());
}
}
+ adapter.close();
+ if (performAutoDownload) {
+ DBTasks.autodownloadUndownloadedItems(context);
+ }
});
}
@@ -385,7 +402,7 @@ public class DBWriter {
// do not shuffle the list on every change
return;
}
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(sortOrder);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(sortOrder);
permutor.reorder(queue);
// Replace ADDED events by a single SORTED event
@@ -609,7 +626,7 @@ public class DBWriter {
adapter.setFeedItemRead(played, itemIds);
adapter.close();
if (broadcastUpdate) {
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
}
});
}
@@ -641,7 +658,7 @@ public class DBWriter {
resetMediaPosition);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -657,7 +674,7 @@ public class DBWriter {
adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED, feedId);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -673,7 +690,7 @@ public class DBWriter {
adapter.setFeedItems(FeedItem.PLAYED, feedId);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -687,7 +704,7 @@ public class DBWriter {
adapter.setFeedItems(FeedItem.PLAYED);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -701,7 +718,7 @@ public class DBWriter {
adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -801,7 +818,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedPreferences(preferences);
adapter.close();
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(preferences.getFeedID()));
});
}
@@ -831,6 +848,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedLastUpdateFailed(feedId, lastUpdateFailed);
adapter.close();
+ EventBus.getDefault().post(new FeedListUpdateEvent(feedId));
});
}
@@ -840,19 +858,23 @@ public class DBWriter {
adapter.open();
adapter.setFeedCustomTitle(feed.getId(), feed.getCustomTitle());
adapter.close();
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(feed));
});
}
/**
- * Sort the FeedItems in the queue with the given Permutor.
+ * Sort the FeedItems in the queue with the given the named sort order.
*
- * @param permutor Encapsulates whole-Queue reordering logic.
* @param broadcastUpdate <code>true</code> if this operation should trigger a
* QueueUpdateBroadcast. This option should be set to <code>false</code>
* if the caller wants to avoid unexpected updates of the GUI.
*/
- public static Future<?> reorderQueue(final Permutor<FeedItem> permutor, final boolean broadcastUpdate) {
+ public static Future<?> reorderQueue(@Nullable SortOrder sortOrder, final boolean broadcastUpdate) {
+ if (sortOrder == null) {
+ Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing.");
+ return dbExec.submit(() -> { });
+ }
+ final Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(sortOrder);
return dbExec.submit(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -884,7 +906,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedItemAutoDownload(feedItem, autoDownload ? 1 : 0);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -903,7 +925,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedItemAutoDownload(feedItem, autoDownload);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -921,7 +943,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedsItemsAutoDownload(feed, autoDownload);
adapter.close();
- EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
});
}
@@ -944,6 +966,19 @@ public class DBWriter {
});
}
+ /**
+ * Set item sort order of the feed
+ *
+ */
+ public static Future<?> setFeedItemSortOrder(long feedId, @Nullable SortOrder sortOrder) {
+ return dbExec.submit(() -> {
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setFeedItemSortOrder(feedId, sortOrder);
+ adapter.close();
+ EventBus.getDefault().post(new FeedEvent(FeedEvent.Action.SORT_ORDER_CHANGED, feedId));
+ });
+ }
/**
* Reset the statistics in DB
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 71f6845c5..3d4ee443b 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
@@ -3,25 +3,33 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.core.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.URLUtil;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.content.ContextCompat;
+
import org.apache.commons.io.FilenameUtils;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.URLChecker;
@@ -31,7 +39,7 @@ import de.danoeh.antennapod.core.util.URLChecker;
* Sends download requests to the DownloadService. This class should always be used for starting downloads,
* otherwise they won't work correctly.
*/
-public class DownloadRequester {
+public class DownloadRequester implements DownloadStateProvider {
private static final String TAG = "DownloadRequester";
private static final String FEED_DOWNLOADPATH = "cache/";
@@ -63,40 +71,57 @@ public class DownloadRequester {
}
/**
- * Starts a new download with the given DownloadRequest. This method should only
+ * Starts a new download with the given a list of DownloadRequest. This method should only
* be used from outside classes if the DownloadRequest was created by the DownloadService to
* ensure that the data is valid. Use downloadFeed(), downloadImage() or downloadMedia() instead.
*
* @param context Context object for starting the DownloadService
- * @param request The DownloadRequest. If another DownloadRequest with the same source URL is already stored, this method
- * call will return false.
- * @return True if the download request was accepted, false otherwise.
+ * @param requests The list of DownloadRequest objects. If another DownloadRequest
+ * with the same source URL is already stored, this one will be skipped.
+ * @return True if any of the download request was accepted, false otherwise.
*/
- public synchronized boolean download(@NonNull Context context,
- @NonNull DownloadRequest request) {
- if (downloads.containsKey(request.getSource())) {
- if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
+ public synchronized boolean download(@NonNull Context context, DownloadRequest... requests) {
+ return download(context, false, requests);
+ }
+
+ private boolean download(@NonNull Context context, boolean cleanupMedia, DownloadRequest... requests) {
+ if (requests.length <= 0) {
return false;
}
- downloads.put(request.getSource(), request);
+ boolean result = false;
+
+ ArrayList<DownloadRequest> requestsToSend = new ArrayList<>(requests.length);
+ for (DownloadRequest request : requests) {
+ if (downloads.containsKey(request.getSource())) {
+ if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
+ continue;
+ }
+ downloads.put(request.getSource(), request);
+ requestsToSend.add(request);
+ result = true;
+ }
Intent launchIntent = new Intent(context, DownloadService.class);
- launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
+ launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend);
+ if (cleanupMedia) {
+ launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, cleanupMedia);
+ }
ContextCompat.startForegroundService(context, launchIntent);
- return true;
+ return result;
}
- private void download(Context context, FeedFile item, FeedFile container, File dest,
- boolean overwriteIfExists, String username, String password,
- String lastModified, boolean deleteOnFailure, Bundle arguments) {
+ @Nullable
+ private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest,
+ boolean overwriteIfExists, String username, String password,
+ String lastModified, boolean deleteOnFailure, Bundle arguments) {
final boolean partiallyDownloadedFileExists = item.getFile_url() != null && new File(item.getFile_url()).exists();
Log.d(TAG, "partiallyDownloadedFileExists: " + partiallyDownloadedFileExists);
if (isDownloadingFile(item)) {
- Log.e(TAG, "URL " + item.getDownload_url()
- + " is already being downloaded");
- return;
+ Log.e(TAG, "URL " + item.getDownload_url()
+ + " is already being downloaded");
+ return null;
}
if (!isFilenameAvailable(dest.toString()) || (!partiallyDownloadedFileExists && dest.exists())) {
Log.d(TAG, "Filename already used.");
@@ -135,8 +160,7 @@ public class DownloadRequester {
.lastModified(lastModified)
.deleteOnFailure(deleteOnFailure)
.withArguments(arguments);
- DownloadRequest request = builder.build();
- download(context, request);
+ return builder.build();
}
/**
@@ -177,8 +201,11 @@ public class DownloadRequester {
args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
- download(context, feed, null, new File(getFeedfilePath(), getFeedfileName(feed)),
+ DownloadRequest request = createRequest(feed, null, new File(getFeedfilePath(), getFeedfileName(feed)),
true, username, password, lastModified, true, args);
+ if (request != null) {
+ download(context, request);
+ }
}
}
@@ -186,29 +213,72 @@ public class DownloadRequester {
downloadFeed(context, feed, false, false);
}
- public synchronized void downloadMedia(Context context, FeedMedia feedmedia)
+ public synchronized void downloadMedia(@NonNull Context context, FeedItem... feedItems)
throws DownloadRequestException {
- if (feedFileValid(feedmedia)) {
- Feed feed = feedmedia.getItem().getFeed();
- String username;
- String password;
- if (feed != null && feed.getPreferences() != null) {
- username = feed.getPreferences().getUsername();
- password = feed.getPreferences().getPassword();
- } else {
- username = null;
- password = null;
- }
+ downloadMedia(true, context, feedItems);
- File dest;
- if (feedmedia.getFile_url() != null) {
- dest = new File(feedmedia.getFile_url());
- } else {
- dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia));
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public synchronized void downloadMedia(boolean performAutoCleanup, @NonNull Context context,
+ FeedItem... items)
+ throws DownloadRequestException {
+ Log.d(TAG, "downloadMedia() called with: performAutoCleanup = [" + performAutoCleanup
+ + "], #items = [" + items.length + "]");
+
+ List<DownloadRequest> requests = new ArrayList<>(items.length);
+ for (FeedItem item : items) {
+ try {
+ DownloadRequest request = createRequest(item.getMedia());
+ if (request != null) {
+ requests.add(request);
+ }
+ } catch (DownloadRequestException e) {
+ if (items.length < 2) {
+ // single download, typically initiated from users
+ throw e;
+ } else {
+ // batch download, typically initiated by auto-download in the background
+ e.printStackTrace();
+ DBWriter.addDownloadStatus(
+ new DownloadStatus(item.getMedia(), item
+ .getMedia()
+ .getHumanReadableIdentifier(),
+ DownloadError.ERROR_REQUEST_ERROR,
+ false, e.getMessage()
+ )
+ );
+ }
}
- download(context, feedmedia, feed,
- dest, false, username, password, null, false, null);
}
+ download(context, performAutoCleanup, requests.toArray(new DownloadRequest[0]));
+ }
+
+ @Nullable
+ private DownloadRequest createRequest(@Nullable FeedMedia feedmedia)
+ throws DownloadRequestException {
+ if (!feedFileValid(feedmedia)) {
+ return null;
+ }
+ Feed feed = feedmedia.getItem().getFeed();
+ String username;
+ String password;
+ if (feed != null && feed.getPreferences() != null) {
+ username = feed.getPreferences().getUsername();
+ password = feed.getPreferences().getPassword();
+ } else {
+ username = null;
+ password = null;
+ }
+
+ File dest;
+ if (feedmedia.getFile_url() != null) {
+ dest = new File(feedmedia.getFile_url());
+ } else {
+ dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia));
+ }
+ return createRequest(feedmedia, feed,
+ dest, false, username, password, null, false, null);
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java
new file mode 100644
index 000000000..ece40353f
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java
@@ -0,0 +1,16 @@
+package de.danoeh.antennapod.core.storage;
+
+import androidx.annotation.NonNull;
+
+import de.danoeh.antennapod.core.feed.FeedFile;
+
+/**
+ * Allow callers to query the states of downloads, but not affect them.
+ */
+public interface DownloadStateProvider {
+ /**
+ * @return {@code true} if the named feedfile is in the downloads list
+ */
+ boolean isDownloadingFile(@NonNull FeedFile item);
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
new file mode 100644
index 000000000..4b28d36b5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
@@ -0,0 +1,97 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+/**
+ * @see DBWriter#addQueueItem(Context, boolean, long...) it uses the class to determine
+ * the positions of the {@link FeedItem} in the queue.
+ */
+class ItemEnqueuePositionCalculator {
+
+ @NonNull
+ private final EnqueueLocation enqueueLocation;
+
+ @VisibleForTesting
+ DownloadStateProvider downloadStateProvider = DownloadRequester.getInstance();
+
+ public ItemEnqueuePositionCalculator(@NonNull EnqueueLocation enqueueLocation) {
+ this.enqueueLocation = enqueueLocation;
+ }
+
+ /**
+ * Determine the position (0-based) that the item(s) should be inserted to the named queue.
+ *
+ * @param curQueue the queue to which the item is to be inserted
+ * @param currentPlaying the currently playing media
+ */
+ public int calcPosition(@NonNull List<FeedItem> curQueue, @Nullable Playable currentPlaying) {
+ switch (enqueueLocation) {
+ case BACK:
+ return curQueue.size();
+ case FRONT:
+ // Return not necessarily 0, so that when a list of items are downloaded and enqueued
+ // in succession of calls (e.g., users manually tapping download one by one),
+ // the items enqueued are kept the same order.
+ // Simply returning 0 will reverse the order.
+ return getPositionOfFirstNonDownloadingItem(0, curQueue);
+ case AFTER_CURRENTLY_PLAYING:
+ int currentlyPlayingPosition = getCurrentlyPlayingPosition(curQueue, currentPlaying);
+ return getPositionOfFirstNonDownloadingItem(
+ currentlyPlayingPosition + 1, curQueue);
+ default:
+ throw new AssertionError("calcPosition() : unrecognized enqueueLocation option: " + enqueueLocation);
+ }
+ }
+
+ private int getPositionOfFirstNonDownloadingItem(int startPosition, List<FeedItem> curQueue) {
+ final int curQueueSize = curQueue.size();
+ for (int i = startPosition; i < curQueueSize; i++) {
+ if (!isItemAtPositionDownloading(i, curQueue)) {
+ return i;
+ } // else continue to search;
+ }
+ return curQueueSize;
+ }
+
+ private boolean isItemAtPositionDownloading(int position, List<FeedItem> curQueue) {
+ FeedItem curItem;
+ try {
+ curItem = curQueue.get(position);
+ } catch (IndexOutOfBoundsException e) {
+ curItem = null;
+ }
+
+ if (curItem != null
+ && curItem.getMedia() != null
+ && downloadStateProvider.isDownloadingFile(curItem.getMedia())) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static int getCurrentlyPlayingPosition(@NonNull List<FeedItem> curQueue,
+ @Nullable Playable currentPlaying) {
+ if (!(currentPlaying instanceof FeedMedia)) {
+ return -1;
+ }
+ final long curPlayingItemId = ((FeedMedia) currentPlaying).getItem().getId();
+ for (int i = 0; i < curQueue.size(); i++) {
+ if (curPlayingItemId == curQueue.get(i).getId()) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
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 fa72cd510..d9f4e319a 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
@@ -13,11 +13,11 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import org.apache.commons.io.FileUtils;
import java.io.File;
@@ -35,8 +35,10 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.LongIntMap;
+import de.danoeh.antennapod.core.util.SortOrder;
import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.core.util.SortOrder.toCodeString;
// TODO Remove media column from feeditem table
@@ -102,6 +104,7 @@ public class PodDBAdapter {
public static final String KEY_IS_PAGED = "is_paged";
public static final String KEY_NEXT_PAGE_LINK = "next_page_link";
public static final String KEY_HIDE = "hide";
+ public static final String KEY_SORT_ORDER = "sort_order";
public static final String KEY_LAST_UPDATE_FAILED = "last_update_failed";
public static final String KEY_HAS_EMBEDDED_PICTURE = "has_embedded_picture";
public static final String KEY_LAST_PLAYED_TIME = "last_played_time";
@@ -139,6 +142,7 @@ public class PodDBAdapter {
+ KEY_IS_PAGED + " INTEGER DEFAULT 0,"
+ KEY_NEXT_PAGE_LINK + " TEXT,"
+ KEY_HIDE + " TEXT,"
+ + 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 + ","
@@ -236,6 +240,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_USERNAME,
TABLE_NAME_FEEDS + "." + KEY_PASSWORD,
TABLE_NAME_FEEDS + "." + KEY_HIDE,
+ 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_REDUCTION,
@@ -325,9 +330,7 @@ public class PodDBAdapter {
SQLiteDatabase newDb;
try {
newDb = SingletonHolder.dbHelper.getWritableDatabase();
- if (Build.VERSION.SDK_INT >= 16) {
- newDb.disableWriteAheadLogging();
- }
+ newDb.disableWriteAheadLogging();
} catch (SQLException ex) {
Log.e(TAG, Log.getStackTraceString(ex));
newDb = SingletonHolder.dbHelper.getReadableDatabase();
@@ -381,6 +384,7 @@ public class PodDBAdapter {
} else {
values.put(KEY_HIDE, "");
}
+ values.put(KEY_SORT_ORDER, toCodeString(feed.getSortOrder()));
values.put(KEY_LAST_UPDATE_FAILED, feed.hasLastUpdateFailed());
if (feed.getId() == 0) {
// Create new entry
@@ -420,6 +424,12 @@ public class PodDBAdapter {
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(feedId)});
}
+ public void setFeedItemSortOrder(long feedId, @Nullable SortOrder sortOrder) {
+ ContentValues values = new ContentValues();
+ values.put(KEY_SORT_ORDER, toCodeString(sortOrder));
+ db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(feedId)});
+ }
+
/**
* Inserts or updates a media entry
*
@@ -1459,7 +1469,7 @@ public class PodDBAdapter {
*/
private static class PodDBHelper extends SQLiteOpenHelper {
- private static final int VERSION = 1070400;
+ private static final int VERSION = 1070401;
private final Context context;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java
index b512dce3f..714b8d8a7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java
@@ -1,47 +1,51 @@
package de.danoeh.antennapod.core.syndication.namespace.atom;
-import org.apache.commons.text.StringEscapeUtils;
-
+import android.os.Build;
+import android.text.Html;
import de.danoeh.antennapod.core.syndication.namespace.Namespace;
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
/** Represents Atom Element which contains text (content, title, summary). */
public class AtomText extends SyndElement {
- public static final String TYPE_TEXT = "text";
- private static final String TYPE_HTML = "html";
- private static final String TYPE_XHTML = "xhtml";
-
- private final String type;
- private String content;
-
- public AtomText(String name, Namespace namespace, String type) {
- super(name, namespace);
- this.type = type;
- }
-
- /** Processes the content according to the type and returns it. */
- public String getProcessedContent() {
- if (type == null) {
- return content;
- } else if (type.equals(TYPE_HTML)) {
- return StringEscapeUtils.unescapeHtml4(content);
- } else if (type.equals(TYPE_XHTML)) {
- return content;
- } else { // Handle as text by default
- return content;
- }
- }
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
-
- public String getType() {
- return type;
- }
+ public static final String TYPE_TEXT = "text";
+ public static final String TYPE_HTML = "html";
+ private static final String TYPE_XHTML = "xhtml";
+
+ private final String type;
+ private String content;
+
+ public AtomText(String name, Namespace namespace, String type) {
+ super(name, namespace);
+ this.type = type;
+ }
+
+ /** Processes the content according to the type and returns it. */
+ public String getProcessedContent() {
+ if (type == null) {
+ return content;
+ } else if (type.equals(TYPE_HTML)) {
+ if (Build.VERSION.SDK_INT >= 24) {
+ return Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY).toString();
+ } else {
+ return Html.fromHtml(content).toString();
+ }
+ } else if (type.equals(TYPE_XHTML)) {
+ return content;
+ } else { // Handle as text by default
+ return content;
+ }
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getType() {
+ return type;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
index 83957456a..7aa5a90e7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
@@ -97,11 +97,9 @@ public class NSAtom extends Namespace {
type = SyndTypeUtils.getMimeTypeFromUrl(href);
}
- if(SyndTypeUtils.enclosureTypeValid(type)) {
- FeedItem currItem = state.getCurrentItem();
- if(currItem != null && !currItem.hasMedia()) {
- currItem.setMedia(new FeedMedia(currItem, href, size, type));
- }
+ FeedItem currItem = state.getCurrentItem();
+ if (SyndTypeUtils.enclosureTypeValid(type) && currItem != null && !currItem.hasMedia()) {
+ currItem.setMedia(new FeedMedia(currItem, href, size, type));
}
} else if (LINK_REL_PAYMENT.equals(rel)) {
state.getCurrentItem().setPaymentLink(href);
@@ -111,9 +109,9 @@ public class NSAtom extends Namespace {
String type = attributes.getValue(LINK_TYPE);
/*
* Use as link if a) no type-attribute is given and
- * feed-object has no link yet b) type of link is
- * LINK_TYPE_HTML or LINK_TYPE_XHTML
- */
+ * feed-object has no link yet b) type of link is
+ * LINK_TYPE_HTML or LINK_TYPE_XHTML
+ */
if (state.getFeed() != null &&
((type == null && state.getFeed().getLink() == null) ||
(LINK_TYPE_HTML.equals(type) || LINK_TYPE_XHTML.equals(type)))) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
index 0c21ca393..9bf9b9db0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
@@ -1,89 +1,58 @@
package de.danoeh.antennapod.core.util;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBWriter;
/**
- * Provides method for sorting the queue according to rules.
+ * Provides method for sorting the a list of {@link FeedItem} according to rules.
*/
-public class QueueSorter {
-
- /**
- * Sorts the queue by the given sort order and sends a broadcast update.
- *
- * @param sortOrder Sort order.
- * @param broadcastUpdate Send broadcast update?
- */
- public static void sort(SortOrder sortOrder, boolean broadcastUpdate) {
- Permutor<FeedItem> permutor = getPermutor(sortOrder);
- if (permutor != null) {
- DBWriter.reorderQueue(permutor, broadcastUpdate);
- }
- }
+public class FeedItemPermutors {
/**
* Returns a Permutor that sorts a list appropriate to the given sort order.
*
- * @param sortOrder Sort order.
- * @return Permutor that sorts a list appropriate to the given sort order. <code>null</code> if the order is unknown or <code>null</code>.
+ * @return Permutor that sorts a list appropriate to the given sort order.
*/
- public static Permutor<FeedItem> getPermutor(SortOrder sortOrder) {
- if (sortOrder == null) {
- return null;
- }
+ @NonNull
+ public static Permutor<FeedItem> getPermutor(@NonNull SortOrder sortOrder) {
Comparator<FeedItem> comparator = null;
Permutor<FeedItem> permutor = null;
switch (sortOrder) {
case EPISODE_TITLE_A_Z:
- comparator = (f1, f2) -> f1.getTitle().compareTo(f2.getTitle());
+ comparator = (f1, f2) -> itemTitle(f1).compareTo(itemTitle(f2));
break;
case EPISODE_TITLE_Z_A:
- comparator = (f1, f2) -> f2.getTitle().compareTo(f1.getTitle());
+ comparator = (f1, f2) -> itemTitle(f2).compareTo(itemTitle(f1));
break;
case DATE_OLD_NEW:
- comparator = (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate());
+ comparator = (f1, f2) -> pubDate(f1).compareTo(pubDate(f2));
break;
case DATE_NEW_OLD:
- comparator = (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate());
+ comparator = (f1, f2) -> pubDate(f2).compareTo(pubDate(f1));
break;
case DURATION_SHORT_LONG:
- comparator = (f1, f2) -> {
- FeedMedia f1Media = f1.getMedia();
- FeedMedia f2Media = f2.getMedia();
- int duration1 = f1Media != null ? f1Media.getDuration() : -1;
- int duration2 = f2Media != null ? f2Media.getDuration() : -1;
-
- if (duration1 == -1 || duration2 == -1)
- return duration2 - duration1;
- else
- return duration1 - duration2;
- };
+ comparator = (f1, f2) -> Integer.compare(duration(f1), duration(f2));
break;
case DURATION_LONG_SHORT:
- comparator = (f1, f2) -> {
- FeedMedia f1Media = f1.getMedia();
- FeedMedia f2Media = f2.getMedia();
- int duration1 = f1Media != null ? f1Media.getDuration() : -1;
- int duration2 = f2Media != null ? f2Media.getDuration() : -1;
-
- return -1 * (duration1 - duration2);
- };
+ comparator = (f1, f2) -> Integer.compare(duration(f2), duration(f1));
break;
case FEED_TITLE_A_Z:
- comparator = (f1, f2) -> f1.getFeed().getTitle().compareTo(f2.getFeed().getTitle());
+ comparator = (f1, f2) -> feedTitle(f1).compareTo(feedTitle(f2));
break;
case FEED_TITLE_Z_A:
- comparator = (f1, f2) -> f2.getFeed().getTitle().compareTo(f1.getFeed().getTitle());
+ comparator = (f1, f2) -> feedTitle(f2).compareTo(feedTitle(f1));
break;
case RANDOM:
permutor = Collections::shuffle;
@@ -103,6 +72,31 @@ public class QueueSorter {
return permutor;
}
+ // Null-safe accessors
+
+ @NonNull
+ private static Date pubDate(@Nullable FeedItem item) {
+ return (item != null && item.getPubDate() != null) ?
+ item.getPubDate() : new Date(0);
+ }
+
+ @NonNull
+ private static String itemTitle(@Nullable FeedItem item) {
+ return (item != null && item.getTitle() != null) ?
+ item.getTitle() : "";
+ }
+
+ private static int duration(@Nullable FeedItem item) {
+ return (item != null && item.getMedia() != null) ?
+ item.getMedia().getDuration() : 0;
+ }
+
+ @NonNull
+ private static String feedTitle(@Nullable FeedItem item) {
+ return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null) ?
+ item.getFeed().getTitle() : "";
+ }
+
/**
* Implements a reordering by pubdate that avoids consecutive episodes from the same feed in
* the queue.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
index a8206d3bd..5ae8dbcc7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
@@ -1,5 +1,10 @@
package de.danoeh.antennapod.core.util;
+import androidx.annotation.NonNull;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -38,6 +43,15 @@ public class FeedItemUtil {
return result;
}
+ @NonNull
+ public static List<Long> getIdList(List<? extends FeedItem> items) {
+ List<Long> result = new ArrayList<>();
+ for (FeedItem item : items) {
+ result.add(item.getId());
+ }
+ return result;
+ }
+
/**
* Get the link for the feed item for the purpose of Share. It fallbacks to
* use the feed's link if the named feed item has no link.
@@ -45,9 +59,9 @@ public class FeedItemUtil {
public static String getLinkWithFallback(FeedItem item) {
if (item == null) {
return null;
- } else if (item.getLink() != null) {
+ } else if (StringUtils.isNotBlank(item.getLink())) {
return item.getLink();
- } else if (item.getFeed() != null) {
+ } else if (StringUtils.isNotBlank(item.getFeed().getLink())) {
return item.getFeed().getLink();
}
return null;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
index e99461806..5579ec27d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
@@ -3,45 +3,48 @@ package de.danoeh.antennapod.core.util;
import android.text.TextUtils;
import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.text.RandomStringGenerator;
/** Generates valid filenames for a given string. */
public class FileNameGenerator {
-
- private static final char[] validChars = (
- "abcdefghijklmnopqrstuvwxyz" +
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
- "0123456789" +
- " _-").toCharArray();
-
- private FileNameGenerator() {
- }
-
- /**
- * This method will return a new string that doesn't contain any illegal
- * characters of the given string.
- */
- public static String generateFileName(String string) {
- StringBuilder buf = new StringBuilder();
- for (int i = 0; i < string.length(); i++) {
- char c = string.charAt(i);
- if(Character.isSpaceChar(c) && (buf.length() == 0 || Character.isSpaceChar(buf.charAt(buf.length()-1)))) {
- continue;
- }
- if (ArrayUtils.contains(validChars, c)) {
- buf.append(c);
- }
- }
- String filename = buf.toString().trim();
- if(TextUtils.isEmpty(filename)) {
- return new RandomStringGenerator.Builder()
- .withinRange('0', 'z')
- .filteredBy(Character::isLetterOrDigit)
- .build()
- .generate(8);
- }
- return filename;
- }
+ private static final char[] validChars =
+ ("abcdefghijklmnopqrstuvwxyz"
+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ + "0123456789"
+ + " _-").toCharArray();
+
+ private FileNameGenerator() {
+ }
+
+ /**
+ * This method will return a new string that doesn't contain any illegal
+ * characters of the given string.
+ */
+ public static String generateFileName(String string) {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < string.length(); i++) {
+ char c = string.charAt(i);
+ if (Character.isSpaceChar(c)
+ && (buf.length() == 0 || Character.isSpaceChar(buf.charAt(buf.length() - 1)))) {
+ continue;
+ }
+ if (ArrayUtils.contains(validChars, c)) {
+ buf.append(c);
+ }
+ }
+ String filename = buf.toString().trim();
+ if (TextUtils.isEmpty(filename)) {
+ return randomString(8);
+ }
+ return filename;
+ }
+
+ private static String randomString(int length) {
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ sb.append(validChars[(int) (Math.random() * validChars.length)]);
+ }
+ return sb.toString();
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java b/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java
index ae6fceb47..e3a10b14a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java
@@ -1,20 +1,42 @@
package de.danoeh.antennapod.core.util;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import static de.danoeh.antennapod.core.util.SortOrder.Scope.INTER_FEED;
+import static de.danoeh.antennapod.core.util.SortOrder.Scope.INTRA_FEED;
+
/**
* Provides sort orders to sort a list of episodes.
*/
public enum SortOrder {
- EPISODE_TITLE_A_Z,
- EPISODE_TITLE_Z_A,
- DATE_OLD_NEW,
- DATE_NEW_OLD,
- DURATION_SHORT_LONG,
- DURATION_LONG_SHORT,
- FEED_TITLE_A_Z,
- FEED_TITLE_Z_A,
- RANDOM,
- SMART_SHUFFLE_OLD_NEW,
- SMART_SHUFFLE_NEW_OLD;
+ DATE_OLD_NEW(1, INTRA_FEED),
+ DATE_NEW_OLD(2, INTRA_FEED),
+ EPISODE_TITLE_A_Z(3, INTRA_FEED),
+ EPISODE_TITLE_Z_A(4, INTRA_FEED),
+ DURATION_SHORT_LONG(5, INTRA_FEED),
+ DURATION_LONG_SHORT(6, INTRA_FEED),
+ FEED_TITLE_A_Z(101, INTER_FEED),
+ FEED_TITLE_Z_A(102, INTER_FEED),
+ RANDOM(103, INTER_FEED),
+ SMART_SHUFFLE_OLD_NEW(104, INTER_FEED),
+ SMART_SHUFFLE_NEW_OLD(105, INTER_FEED);
+
+ public enum Scope {
+ INTRA_FEED, INTER_FEED
+ }
+
+ public final int code;
+
+ @NonNull
+ public final Scope scope;
+
+ SortOrder(int code, @NonNull Scope scope) {
+ this.code = code;
+ this.scope = scope;
+ }
/**
* Converts the string representation to its enum value. If the string value is unknown,
@@ -27,4 +49,23 @@ public enum SortOrder {
return defaultValue;
}
}
+
+ @Nullable
+ public static SortOrder fromCodeString(@Nullable String codeStr) {
+ if (TextUtils.isEmpty(codeStr)) {
+ return null;
+ }
+ int code = Integer.parseInt(codeStr);
+ for (SortOrder sortOrder : values()) {
+ if (sortOrder.code == code) {
+ return sortOrder;
+ }
+ }
+ throw new IllegalArgumentException("Unsupported code: " + code);
+ }
+
+ @Nullable
+ public static String toCodeString(@Nullable SortOrder sortOrder) {
+ return sortOrder != null ? Integer.toString(sortOrder.code) : null;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
index a96eda3c5..51fe2da78 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
@@ -7,7 +7,7 @@ import de.danoeh.antennapod.core.feed.FeedItem;
/** Compares the pubDate of two FeedItems for sorting*/
public class FeedItemPubdateComparator implements Comparator<FeedItem> {
- /** Returns a new instance of this comparator in reverse order.
+ /** Returns a new instance of this comparator in reverse order.
public static FeedItemPubdateComparator newInstance() {
FeedItemPubdateComparator
}*/
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 7290b9d98..3f5993700 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
@@ -15,147 +15,142 @@ import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
* create a subclass of it and overwrite the onStart* - or onEnd* - methods.
*/
public class ID3Reader {
- private static final int HEADER_LENGTH = 10;
- private static final int ID3_LENGTH = 3;
- private static final int FRAME_ID_LENGTH = 4;
+ private static final int HEADER_LENGTH = 10;
+ private static final int ID3_LENGTH = 3;
+ private static final int FRAME_ID_LENGTH = 4;
- private static final int ACTION_SKIP = 1;
- static final int ACTION_DONT_SKIP = 2;
+ private static final int ACTION_SKIP = 1;
+ static final int ACTION_DONT_SKIP = 2;
- private int readerPosition;
+ private int readerPosition;
- private static final byte ENCODING_UTF16_WITH_BOM = 1;
+ private static final byte ENCODING_UTF16_WITH_BOM = 1;
private static final byte ENCODING_UTF16_WITHOUT_BOM = 2;
private static final byte ENCODING_UTF8 = 3;
private TagHeader tagHeader;
- ID3Reader() {
- }
-
- public final void readInputStream(InputStream input) throws IOException,
- ID3ReaderException {
- int rc;
- readerPosition = 0;
- char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
- tagHeader = createTagHeader(tagHeaderSource);
- if (tagHeader == null) {
- onNoTagHeaderFound();
- } else {
- rc = onStartTagHeader(tagHeader);
- if (rc == ACTION_SKIP) {
- onEndTag();
- } else {
- while (readerPosition < tagHeader.getSize()) {
- FrameHeader frameHeader = createFrameHeader(readBytes(
- input, HEADER_LENGTH));
- if (checkForNullString(frameHeader.getId())) {
- break;
- } else {
- rc = onStartFrameHeader(frameHeader, input);
- if (rc == ACTION_SKIP) {
-
- if (frameHeader.getSize() + readerPosition > tagHeader
- .getSize()) {
- break;
- } else {
- skipBytes(input, frameHeader.getSize());
- }
- }
- }
- }
- onEndTag();
- }
- }
- }
-
- /** Returns true if string only contains null-bytes. */
- private boolean checkForNullString(String s) {
- if (!s.isEmpty()) {
- int i = 0;
- if (s.charAt(i) == 0) {
- for (i = 1; i < s.length(); i++) {
- if (s.charAt(i) != 0) {
- return false;
- }
- }
- return true;
- }
- return false;
- } else {
- return true;
- }
-
- }
-
- /**
- * Read a certain number of bytes from the given input stream. This method
- * changes the readerPosition-attribute.
- */
+ ID3Reader() {
+ }
+
+ public final void readInputStream(InputStream input) throws IOException,
+ ID3ReaderException {
+ int rc;
+ readerPosition = 0;
+ char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
+ tagHeader = createTagHeader(tagHeaderSource);
+ if (tagHeader == null) {
+ onNoTagHeaderFound();
+ } else {
+ rc = onStartTagHeader(tagHeader);
+ if (rc == ACTION_SKIP) {
+ onEndTag();
+ } else {
+ while (readerPosition < tagHeader.getSize()) {
+ FrameHeader frameHeader = createFrameHeader(readBytes(input, HEADER_LENGTH));
+ if (checkForNullString(frameHeader.getId())) {
+ break;
+ }
+ rc = onStartFrameHeader(frameHeader, input);
+ if (rc == ACTION_SKIP) {
+ if (frameHeader.getSize() + readerPosition > tagHeader.getSize()) {
+ break;
+ }
+ skipBytes(input, frameHeader.getSize());
+ }
+ }
+ onEndTag();
+ }
+ }
+ }
+
+ /** Returns true if string only contains null-bytes. */
+ private boolean checkForNullString(String s) {
+ if (!s.isEmpty()) {
+ int i = 0;
+ if (s.charAt(i) == 0) {
+ for (i = 1; i < s.length(); i++) {
+ if (s.charAt(i) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ /**
+ * Read a certain number of bytes from the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
char[] readBytes(InputStream input, int number)
- throws IOException, ID3ReaderException {
- char[] header = new char[number];
- for (int i = 0; i < number; i++) {
- int b = input.read();
- readerPosition++;
- if (b != -1) {
- header[i] = (char) b;
- } else {
- throw new ID3ReaderException("Unexpected end of stream");
- }
- }
- return header;
- }
-
- /**
- * Skip a certain number of bytes on the given input stream. This method
- * changes the readerPosition-attribute.
- */
+ throws IOException, ID3ReaderException {
+ char[] header = new char[number];
+ for (int i = 0; i < number; i++) {
+ int b = input.read();
+ readerPosition++;
+ if (b != -1) {
+ header[i] = (char) b;
+ } else {
+ throw new ID3ReaderException("Unexpected end of stream");
+ }
+ }
+ return header;
+ }
+
+ /**
+ * Skip a certain number of bytes on the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
void skipBytes(InputStream input, int number) throws IOException {
- if (number <= 0) {
- number = 1;
- }
- IOUtils.skipFully(input, number);
-
- readerPosition += number;
- }
-
- private TagHeader createTagHeader(char[] source) throws ID3ReaderException {
- boolean hasTag = (source[0] == 0x49) && (source[1] == 0x44)
- && (source[2] == 0x33);
- if (source.length != HEADER_LENGTH) {
- throw new ID3ReaderException("Length of header must be "
- + HEADER_LENGTH);
- }
- if (hasTag) {
- String id = new String(source, 0, ID3_LENGTH);
- char version = (char) ((source[3] << 8) | source[4]);
- byte flags = (byte) source[5];
- int size = (source[6] << 24) | (source[7] << 16) | (source[8] << 8)
- | source[9];
+ if (number <= 0) {
+ number = 1;
+ }
+ IOUtils.skipFully(input, number);
+
+ readerPosition += number;
+ }
+
+ private TagHeader createTagHeader(char[] source) throws ID3ReaderException {
+ boolean hasTag = (source[0] == 0x49) && (source[1] == 0x44)
+ && (source[2] == 0x33);
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ if (hasTag) {
+ String id = new String(source, 0, ID3_LENGTH);
+ char version = (char) ((source[3] << 8) | source[4]);
+ byte flags = (byte) source[5];
+ int size = (source[6] << 24) | (source[7] << 16) | (source[8] << 8)
+ | source[9];
size = unsynchsafe(size);
- return new TagHeader(id, size, version, flags);
- } else {
- return null;
- }
- }
-
- private FrameHeader createFrameHeader(char[] source)
- throws ID3ReaderException {
- if (source.length != HEADER_LENGTH) {
- throw new ID3ReaderException("Length of header must be "
- + HEADER_LENGTH);
- }
- String id = new String(source, 0, FRAME_ID_LENGTH);
+ return new TagHeader(id, size, version, flags);
+ } else {
+ return null;
+ }
+ }
+
+ private FrameHeader createFrameHeader(char[] source)
+ throws ID3ReaderException {
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ String id = new String(source, 0, FRAME_ID_LENGTH);
int size = (((int) source[4]) << 24) | (((int) source[5]) << 16)
- | (((int) source[6]) << 8) | source[7];
+ | (((int) source[6]) << 8) | source[7];
if (tagHeader != null && tagHeader.getVersion() >= 0x0400) {
size = unsynchsafe(size);
}
- char flags = (char) ((source[8] << 8) | source[9]);
- return new FrameHeader(id, size, flags);
- }
+ char flags = (char) ((source[8] << 8) | source[9]);
+ return new FrameHeader(id, size, flags);
+ }
private int unsynchsafe(int in) {
int out = 0;
@@ -170,43 +165,44 @@ public class ID3Reader {
return out;
}
- protected int readString(StringBuilder buffer, InputStream input, int max) throws IOException,
- ID3ReaderException {
- if (max > 0) {
- char[] encoding = readBytes(input, 1);
- max--;
-
- if (encoding[0] == ENCODING_UTF16_WITH_BOM || encoding[0] == ENCODING_UTF16_WITHOUT_BOM) {
+ protected int readString(StringBuilder buffer, InputStream input, int max) throws IOException,
+ ID3ReaderException {
+ if (max > 0) {
+ char[] encoding = readBytes(input, 1);
+ max--;
+
+ if (encoding[0] == ENCODING_UTF16_WITH_BOM || encoding[0] == ENCODING_UTF16_WITHOUT_BOM) {
return readUnicodeString(buffer, input, max, Charset.forName("UTF-16")) + 1; // take encoding byte into account
- } else if (encoding[0] == ENCODING_UTF8) {
+ } else if (encoding[0] == ENCODING_UTF8) {
return readUnicodeString(buffer, input, max, Charset.forName("UTF-8")) + 1; // take encoding byte into account
} else {
- return readISOString(buffer, input, max) + 1; // take encoding byte into account
- }
- } else {
+ return readISOString(buffer, input, max) + 1; // take encoding byte into account
+ }
+ } else {
if (buffer != null) {
buffer.append("");
}
- return 0;
- }
- }
-
- protected int readISOString(StringBuilder buffer, InputStream input, int max)
- throws IOException, ID3ReaderException {
- int bytesRead = 0;
- char c;
- while (++bytesRead <= max && (c = (char) input.read()) > 0) {
+ return 0;
+ }
+ }
+
+ protected int readISOString(StringBuilder buffer, InputStream input, int max)
+ throws IOException, ID3ReaderException {
+ int bytesRead = 0;
+ char c;
+ while (++bytesRead <= max && (c = (char) input.read()) > 0) {
if (buffer != null) {
- buffer.append(c);
+ buffer.append(c);
}
- }
- return bytesRead;
- }
-
- private int readUnicodeString(StringBuilder strBuffer, InputStream input, int max, Charset charset)
- throws IOException, ID3ReaderException {
- byte[] buffer = new byte[max];
- int c, cZero = -1;
+ }
+ return bytesRead;
+ }
+
+ private int readUnicodeString(StringBuilder strBuffer, InputStream input, int max, Charset charset)
+ throws IOException, ID3ReaderException {
+ byte[] buffer = new byte[max];
+ int c;
+ int cZero = -1;
int i = 0;
for (; i < max; i++) {
c = input.read();
@@ -225,26 +221,26 @@ public class ID3Reader {
}
}
if (strBuffer != null) {
- strBuffer.append(charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString());
+ strBuffer.append(charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString());
}
return i;
- }
+ }
- int onStartTagHeader(TagHeader header) {
- return ACTION_SKIP;
- }
+ int onStartTagHeader(TagHeader header) {
+ return ACTION_SKIP;
+ }
- int onStartFrameHeader(FrameHeader header, InputStream input)
- throws IOException, ID3ReaderException {
- return ACTION_SKIP;
- }
+ int onStartFrameHeader(FrameHeader header, InputStream input)
+ throws IOException, ID3ReaderException {
+ return ACTION_SKIP;
+ }
- void onEndTag() {
+ void onEndTag() {
- }
+ }
- void onNoTagHeaderFound() {
+ void onNoTagHeaderFound() {
- }
+ }
}
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 ba903eeb9..e7f6ad4f1 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
@@ -29,7 +29,7 @@ import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
@@ -675,8 +675,7 @@ public class PlaybackController {
}
public boolean canSetPlaybackSpeed() {
- return org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(activity.getApplicationContext())
- || UserPreferences.useSonic()
+ return UserPreferences.useSonic()
|| UserPreferences.useExoplayer()
|| Build.VERSION.SDK_INT >= 23
|| (playbackService != null && playbackService.canSetSpeed());
@@ -705,7 +704,7 @@ public class PlaybackController {
if (playbackService != null && canSetPlaybackSpeed()) {
return playbackService.getCurrentPlaybackSpeed();
} else {
- return PlaybackSpeedHelper.getCurrentPlaybackSpeed(getMedia());
+ return PlaybackSpeedUtils.getCurrentPlaybackSpeed(getMedia());
}
}
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 0fe1f0036..5b9452d6f 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
@@ -32,7 +32,35 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
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; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } a.timecode { color: #669900; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }";
+ 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;
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 61072f1ad..3550f28c6 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
@@ -1,12 +1,19 @@
package de.danoeh.antennapod.core.util.syndication;
+import android.text.TextUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
import org.jsoup.helper.StringUtil;
+import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
+import java.util.regex.Pattern;
+
/**
* This class is based on <code>HtmlToPlainText</code> from jsoup's examples package.
*
@@ -27,6 +34,39 @@ import org.jsoup.select.NodeVisitor;
public class HtmlToPlainText {
/**
+ * Use this method to strip off HTML encoding from given text
+ * <p>
+ * Replaces bullet points with *, ignores colors/bold/...
+ *
+ * @param str String with any encoding
+ * @return Human readable text with minimal HTML formatting
+ */
+ public static String getPlainText(String str) {
+ if (!TextUtils.isEmpty(str) && isHtml(str)) {
+ HtmlToPlainText formatter = new HtmlToPlainText();
+ Document feedDescription = Jsoup.parse(str);
+ str = StringUtils.trim(formatter.getPlainText(feedDescription));
+ } else if (TextUtils.isEmpty(str)) {
+ str = "";
+ }
+
+ return str;
+ }
+
+ /**
+ * 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
+ */
+ private static boolean isHtml(String str) {
+ final String HTML_TAG_PATTERN = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
+ Pattern htmlValidator = TextUtils.isEmpty(HTML_TAG_PATTERN) ? null : Pattern.compile(HTML_TAG_PATTERN);
+
+ return htmlValidator.matcher(str).find();
+ }
+
+ /**
* Format an Element to plain-text
* @param element the root element to format
* @return formatted text
diff --git a/core/src/main/res/drawable-hdpi/ic_filter_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_filter_grey600_24dp.png
deleted file mode 100644
index 0d2f6ccb4..000000000
--- a/core/src/main/res/drawable-hdpi/ic_filter_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_filter_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_filter_white_24dp.png
deleted file mode 100644
index ea6f9078e..000000000
--- a/core/src/main/res/drawable-hdpi/ic_filter_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_grey600_24dp.png
deleted file mode 100644
index f8f215236..000000000
--- a/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_white_24dp.png
deleted file mode 100644
index eb2611f95..000000000
--- a/core/src/main/res/drawable-hdpi/ic_settings_input_antenna_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_filter_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_filter_grey600_24dp.png
deleted file mode 100644
index b78e7d4d2..000000000
--- a/core/src/main/res/drawable-mdpi/ic_filter_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_filter_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_filter_white_24dp.png
deleted file mode 100644
index 59a2ec755..000000000
--- a/core/src/main/res/drawable-mdpi/ic_filter_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_grey600_24dp.png
deleted file mode 100644
index dafbeca33..000000000
--- a/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_white_24dp.png
deleted file mode 100644
index ccf89acea..000000000
--- a/core/src/main/res/drawable-mdpi/ic_settings_input_antenna_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/logo.png b/core/src/main/res/drawable-nodpi/teaser.png
index 3b5261b28..3b5261b28 100755..100644
--- a/app/src/main/assets/logo.png
+++ b/core/src/main/res/drawable-nodpi/teaser.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_filter_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_filter_grey600_24dp.png
deleted file mode 100644
index 4c6a96eba..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_filter_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_filter_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_filter_white_24dp.png
deleted file mode 100644
index 9416c70ec..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_filter_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_grey600_24dp.png
deleted file mode 100644
index 49813c945..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_white_24dp.png
deleted file mode 100644
index bac1560a2..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_settings_input_antenna_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_filter_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_filter_grey600_24dp.png
deleted file mode 100644
index 5846ad140..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_filter_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_filter_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_filter_white_24dp.png
deleted file mode 100644
index 1263ae82e..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_filter_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_grey600_24dp.png
deleted file mode 100644
index 37ce4c61a..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_white_24dp.png
deleted file mode 100644
index 0609c3154..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_settings_input_antenna_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_filter_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_filter_grey600_24dp.png
deleted file mode 100644
index 3fe8eb4dd..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_filter_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_filter_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_filter_white_24dp.png
deleted file mode 100644
index cb2207f11..000000000
--- a/core/src/main/res/drawable-xxxhdpi/ic_filter_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable/borderless_button_dark.xml b/core/src/main/res/drawable/borderless_button_dark.xml
index 6d263938d..3a44d81a2 100644
--- a/core/src/main/res/drawable/borderless_button_dark.xml
+++ b/core/src/main/res/drawable/borderless_button_dark.xml
@@ -1,13 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"><shape android:shape="rectangle">
+ <item android:state_pressed="true">
+ <shape android:shape="rectangle">
<solid android:color="@color/selection_background_color_dark" />
- </shape></item>
- <item android:state_focused="true"><shape android:shape="rectangle">
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape android:shape="rectangle">
<solid android:color="@color/selection_background_color_dark" />
- </shape></item>
- <item><shape android:shape="rectangle">
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
- </shape></item>
-
+ </shape>
+ </item>
</selector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_filter_grey600_24dp.xml b/core/src/main/res/drawable/ic_filter_grey600_24dp.xml
new file mode 100644
index 000000000..dc164504c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_filter_grey600_24dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#FF757575" android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
+</vector>
diff --git a/core/src/main/res/drawable/ic_filter_white_24dp.xml b/core/src/main/res/drawable/ic_filter_white_24dp.xml
new file mode 100644
index 000000000..4b1b88581
--- /dev/null
+++ b/core/src/main/res/drawable/ic_filter_white_24dp.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
+</vector>
diff --git a/core/src/main/res/drawable/ic_key_grey600.xml b/core/src/main/res/drawable/ic_key_grey600.xml
new file mode 100644
index 000000000..71cc98f2f
--- /dev/null
+++ b/core/src/main/res/drawable/ic_key_grey600.xml
@@ -0,0 +1,6 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FF757575"
+ android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_key.xml b/core/src/main/res/drawable/ic_key_white.xml
index c8a817eeb..c8a817eeb 100644
--- a/core/src/main/res/drawable/ic_notification_key.xml
+++ b/core/src/main/res/drawable/ic_key_white.xml
diff --git a/core/src/main/res/drawable/ic_playback_speed_dark_24dp.xml b/core/src/main/res/drawable/ic_playback_speed_dark_24dp.xml
new file mode 100644
index 000000000..456aeba71
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playback_speed_dark_24dp.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#FF757575" android:pathData="M 12,15.976673 A 2.9838134,2.9838134 0 0 1 9.0161867,12.992861 c 0,-1.113957 0.6067085,-2.08867 1.4919063,-2.595918 L 20.165701,4.8072659 14.66554,14.335576 C 14.168238,15.310289 13.163688,15.976673 12,15.976673 M 12,3.0468162 c 1.800234,0 3.481116,0.4973021 4.943185,1.3128776 l -2.08867,1.2034715 C 13.989209,5.2249998 12.994606,5.036025 12,5.036025 a 7.9568354,7.9568354 0 0 0 -7.9568354,7.956836 c 0,2.198075 0.885198,4.187283 2.3273743,5.619514 h 0.00995 c 0.3878957,0.387896 0.3878957,1.014497 0,1.402393 -0.3878958,0.387895 -1.0244427,0.387895 -1.4123384,0.01 v 0 C 3.1679127,18.224479 2.0539558,15.737969 2.0539558,12.992861 A 9.9460442,9.9460442 0 0 1 12,3.0468162 m 9.946045,9.9460448 c 0,2.745108 -1.113958,5.231618 -2.914192,7.031853 v 0 c -0.387896,0.377949 -1.014497,0.377949 -1.402392,-0.01 -0.387896,-0.387897 -0.387896,-1.014497 0,-1.402392 v 0 c 1.442176,-1.442178 2.327374,-3.421439 2.327374,-5.619515 0,-0.994605 -0.188974,-1.989209 -0.537086,-2.884354 L 20.62322,8.0197843 c 0.825522,1.4919063 1.322825,3.1628417 1.322825,4.9730227 z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playback_speed_dark.xml b/core/src/main/res/drawable/ic_playback_speed_dark_48dp.xml
index 7c7d1cf8c..de7ca9d78 100644
--- a/core/src/main/res/drawable/ic_playback_speed_dark.xml
+++ b/core/src/main/res/drawable/ic_playback_speed_dark_48dp.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
+ android:height="48dp"
+ android:width="48dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FF757575" android:pathData="M 12,14.888154 A 2.2284437,2.2284437 0 0 1 9.7715563,12.659711 c 0,-0.831952 0.4531167,-1.559911 1.1142217,-1.938746 L 18.098507,6.5463469 13.990743,13.66251 C 13.619336,14.390469 12.869093,14.888154 12,14.888154 m 0,-9.6565888 c 1.344494,0 2.599851,0.3714073 3.691789,0.9805151 L 14.131878,7.110886 C 13.485629,6.858329 12.742815,6.7171943 12,6.7171943 A 5.9425165,5.9425165 0 0 0 6.0574835,12.659711 c 0,1.64162 0.661105,3.127249 1.7381861,4.196902 h 0.00743 c 0.2896977,0.289697 0.2896977,0.757671 0,1.047369 -0.2896977,0.289697 -0.7650991,0.289697 -1.0547967,0.0075 v 0 C 5.4038067,16.566915 4.5718544,14.709879 4.5718544,12.659711 A 7.4281456,7.4281456 0 0 1 12,5.2315652 m 7.428145,7.4281458 c 0,2.050168 -0.831952,3.907204 -2.176446,5.251699 v 0 c -0.289698,0.282269 -0.757671,0.282269 -1.047369,-0.0075 -0.289697,-0.289698 -0.289697,-0.757671 0,-1.047368 v 0 c 1.077082,-1.077082 1.738186,-2.555282 1.738186,-4.196902 0,-0.742815 -0.141134,-1.48563 -0.401119,-2.154163 l 0.898805,-1.5599106 c 0.616537,1.1142216 0.987943,2.3621496 0.987943,3.7140736 z" />
diff --git a/core/src/main/res/drawable/ic_playback_speed_white_24dp.xml b/core/src/main/res/drawable/ic_playback_speed_white_24dp.xml
new file mode 100644
index 000000000..183bb595a
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playback_speed_white_24dp.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#ffffffff" android:pathData="M 12,15.976673 A 2.9838134,2.9838134 0 0 1 9.0161867,12.992861 c 0,-1.113957 0.6067085,-2.08867 1.4919063,-2.595918 L 20.165701,4.8072659 14.66554,14.335576 C 14.168238,15.310289 13.163688,15.976673 12,15.976673 M 12,3.0468162 c 1.800234,0 3.481116,0.4973021 4.943185,1.3128776 l -2.08867,1.2034715 C 13.989209,5.2249998 12.994606,5.036025 12,5.036025 a 7.9568354,7.9568354 0 0 0 -7.9568354,7.956836 c 0,2.198075 0.885198,4.187283 2.3273743,5.619514 h 0.00995 c 0.3878957,0.387896 0.3878957,1.014497 0,1.402393 -0.3878958,0.387895 -1.0244427,0.387895 -1.4123384,0.01 v 0 C 3.1679127,18.224479 2.0539558,15.737969 2.0539558,12.992861 A 9.9460442,9.9460442 0 0 1 12,3.0468162 m 9.946045,9.9460448 c 0,2.745108 -1.113958,5.231618 -2.914192,7.031853 v 0 c -0.387896,0.377949 -1.014497,0.377949 -1.402392,-0.01 -0.387896,-0.387897 -0.387896,-1.014497 0,-1.402392 v 0 c 1.442176,-1.442178 2.327374,-3.421439 2.327374,-5.619515 0,-0.994605 -0.188974,-1.989209 -0.537086,-2.884354 L 20.62322,8.0197843 c 0.825522,1.4919063 1.322825,3.1628417 1.322825,4.9730227 z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playback_speed_white.xml b/core/src/main/res/drawable/ic_playback_speed_white_48dp.xml
index cc6af0d55..cc6af0d55 100644
--- a/core/src/main/res/drawable/ic_playback_speed_white.xml
+++ b/core/src/main/res/drawable/ic_playback_speed_white_48dp.xml
diff --git a/core/src/main/res/drawable/ic_stream_grey600.xml b/core/src/main/res/drawable/ic_stream_grey600.xml
new file mode 100644
index 000000000..da9636f8c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_stream_grey600.xml
@@ -0,0 +1,14 @@
+<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="m20.5099,12.0219c0,-1.41 -1.143,-2.553 -2.553,-2.553h-1.2765v-0.4255c0,-2.5849 -2.0955,-4.6804 -4.6804,-4.6804 -2.1275,0 -3.9401,1.4382 -4.5017,3.4039H6.8941c-1.8799,0 -3.4039,1.524 -3.4039,3.4039 0,1.8799 1.524,3.4039 3.4039,3.4039h2.553v1.702H6.8941c-2.8199,0 -5.1059,-2.286 -5.1059,-5.1059 0,-2.6381 1.9913,-4.7996 4.5528,-5.0719C7.4047,4.0566 9.5407,2.661 12,2.661c3.0976,0 5.6761,2.2041 6.2633,5.14 2.2041,0.1532 3.9486,1.9743 3.9486,4.2209 0,1.4041 -0.6445,2.3463 -1.5471,3.1885 -0.4185,-0.2876 -0.8313,-0.5368 -1.3724,-0.8908 1.092,-0.8808 1.2175,-1.8722 1.2175,-2.2977z"
+ android:strokeWidth="0.85098612"/>
+ <path
+ android:fillColor="#FF757575"
+ android:pathData="m12.2085,13.3476v9.4907l7.4569,-4.7453z"
+ android:strokeWidth="0.67790419"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_stream_white.xml b/core/src/main/res/drawable/ic_stream_white.xml
index 3c59bbad7..aca761365 100644
--- a/core/src/main/res/drawable/ic_stream_white.xml
+++ b/core/src/main/res/drawable/ic_stream_white.xml
@@ -1,5 +1,14 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,5c-3.87,0 -7,3.13 -7,7h2c0,-2.76 2.24,-5 5,-5s5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM13,14.29c0.88,-0.39 1.5,-1.26 1.5,-2.29 0,-1.38 -1.12,-2.5 -2.5,-2.5S9.5,10.62 9.5,12c0,1.02 0.62,1.9 1.5,2.29v3.3L7.59,21 9,22.41l3,-3 3,3L16.41,21 13,17.59v-3.3zM12,1C5.93,1 1,5.93 1,12h2c0,-4.97 4.03,-9 9,-9s9,4.03 9,9h2c0,-6.07 -4.93,-11 -11,-11z"/>
+<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="m20.5099,12.0219c0,-1.41 -1.143,-2.553 -2.553,-2.553h-1.2765v-0.4255c0,-2.5849 -2.0955,-4.6804 -4.6804,-4.6804 -2.1275,0 -3.9401,1.4382 -4.5017,3.4039H6.8941c-1.8799,0 -3.4039,1.524 -3.4039,3.4039 0,1.8799 1.524,3.4039 3.4039,3.4039h2.553v1.702H6.8941c-2.8199,0 -5.1059,-2.286 -5.1059,-5.1059 0,-2.6381 1.9913,-4.7996 4.5528,-5.0719C7.4047,4.0566 9.5407,2.661 12,2.661c3.0976,0 5.6761,2.2041 6.2633,5.14 2.2041,0.1532 3.9486,1.9743 3.9486,4.2209 0,1.4041 -0.6445,2.3463 -1.5471,3.1885 -0.4185,-0.2876 -0.8313,-0.5368 -1.3724,-0.8908 1.092,-0.8808 1.2175,-1.8722 1.2175,-2.2977z"
+ android:strokeWidth="0.85098612"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="m12.2085,13.3476v9.4907l7.4569,-4.7453z"
+ android:strokeWidth="0.67790419"/>
</vector>
diff --git a/core/src/main/res/layout/player_widget.xml b/core/src/main/res/layout/player_widget.xml
index f8d909a97..5f49fb7ef 100644
--- a/core/src/main/res/layout/player_widget.xml
+++ b/core/src/main/res/layout/player_widget.xml
@@ -5,10 +5,12 @@
android:padding="@dimen/widget_margin" >
<RelativeLayout
+ android:id="@+id/widgetLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#262C31" >
+
<ImageButton
android:id="@+id/butPlay"
android:contentDescription="@string/play_label"
@@ -17,7 +19,7 @@
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_margin="12dp"
- android:background="@drawable/borderless_button_dark"
+ android:background="@android:color/transparent"
android:src="@drawable/ic_play_arrow_white_24dp" />
<LinearLayout
@@ -28,7 +30,7 @@
android:layout_alignParentStart="true"
android:layout_toLeftOf="@id/butPlay"
android:layout_toStartOf="@id/butPlay"
- android:background="@drawable/borderless_button_dark"
+ android:background="@android:color/transparent"
android:gravity="fill_horizontal"
android:orientation="horizontal" >
@@ -36,6 +38,7 @@
android:id="@+id/imgvCover"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="match_parent"
+ android:src="@drawable/ic_antenna"
android:layout_margin="12dp" />
<LinearLayout
diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml
index bc0bbfa3c..aeb5738b2 100644
--- a/core/src/main/res/values-ca/strings.xml
+++ b/core/src/main/res/values-ca/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronitzar amb altres dispositius</string>
<string name="gpodnet_auth_label">Inici de sessió a gpodder.net</string>
- <string name="free_space_label">%1$s lliure</string>
<string name="episode_cache_full_title">La memòria cau d\'episodis és plena</string>
<string name="episode_cache_full_message">S\'ha arribat al límit de la memòria cau d\'episodis. Pots incrementar-ne la capacitat a la configuració.</string>
- <string name="synchronizing">Sincronitzant...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Temps total de podcasts reproduïts</string>
<string name="statistics_details_dialog">%1$d de %2$depisodis començats.\n\nReproduïts %3$s de %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Autor/s</string>
<string name="language_label">Llengua</string>
<string name="url_label">Adreça web</string>
- <string name="podcast_settings_label">Configuració</string>
<string name="cover_label">Imatge</string>
<string name="error_label">Error</string>
<string name="error_msg_prefix">S\'ha produït un error:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">L\'emmagatzemament extern no està disponible. Assegureu-vos que està muntat per què l\'aplicació funcioni correctament.</string>
<string name="chapters_label">Capítols</string>
<string name="chapter_duration">Durada: %1$s</string>
- <string name="shownotes_label">Notes del programa</string>
<string name="description_label">Descripció</string>
- <string name="most_recent_prefix">Episodi més recent: \u0020</string>
<string name="episodes_suffix">\u0020episodis</string>
- <string name="length_prefix">Durada:\u0020</string>
- <string name="size_prefix">Mida:\u0020</string>
<string name="processing_label">S\'està processant</string>
- <string name="loading_label">Carregant...</string>
<string name="save_username_password_label">Desa nom d\'usuari i contrasenya</string>
<string name="close_label">Tanca</string>
<string name="retry_label">Reintenta</string>
@@ -103,8 +95,6 @@
<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="podcastdirectories_label">Cerca podcast al directori</string>
- <string name="podcastdirectories_descr">Per trobar podcasts nous, podeu cercar a iTunes o fyyd, o bé navegar per gpodder.net per nom, categoria o popularitat.</string>
<string name="browse_gpoddernet_label">Navega gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marca-ho tot com a llegit</string>
@@ -127,7 +117,6 @@
<string name="feed_delete_confirmation_msg">Si us plau, confirmeu que voleu eliminar el podcast \"%1$s\" i tots els seus episodis (fins i tot els ja baixats).</string>
<string name="feed_remover_msg">Eliminant podcast</string>
<string name="load_complete_feed">Refrescar tot el podcast</string>
- <string name="hide_episodes_title">Amaga Episodis</string>
<string name="batch_edit">Editar lot</string>
<string name="hide_unplayed_episodes_label">Per reproduir</string>
<string name="hide_paused_episodes_label">Pausat</string>
@@ -145,9 +134,7 @@
<string name="download_label">Baixa</string>
<string name="play_label">Reprodueix</string>
<string name="pause_label">Pausa</string>
- <string name="stop_label">Atura</string>
<string name="stream_label">Reprodueix sense baixar</string>
- <string name="remove_label">Suprimeix</string>
<string name="delete_label">Esborrar</string>
<string name="delete_failed">No s\'ha pogut esborrar el fitxer. Reiniciar el dispositiu pot ajudar.</string>
<string name="mark_read_label">Marca com a llegit</string>
@@ -168,14 +155,12 @@
<string name="removed_item">S\'ha suprimit l\'element</string>
<!--Download messages and labels-->
<string name="download_successful">ha funcionat</string>
- <string name="download_failed">ha fallat</string>
<string name="download_pending">Baixada pendent</string>
<string name="download_running">Baixada en procés</string>
<string name="download_error_details">Detalls</string>
<string name="download_error_details_message">%1$s \n\nURL del fitxer:\n%2$s</string>
<string name="download_error_device_not_found">No s\'ha trobat cap dispositiu d\'emmagatzemament</string>
<string name="download_error_insufficient_space">No hi ha prou espai</string>
- <string name="download_error_file_error">Error de fitxer</string>
<string name="download_error_http_data_error">Error de dades HTTP</string>
<string name="download_error_error_unknown">Error desconegut</string>
<string name="download_error_parser_exception">Error de l\'analitzador</string>
@@ -185,7 +170,6 @@
<string name="download_error_unauthorized">Error d\'autenticació</string>
<string name="download_error_file_type_type">Error de tipus de fitxer</string>
<string name="download_error_forbidden">Prohibit</string>
- <string name="cancel_all_downloads_label">Cancel·la totes les baixades</string>
<string name="download_canceled_msg">S\'ha cancel·lat la baixada</string>
<string name="download_canceled_autodownload_enabled_msg">Baixada cancel·lada\nDesactivada les <i>baixades automàtiques</i> per aquest element</string>
<string name="download_report_title">Baixades completades amb error(s)</string>
@@ -204,7 +188,6 @@
<string name="download_log_title_unknown">Títol desconegut</string>
<string name="download_type_feed">Canal</string>
<string name="download_type_media">Fitxer</string>
- <string name="download_type_image">Imatge</string>
<string name="download_request_error_dialog_message_prefix">S\'ha produït un error en intentar baixar el fitxer:\u0020</string>
<string name="authentication_notification_title">Cal autenticar-se</string>
<string name="authentication_notification_msg">Es necessita un usuari i una contrasenya per accedir al recurs</string>
@@ -224,7 +207,6 @@
<string name="no_media_playing_label">No s\'està reproduint res</string>
<string name="player_buffering_msg">S\'està carregant</string>
<string name="player_go_to_picture_in_picture">Mode Picture-in-picture</string>
- <string name="playbackservice_notification_title">Podcast en reproducció</string>
<string name="unknown_media_key">AntennaPod - Control desconegut: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Bloqueja la cua</string>
@@ -233,7 +215,6 @@
<string name="queue_unlocked">Cua no bloquejada</string>
<string name="clear_queue_label">Buida la cua</string>
<string name="undo">Desfés</string>
- <string name="removed_from_queue">Ítem esborrat</string>
<string name="move_to_top_label">Mou al principi</string>
<string name="move_to_bottom_label">Mou al final</string>
<string name="sort">Ordena</string>
@@ -249,18 +230,14 @@
<!--Variable Speed-->
<string name="download_plugin_label">Baixa el connector</string>
<string name="no_playback_plugin_title">Connector no instal·lat</string>
- <string name="no_playback_plugin_or_sonic_msg">Per a què funcioni la velocitat de reproducció variable, recomanem activar el Sonic Media Player integrat [Android 4.1+].\n\nAltra opció és baixar el connector de tercers <i>Prestissimo</i> de la Play Store.\nQualsevol problema amb Prestissimo no és responsabilitat d\'AntennaPod i haurà d\'informar-ne al seu propietari.</string>
<string name="set_playback_speed_label">Velocitats de reproducció</string>
<string name="enable_sonic">Activa Sonic</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Encara no us heu subscrit a cap podcast.</string>
<string name="no_shownotes_label">Aquest episodi no té notes.</string>
<string name="no_chapters_label">Aquest episodi no té capítols</string>
<!--Preferences-->
<string name="storage_pref">Emmagatzematge</string>
<string name="project_pref">Projecte</string>
- <string name="other_pref">Altres</string>
- <string name="about_pref">Quant a</string>
<string name="queue_label">Cua</string>
<string name="integrations_label">Integracions</string>
<string name="automation">Automatització</string>
@@ -297,18 +274,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Establir hora del dia</string>
<string name="pref_autoUpdateIntervallOrTime_every">cada %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">als %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Només baixa fitxers a través d\'una xarxa sense fils</string>
<string name="pref_followQueue_title">Reproducció continuada</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Baixa a través de xarxes sense fils</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Desconnexió d\'auriculars</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Connexió d\'auriculars</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Connexió de bluetooth</string>
<string name="pref_mobileUpdate_title">Actualitzacions sobre xarxes mòbils</string>
- <string name="refreshing_label">S\'està actualitzant</string>
<string name="user_interface_label">Interfície d\'usuari</string>
<string name="pref_set_theme_title">Selecciona el tema</string>
- <string name="pref_nav_drawer_title">Personalitza el calaix de navegació</string>
- <string name="pref_nav_drawer_sum">Personalitza l\'aparença del calaix de navegació.</string>
<string name="pref_nav_drawer_items_title">Estableix els elements del calaix de navegació</string>
<string name="pref_nav_drawer_items_sum">Canvia els elements que apareixen al calaix de navegació.</string>
<string name="pref_nav_drawer_feed_order_title">Estableix l\'ordre de les subscripcions</string>
@@ -320,8 +292,6 @@
<string name="pref_automatic_download_sum">Configureu la baixada automàtica d\'episodis.</string>
<string name="pref_autodl_wifi_filter_title">Activa el filtre de la xarxa sense fils</string>
<string name="pref_autodl_wifi_filter_sum">Permet les baixades automàtiques només per a les xarxes sense fils seleccionades.</string>
- <string name="pref_autodl_allow_on_mobile_title">Baixa usant la connexió del mòbil.</string>
- <string name="pref_autodl_allow_on_mobile_sum">Permet la baixada automàtica mitjançant la connexió de dades del mòbil.</string>
<string name="pref_automatic_download_on_battery_title">Baixa mentre no es carrega</string>
<string name="pref_automatic_download_on_battery_sum">Permet les baixades automàtiques mentre la bateria no es carrega</string>
<string name="pref_parallel_downloads_title">Baixades paral·leles</string>
@@ -345,7 +315,6 @@
<string name="pref_gpodnet_full_sync_sum">Sincronitza amb gpodder.net totes les subscripcions i els estats dels episodis.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Darrer intent de sincronització: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronització en marxa</string>
- <string name="pref_gpodnet_full_sync_started">Sincronització completa en marxa</string>
<string name="pref_gpodnet_login_status"><![CDATA[Connectat com a <i>%1$s</i> amb el dispositiu <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Notifica els errors de sincronització</string>
<string name="pref_gpodnet_notifications_sum">Aquest paràmetre no s\'aplica als errors d\'autenticació. </string>
@@ -368,21 +337,14 @@
<string name="pref_showDownloadReport_title">Mostra informació de baixades</string>
<string name="pref_showDownloadReport_sum">Si les descàrregues fallen, genera un informe que mostra els detalls de la fallada.</string>
<string name="pref_expand_notify_unsupport_toast">Les versions d\'Android anteriors a la 4.1 no suporten les notificacions ampliades.</string>
- <string name="pref_queueAddToFront_sum">Afegeix nous episodis al principi de la cua.</string>
- <string name="pref_queueAddToFront_title">Posa en cua al davant</string>
<string name="pref_smart_mark_as_played_disabled">Desactivat</string>
<string name="pref_image_cache_size_title">Mida de la memòria cau de les imatges</string>
<string name="pref_image_cache_size_sum">Mida de la memòria cau en el disc de les imatges.</string>
- <string name="crash_report_title">Informa d\'un tancament abrupte</string>
- <string name="crash_report_sum">Envia l\'últim informe de tancament abrupte per e-mail</string>
- <string name="send_email">Envia e-mail</string>
<string name="experimental_pref">Experimental</string>
<string name="pref_media_player_message">Seleccioneu el reproductor multimèdia per a reproduir fitxers</string>
<string name="pref_current_value">Valor actual: %1$s</string>
<string name="pref_proxy_title">Servidor intermediari</string>
<string name="pref_proxy_sum">Estableix un servidor intermediari</string>
- <string name="pref_faq">PMF</string>
- <string name="pref_known_issues">Problemes coneguts</string>
<string name="pref_no_browser_found">No s\'ha trobat cap navegador web.</string>
<string name="pref_cast_title">Suport per a Chromecast</string>
<string name="pref_cast_message_play_flavor">Habilita el suport per la reproducció remota en dispositius de difusió (com ara Chromecast, Audio Speakers o Android TV). </string>
@@ -394,6 +356,8 @@
<string name="pref_videoBehavior_sum">Comportament a l\'abandonar la reproducció d\'un vídeo</string>
<string name="stop_playback">Atura reproducció</string>
<string name="continue_playback">Continuar reproducció d\'audio</string>
+ <!--About screen-->
+ <string name="about_pref">Quant a</string>
<!--Search-->
<string name="search_hint">Cerca episodis</string>
<string name="found_in_shownotes_label">Troba a les notes dels canals</string>
@@ -405,22 +369,16 @@
<string name="found_in_title_label">Trobat al títol</string>
<string name="no_results_for_query">No s\'han trobat resultats per \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Els fitxers OPML us permeten moure els podcasts d\'un gestor de podcasts a un altre.</string>
<string name="opml_import_option">Opció %1$d</string>
<string name="opml_import_explanation_1">Tria una ruta de fitxer específic del sistema de fitxers local.</string>
- <string name="opml_import_explanation_2">Utilitza una aplicació externa com Dropbox, Google Drive o el seu gestor de fitxers preferit per obrir un fitxer OPML.</string>
<string name="opml_import_explanation_3">Algunes aplicacions com Google Mail, Dropbox, Google Drive i la majoria dels administradors d\'arxius pot <i>obrir</i> arxius OPML <i>amb</i> AntennaPod.</string>
- <string name="start_import_label">Inicia la importació</string>
<string name="opml_import_label">Importació d\'OPML</string>
- <string name="opml_directory_error">Error!</string>
<string name="reading_opml_label">S\'està llegint el fitxer OPML</string>
<string name="opml_reader_error">S\'ha produït un error en llegir el document OPML: </string>
<string name="opml_import_error_no_file">Cap fitxer seleccionat!</string>
<string name="select_all_label">Selecciona-ho tot</string>
<string name="deselect_all_label">Deselecciona-ho tot</string>
- <string name="select_options_label">Selecciona...</string>
<string name="choose_file_from_filesystem">Des de sistema d\'arxius local</string>
- <string name="choose_file_from_external_application">Utilitza aplicació externa</string>
<string name="opml_export_label">Exportació OPML</string>
<string name="html_export_label">Exporta HTML</string>
<string name="exporting_label">Exportant...</string>
@@ -511,8 +469,6 @@
<string name="pref_restart_required">AntennaPod s\'ha de reiniciar perquè aquest canvi tingui efecte.</string>
<!--Online feed view-->
<string name="subscribe_label">Subscriu</string>
- <string name="subscribed_label">Subscrit</string>
- <string name="downloading_label">S\'està baixant...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Rebobina</string>
<string name="fast_forward_label">Avança ràpidament</string>
@@ -614,7 +570,6 @@
<string name="cast_failed_setting_volume">Error en establir el volum</string>
<string name="cast_failed_no_connection">No hi ha cap connexió amb el dispositiu de difusió </string>
<string name="cast_failed_no_connection_trans">S\'ha perdut la connexió amb el dispositiu de difusió. L\'aplicació està tractant de restablir la connexió, si això és possible. Espereu uns segons i torneu-ho a provar.</string>
- <string name="cast_failed_perform_action">No s\'ha pogut dur a terme l\'acció</string>
<string name="cast_failed_status_request">No s\'ha pogut sincronitzar amb el dispositiu de difusió</string>
<string name="cast_failed_seek">No s\'ha pogut cercar la nova posició al dispositiu de difusió</string>
<string name="cast_failed_receiver_player_error">El reproductor receptor ha trobat un error greu</string>
@@ -628,4 +583,5 @@
<string name="notification_channel_playing_description">Permet controlar la reproducció. Aquesta és la notificació principal que veureu durant la reproducció d\'un podcast.</string>
<string name="notification_channel_error">Errors</string>
<string name="notification_channel_error_description">Mostrar si quelcom va malament, per exemple si una baixada o sincronització fallen.</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-cs-rCZ/strings.xml b/core/src/main/res/values-cs-rCZ/strings.xml
index 9ce2f074b..8540d1267 100644
--- a/core/src/main/res/values-cs-rCZ/strings.xml
+++ b/core/src/main/res/values-cs-rCZ/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synchronizovat s ostatními přístroji</string>
<string name="gpodnet_auth_label">Login pro gpodder.net</string>
- <string name="free_space_label">%1$s volné</string>
<string name="episode_cache_full_title">Odkládací prostor pro epizody je plný</string>
<string name="episode_cache_full_message">Došlo k zaplnění limitu odkládacího prostoru pro epizody. Můžete navýšit vyhrazený prostor v Nastavení.</string>
- <string name="synchronizing">Synchronizace...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Celkový čas poslechnutých podcastů:</string>
<string name="statistics_details_dialog">%1$d z %2$d započatých epizod.\n\nPřehraných %3$s z %4$s.</string>
@@ -59,21 +57,16 @@
<string name="author_label">Přispěvatel(é)</string>
<string name="language_label">Jazyk</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Nastavení</string>
<string name="cover_label">Obrázek</string>
<string name="error_label">Chyba</string>
<string name="error_msg_prefix">Došlo k chybě:</string>
<string name="refresh_label">Obnovit</string>
<string name="external_storage_error_msg">Není dostupné žádné externí uložiště. Pro správnou funkci aplikace se prosím ujistěte, že je připojeno externí úložiště.</string>
<string name="chapters_label">Kapitoly</string>
- <string name="shownotes_label">Poznámky</string>
+ <string name="chapter_duration">Délka: %1$s</string>
<string name="description_label">Popis</string>
- <string name="most_recent_prefix">Poslední epizoda:\u0020</string>
<string name="episodes_suffix">\u0020epizod</string>
- <string name="length_prefix">Délka:\u0020</string>
- <string name="size_prefix">Velikost:\u0020</string>
<string name="processing_label">Zpracovávám</string>
- <string name="loading_label">Načítání</string>
<string name="save_username_password_label">Uložit uživatelské jméno a heslo</string>
<string name="close_label">Zavřít</string>
<string name="retry_label">Zkusit znovu</string>
@@ -99,8 +92,6 @@
<string name="feedurl_label">URL kanálu</string>
<string name="etxtFeedurlHint">URL nebo webová stránka kanálu</string>
<string name="txtvfeedurl_label">Přidat podcast pomocí URL</string>
- <string name="podcastdirectories_label">Najít podcast v seznamu</string>
- <string name="podcastdirectories_descr">Nové podcasty můžete hledat pomocí iTunes nebo fyyd, nebo hledat na gpodder.net podle jména, kategorie či popularity.</string>
<string name="browse_gpoddernet_label">Prohledávat gpodder.net</string>
<string name="discover_more">více »</string>
<!--Actions on feeds-->
@@ -112,9 +103,12 @@
<string name="remove_feed_label">Odstranit podcast</string>
<string name="share_label">Sdílet</string>
<string name="share_link_label">Sdílet URL epizody</string>
+ <string name="share_link_with_position_label">Sdílet URL epizody s časovou značkou</string>
<string name="share_file_label">Sdílet soubor</string>
<string name="share_feed_url_label">Sdílet URL kanálu</string>
- <string name="hide_episodes_title">Skrýt epizody</string>
+ <string name="share_item_url_label">Sdílet URL souboru</string>
+ <string name="share_item_url_with_position_label">Sdílet URL souboru s časovou značkou</string>
+ <string name="feed_remover_msg">Odstraňování podcastu</string>
<string name="hide_unplayed_episodes_label">Neposlechnuté</string>
<string name="hide_paused_episodes_label">Pozastavené</string>
<string name="hide_played_episodes_label">Poslechnuté</string>
@@ -130,10 +124,9 @@
<string name="download_label">Stáhnout</string>
<string name="play_label">Přehrát</string>
<string name="pause_label">Pozastavit</string>
- <string name="stop_label">Zastavit</string>
<string name="stream_label">Vysílat</string>
- <string name="remove_label">Odstranit</string>
<string name="delete_label">Smazat</string>
+ <string name="delete_failed">Nelze smazat soubor. Restart přístroje může pomoci.</string>
<string name="delete_episode_label">Smazat epizodu</string>
<string name="mark_read_label">Označit jako poslechnuté</string>
<string name="marked_as_read_label">Označeno jako poslechnuté</string>
@@ -153,13 +146,12 @@
<string name="removed_item">Položka odebrána</string>
<!--Download messages and labels-->
<string name="download_successful">úspěšné</string>
- <string name="download_failed">selhalo</string>
<string name="download_pending">Čeká na stažení</string>
<string name="download_running">Probíhá stahování</string>
<string name="download_error_details">Detaily</string>
+ <string name="download_error_details_message">%1$s \n\nURL souboru:\n%2$s</string>
<string name="download_error_device_not_found">Úložné zařízení nenalezeno</string>
<string name="download_error_insufficient_space">Nedostatek volného místa</string>
- <string name="download_error_file_error">Souborová chyba</string>
<string name="download_error_http_data_error">HTTP chyba</string>
<string name="download_error_error_unknown">Neznámá chyba</string>
<string name="download_error_parser_exception">Výjimka parseru</string>
@@ -169,7 +161,6 @@
<string name="download_error_unauthorized">Chyba přihlášení</string>
<string name="download_error_file_type_type">Chyba typu souboru</string>
<string name="download_error_forbidden">Zákázáno</string>
- <string name="cancel_all_downloads_label">Zrušit všechna stahování</string>
<string name="download_canceled_msg">Stahování zrušeno</string>
<string name="download_canceled_autodownload_enabled_msg">Stahování zrušeno\nVypnuto <i>automatické stahování</i> této položky</string>
<string name="download_report_title">Stahování dokončeno s chybou</string>
@@ -190,7 +181,6 @@
<string name="download_log_title_unknown">Neznámý název</string>
<string name="download_type_feed">Kanál</string>
<string name="download_type_media">Soubor</string>
- <string name="download_type_image">Obrázek</string>
<string name="download_request_error_dialog_message_prefix">Nastala chyba při pokusu o stažení souboru:\u0020</string>
<string name="authentication_notification_title">Vyžadováno ověření</string>
<string name="authentication_notification_msg">Zdroj který jste vybrali vyžaduje zadání uživatelského jména a hesla</string>
@@ -209,7 +199,6 @@
<string name="playback_error_unknown">Neznámá chyba</string>
<string name="no_media_playing_label">Žádné probíhající přehrávání</string>
<string name="player_buffering_msg">Načítání</string>
- <string name="playbackservice_notification_title">Přehrávaný podcast</string>
<string name="unknown_media_key">AntennaPod - Neznámý klíč médií: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Zamknout frontu</string>
@@ -218,15 +207,15 @@
<string name="queue_unlocked">Fronta odemknuta</string>
<string name="clear_queue_label">Vyprázdnit frontu</string>
<string name="undo">Zpět</string>
- <string name="removed_from_queue">Položka odebrána</string>
<string name="move_to_top_label">Posunout na začátek</string>
<string name="move_to_bottom_label">Posunout na konec</string>
<string name="sort">Řazení</string>
<string name="date">Datum</string>
<string name="duration">Délka</string>
<string name="episode_title">Název epizody</string>
- <string name="feed_title">Jméno podcastu</string>
+ <string name="feed_title">Název podcastu</string>
<string name="random">Náhodné</string>
+ <string name="smart_shuffle">Chytré míchání</string>
<string name="ascending">Vzestupně</string>
<string name="descending">Sestupně</string>
<string name="clear_queue_confirmation_msg">Potvrďte prosím, že chcete vyčistit tuto frontu a VŠECHNY v ní obsažené epizody</string>
@@ -235,17 +224,19 @@
<!--Variable Speed-->
<string name="download_plugin_label">Stáhnout modul</string>
<string name="no_playback_plugin_title">Modul není nainstalován</string>
- <string name="no_playback_plugin_or_sonic_msg">Pro fungující proměnné rychlosti přehrávání doporučujeme povolit zabudovaný Sonic mediaplayer [Android 4.1+].\n\nAlternativně je možné z Obchodu Play stáhnout zásuvný modul třetí strany <i>Prestissimo</i>.\nAntennaPod nenese zodpovědnost za jakékoliv problémy s modulem Prestissimo a ty by měly být hlášeny jeho vývojářům.</string>
<string name="set_playback_speed_label">Rychlosti přehrávání</string>
<string name="enable_sonic">Povolit Sonic</string>
<!--Empty list labels-->
<string name="no_shownotes_label">Tato epizoda neobsahuje žádné poznámky.</string>
+ <string name="no_run_downloads_head_label">Neběží žádná stahování</string>
+ <string name="no_history_head_label">Žádná historie</string>
+ <string name="no_all_episodes_head_label">Žádné epizody</string>
+ <string name="no_new_episodes_head_label">Žádné nové epizody</string>
+ <string name="no_fav_episodes_head_label">Žádné oblíbené epizody</string>
<string name="no_chapters_label">Tato epizoda nemá žádné kapitoly.</string>
<!--Preferences-->
<string name="storage_pref">Úložiště</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Ostatní</string>
- <string name="about_pref">O aplikaci</string>
<string name="queue_label">Fronta</string>
<string name="import_export_pref">Importovat/Exportovat</string>
<string name="appearance">Vzhled</string>
@@ -260,8 +251,10 @@
<string name="pref_auto_delete_sum">Smazat díl po jeho přehrání</string>
<string name="pref_auto_delete_title">Automatické mazání</string>
<string name="pref_smart_mark_as_played_sum">Označit epizody jako poslechnuté i pokud ještě zbývá určitý počet sekund přehrávání do jejich konce</string>
+ <string name="pref_smart_mark_as_played_title">Chytré označování jako poslechnuté</string>
<string name="pref_skip_keeps_episodes_sum">Neodstraňovat epizody při jejich přeskočení</string>
<string name="pref_skip_keeps_episodes_title">Nemazat přeskočené epizody</string>
+ <string name="pref_favorite_keeps_episodes_title">Nemazat oblíbené epizody</string>
<string name="playback_pref">Přehrávání</string>
<string name="network_pref">Síť</string>
<string name="pref_autoUpdateIntervallOrTime_title">Aktualizovat interval nebo čas v průběhu dne</string>
@@ -272,18 +265,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Nastavit čas v průběhu dne</string>
<string name="pref_autoUpdateIntervallOrTime_every">každých %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">v %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Stahovat soubory pouze pomocí WiFi</string>
<string name="pref_followQueue_title">Kontinuální přehrávání</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi stahování</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Sluchátka odpojena</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Sluchátka připojena</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth připojeno</string>
<string name="pref_mobileUpdate_title">Mobilní aktualizace</string>
- <string name="refreshing_label">Obnovuji</string>
<string name="user_interface_label">Uživatelské rozhraní</string>
<string name="pref_set_theme_title">Vybrat motiv</string>
- <string name="pref_nav_drawer_title">Upravit navigační panel</string>
- <string name="pref_nav_drawer_sum">Upravit vzhled navigačního panelu.</string>
<string name="pref_nav_drawer_items_title">Změnit navigační panel</string>
<string name="pref_nav_drawer_items_sum">Upravit zobrazení položek v navigačním panelu.</string>
<string name="pref_nav_drawer_feed_order_title">Nastavit pořadí sbírek</string>
@@ -316,14 +304,17 @@
<string name="pref_gpodnet_full_sync_sum">Synchronizovat všechny odběry a stav epizod s gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Poslední pokus o synchronizaci: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synchronizace spuštěna</string>
- <string name="pref_gpodnet_full_sync_started">Spuštěna plná synchronizace</string>
<string name="pref_gpodnet_login_status"><![CDATA[Přihlášen jako <i>%1$s</i> z přístroje <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Zobrazovat upozornění na chyby synchronizace</string>
<string name="pref_gpodnet_notifications_sum">Toto nastavení se netýká chyb přihlášení.</string>
<string name="pref_playback_speed_title">Rychlosti přehrávání</string>
<string name="pref_playback_speed_sum">Přizpůsobení rychlosti je dostupné pro přehrávání zvuku různými rychlostmi</string>
+ <string name="pref_fast_forward">Délka času posunu vpřed</string>
+ <string name="pref_rewind">Délka času posunu zpět</string>
<string name="pref_gpodnet_sethostname_title">Nastavit hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Použít přednastaveného hosta</string>
+ <string name="pref_expandNotify_title">Vysoká priorita pro upozornění</string>
+ <string name="pref_expandNotify_sum">Toto obvykle přidá tlačítka ovládání přehrávání do zpráv upozornění</string>
<string name="pref_persistNotify_title">Pevné ovládání přehrávání</string>
<string name="pref_persistNotify_sum">Zachovat upozornění a ovládání na obrazovce uzamčení i při pozastaveném přehrávání.</string>
<string name="pref_compact_notification_buttons_title">Nastavení tlačítek uzamčené obrazovky</string>
@@ -335,53 +326,46 @@
<string name="pref_showDownloadReport_title">Zobrazit report stahování</string>
<string name="pref_showDownloadReport_sum">Pokud selže stahování, vygenerovat report zobrazující detaily o chybě.</string>
<string name="pref_expand_notify_unsupport_toast">Verze Androidu nižší než 4.1 nepodporují rozšířená upozornění.</string>
- <string name="pref_queueAddToFront_sum">Přidávat nové epizody na začátek fronty.</string>
- <string name="pref_queueAddToFront_title">Přidat na začátek.</string>
<string name="pref_smart_mark_as_played_disabled">Vypnuto</string>
<string name="pref_image_cache_size_title">Velikost odkládací paměti obrázků</string>
<string name="pref_image_cache_size_sum">Velikost diskové paměti pro obrázky.</string>
- <string name="crash_report_title">Hlášení pádů</string>
- <string name="crash_report_sum">Odesílat hlášení o posledním pádu aplikace emailem</string>
- <string name="send_email">Poslat email</string>
<string name="experimental_pref">Experimentální</string>
<string name="pref_current_value">Aktuální hodnota: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Nastavit síťovou proxy</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Známé chyby</string>
<string name="pref_no_browser_found">Webový prohlížeč nenalezen.</string>
<string name="pref_cast_title">Chromecast podpora</string>
<string name="pref_cast_message_play_flavor">Povolit podporu vzdáleného přehrávání médií na Cast přístrojích (jako třeba Chromecast, Audio Speakers nebo Android TV)</string>
<string name="pref_cast_message_free_flavor">Chromecast vyžaduje proprietární knihovny třetích stran, které jsou vypnuty v této verzi AntennaPod</string>
+ <string name="pref_skip_silence_title">Přeskočit ticho v audio souboru</string>
+ <!--About screen-->
+ <string name="about_pref">O aplikaci</string>
<!--Search-->
<string name="search_hint">Hledat epizody</string>
<string name="found_in_shownotes_label">Nalezeno v poznámkách pořadu</string>
<string name="found_in_chapters_label">Nalezeno v kapitolách</string>
+ <string name="found_in_feeds_label">Nalezeno v podcastech</string>
<string name="search_status_no_results">Žádné výsledky</string>
<string name="search_label">Vyhledat</string>
<string name="found_in_title_label">Nalezeno v názvu</string>
<string name="no_results_for_query">Nebyly nalezeny žádné výsledky pro \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML soubory umožňují přenést vaše podcasty z jednoho přehrávače na jiný.</string>
<string name="opml_import_option">Možnost %1$d</string>
<string name="opml_import_explanation_1">Vybrat umístění v místním souborovém systému.</string>
- <string name="opml_import_explanation_2">Použít externí aplikaci jako třeba Dropbox, Google Drive nebo oblíbeného správce souborů k otevření OPML souboru.</string>
<string name="opml_import_explanation_3">Mnoho aplikací jako třeba Google Mail, Dropbox, Google Drive a většina správců souborů umí <i>otevřít</i> OPML soubory <i>v aplikaci</i> AntennaPod.</string>
- <string name="start_import_label">Importovat</string>
<string name="opml_import_label">OPML import</string>
- <string name="opml_directory_error">CHYBA!</string>
<string name="reading_opml_label">Načítání OPML souboru</string>
<string name="opml_reader_error">Došlo k chybě při čtení OPML dokumentu:</string>
<string name="opml_import_error_no_file">Nebyl vybrán žádný soubor!</string>
<string name="select_all_label">Označit vše</string>
<string name="deselect_all_label">Zrušit výběr</string>
- <string name="select_options_label">Vybrat</string>
<string name="choose_file_from_filesystem">Z místního souborového systému</string>
- <string name="choose_file_from_external_application">Použít externí aplikaci</string>
<string name="opml_export_label">OPML export</string>
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Export</string>
<string name="export_error_label">Chyba exportu</string>
+ <string name="export_success_title">Export úspěšný</string>
+ <string name="export_success_sum">Exportovaný soubor zapsán do:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Pro přečtení OPML souboru je vyžadován přístup k externímu úložišti</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Nastavit časovač vypnutí</string>
@@ -472,8 +456,6 @@
<string name="pref_restart_required">Pro aktivování změn nastavení bylo třeba restartovat aplikaci AntennaPod.</string>
<!--Online feed view-->
<string name="subscribe_label">Odebírat</string>
- <string name="subscribed_label">Odebíráno</string>
- <string name="downloading_label">Stahování</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Posunout zpět</string>
<string name="fast_forward_label">Posunout vpřed</string>
@@ -557,8 +539,12 @@
<string name="proxy_port_invalid_error">Neplatný port</string>
<!--Subscriptions fragment-->
<!--Database import/export-->
+ <string name="import_export">Import/export databáze</string>
<string name="label_import">Importovat</string>
<string name="label_export">Expotovat</string>
+ <string name="import_select_file">Vybrat soubor pro import</string>
+ <string name="export_ok">Export úspěšný</string>
+ <string name="import_ok">Import úspěšný.\n\nPotvrďte restart AntennaPod tlačítkem OK</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Přehrát na...</string>
<string name="cast_disconnect_label">Odpojit sezení vysílání</string>
@@ -570,11 +556,11 @@
<string name="cast_failed_setting_volume">Selhalo nastavení hlasitosti</string>
<string name="cast_failed_no_connection">Není dostupné připojení k přehrávacímu přístroji</string>
<string name="cast_failed_no_connection_trans">Došlo ke ztrátě připojení k přehrávacímu přístroji. Aplikace se pokouší znovu připojit, pokud to bude možné. Počkejte několik sekund a zkuste znovu.</string>
- <string name="cast_failed_perform_action">Selhalo provedení akce</string>
<string name="cast_failed_status_request">Selhala synchronizace s vysílačem</string>
<string name="cast_failed_seek">Selhal posun na novou pozici na vysílači</string>
<string name="cast_failed_receiver_player_error">Přijímač zaznamenal závažnou chybu</string>
<string name="cast_failed_media_error_skipping">Chyba přehrávání médií. Přeskakuji...</string>
<!--Notification channels-->
<string name="notification_channel_downloading">Stahuji</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-da/strings.xml b/core/src/main/res/values-da/strings.xml
index a474a5153..728975847 100644
--- a/core/src/main/res/values-da/strings.xml
+++ b/core/src/main/res/values-da/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synkronisér med andre enheder</string>
<string name="gpodnet_auth_label">Login til gpodder.net</string>
- <string name="free_space_label">%1$s ledig</string>
<string name="episode_cache_full_title">Mellemlageret for udsendelser er fuldt</string>
<string name="episode_cache_full_message">Der er ikke mere plads i mellemlageret for udsendelser. Du kan øge størrelsen på mellemlageret i Indstillinger.</string>
- <string name="synchronizing">Synkroniserer ...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Samlet varighed for afspillede podcasts:</string>
<string name="statistics_details_dialog">%1$d af %2$d udsendelser startet.\n\nAfspillet %3$s af %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Forfatter(e)</string>
<string name="language_label">Sprog</string>
<string name="url_label">Webadresse</string>
- <string name="podcast_settings_label">Indstillinger </string>
<string name="cover_label">Billede</string>
<string name="error_label">Fejl</string>
<string name="error_msg_prefix">En fejl er opstået:</string>
@@ -71,14 +68,9 @@
<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>
<string name="chapter_duration">Varighed: %1$s</string>
- <string name="shownotes_label">Beskrivelse</string>
<string name="description_label">Beskrivelse</string>
- <string name="most_recent_prefix">Seneste udsendelse:\u0020</string>
<string name="episodes_suffix">\u0020udsendelser</string>
- <string name="length_prefix">Længde:\u0020</string>
- <string name="size_prefix">Størrelse:\u0020</string>
<string name="processing_label">Behandler</string>
- <string name="loading_label">Indlæser...</string>
<string name="save_username_password_label">Gem brugernavn og adgangskode</string>
<string name="close_label">Luk</string>
<string name="retry_label">Prøv igen</string>
@@ -102,8 +94,6 @@
<string name="feedurl_label">Webadresse for feed</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Tilføj podcast via webadresse</string>
- <string name="podcastdirectories_label">Find podcast i mappe</string>
- <string name="podcastdirectories_descr">For at finde nye podcasts kan du søge på iTunes eller fyyd, eller du kan bladre på gpodder.net efter navn, kategori eller popularitet.</string>
<string name="browse_gpoddernet_label">Gennemse gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marker alle som afspillet</string>
@@ -120,7 +110,6 @@
<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>
- <string name="hide_episodes_title">Skjul udsendelser</string>
<string name="batch_edit">Masseredigering</string>
<string name="select_all_above">Vælg alt ovenfor</string>
<string name="select_all_below">Vælg alt nedenfor</string>
@@ -140,9 +129,7 @@
<string name="download_label">Hent</string>
<string name="play_label">Afspil</string>
<string name="pause_label">Sæt på pause</string>
- <string name="stop_label">Stop</string>
<string name="stream_label">Stream</string>
- <string name="remove_label">Fjern</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="mark_read_label">Marker som læst</string>
@@ -163,14 +150,12 @@
<string name="removed_item">Element fjernet</string>
<!--Download messages and labels-->
<string name="download_successful">lykkedes</string>
- <string name="download_failed">fejlede</string>
<string name="download_pending">Afventer overførsel</string>
<string name="download_running">Overfører</string>
<string name="download_error_details">Detaljer</string>
<string name="download_error_details_message">%1$s \n\nFil-URL:\n%2$s</string>
<string name="download_error_device_not_found">Kan ikke finde lagerenhed</string>
<string name="download_error_insufficient_space">Ikke nok plads</string>
- <string name="download_error_file_error">Filfejl</string>
<string name="download_error_http_data_error">HTTP-datafejl</string>
<string name="download_error_error_unknown">Ukendt fejl</string>
<string name="download_error_parser_exception">Parser-undtagelse</string>
@@ -180,7 +165,6 @@
<string name="download_error_unauthorized">Godkendelsesfejl</string>
<string name="download_error_file_type_type">Filtypefejl</string>
<string name="download_error_forbidden">Adgang nægtet</string>
- <string name="cancel_all_downloads_label">Annuller alle overførsler</string>
<string name="download_canceled_msg">Overførsel annulleret</string>
<string name="download_canceled_autodownload_enabled_msg">Overførsel annulleret\n<i>Automatisk overførsel</i> blev slået fra for dette element</string>
<string name="download_report_title">Overførsler afsluttet med fejl</string>
@@ -199,7 +183,6 @@
<string name="download_log_title_unknown">Ukendt titel</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Mediefil</string>
- <string name="download_type_image">Billede</string>
<string name="download_request_error_dialog_message_prefix">En fejl opstod ved overførsel af filen:\u0020</string>
<string name="authentication_notification_title">Godkendelse kræves</string>
<string name="authentication_notification_msg">Ressourcen du efterspurgte kræver brugernavn og adgangskode</string>
@@ -219,7 +202,6 @@
<string name="no_media_playing_label">Ingen medier afspiller</string>
<string name="player_buffering_msg">Henter data til buffer</string>
<string name="player_go_to_picture_in_picture">Billed-i-billed-tilstand</string>
- <string name="playbackservice_notification_title">Afspiller podcast</string>
<string name="unknown_media_key">AntennaPod - Ukendt medienøgle: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Lås kø</string>
@@ -228,7 +210,6 @@
<string name="queue_unlocked">Kø låst op</string>
<string name="clear_queue_label">Ryd kø</string>
<string name="undo">Fortryd</string>
- <string name="removed_from_queue">Element slettet</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>
@@ -243,18 +224,14 @@
<!--Variable Speed-->
<string name="download_plugin_label">Hent plugin</string>
<string name="no_playback_plugin_title">Plugin er ikke installeret</string>
- <string name="no_playback_plugin_or_sonic_msg">For at afspilning med variabel hastighed skal fungere, anbefaler vi at slå den indbyggede Sonic-medieafspiller til (Android 4.1+).\n\nAlternativt kan du hente tredjeparts-pluginnet <i>Prestissimo</i> fra Play Store.\nProblemer med Prestissimo er ikke AntennaPods ansvar og skal rapporteres til pluginnets ejer.</string>
<string name="set_playback_speed_label">Afspilningshastigheder</string>
<string name="enable_sonic">Slå Sonic til</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Du har ikke abonneret på nogen podcasts endnu.</string>
<string name="no_shownotes_label">Denne udsendelse har ingen beskrivelse.</string>
<string name="no_chapters_label">Denne udsendelse har ingen kapitler.</string>
<!--Preferences-->
<string name="storage_pref">Lagring</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Andre</string>
- <string name="about_pref">Om</string>
<string name="queue_label">Kø</string>
<string name="integrations_label">Integrerede tjenester</string>
<string name="automation">Automatisering</string>
@@ -290,18 +267,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Indstil klokkeslæt</string>
<string name="pref_autoUpdateIntervallOrTime_every">hver %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">kl. %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Overfør mediefiler kun via wi-fi</string>
<string name="pref_followQueue_title">Kontinuerlig afspilning</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Overførsel af mediefiler over wi-fi</string>
<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_mobileUpdate_title">Mobile opdateringer</string>
- <string name="refreshing_label">Opdaterer</string>
<string name="user_interface_label">Brugergrænseflade</string>
<string name="pref_set_theme_title">Vælg tema</string>
- <string name="pref_nav_drawer_title">Tilpas navigationspanel</string>
- <string name="pref_nav_drawer_sum">Tilpas udseendet af navigationspanelet</string>
<string name="pref_nav_drawer_items_title">Vælg elementer i navigationspanelet</string>
<string name="pref_nav_drawer_items_sum">Vælg hvilke elementer, der skal vises i navigationspanelet.</string>
<string name="pref_nav_drawer_feed_order_title">Vælg abonnementsorden</string>
@@ -312,8 +284,6 @@
<string name="pref_automatic_download_sum">Konfigurer automatisk overførsel af udsendelser</string>
<string name="pref_autodl_wifi_filter_title">Slå wi-fi-filter til</string>
<string name="pref_autodl_wifi_filter_sum">Tillad automatisk overførsel kun på de valgte wi-fi-netværk</string>
- <string name="pref_autodl_allow_on_mobile_title">Hent på mobilt netværk</string>
- <string name="pref_autodl_allow_on_mobile_sum">Tillad automatisk overførsel på den mobile dataforbindelse</string>
<string name="pref_automatic_download_on_battery_title">Tillad overførsel ved batteridrift</string>
<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>
@@ -337,7 +307,6 @@
<string name="pref_gpodnet_full_sync_sum">Synkroniser tilstande for alle abonnementer og udsendelser med gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Sidste synkroniseringsforsøg: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synkronisering startet</string>
- <string name="pref_gpodnet_full_sync_started">Fuld synkronisering startet</string>
<string name="pref_gpodnet_login_status"><![CDATA[Logget ind som <i>%1$s</i> med enheden <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Vis notifikationer med synkroniseringsfejl</string>
<string name="pref_gpodnet_notifications_sum">Denne indstilling vedrører ikke godkendelsesfejl.</string>
@@ -362,20 +331,13 @@
<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_queueAddToFront_sum">Føj nye udsendelser til starten af køen.</string>
- <string name="pref_queueAddToFront_title">Føj til starten af køen</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="crash_report_title">Nedbrudsrapport</string>
- <string name="crash_report_sum">Send den seneste nedbrudsrapport via e-mail</string>
- <string name="send_email">Send e-mail</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>
<string name="pref_proxy_sum">Indstil en netværksproxy</string>
- <string name="pref_faq">Ofte spurgte spørgsmål</string>
- <string name="pref_known_issues">Kendte problemer</string>
<string name="pref_no_browser_found">Ingen webbrowser fundet</string>
<string name="pref_cast_title">Chromecast-understøttelse</string>
<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>
@@ -396,6 +358,8 @@
<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>
<!--Search-->
<string name="search_hint">Søg efter udsendelser</string>
<string name="found_in_shownotes_label">Fundet i beskrivelse</string>
@@ -407,22 +371,16 @@
<string name="found_in_title_label">Fundet i titel</string>
<string name="no_results_for_query">Ingen resultater fundet for \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML-filer lader dig flytte dine podcasts fra én podcastafspiller til en anden.</string>
<string name="opml_import_option">Valgmulighed %1$d</string>
<string name="opml_import_explanation_1">Vælg en bestemt filsti fra det lokale filsystem.</string>
- <string name="opml_import_explanation_2">Brug en ekstern app som Dropbox, Google Drive eller dit foretrukne filhåndteringsprogram til at åbne en OPML-fil.</string>
<string name="opml_import_explanation_3">Mange apps som Gmail, Dropbox, Google Drive og de fleste filhåndteringsprogrammer kan <i>åbne</i> OPML-filer <i>med</i> AntennaPod.</string>
- <string name="start_import_label">Start import</string>
<string name="opml_import_label">OPML-import</string>
- <string name="opml_directory_error">FEJL!</string>
<string name="reading_opml_label">Indlæser OPML-fil</string>
<string name="opml_reader_error">Der opstod en fejl, da OPML-dokumentet blev forsøgt indlæst</string>
<string name="opml_import_error_no_file">Ingen fil valgt!</string>
<string name="select_all_label">Vælg alle</string>
<string name="deselect_all_label">Fravælg alle</string>
- <string name="select_options_label">Vælg…</string>
<string name="choose_file_from_filesystem">Fra lokalt filsystem</string>
- <string name="choose_file_from_external_application">Brug ekstern app</string>
<string name="opml_export_label">OPML eksport</string>
<string name="html_export_label">HTML-eksport</string>
<string name="exporting_label">Eksporterer…</string>
@@ -513,8 +471,6 @@
<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="subscribed_label">Abonneret</string>
- <string name="downloading_label">Overfører…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Spol tilbage</string>
<string name="fast_forward_label">Spol frem</string>
@@ -615,7 +571,6 @@
<string name="cast_failed_setting_volume">Kunne ikke indstille lydstyrken</string>
<string name="cast_failed_no_connection">Der er ingen forbindelse til cast-enheden</string>
<string name="cast_failed_no_connection_trans">Forbindelsen til cast-enheden er mistet. Appen forsøger at genskabe forbindelsen, om muligt. Vent venligst et par sekunder og prøv igen.</string>
- <string name="cast_failed_perform_action">Det lykkedes ikke at udføre handlingen</string>
<string name="cast_failed_status_request">Det lykkedes ikke at synkronisere med cast-enheden</string>
<string name="cast_failed_seek">Det lykkedes ikke at søge til den nye position på cast-enheden</string>
<string name="cast_failed_receiver_player_error">Modtagerafspilleren er stødt på en alvorlig fejl</string>
@@ -625,4 +580,5 @@
<string name="notification_channel_downloading">Henter</string>
<string name="notification_channel_downloading_description">Vises samtidig med den hentes.</string>
<string name="notification_channel_error">Fejl</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml
index d191f4059..0d5158d24 100644
--- a/core/src/main/res/values-de/strings.xml
+++ b/core/src/main/res/values-de/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Mit anderen Geräten synchronisieren</string>
<string name="gpodnet_auth_label">gpodder.net Anmeldung</string>
- <string name="free_space_label">%1$s frei</string>
<string name="episode_cache_full_title">Episodenspeicher voll</string>
<string name="episode_cache_full_message">Der Episodenspeicher ist voll. Du kannst die Größe des Episodenspeichers in den Einstellungen erhöhen.</string>
- <string name="synchronizing">Synchronisiere...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Gesamtzeit gespielter Podcasts:</string>
<string name="statistics_details_dialog">%1$d von %2$d Episoden gestartet.\n\n%3$s von %4$s Episoden gespielt.</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">Berechne die tatsächlich gespielte Dauer. Zweimaliges Abspielen wird auch zwei Mal gezählt, als gespielt markierte Episoden jedoch gar nicht</string>
<string name="statistics_mode_count_all">Aufsummieren aller als gespielt markierter Episoden</string>
<string name="statistics_speed_not_counted">Hinweis: Die Abspielgeschwindigkeit wird nicht berücksichtigt.</string>
+ <string name="statistics_reset_data">Statistiken zurücksetzen</string>
+ <string name="statistics_reset_data_msg">Die Abspieldauer wird für alle Episoden gelöscht. Möchtest du wirklich fortfahren?</string>
<!--Main activity-->
<string name="drawer_open">Menü öffnen</string>
<string name="drawer_close">Menü schließen</string>
@@ -63,7 +63,6 @@
<string name="author_label">Autor(en)</string>
<string name="language_label">Sprache</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Einstellungen</string>
<string name="cover_label">Bild</string>
<string name="error_label">Fehler</string>
<string name="error_msg_prefix">Ein Fehler ist aufgetreten:</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">Der externe Speicher ist nicht verfügbar. Bitte stelle sicher, dass das externe Speichermedium eingelegt ist, damit die App funktioniert.</string>
<string name="chapters_label">Kapitel</string>
<string name="chapter_duration">Dauer: %1$s</string>
- <string name="shownotes_label">Shownotizen</string>
<string name="description_label">Beschreibung</string>
- <string name="most_recent_prefix">Letzte Episode:\u0020</string>
<string name="episodes_suffix">\u0020Episoden</string>
- <string name="length_prefix">Länge:\u0020</string>
- <string name="size_prefix">Größe:\u0020</string>
<string name="processing_label">Verarbeite</string>
- <string name="loading_label">Wird geladen…</string>
<string name="save_username_password_label">Benutzername und Password merken</string>
<string name="close_label">Schließen</string>
<string name="retry_label">Erneut versuchen</string>
@@ -108,8 +102,6 @@
<string name="feedurl_label">Feed URL</string>
<string name="etxtFeedurlHint">URL des Feeds oder der Webseite</string>
<string name="txtvfeedurl_label">Podcast per URL hinzufügen</string>
- <string name="podcastdirectories_label">Podcast in Verzeichnis finden</string>
- <string name="podcastdirectories_descr">Für neue Podcasts kannst du iTunes oder fyyd durchsuchen, oder gpodder.net nach Name, Kategorie oder Popularität durchstöbern</string>
<string name="browse_gpoddernet_label">gpodder.net durchsuchen</string>
<string name="discover">Entdecken</string>
<string name="discover_more">mehr »</string>
@@ -131,13 +123,13 @@
<string name="share_link_label">Episoden URL Teilen</string>
<string name="share_link_with_position_label">Teile Link mit Zeitmarke</string>
<string name="share_file_label">Teile Datei</string>
+ <string name="share_website_url_label">Teile URL der Website</string>
<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="hide_episodes_title">Episoden verbergen</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>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Abspielen</string>
<string name="pause_label">Pausieren</string>
- <string name="stop_label">Stop</string>
<string name="stream_label">Streamen</string>
- <string name="remove_label">Entfernen</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>
@@ -207,14 +197,12 @@
<string name="removed_item">Element entfernt</string>
<!--Download messages and labels-->
<string name="download_successful">erfolgreich</string>
- <string name="download_failed">fehlgeschlagen</string>
<string name="download_pending">Download anstehend</string>
<string name="download_running">Download läuft</string>
<string name="download_error_details">Details</string>
<string name="download_error_details_message">%1$s \n\nDatei-URL:\n%2$s</string>
<string name="download_error_device_not_found">Speichermedium nicht gefunden</string>
<string name="download_error_insufficient_space">Zu wenig Speicherplatz</string>
- <string name="download_error_file_error">Dateifehler</string>
<string name="download_error_http_data_error">HTTP Datenfehler</string>
<string name="download_error_error_unknown">Unbekannter Fehler</string>
<string name="download_error_parser_exception">Parserfehler</string>
@@ -224,7 +212,6 @@
<string name="download_error_unauthorized">Authentifizierungsfehler</string>
<string name="download_error_file_type_type">Dateityp-Fehler</string>
<string name="download_error_forbidden">Verboten</string>
- <string name="cancel_all_downloads_label">Alle Downloads abbrechen</string>
<string name="download_canceled_msg">Download abgebrochen</string>
<string name="download_canceled_autodownload_enabled_msg">Download abgebrochen\n<i>Automatischen Download</i> für diese Episode deaktiviert</string>
<string name="download_report_title">Downloads endeten mit Fehler(n)</string>
@@ -243,7 +230,6 @@
<string name="download_log_title_unknown">Unbekannter Titel</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Mediendatei</string>
- <string name="download_type_image">Bild</string>
<string name="download_request_error_dialog_message_prefix">Beim Herunterladen der Datei ist ein Fehler aufgetreten:\u0020</string>
<string name="null_value_podcast_error">Es wurde kein Podcast angegeben, der angezeigt werden könnte.</string>
<string name="authentication_notification_title">Authentifizierung erforderlich</string>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">Keine Medienwiedergabe</string>
<string name="player_buffering_msg">Puffert</string>
<string name="player_go_to_picture_in_picture">Bild-in-Bild-Modus</string>
- <string name="playbackservice_notification_title">Spiele Podcast ab</string>
<string name="unknown_media_key">AntennaPod - Unbekannte Medientaste: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Abspielliste sperren</string>
<string name="unlock_queue">Abspielliste entsperren</string>
<string name="queue_locked">Abspielliste gesperrt</string>
<string name="queue_unlocked">Abspielliste entsperrt</string>
+ <string name="queue_lock_warning">Wenn du die Abspielliste sperrst, kannst du Episoden nicht mehr verschieben oder wegwischen.</string>
+ <string name="checkbox_do_not_show_again">Nicht mehr anzeigen</string>
<string name="clear_queue_label">Abspielliste leeren</string>
<string name="undo">Rückgängig</string>
- <string name="removed_from_queue">Element entfernt</string>
<string name="move_to_top_label">Zum Anfang verschieben</string>
<string name="move_to_bottom_label">Zum Ende verschieben</string>
<string name="sort">Sortieren</string>
@@ -297,13 +283,12 @@
<!--Variable Speed-->
<string name="download_plugin_label">Plugin herunterladen</string>
<string name="no_playback_plugin_title">Plugin nicht installiert</string>
- <string name="no_playback_plugin_or_sonic_msg">Um variable Wiedergabegeschwindigkeit benutzen zu können, empfehlen wir, den integrierten Sonic Mediaplayer [Android 4.1+] zu aktivieren.\n\nAlternativ kannst du das kostenlose Plugin <i>Prestissimo</i> eines Drittanbieters aus dem Play Store herunterladen.\nProbleme mit Prestissimo liegen nicht im Verantwortungsbereich von AntennaPod und sollten dem Entwickler des Plugins gemeldet werden.</string>
+ <string name="no_playback_plugin_or_sonic_msg">Um variable Wiedergabegeschwindigkeit benutzen zu können, empfehlen wir, den integrierten Sonic Mediaplayer zu aktivieren.</string>
<string name="set_playback_speed_label">Wiedergabegeschwindigkeiten</string>
<string name="enable_sonic">Sonic aktivieren</string>
<!--Empty list labels-->
<string name="no_items_header_label">Leere Abspielliste</string>
<string name="no_items_label">Du kannst Episoden hinzufügen, indem du sie herunterlädst oder sie lange antippst und \"Zur Abspielliste hinzufügen\" auswählst.</string>
- <string name="no_feeds_label">Du hast noch keine Feeds abonniert.</string>
<string name="no_shownotes_label">Episode hat keine Shownotizen.</string>
<string name="no_run_downloads_head_label">Keine laufenden Downloads</string>
<string name="no_run_downloads_label">Du kannst Episoden auf der Podcast-Seite herunterladen.</string>
@@ -321,11 +306,11 @@
<string name="no_fav_episodes_label">Du kannst Episoden als Favorit markieren, indem du sie lange antippst.</string>
<string name="no_chapters_head_label">Keine Kapitel</string>
<string name="no_chapters_label">Diese Episode hat keine Kapitel.</string>
+ <string name="no_subscriptions_head_label">Keine Abonnements</string>
+ <string name="no_subscriptions_label">Um einen Podcast zu abonnieren, tippe auf das Plus-Symbol.</string>
<!--Preferences-->
<string name="storage_pref">Speicher</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Anderes</string>
- <string name="about_pref">Über</string>
<string name="queue_label">Abspielliste</string>
<string name="integrations_label">Einbindungen</string>
<string name="automation">Automatisierung</string>
@@ -368,12 +353,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Tageszeit festlegen</string>
<string name="pref_autoUpdateIntervallOrTime_every">jede %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">um %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Lade Mediendateien nur über WiFi</string>
<string name="pref_followQueue_title">Durchgehendes Abspielen</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi Medien-Download</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Kopfhörer-Trennung</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Kopfhörer wieder eingesteckt</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth wieder verbunden</string>
+ <string name="pref_stream_over_download_title">Streamen bevorzugen</string>
+ <string name="pref_stream_over_download_sum">Zeige Stream-Button statt Download-Button in Listen</string>
<string name="pref_mobileUpdate_title">Mobile Aktualisierungen</string>
<string name="pref_mobileUpdate_sum">Auswählen, was über mobile Daten erlaubt sein soll</string>
<string name="pref_mobileUpdate_refresh">Podcasts aktualisieren</string>
@@ -381,11 +366,8 @@
<string name="pref_mobileUpdate_auto_download">Automatisches Herunterladen</string>
<string name="pref_mobileUpdate_episode_download">Episoden herunterladen</string>
<string name="pref_mobileUpdate_streaming">Streamen</string>
- <string name="refreshing_label">Aktualisiere</string>
<string name="user_interface_label">Benutzeroberfläche</string>
<string name="pref_set_theme_title">Theme auswählen</string>
- <string name="pref_nav_drawer_title">Seitenleiste anpassen</string>
- <string name="pref_nav_drawer_sum">Passe das Aussehen der Seitenleiste an.</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_feed_order_title">Reihenfolge der Abonnements einstellen</string>
@@ -397,13 +379,16 @@
<string name="pref_automatic_download_sum">Konfiguriere das automatische Herunterladen von Episoden.</string>
<string name="pref_autodl_wifi_filter_title">W-LAN-Filter aktivieren</string>
<string name="pref_autodl_wifi_filter_sum">Erlaube das automatische Herunterladen nur in ausgewählten W-LAN Netzwerken.</string>
- <string name="pref_autodl_allow_on_mobile_title">Download bei Mobilfunk-Verbindung</string>
- <string name="pref_autodl_allow_on_mobile_sum">Erlaube einen automatischen Download bei einer Mobilfunk-Verbindung.</string>
+ <string name="autodl_wifi_filter_permission_title">Berechtigung notwendig</string>
+ <string name="autodl_wifi_filter_permission_message">Zur Verwendung des Wi-Fi Filter wird Zugriff auf den Standort benötigt. Tippe, um die Berechtigung zu erteilen.</string>
<string name="pref_automatic_download_on_battery_title">Automatischer Download im Batterie Modus</string>
<string name="pref_automatic_download_on_battery_sum">Automatische Downloads auch erlauben, wenn die Batterie nicht geladen wird</string>
<string name="pref_parallel_downloads_title">Parallele Downloads</string>
<string name="pref_episode_cache_title">Episodenspeicher</string>
<string name="pref_episode_cache_summary">Gesamtzahl an Episoden, die auf dem Gerät gespeichert werden. Automatisches Herunterladen wird pausiert, wenn diese Anzahl erreicht ist.</string>
+ <string name="pref_episode_cover_title">Episoden-Bilder verwenden</string>
+ <string name="pref_episode_cover_summary">Benutze Episoden-Bilder, wenn verfügbar. Ist die Option deaktiviert, wird immer das Podcast-Bild angezeigt.</string>
+ <string name="pref_theme_title_use_system">System-Design verwenden</string>
<string name="pref_theme_title_light">Hell</string>
<string name="pref_theme_title_dark">Dunkel</string>
<string name="pref_theme_title_trueblack">Schwarz (für AMOLED)</string>
@@ -423,12 +408,12 @@
<string name="pref_gpodnet_full_sync_sum">Kompletten Abonnement- und Episoden-Status mit gpodder.net synchronisieren.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Letzter Synchronisierungsversuch: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synchronisation gestartet</string>
- <string name="pref_gpodnet_full_sync_started">Komplette Synchronisierung gestartet</string>
<string name="pref_gpodnet_login_status"><![CDATA[Eingeloggt als <i>%1$s</i> mit dem Gerät <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Zeige Benachrichtungen bei Synchronisierungsfehlern</string>
<string name="pref_gpodnet_notifications_sum">Diese Einstellung gilt nicht für Authentifizierungsfehler.</string>
<string name="pref_playback_speed_title">Wiedergabegeschwindigkeiten</string>
<string name="pref_playback_speed_sum">Lege die verfügbaren Werte für die Veränderung der Wiedergabeschwindigkeit fest</string>
+ <string name="pref_feed_playback_speed_sum">Die Abspielgeschwindigkeit, die beim Starten von Episoden dieses Podcasts verwendet werden soll</string>
<string name="pref_playback_time_respects_speed_title">Medieninfo an Abspielgeschwindigkeit anpassen</string>
<string name="pref_playback_time_respects_speed_sum">Abspielposition und Dauer werden auf Abspielgeschwindigkeit angepasst</string>
<string name="pref_fast_forward">Vorspulzeit</string>
@@ -450,21 +435,25 @@
<string name="pref_showDownloadReport_title">Zeige Download-Bericht</string>
<string name="pref_showDownloadReport_sum">Wenn Downloads fehlschlagen, erstelle einen Bericht, der die Details des Fehlschlages beschreibt.</string>
<string name="pref_expand_notify_unsupport_toast">Android-Versionen vor 4.1 unterstützen keine erweiterten Benachrichtigungen.</string>
- <string name="pref_queueAddToFront_sum">Neue Folgen zum Anfang der Abspielliste hinzufügen.</string>
- <string name="pref_queueAddToFront_title">Zum Anfang der Abspielliste hinzufügen</string>
+ <string name="pref_enqueue_location_title">Position beim Einreihen</string>
+ <string name="pref_enqueue_location_sum">Füge Episoden %1$s hinzu</string>
+ <string name="enqueue_location_back">Hinten</string>
+ <string name="enqueue_location_front">Vorne</string>
+ <string name="enqueue_location_after_current">Hinter der aktuell gespielten Episode</string>
<string name="pref_smart_mark_as_played_disabled">Deaktiviert</string>
<string name="pref_image_cache_size_title">Größe des Bilder-Zwischenspeichers</string>
<string name="pref_image_cache_size_sum">Größe des Zwischenspeichers für Bilder</string>
- <string name="crash_report_title">Absturzbericht</string>
- <string name="crash_report_sum">Sende den aktuellen Absturzbericht per E-Mail</string>
- <string name="send_email">E-Mail senden</string>
+ <string name="view_mailing_list">Mailingliste anzeigen</string>
+ <string name="bug_report_title">Fehler melden</string>
+ <string name="open_bug_tracker">Bug-Tracker öffnen</string>
+ <string name="copy_to_clipboard">In die Zwischenablage kopieren</string>
+ <string name="copied_to_clipboard">In die Zwischenablage kopiert</string>
<string name="experimental_pref">Experimentell</string>
<string name="pref_media_player_message">Wähle, welcher Medienabspieler benutzt werden soll, um Dateien abzuspielen</string>
<string name="pref_current_value">Aktueller Wert: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Richte einen Netzwerk-Proxy ein</string>
<string name="pref_faq">Häufig gestellte Fragen</string>
- <string name="pref_known_issues">Bekannte Probleme</string>
<string name="pref_no_browser_found">Kein Browser gefunden.</string>
<string name="pref_cast_title">Chromecast-Unterstützung</string>
<string name="pref_cast_message_play_flavor">Aktiviere die Unterstützung von Cast-Geräten (Chromecast, Lautsprecher oder Android TV) zum entfernten Abspielen</string>
@@ -490,6 +479,16 @@
<string name="back_button_go_to_page_title">Seite auswählen</string>
<string name="pref_delete_removes_from_queue_title">Löschen entfernt aus Abspielliste</string>
<string name="pref_delete_removes_from_queue_sum">Entferne Episoden automatisch aus der Abspielliste, wenn sie gelöscht werden.</string>
+ <!--About screen-->
+ <string name="about_pref">Über</string>
+ <string name="antennapod_version">AntennaPod-Version</string>
+ <string name="developers">Entwickler</string>
+ <string name="developers_summary">Jeder kann helfen, AntennaPod besser zu machen</string>
+ <string name="translators">Übersetzer</string>
+ <string name="translators_summary">Übersetzungen werden von Nutzern auf Transifex erstellt</string>
+ <string name="privacy_policy">Datenschutzrichtlinie</string>
+ <string name="licenses">Lizenzen</string>
+ <string name="licenses_summary">AntennaPod verwendet tolle andere Software</string>
<!--Search-->
<string name="search_hint">Suche nach Episoden</string>
<string name="found_in_shownotes_label">In Shownotizen gefunden</string>
@@ -501,22 +500,16 @@
<string name="found_in_title_label">In Titel gefunden</string>
<string name="no_results_for_query">Es wurden keine Ergebnisse für \"%1$s\" gefunden</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Mit OPML Dateien kannst du deine Podcasts von einem Podcatcher auf einen anderen übertragen</string>
<string name="opml_import_option">Option %1$d</string>
<string name="opml_import_explanation_1">Wähle einen bestimmten Dateipfad aus dem lokalen Dateisystem.</string>
- <string name="opml_import_explanation_2">Verwende externe Anwendungen wie Dropbox, Google Drive oder deinen Lieblingsdateimanager, um eine OPML-Datei zu öffnen.</string>
<string name="opml_import_explanation_3">Viele Anwendungen wie Google Mail, Dropbox, Google Drive und die meisten Dateimanager können OPML-Dateien <i>mit</i> AntennaPod <i>öffnen</i>.</string>
- <string name="start_import_label">Import starten</string>
<string name="opml_import_label">OPML Import</string>
- <string name="opml_directory_error">FEHLER!</string>
<string name="reading_opml_label">Lese OPML Datei</string>
<string name="opml_reader_error">Beim Einlesen der OPML-Datei ist ein Fehler aufgetreten:</string>
<string name="opml_import_error_no_file">Keine Datei ausgewählt!</string>
<string name="select_all_label">Alle auswählen</string>
<string name="deselect_all_label">Auswahl zurücksetzen</string>
- <string name="select_options_label">Auswählen…</string>
<string name="choose_file_from_filesystem">Vom lokalen Dateisystem</string>
- <string name="choose_file_from_external_application">Verwende externe Anwendung</string>
<string name="opml_export_label">OPML Export</string>
<string name="html_export_label">HTML Export</string>
<string name="exporting_label">Exportiere…</string>
@@ -609,8 +602,7 @@
<string name="pref_restart_required">AntennaPod muss neu gestartet werden, damit die Änderungen wirksam werden.</string>
<!--Online feed view-->
<string name="subscribe_label">Abonnieren</string>
- <string name="subscribed_label">Abonniert</string>
- <string name="downloading_label">Lade herunter…</string>
+ <string name="subscribing_label">Abonniere…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Zurückspulen</string>
<string name="fast_forward_label">Vorspulen</string>
@@ -718,7 +710,6 @@
<string name="cast_failed_setting_volume">Fehler beim Einstellen der Lautstärke</string>
<string name="cast_failed_no_connection">Keine Verbindung zum Cast-Gerät</string>
<string name="cast_failed_no_connection_trans">Verbindung zum Cast-Device unterbrochen. Wenn möglich, wird die App die Verbindung wiederherstellen. Bitte warte ein paar Sekunden und versuche es erneut.</string>
- <string name="cast_failed_perform_action">Aktion konnte nicht ausgeführt werden</string>
<string name="cast_failed_status_request">Synchronization mit Cast-Gerät fehlgeschlagen</string>
<string name="cast_failed_seek">Spulen zur neuen Position fehlgeschlagen</string>
<string name="cast_failed_receiver_player_error">Es wurde ein schwerer Fehler beim Empfangsgerät festgestellt</string>
@@ -733,4 +724,8 @@
<string name="notification_channel_error">Fehler</string>
<string name="notification_channel_error_description">Wird gezeigt, wenn etwas schief gegangen ist, etwa wenn das Herunterladen oder die gpodder-Synchronisierung fehlschlägt.</string>
<string name="import_bad_file">Invalide/beschädigte Datei</string>
+ <!--Widget settings-->
+ <string name="widget_settings">Widget-Einstellungen</string>
+ <string name="widget_create_button">Widget erstellen</string>
+ <string name="widget_opacity">Deckkraft</string>
</resources>
diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml
index 01ee726de..c47cd0799 100644
--- a/core/src/main/res/values-es/strings.xml
+++ b/core/src/main/res/values-es/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronizar con otros dispositivos</string>
<string name="gpodnet_auth_label">Iniciar sesión en gpodder.net</string>
- <string name="free_space_label">%1$s libre</string>
<string name="episode_cache_full_title">Almacenamiento de episodios completo</string>
<string name="episode_cache_full_message">Se ha alcanzado el límite de almacenamiento de episodios. Puede aumentar el tamaño en opciones.</string>
- <string name="synchronizing">Sincronizando…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Tiempo total de reproducción de pódcast:</string>
<string name="statistics_details_dialog">%1$d episodios iniciados de %2$d.\n\nReproducidos %3$s de %4$s.</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">Calcula la duración real reproducida. Reproducir dos veces cuenta doble y marcar como leído no cuenta como reproducido</string>
<string name="statistics_mode_count_all">Suma de todos los pódcast marcados como reproducidos</string>
<string name="statistics_speed_not_counted">Aviso: La velocidad de reproducción nunca se tiene en cuenta.</string>
+ <string name="statistics_reset_data">Borrar estadísticas</string>
+ <string name="statistics_reset_data_msg">Esto borrará el historial de duración de todos los episodios. ¿Estás seguro de que quieres hacerlo?</string>
<!--Main activity-->
<string name="drawer_open">Abrir menú</string>
<string name="drawer_close">Cerrar menú</string>
@@ -63,7 +63,6 @@
<string name="author_label">Autor(es)</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Ajustes</string>
<string name="cover_label">Imagen</string>
<string name="error_label">Error</string>
<string name="error_msg_prefix">Se produjo un error:</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">No se encuentra un almacenamiento externo. Asegúrese de que su almacenamiento externo esté montado para que la aplicación funcione correctamente.</string>
<string name="chapters_label">Capítulos</string>
<string name="chapter_duration">Duración: %1$s</string>
- <string name="shownotes_label">Notas del programa</string>
<string name="description_label">Descripción</string>
- <string name="most_recent_prefix">Episodio más reciente:\u0020</string>
<string name="episodes_suffix">\u0020episodios</string>
- <string name="length_prefix">Duración:\u0020</string>
- <string name="size_prefix">Tamaño:\u0020</string>
<string name="processing_label">Procesando</string>
- <string name="loading_label">Cargando…</string>
<string name="save_username_password_label">Guardar usuario y contraseña</string>
<string name="close_label">Cerrar</string>
<string name="retry_label">Reintentar</string>
@@ -108,8 +102,6 @@
<string name="feedurl_label">URL del canal</string>
<string name="etxtFeedurlHint">www.ejemplo.com/feed</string>
<string name="txtvfeedurl_label">Añadir pódcast por URL</string>
- <string name="podcastdirectories_label">Buscar pódcast en el directorio</string>
- <string name="podcastdirectories_descr">Para nuevos pódcast, puede buscar en iTunes o fyyd, o explorar en gpodder.net por nombre, categoría o popularidad.</string>
<string name="browse_gpoddernet_label">Explorar en gpodder.net</string>
<string name="discover">Descubrir</string>
<string name="discover_more">más »</string>
@@ -131,13 +123,13 @@
<string name="share_link_label">Compartir URL del episodio</string>
<string name="share_link_with_position_label">Compartir URL del episodio con posición</string>
<string name="share_file_label">Compartir el archivo</string>
+ <string name="share_website_url_label">Compartir URL Web</string>
<string name="share_feed_url_label">Compartir URL del canal</string>
<string name="share_item_url_label">Compartir URL del archivo</string>
<string name="share_item_url_with_position_label">Compartir URL del archivo con posición</string>
<string name="feed_delete_confirmation_msg">Confirme que quiere borrar el pódcast \"%1$s\" y TODOS los episodios (incluidos los descargados).</string>
<string name="feed_remover_msg">Eliminando el pódcast</string>
<string name="load_complete_feed">Actualizar el pódcast completo</string>
- <string name="hide_episodes_title">Ocultar episodios</string>
<string name="batch_edit">Edición por lotes</string>
<string name="select_all_above">Seleccionar todo lo anterior</string>
<string name="select_all_below">Seleccionar todo lo siguiente</string>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Reproducir</string>
<string name="pause_label">Pausar</string>
- <string name="stop_label">Detener</string>
<string name="stream_label">Escuchar en directo</string>
- <string name="remove_label">Eliminar</string>
<string name="delete_label">Borrar</string>
<string name="delete_failed">No se puede borrar el fichero. Reiniciar el dispositivo podría ayudar.</string>
<string name="delete_episode_label">Borrar Episodio</string>
@@ -207,14 +197,12 @@
<string name="removed_item">Elemento elminado</string>
<!--Download messages and labels-->
<string name="download_successful">exitosa</string>
- <string name="download_failed">fallida</string>
<string name="download_pending">Descarga pendiente</string>
<string name="download_running">Descarga en curso</string>
<string name="download_error_details">Detalles</string>
<string name="download_error_details_message">%1$s \n\nURL de archivo:\n%2$s</string>
<string name="download_error_device_not_found">No se ha encontrado un dispositivo de almacenamiento</string>
<string name="download_error_insufficient_space">Espacio insuficiente</string>
- <string name="download_error_file_error">Error de archivo</string>
<string name="download_error_http_data_error">Error de datos HTTP</string>
<string name="download_error_error_unknown">Error desconocido</string>
<string name="download_error_parser_exception">Excepción del analizador</string>
@@ -224,7 +212,6 @@
<string name="download_error_unauthorized">Error de autenticación</string>
<string name="download_error_file_type_type">Tipo de archivo erróneo</string>
<string name="download_error_forbidden">Prohibido</string>
- <string name="cancel_all_downloads_label">Cancelar todas las descargas</string>
<string name="download_canceled_msg">Descarga cancelada</string>
<string name="download_canceled_autodownload_enabled_msg">Descarga cancelada\nSe desactivó la <i>descarga automática</i> en este elemento</string>
<string name="download_report_title">Descargas completadas con error(es)</string>
@@ -243,7 +230,6 @@
<string name="download_log_title_unknown">Título desconocido</string>
<string name="download_type_feed">Canal</string>
<string name="download_type_media">Archivo multimedia</string>
- <string name="download_type_image">Imagen</string>
<string name="download_request_error_dialog_message_prefix">Ha ocurrido un error al intentar descargar el archivo:\u0020</string>
<string name="null_value_podcast_error">No se proporcionó ningún pódcast que pudiera mostrarse.</string>
<string name="authentication_notification_title">Autenticación requerida</string>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">No hay medios en reproducción</string>
<string name="player_buffering_msg">Almacenando</string>
<string name="player_go_to_picture_in_picture">Modo picture-in-picture</string>
- <string name="playbackservice_notification_title">Reproduciendo el pódcast</string>
<string name="unknown_media_key">AntennaPod - Tecla multimedia desconocida: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Bloquear cola</string>
<string name="unlock_queue">Desbloquear cola</string>
<string name="queue_locked">Cola bloqueada</string>
<string name="queue_unlocked">Cola desbloqueada</string>
+ <string name="queue_lock_warning">Si bloqueas la cola, no podrás mover o marcar los episodios.</string>
+ <string name="checkbox_do_not_show_again">No mostrar de nuevo</string>
<string name="clear_queue_label">Vaciar la cola</string>
<string name="undo">Deshacer</string>
- <string name="removed_from_queue">Elemento eliminado</string>
<string name="move_to_top_label">Mover al principio</string>
<string name="move_to_bottom_label">Mover al final</string>
<string name="sort">Ordenar</string>
@@ -297,13 +283,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Descargar complemento</string>
<string name="no_playback_plugin_title">Complemento no instalado</string>
- <string name="no_playback_plugin_or_sonic_msg">Para que funcione la velocidad de reproducción variable, recomendamos habilitar el reproductor Sonic [Android 4.1+].\n\nTambién puede descargar el plugin <i>Prestissimo</i> de la Play Store.\nNingún problema con Prestissimo es reponsabilidad de AntennaPod y debería ser reportado al propietario del plugin.</string>
<string name="set_playback_speed_label">Velocidades de reproducción</string>
<string name="enable_sonic">Activar Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Sin episodios en la cola</string>
<string name="no_items_label">Añada un episodio descargándolo o presione prolongadamente un episodio y seleccione \"Añadir a la cola\".</string>
- <string name="no_feeds_label">No se ha suscrito a ningún pódcast.</string>
<string name="no_shownotes_label">Este episodio no tiene notas del programa.</string>
<string name="no_run_downloads_head_label">No hay descargas en ejecución</string>
<string name="no_run_downloads_label">Puede descargar episodios en la pantalla de detalles del pódcast.</string>
@@ -321,11 +305,11 @@
<string name="no_fav_episodes_label">Puede añadir episodios a los favoritos presionándolos durante un tiempo prolongado.</string>
<string name="no_chapters_head_label">Sin capítulos</string>
<string name="no_chapters_label">Este episodio no tiene capítulos.</string>
+ <string name="no_subscriptions_head_label">No hay subscripciones</string>
+ <string name="no_subscriptions_label">Para suscribirse a un podcast, pulsa el icono \"más\" de abajo.</string>
<!--Preferences-->
<string name="storage_pref">Almacenamiento</string>
<string name="project_pref">Proyecto</string>
- <string name="other_pref">Otros</string>
- <string name="about_pref">Acerca de</string>
<string name="queue_label">Cola</string>
<string name="integrations_label">Integraciones</string>
<string name="automation">Automatización</string>
@@ -368,12 +352,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Ajustar hora del día</string>
<string name="pref_autoUpdateIntervallOrTime_every">todos los %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">a las %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Descargar los contenidos solo por wifi</string>
<string name="pref_followQueue_title">Reproducción continua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Descarga de contenidos por wifi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Desconectar sin los auriculares</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Reconectar con los auriculares</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Reconectar con Bluetooth</string>
+ <string name="pref_stream_over_download_title">Preferir Streaming</string>
+ <string name="pref_stream_over_download_sum">Muestra el botón de stream en lugar del botón de descargar en las listas.</string>
<string name="pref_mobileUpdate_title">Actualizaciones por red móvil</string>
<string name="pref_mobileUpdate_sum">Seleccionar lo que se debe permitir descargar con datos móviles</string>
<string name="pref_mobileUpdate_refresh">Actualizar fuente</string>
@@ -381,11 +365,8 @@
<string name="pref_mobileUpdate_auto_download">Auto descargar</string>
<string name="pref_mobileUpdate_episode_download">Descarga de episodio</string>
<string name="pref_mobileUpdate_streaming">Streaming</string>
- <string name="refreshing_label">Actualizando</string>
<string name="user_interface_label">Interfaz de usuario</string>
<string name="pref_set_theme_title">Elegir un tema</string>
- <string name="pref_nav_drawer_title">Personalizar el cajón de navegación</string>
- <string name="pref_nav_drawer_sum">Personalizar la apariencia del cajón de navegación</string>
<string name="pref_nav_drawer_items_title">Cambiar el cajón de navegación</string>
<string name="pref_nav_drawer_items_sum">Cambiar los ítems que aparecen en el cajón de navegación</string>
<string name="pref_nav_drawer_feed_order_title">Ajustar orden de suscripción</string>
@@ -397,13 +378,16 @@
<string name="pref_automatic_download_sum">Configurar la descarga automática de episodios.</string>
<string name="pref_autodl_wifi_filter_title">Activar el filtro wifi</string>
<string name="pref_autodl_wifi_filter_sum">Permitir la descarga automática sólo desde las redes wifi seleccionadas.</string>
- <string name="pref_autodl_allow_on_mobile_title">Descargar bajo conexión móvil</string>
- <string name="pref_autodl_allow_on_mobile_sum">Permitir la descarga automática a través de la conexión de datos móviles.</string>
+ <string name="autodl_wifi_filter_permission_title">Permiso requerido</string>
+ <string name="autodl_wifi_filter_permission_message">Se requiere el permiso de ubicación para filtrar por redes Wi-Fi. Click para conceder el permiso.</string>
<string name="pref_automatic_download_on_battery_title">Descargar cuando no se está cargando</string>
<string name="pref_automatic_download_on_battery_sum">Permitir la descarga automática cuando la batería no se esté cargando</string>
<string name="pref_parallel_downloads_title">Descargas paralelas</string>
<string name="pref_episode_cache_title">Almacenamiento de episodios</string>
<string name="pref_episode_cache_summary">Número total de episodios cacheados en el dispositivo. La descarga automática se suspenderá si se alcanza este número.</string>
+ <string name="pref_episode_cover_title">Usar portada del episodio</string>
+ <string name="pref_episode_cover_summary">Usar la portada del episodio cuando sea posible. Si se desactiva, la aplicación siempre usará la portada del podcast.</string>
+ <string name="pref_theme_title_use_system">Usar tema del sistema</string>
<string name="pref_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Oscuro</string>
<string name="pref_theme_title_trueblack">Negro (para AMOLED)</string>
@@ -423,12 +407,12 @@
<string name="pref_gpodnet_full_sync_sum">Sincronizar todas las suscripciones y episodios con gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Último intento de sincronización: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronización iniciada</string>
- <string name="pref_gpodnet_full_sync_started">Sincronización completa iniciada</string>
<string name="pref_gpodnet_login_status"><![CDATA[Identificado como <i>%1$s</i> con dispositivo <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Mostrar errores de sincronización</string>
<string name="pref_gpodnet_notifications_sum">Este ajuste no afecta a los errores de autenticación.</string>
<string name="pref_playback_speed_title">Velocidades de reproducción</string>
<string name="pref_playback_speed_sum">Personalice las velocidades disponibles en la reproducción de audio a velocidad variable</string>
+ <string name="pref_feed_playback_speed_sum">La velocidad a la que comenzarán los episodios de este feed</string>
<string name="pref_playback_time_respects_speed_title">Ajustar la información del medio según la velocidad de reproducción</string>
<string name="pref_playback_time_respects_speed_sum">La posición y duración están adaptadas a la velocidad de reproducción</string>
<string name="pref_fast_forward">Intervalo de avance</string>
@@ -450,21 +434,25 @@
<string name="pref_showDownloadReport_title">Mostrar informe de descarga</string>
<string name="pref_showDownloadReport_sum">Si la descarga falla, generar un informe con los detalles del fallo</string>
<string name="pref_expand_notify_unsupport_toast">Las versiones de Android anteriores a la 4.1 no soportan notificaciones expandidas</string>
- <string name="pref_queueAddToFront_sum">Añadir nuevos episodios al principio de la cola.</string>
- <string name="pref_queueAddToFront_title">Poner al principio de la cola.</string>
+ <string name="pref_enqueue_location_title">Añadir a la cola en cierta ubicación</string>
+ <string name="pref_enqueue_location_sum">Añadir episodios a: %1$s</string>
+ <string name="enqueue_location_back">Final</string>
+ <string name="enqueue_location_front">Principio</string>
+ <string name="enqueue_location_after_current">Después del episodio actual</string>
<string name="pref_smart_mark_as_played_disabled">Deshabilitado</string>
<string name="pref_image_cache_size_title">Tamaño del almacenamiento de imágenes</string>
<string name="pref_image_cache_size_sum">Tamaño del almacenamiento de imágenes en disco.</string>
- <string name="crash_report_title">Informe de fallos</string>
- <string name="crash_report_sum">Enviar el último informe de fallo por correo electrónico</string>
- <string name="send_email">Enviar correo electrónico</string>
+ <string name="view_mailing_list">Ver lista de correo</string>
+ <string name="bug_report_title">Reportar bug</string>
+ <string name="open_bug_tracker">Abrir bug tracker</string>
+ <string name="copy_to_clipboard">Copiar al portapapeles</string>
+ <string name="copied_to_clipboard">Copiado al portapapeles</string>
<string name="experimental_pref">Experimental</string>
<string name="pref_media_player_message">Seleccione qué reproductor multimedia usar para reproducir archivos</string>
<string name="pref_current_value">Valor actual: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Configurar proxy de red</string>
- <string name="pref_faq">Preguntas frecuentes</string>
- <string name="pref_known_issues">Problemas conocidos</string>
+ <string name="pref_faq">Preguntas de Uso Frecuente (FAQ)</string>
<string name="pref_no_browser_found">No se ha encontrado un navegador web.</string>
<string name="pref_cast_title">Soporte para Chromecast</string>
<string name="pref_cast_message_play_flavor">Habilitar soporte para reproducción remota en dispositivos Cast (como Chromecast, altavoces o Android TV)</string>
@@ -490,6 +478,16 @@
<string name="back_button_go_to_page_title">Seleccionar página</string>
<string name="pref_delete_removes_from_queue_title">Eliminar quita de la cola</string>
<string name="pref_delete_removes_from_queue_sum">Quitar automáticamente un episodio de la cola cuando se elimina.</string>
+ <!--About screen-->
+ <string name="about_pref">Acerca de</string>
+ <string name="antennapod_version">Versión de AntennaPod</string>
+ <string name="developers">Desarrolladores</string>
+ <string name="developers_summary">Todos pueden ayudar a mejorar AntennaPod</string>
+ <string name="translators">Traductores</string>
+ <string name="translators_summary">Las traducciones las crean los usuarios de AntennaPod usando Transifex</string>
+ <string name="privacy_policy">Política de privacidad</string>
+ <string name="licenses">Licencias</string>
+ <string name="licenses_summary">AntennaPod usa otros software estupendos</string>
<!--Search-->
<string name="search_hint">Buscar episodios</string>
<string name="found_in_shownotes_label">Encontrado en las notas del programa</string>
@@ -501,22 +499,16 @@
<string name="found_in_title_label">Encontrado en el título</string>
<string name="no_results_for_query">No se han encontrado resultados de \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Los archivos OPML le permiten migrar sus pódcast de una aplicación a otra.</string>
<string name="opml_import_option">Opción %1$d</string>
<string name="opml_import_explanation_1">Elegir un una ruta del sistema de ficheros local.</string>
- <string name="opml_import_explanation_2">Usar una aplicación externa tipo Dropbox, Google Drive o su gestor de ficheros favorito para abrir un archivo OPML.</string>
<string name="opml_import_explanation_3">Muchas aplicaciones como Google Mail, Dropbox, Google Drive y la mayoría de gestores de ficheros pueden <i>abrir</i> archivos OPML <i>con</i> AntennaPod.</string>
- <string name="start_import_label">Comenzar la importación</string>
<string name="opml_import_label">Importar de OPML</string>
- <string name="opml_directory_error">¡ERROR!</string>
<string name="reading_opml_label">Leyendo el archivo OPML</string>
<string name="opml_reader_error">Error al leer el documento OPML:</string>
<string name="opml_import_error_no_file">¡Debe seleccionar un archivo!</string>
<string name="select_all_label">Seleccionar todo</string>
<string name="deselect_all_label">Deseleccionar todo</string>
- <string name="select_options_label">Seleccionar…</string>
<string name="choose_file_from_filesystem">Desde el sistema de ficheros local</string>
- <string name="choose_file_from_external_application">Usar una aplicación externa</string>
<string name="opml_export_label">Exportar a OPML</string>
<string name="html_export_label">Exportar a HTML</string>
<string name="exporting_label">Exportando…</string>
@@ -609,8 +601,7 @@
<string name="pref_restart_required">Es necesario reiniciar AntennaPod para aplicar los cambios.</string>
<!--Online feed view-->
<string name="subscribe_label">Suscribirse</string>
- <string name="subscribed_label">Suscrito</string>
- <string name="downloading_label">Descargando…</string>
+ <string name="subscribing_label">Suscribiendo...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Retroceso</string>
<string name="fast_forward_label">Avance</string>
@@ -718,7 +709,6 @@
<string name="cast_failed_setting_volume">Error al ajustar el volumen</string>
<string name="cast_failed_no_connection">No hay ninguna conexión con el dispositivo de cast</string>
<string name="cast_failed_no_connection_trans">Se ha perdido la conexión con dispositivo de cast. La aplicación está intentando restablecer la conexión. Espere unos segundos y vuelva a intentarlo.</string>
- <string name="cast_failed_perform_action">Error al realizar la acción</string>
<string name="cast_failed_status_request">Error al sincronizar con el dispositivo de cast</string>
<string name="cast_failed_seek">Error al buscar la nueva posición en el dispositivo de cast</string>
<string name="cast_failed_receiver_player_error">El reproductor ha encontrado un error grave</string>
@@ -733,4 +723,7 @@
<string name="notification_channel_error">Errores</string>
<string name="notification_channel_error_description">Muestra si algo salió mal, por ejemplo, si falla la descarga o la sincronización de gpodder</string>
<string name="import_bad_file">Archivo inválido/corrupto</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">Crear widget</string>
+ <string name="widget_opacity">Opacidad</string>
</resources>
diff --git a/core/src/main/res/values-et/strings.xml b/core/src/main/res/values-et/strings.xml
index 5188d5366..d10da15a2 100644
--- a/core/src/main/res/values-et/strings.xml
+++ b/core/src/main/res/values-et/strings.xml
@@ -1,11 +1,13 @@
<?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">Uuenda tellimusi</string>
<string name="feeds_label">Uudisvood</string>
<string name="statistics_label">Statistika</string>
<string name="add_feed_label">Lisa taskuhääling</string>
<string name="episodes_label">Saated</string>
<string name="all_episodes_short_label">Kõik</string>
+ <string name="new_episodes_label">Uued</string>
<string name="favorite_episodes_label">Lemmikud</string>
<string name="new_label">Uus</string>
<string name="settings_label">Seaded</string>
@@ -18,11 +20,19 @@
<string name="cancel_download_label">Tühista\nLaadi alla</string>
<string name="playback_history_label">Esitamise ajalugu</string>
<string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Süngi teiste seadmetega</string>
<string name="gpodnet_auth_label">gpodder.net kasutajanimi</string>
- <string name="free_space_label">%1$s vaba</string>
<string name="episode_cache_full_title">Saadete vahemälu on täis</string>
+ <string name="episode_cache_full_message">Saadete vahemälu limiit on täis. Vahemälu limiiti saab suurendada seadete alt.</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Kuulatud taskuhäälingute kogupikkus:</string>
+ <string name="statistics_details_dialog">%1$d %2$d-st saatest on alustatud.\n\nKuulatud on %3$s saadet %4$s-st.</string>
+ <string name="statistics_mode">Statistika režiim</string>
+ <string name="statistics_mode_normal">Arvuta kuulamise pikkus tegelikult kuulatud aja järgi. Kaks korda kuulatud koht arvestatakse topelt, aga kuulatuks märkimist ei arvestata</string>
+ <string name="statistics_mode_count_all">Liidetakse kõik saated, mis on märgitud kuulatuks</string>
+ <string name="statistics_speed_not_counted">Märkus: taasesituse kiirust ei arvestata kunagi.</string>
+ <string name="statistics_reset_data">Lähtesta statistika andmed</string>
+ <string name="statistics_reset_data_msg">See lähtestab kõigi esitatud saadete kuulamise pikkuse ajaloo. Kas oled kindel, et tahad jätkata?</string>
<!--Main activity-->
<string name="drawer_open">Ava menüü</string>
<string name="drawer_close">Sulge menüü</string>
@@ -30,9 +40,11 @@
<string name="drawer_feed_order_unplayed_episodes">Sorteeri arvu järgi</string>
<string name="drawer_feed_order_alphabetical">Sorteeri tähestiku järgi</string>
<string name="drawer_feed_order_last_update">Sorteeri avaldamise kuupäeva järgi</string>
- <string name="drawer_feed_counter_new_unplayed">Uute ja kuulamata osade arv</string>
+ <string name="drawer_feed_order_most_played">Sorteeri kuulatud saadete järgi</string>
+ <string name="drawer_feed_counter_new_unplayed">Uute ja kuulamata saadete arv</string>
<string name="drawer_feed_counter_new">Uute saadete arv</string>
<string name="drawer_feed_counter_unplayed">Kuulamata saadete arv</string>
+ <string name="drawer_feed_counter_downloaded">Allalaaditud saadete arv</string>
<string name="drawer_feed_counter_none">Pole</string>
<!--Webview actions-->
<string name="open_in_browser_label">Ava veebisirvijas</string>
@@ -48,28 +60,27 @@
<string name="yes">Jah</string>
<string name="no">Ei</string>
<string name="reset">Nulli</string>
+ <string name="author_label">Autor(id)</string>
<string name="language_label">Keel</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Seaded</string>
<string name="cover_label">Pilt</string>
<string name="error_label">Viga</string>
<string name="error_msg_prefix">Tekkis viga:</string>
+ <string name="needs_storage_permission">Selle tegevuse jaoks tuleb lubada salvestusruumi kasutamine</string>
<string name="refresh_label">Värskenda</string>
+ <string name="external_storage_error_msg">Välist salvestusruumi pole saadaval. Veendu, et väline seade oleks haagitud, et äpp töötaks õigesti.</string>
<string name="chapters_label">Peatükid</string>
- <string name="shownotes_label">Saate märkmed</string>
+ <string name="chapter_duration">Pikkus: %1$s</string>
<string name="description_label">Kirjeldus</string>
- <string name="most_recent_prefix">Kõige värskem saade:\u0020</string>
<string name="episodes_suffix">\u0020saadet</string>
- <string name="length_prefix">Pikkus:\u0020</string>
- <string name="size_prefix">Suurus:\u0020</string>
<string name="processing_label">Töötlemine</string>
- <string name="loading_label">Laadimine...</string>
<string name="save_username_password_label">Salvesta kasutajanimi ja parool</string>
<string name="close_label">Sulge</string>
<string name="retry_label">Proovi uuesti</string>
- <string name="auto_download_label">Kaasa heli allalaadimises</string>
+ <string name="auto_download_label">Lisa automaatsetesse allalaadimistesse</string>
<string name="auto_download_apply_to_items_title">Rakenda eelmistele saadetele</string>
- <string name="auto_delete_label">Saadeta automaatne kustutamine</string>
+ <string name="auto_download_apply_to_items_message">Uus <i>Automaatse allalaadimise</i> säte rakendatakse uutele saadetele.\nKas tahad seda rakendada ka varem avaldatud saadetele?</string>
+ <string name="auto_delete_label">Saadete automaatne kustutamine</string>
<string name="parallel_downloads_suffix">\u0020samaaegset allalaadimist</string>
<string name="feed_auto_download_global">Üldine vaikeväärtus</string>
<string name="feed_auto_download_always">Alati</string>
@@ -78,24 +89,50 @@
<string name="episode_cleanup_never">Mitte kunagi</string>
<string name="episode_cleanup_queue_removal">Kui pole järjekorras</string>
<string name="episode_cleanup_after_listening">Pärast lõpetamist</string>
+ <plurals name="episode_cleanup_hours_after_listening">
+ <item quantity="one">1 tund pärast lõpetamist</item>
+ <item quantity="other">%d tundi pärast lõpetamist</item>
+ </plurals>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 päev pärast lõpetamist</item>
<item quantity="other">%d päeva pärast lõpetamist</item>
</plurals>
+ <string name="num_selected_label">%d valitud</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Uudisvoo URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Lisa taskuhääling URL-iga</string>
- <string name="podcastdirectories_label">Leia taskuhääling kaustast</string>
<string name="browse_gpoddernet_label">Sirvi gpodder.net-i</string>
+ <string name="discover">Avasta</string>
+ <string name="discover_more">veel »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Märgi kuulatuks</string>
- <string name="mark_all_read_msg">Märgi kõik saated esitatuks</string>
+ <string name="mark_all_read_msg">Märgi kõik saated kuulatuks</string>
+ <string name="mark_all_read_confirmation_msg">Palun kinnita, et tahad kõik saated märkida kuulatuks.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Palun kinnita, et tahad kõik saated selles taskuhäälingus märkida kuulatuks.</string>
+ <string name="remove_all_new_flags_label">Eemalda kõik \"uus\" sildid</string>
+ <string name="removed_all_new_flags_msg">Eemaldati kõik \"uus\" sildid</string>
+ <string name="remove_all_new_flags_confirmation_msg">Palun kinnita, et tahad eemaldada kõik \"uus\" sildid kõigilt saadetelt.</string>
<string name="show_info_label">Näita infot</string>
+ <string name="show_feed_settings_label">Näita taskuhäälingu seadeid</string>
+ <string name="feed_info_label">Taskuhäälingu info</string>
+ <string name="feed_settings_label">Taskuhäälingu seaded</string>
+ <string name="rename_feed_label">Muuda taskuhäälingu nime</string>
+ <string name="remove_feed_label">Eemalda taskuhääling</string>
<string name="share_label">Jaga...</string>
+ <string name="share_link_label">Jaga saate linki</string>
+ <string name="share_link_with_position_label">Jaga saate linki koos asukohaga</string>
<string name="share_file_label">Jaga faili</string>
- <string name="share_feed_url_label">Jaga uudisvoo URL-i</string>
- <string name="hide_episodes_title">Peida saated</string>
+ <string name="share_website_url_label">Jaga veebilehe linki</string>
+ <string name="share_feed_url_label">Jaga uudisvoo linki</string>
+ <string name="share_item_url_label">Jaga meediafaili linki</string>
+ <string name="share_item_url_with_position_label">Jaga meediafaili linki koos asukohaga</string>
+ <string name="feed_delete_confirmation_msg">Palun kinnita, et tahad kustutada taskuhäälingu \"%1$s\" ja KÕIK selle saated (ka allalaaditud saated).</string>
+ <string name="feed_remover_msg">Taskuhäälingu eemaldamine</string>
+ <string name="load_complete_feed">Värskenda kogu taskuhääling</string>
+ <string name="batch_edit">Korraga muutmine</string>
+ <string name="select_all_above">Vali kõik ülemised</string>
+ <string name="select_all_below">Vali kõik alumised</string>
<string name="hide_unplayed_episodes_label">Esitamata</string>
<string name="hide_paused_episodes_label">Peatatud</string>
<string name="hide_played_episodes_label">Esitatud</string>
@@ -104,42 +141,68 @@
<string name="hide_downloaded_episodes_label">Alla laaditud</string>
<string name="hide_not_downloaded_episodes_label">Pole alla laaditud</string>
<string name="hide_has_media_label">On meediafaile</string>
+ <string name="hide_is_favorite_label">On lemmik</string>
<string name="filtered_label">Filtreeritud</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Viimane värskendamine ebaõnnestus</string>
<string name="open_podcast">Ava taskuhääling</string>
<!--actions on feeditems-->
<string name="download_label">Laadi alla</string>
+ <plurals name="downloading_batch_label">
+ <item quantity="one">%d saate allalaadimine.</item>
+ <item quantity="other">%d saate allalaadimine.</item>
+ </plurals>
<string name="play_label">Esita</string>
<string name="pause_label">Peata</string>
- <string name="stop_label">Stopp</string>
<string name="stream_label">Voogedastusena</string>
- <string name="remove_label">Eemalda</string>
<string name="delete_label">Kustuta</string>
- <string name="mark_read_label">Märgi esitatuks</string>
+ <string name="delete_failed">Faili ei saa kustutada. Aidata võib seadme taaskäivitamine.</string>
+ <string name="delete_episode_label">Kustuta saade</string>
+ <plurals name="deleted_episode_batch_label">
+ <item quantity="one">Kustutati %d saade.</item>
+ <item quantity="other">Kustutati %d saadet.</item>
+ </plurals>
+ <string name="remove_new_flag_label">Eemalda silt \"uus\"</string>
+ <string name="removed_new_flag_label">Eemaldati silt \"uus\"</string>
+ <string name="mark_read_label">Märgi kuulatuks</string>
<string name="marked_as_read_label">Märgitud kuulatuks</string>
+ <plurals name="marked_read_batch_label">
+ <item quantity="one">%d saade märgiti kuulatuks.</item>
+ <item quantity="other">%d saadet märgiti kuulatuks.</item>
+ </plurals>
<string name="mark_unread_label">Märgitud kui kuulamata</string>
+ <plurals name="marked_unread_batch_label">
+ <item quantity="one">%d saade märgiti kui kuulamata.</item>
+ <item quantity="other">%d saadet märgiti kui kuulamata.</item>
+ </plurals>
<string name="add_to_queue_label">Lisa järjekorda</string>
<string name="added_to_queue_label">Lisatud järjekorda</string>
+ <plurals name="added_to_queue_batch_label">
+ <item quantity="one">%d saade lisati järjekorda.</item>
+ <item quantity="other">%d saadet lisati järjekorda.</item>
+ </plurals>
<string name="remove_from_queue_label">Eemalda järjekorrast</string>
+ <plurals name="removed_from_queue_batch_label">
+ <item quantity="one">%d saade eemaldati järjekorrast.</item>
+ <item quantity="other">%d saadet eemaldati järjekorrast.</item>
+ </plurals>
<string name="add_to_favorite_label">Lisa lemmikuks</string>
<string name="added_to_favorites">Lisatud lemmikuks</string>
<string name="remove_from_favorite_label">Eemalda lemmikutest</string>
<string name="removed_from_favorites">Eemaldatud lemmikutest</string>
<string name="visit_website_label">Külasta veebilehte</string>
- <string name="skip_episode_label">Jäda saade vahele</string>
+ <string name="skip_episode_label">Jäta saade vahele</string>
<string name="activate_auto_download">Lülita automaatne allalaadimine sisse</string>
<string name="deactivate_auto_download">Lülita automaatne allalaadimine välja</string>
<string name="reset_position">Nulli esitamise asukoht</string>
<string name="removed_item">Kirje on eemaldatud</string>
<!--Download messages and labels-->
<string name="download_successful">korras</string>
- <string name="download_failed">ebaõnnestus</string>
<string name="download_pending">Ootel allalaadimine</string>
<string name="download_running">Allalaadimine on käimas</string>
<string name="download_error_details">Üksikasjad</string>
+ <string name="download_error_details_message">%1$s \n\nFaili URL:\n%2$s</string>
<string name="download_error_device_not_found">Salvestuskohta ei leitud</string>
<string name="download_error_insufficient_space">Pole piisavalt ruumi</string>
- <string name="download_error_file_error">Faili viga</string>
<string name="download_error_http_data_error">HTTP andmete viga</string>
<string name="download_error_error_unknown">Tundmatu tõrge</string>
<string name="download_error_parser_exception">Parsimise järjekord</string>
@@ -149,8 +212,8 @@
<string name="download_error_unauthorized">Autentimise viga</string>
<string name="download_error_file_type_type">Failitüübi viga</string>
<string name="download_error_forbidden">Keelatud</string>
- <string name="cancel_all_downloads_label">Tühista kõik allalaadimised</string>
<string name="download_canceled_msg">Allalaadimine on tühistatud</string>
+ <string name="download_canceled_autodownload_enabled_msg">Allalaadimine tühistati\nKeelati selle saate <i>automaatne allalaadimine</i></string>
<string name="download_report_title">Allalaadimised lõpetati veaga (vigadega)</string>
<string name="download_report_content_title">Allalaadimise aruanne</string>
<string name="download_error_malformed_url">Vigane URL-i vorming</string>
@@ -158,17 +221,25 @@
<string name="download_error_request_error">Päringu viga</string>
<string name="download_error_db_access">Andmebaasile ligipääsu viga</string>
<plurals name="downloads_left">
- <item quantity="one">%d allalaadimine järgi</item>
- <item quantity="other">%d allalaadimisi järgi</item>
+ <item quantity="one">%d allalaadimine jäänud</item>
+ <item quantity="other">%d allalaadimist jäänud</item>
</plurals>
<string name="downloads_processing">Allalaadimiste töötlemine</string>
<string name="download_notification_title">Taskuhäälingu andmete allalaadimine</string>
+ <string name="download_report_content">%1$d allalaadimine õnnestus, %2$d nurjus</string>
<string name="download_log_title_unknown">Tundmatu pealkiri</string>
<string name="download_type_feed">Uudisvoog</string>
<string name="download_type_media">Meediafail</string>
- <string name="download_type_image">Pilt</string>
+ <string name="download_request_error_dialog_message_prefix">Faili allalaadimisel esines viga:\u0020</string>
+ <string name="null_value_podcast_error">Pole ühtegi taskuhäälingut, mida näidata.</string>
<string name="authentication_notification_title">Autentimine on nõutud</string>
- <string name="confirm_mobile_download_dialog_title">Kinnita üle mobiili allalaadimine</string>
+ <string name="authentication_notification_msg">Soovitud ressurss vajab kasutajanime ja parooli</string>
+ <string name="confirm_mobile_download_dialog_title">Kinnita allalaadimine andmeside kaudu</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Allalaadimine mobiilse andmeside kaudu on seadetest keelatud.\n\nSa võid valida, kas lisad saate ainult järjekorda või ajutiselt lubad allalaadimise.\n\n<small>Sinu valikut peetakse meeles 10 minutit.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Allalaadimine mobiilse andmeside kaudu on seadetest keelatud.\n\nKas tahad allalaadimise ajutiselt lubada?\n\n<small>Sinu valikut peetakse meeles 10 minutit.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Kinnita voogedastus andmeside kaudu</string>
+ <string name="confirm_mobile_streaming_notification_message">Voogedastus andmeside kaudu on seadetest keelatud. Koputa, et ikka striimida.</string>
+ <string name="confirm_mobile_streaming_button_always">Luba alati</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Järjekorda</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Luba ajutiselt</string>
<!--Mediaplayer messages-->
@@ -178,154 +249,285 @@
<string name="player_ready_msg">Valmis</string>
<string name="player_seeking_msg">Otsimine</string>
<string name="playback_error_server_died">Server suri ära</string>
+ <string name="playback_error_unsupported">Toetamata meedia liik</string>
+ <string name="playback_error_timeout">Tegevus aegus</string>
<string name="playback_error_unknown">Tundmatu tõrge</string>
<string name="no_media_playing_label">Mingit faili ei esitata</string>
<string name="player_buffering_msg">Puhverdamine</string>
- <string name="playbackservice_notification_title">Taskuhäälingu esitamine</string>
+ <string name="player_go_to_picture_in_picture">Pilt-pildis režiim</string>
<string name="unknown_media_key">AntennaPod - Tundmatu meedia võti: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Lukusta järjekord</string>
<string name="unlock_queue">Eemalda järjekorra lukustus</string>
<string name="queue_locked">Järjekord on lukustatud</string>
<string name="queue_unlocked">Järjekorra lukustus on eemaldatud</string>
+ <string name="queue_lock_warning">Kui lukustad järjekorra, ei saa saadete järjekorda enam muuta ega neid välja pühkida.</string>
+ <string name="checkbox_do_not_show_again">Ära enam näita</string>
<string name="clear_queue_label">Tühjenda järjekord</string>
<string name="undo">Tühista</string>
- <string name="removed_from_queue">Kirje on eemaldatud</string>
<string name="move_to_top_label">Liiguta üles</string>
<string name="move_to_bottom_label">Liiguta alla</string>
<string name="sort">Sorteeri</string>
+ <string name="keep_sorted">Hoia sordituna</string>
<string name="date">Kuupäeva järgi</string>
<string name="duration">Kestuse järgi</string>
<string name="episode_title">Saate pealkiri</string>
+ <string name="feed_title">Taskuhäälingu pealkiri</string>
+ <string name="random">Juhuslik</string>
+ <string name="smart_shuffle">Nutikalt juhuslik</string>
<string name="ascending">Kasvavalt</string>
<string name="descending">Kahanevalt</string>
+ <string name="clear_queue_confirmation_msg">Palun kinnita, et tahad puhastada järjekorra KÕIGIST selles olevatest saadetest</string>
+ <string name="sort_old_to_new">Vanemad enne</string>
+ <string name="sort_new_to_old">Uuemad enne</string>
<!--Variable Speed-->
<string name="download_plugin_label">Laadi plugin alla</string>
<string name="no_playback_plugin_title">Pluginat pole paigaldatud</string>
<string name="set_playback_speed_label">Esitamise kiirused</string>
<string name="enable_sonic">Luba Sonicu kasutamine</string>
<!--Empty list labels-->
- <string name="no_shownotes_label">Sellel saatel pole mingeid märkusi.</string>
- <string name="no_chapters_label">Saatel pole peatükke.</string>
+ <string name="no_items_header_label">Järjekorras ei ole saateid</string>
+ <string name="no_items_label">Saate lisamiseks laadi see alla või hoia pikalt saatel ja vali \"Lisa järjekorda\".</string>
+ <string name="no_shownotes_label">Sellel saatel pole märkuseid.</string>
+ <string name="no_run_downloads_head_label">Praegu ei laadita midagi alla</string>
+ <string name="no_run_downloads_label">Saateid saab alla laadida taskuhäälingu vaatest.</string>
+ <string name="no_comp_downloads_head_label">Alla laaditud saateid ei ole</string>
+ <string name="no_comp_downloads_label">Saateid saab alla laadida taskuhäälingu vaatest.</string>
+ <string name="no_log_downloads_head_label">Allalaadimise logi on tühi</string>
+ <string name="no_log_downloads_label">Allalaadimise logi ilmub siia siis, kui see on tekib.</string>
+ <string name="no_history_head_label">Ajalugu pole</string>
+ <string name="no_history_label">Pärast seda, kui sa kuulad mõnd saadet, näidatakse seda siin.</string>
+ <string name="no_all_episodes_head_label">Saateid pole</string>
+ <string name="no_all_episodes_label">Kui lisad taskuhäälingu, näeb siin selle saateid.</string>
+ <string name="no_new_episodes_head_label">Uusi saateid pole</string>
+ <string name="no_new_episodes_label">Kui avaldatakse uusi saateid, näeb neid siin.</string>
+ <string name="no_fav_episodes_head_label">Lemmiksaateid pole</string>
+ <string name="no_fav_episodes_label">Saateid saab märkida lemmikuks nendel pikalt vajutades.</string>
+ <string name="no_chapters_head_label">Peatükke pole</string>
+ <string name="no_chapters_label">Sellel saatel pole peatükke.</string>
+ <string name="no_subscriptions_head_label">Tellimusi pole</string>
+ <string name="no_subscriptions_label">Taskuhäälingu tellimiseks vajuta all plussmärgil.</string>
<!--Preferences-->
<string name="storage_pref">Salvestusruum</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Muud</string>
- <string name="about_pref">Info</string>
<string name="queue_label">Järjekord</string>
- <string name="pref_episode_cleanup_title">Saadete kustutamien</string>
+ <string name="integrations_label">Integratsioon</string>
+ <string name="automation">Automaatika</string>
+ <string name="download_pref_details">Üksikasjad</string>
+ <string name="import_export_pref">Import/eksport</string>
+ <string name="import_export_search_keywords">varundus, taastamine</string>
+ <string name="appearance">Välimus</string>
+ <string name="external_elements">Välised elemendid</string>
+ <string name="interruptions">Katkestused</string>
+ <string name="playback_control">Esituse nupud</string>
+ <string name="preference_search_hint">Otsing...</string>
+ <string name="preference_search_no_results">Tulemusi pole</string>
+ <string name="preference_search_clear_history">Puhasta ajalugu</string>
+ <string name="media_player">Meediaesitaja</string>
+ <string name="pref_episode_cleanup_title">Saadete kustutamine</string>
+ <string name="pref_episode_cleanup_summary">Saated, mis ei ole järjekorras ega lemmikud, on eemaldamise kandidaadid, kui automaatselt allalaaditavate saadete jaoks on vaja rohkem ruumi</string>
+ <string name="pref_pauseOnDisconnect_sum">Esitus pausitakse, kui kõrvaklapid või bluetooth eemaldatakse</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Esitus jätkub, kui kõrvaklapid uuesti ühendatakse</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Esitus jätkub kui bluetooth uuesti ühendub</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Edasi nupp jätab vahele</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Edasi nupu vajutamine bluetoothi seadmel edasi kerimise asemel hüppab järgmisele saatele</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Tagasi nupp alustab uuesti</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">Riistvaralise tagasi nupu vajutamine alustab tagasi kerimise asemel praegust saadet algusest</string>
<string name="pref_followQueue_sum">Kui saade lõpeb, siis esita kohe järgmine järjekorras olev saade.</string>
<string name="pref_auto_delete_sum">Kustuta saated, kui need on kuulatud</string>
<string name="pref_auto_delete_title">Automaatne kustutamine</string>
+ <string name="pref_smart_mark_as_played_sum">Märgi saated kuulatuks ka siis, kui vähem kui määratud arv sekundeid on jäänud saate lõpuni</string>
+ <string name="pref_smart_mark_as_played_title">Nutikas kuulatuks märkmine</string>
<string name="pref_skip_keeps_episodes_sum">Hoia saated alles, kui need jäetakse vahele</string>
<string name="pref_skip_keeps_episodes_title">Hoia vahelejäetud osad alles</string>
+ <string name="pref_favorite_keeps_episodes_sum">Saadete säilitamine, kui need on märgitud lemmikuks</string>
<string name="pref_favorite_keeps_episodes_title">Säilita lemmikosad</string>
<string name="playback_pref">Esitamine</string>
<string name="network_pref">Võrk</string>
<string name="pref_autoUpdateIntervallOrTime_title">Uuendamise intervall või kellaaeg</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Määra intervall või kindel aeg päevas, millal voogusid automaatselt värskendatakse</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Sa võid määrata <i>intervalli</i>, näiteks \"iga 2 tunni järel\", või määrata <i>millal päeva jooksul</i>, näiteks \"7:00\" või <i>keelata</i> automaatsed uuendused täielikult.\n\n<small>Pane tähele: uuendamise ajad ei ole täpsed. Võib esineda väike viivitus.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Lülita välja</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Määra intervall</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Määra aeg päevades</string>
<string name="pref_autoUpdateIntervallOrTime_every">iga %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">kell %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Laadi meediafaile alla ainult üle Wifi</string>
<string name="pref_followQueue_title">Pidev esitamine</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Meedia allalaadimine üle Wifi</string>
- <string name="pref_pauseOnHeadsetDisconnect_title">Kõrvaklappide eemaldmine</string>
+ <string name="pref_pauseOnHeadsetDisconnect_title">Kõrvaklappide eemaldamine</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Kõrvaklappide uuesti ühendamine</string>
- <string name="pref_unpauseOnBluetoothReconnect_title">Bloetoothi uuesti ühendamin</string>
+ <string name="pref_unpauseOnBluetoothReconnect_title">Bluetoothi uuesti ühendamine</string>
+ <string name="pref_stream_over_download_title">Eelista voogedastust</string>
+ <string name="pref_stream_over_download_sum">Loeteludes kuvatakse allalaadimise asemel voogedastuse nuppu.</string>
<string name="pref_mobileUpdate_title">Uuendamine üle andmeside</string>
- <string name="refreshing_label">Värskendamine</string>
- <string name="user_interface_label">Kasutajalidies</string>
+ <string name="pref_mobileUpdate_sum">Vali, mis peaks olema lubatud andmeside kaudu</string>
+ <string name="pref_mobileUpdate_refresh">Voo värskendamine</string>
+ <string name="pref_mobileUpdate_images">Kaanepildid</string>
+ <string name="pref_mobileUpdate_auto_download">Automaatne allalaadimine</string>
+ <string name="pref_mobileUpdate_episode_download">Saate allalaadimine</string>
+ <string name="pref_mobileUpdate_streaming">Voogedastus</string>
+ <string name="user_interface_label">Kasutajaliides</string>
<string name="pref_set_theme_title">Vali teema</string>
- <string name="pref_nav_drawer_title">Kohanda peamenüüd</string>
- <string name="pref_nav_drawer_sum">Kohanda peamenüü välimust</string>
<string name="pref_nav_drawer_items_title">Määra peamenüü osad</string>
- <string name="pref_nav_drawer_items_sum">Määra, mis osad on peamenüüs nähtaval.</string>
+ <string name="pref_nav_drawer_items_sum">Määra, mis osad on peamenüüs.</string>
<string name="pref_nav_drawer_feed_order_title">Määra tellimuste järjekord</string>
<string name="pref_nav_drawer_feed_order_sum">Muuda oma tellimuste järjekorda</string>
<string name="pref_nav_drawer_feed_counter_title">Määra tellimuste loendur</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Muuda, millist infot kuvatakse tellimuste loenduris. See mõjutab ka tellimuste järjekorda, kui \"Tellimuste järjekorra\" aluseks on \"Loendur\".</string>
+ <string name="pref_set_theme_sum">AntennaPodi välimuse muutmine</string>
<string name="pref_automatic_download_title">Automaatne allalaadimine</string>
+ <string name="pref_automatic_download_sum">Saadete automaatse allalaadimise seadistamine.</string>
<string name="pref_autodl_wifi_filter_title">Luba Wifi filter</string>
<string name="pref_autodl_wifi_filter_sum">Luba automaatne allalaadimine ainult valitud Wifi võrkudes.</string>
- <string name="pref_automatic_download_on_battery_title">Allalaadmiine kui seade ei laadi</string>
+ <string name="autodl_wifi_filter_permission_title">Vaja on õiguseid</string>
+ <string name="autodl_wifi_filter_permission_message">Asukoha lubamine on vajalik Wifi filtri jaoks. Loa andmiseks koputa.</string>
+ <string name="pref_automatic_download_on_battery_title">Allalaadimine, kui seade ei lae</string>
<string name="pref_automatic_download_on_battery_sum">Luba automaatne allalaadimine ka siis, kui seade pole laadimas</string>
<string name="pref_parallel_downloads_title">Samaaegsed allalaadimised</string>
<string name="pref_episode_cache_title">Saadete vahemälu</string>
+ <string name="pref_episode_cache_summary">Seadme puhvrisse allalaaditud saadete koguarv. Automaatne allalaadimine peatub, kui selle numbrini jõutakse.</string>
+ <string name="pref_episode_cover_title">Kasuta saate kaanepilti</string>
+ <string name="pref_episode_cover_summary">Kasuta saate kaanepilti alati, kui see on olemas. Kui see pole märgitud, kasutab äpp alati taskuhäälingu kaanepilti.</string>
+ <string name="pref_theme_title_use_system">Kasuta süsteemi kujundust</string>
<string name="pref_theme_title_light">Hele</string>
<string name="pref_theme_title_dark">Tume</string>
+ <string name="pref_theme_title_trueblack">Must (AMOLED)</string>
<string name="pref_episode_cache_unlimited">Piiramatult</string>
<string name="pref_update_interval_hours_plural">tundi</string>
<string name="pref_update_interval_hours_singular">tund</string>
<string name="pref_update_interval_hours_manual">Käsitsi</string>
<string name="pref_gpodnet_authenticate_title">Logi sisse</string>
+ <string name="pref_gpodnet_authenticate_sum">Logi sisse oma gpodder.net kontoga, et oma tellimused sünkroonida.</string>
<string name="pref_gpodnet_logout_title">Logi välja</string>
<string name="pref_gpodnet_logout_toast">Väljalogimine oli edukas</string>
<string name="pref_gpodnet_setlogin_information_title">Muuda kasutajakonto infot</string>
+ <string name="pref_gpodnet_setlogin_information_sum">Muuda oma gpodder.net konto sisselogimise andmeid.</string>
<string name="pref_gpodnet_sync_changes_title">Sünkrooni muudatused kohe</string>
+ <string name="pref_gpodnet_sync_changes_sum">Sünkroniseeri oma tellimuste ja saadete olekute muutuseid gpodder.net-iga</string>
<string name="pref_gpodnet_full_sync_title">Täielik sünkroonimine kohe</string>
+ <string name="pref_gpodnet_full_sync_sum">Sünkroniseeri kõiki tellimusi ja saate olekuid gpodder.net-iga.</string>
+ <string name="pref_gpodnet_sync_sum_last_sync_line">Viimane sünkroniseerimise katse: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sünkroonimine on käivitatud</string>
<string name="pref_gpodnet_login_status"><![CDATA[Sisseloginud kui <i>%1$s</i> seadmega <i>%2$s</i>]]></string>
+ <string name="pref_gpodnet_notifications_title">Kuva sünkroniseerimise vigu teadaannetena</string>
+ <string name="pref_gpodnet_notifications_sum">See seadistus ei rakendu autentimise vigadele.</string>
<string name="pref_playback_speed_title">Esitamise kiirused</string>
+ <string name="pref_playback_speed_sum">Muuda, millised valikud on audio esitamisel kiiruste loendis</string>
+ <string name="pref_feed_playback_speed_sum">Millise kiirusega alustatakse selle voo saadete heli esitamist</string>
+ <string name="pref_playback_time_respects_speed_title">Meedia info kohandamine esituse kiirusega</string>
+ <string name="pref_playback_time_respects_speed_sum">Kuvatav asukoht ja kestus kohandatakse esituse kiiruse järgi</string>
+ <string name="pref_fast_forward">Edasi kerimise hüpe</string>
+ <string name="pref_fast_forward_sum">Määra, mitu sekundit edasi hüpatakse, kui vajutatakse edasi kerimise nuppu</string>
+ <string name="pref_rewind">Tagasi kerimise hüpe</string>
+ <string name="pref_rewind_sum">Määra, mitu sekundit tagasi hüpatakse, kui vajutatakse tagasi kerimise nuppu</string>
<string name="pref_gpodnet_sethostname_title">Määra hostinimi</string>
<string name="pref_gpodnet_sethostname_use_default_host">Kasuta vaikimisi hosti</string>
+ <string name="pref_expandNotify_title">Kõrge teate prioriteet</string>
+ <string name="pref_expandNotify_sum">See tavaliselt kuvab teadet laiemana ning näha on esitusnupud.</string>
<string name="pref_persistNotify_title">Püsivad taasesitamise nupud</string>
- <string name="pref_compact_notification_buttons_title">Määra lukustutekraani nupud</string>
+ <string name="pref_persistNotify_sum">Säilita märguande ja lukustuskuva juhtnupud, kui esitus on pausil.</string>
+ <string name="pref_compact_notification_buttons_title">Määra lukustusekraani nupud</string>
+ <string name="pref_compact_notification_buttons_sum">Muuda esituse nuppe lukuekraanil. Esitamise/pausi nupp on alati olemas.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">Vali maksimaalselt %1$d kirjet</string>
+ <string name="pref_compact_notification_buttons_dialog_error">Maksimaalselt saab valida %1$d kirjet.</string>
<string name="pref_lockscreen_background_title">Määra lukustusekraani taustapilt</string>
+ <string name="pref_lockscreen_background_sum">Määra lukuekraani taustaks selle saate pilt. Kõrvalmõjuna kuvab see pilti ka teistes rakendustes.</string>
<string name="pref_showDownloadReport_title">Näita allalaadimise aruannet</string>
- <string name="pref_queueAddToFront_title">Pane järjekorra algusesse</string>
+ <string name="pref_showDownloadReport_sum">Kui allalaadimised nurjuvad, genereeri raport, mis kuvab vea üksikasju.</string>
+ <string name="pref_expand_notify_unsupport_toast">Vanemad Androidi versioonid kui 4.1 ei toeta laiendatud teavitusi.</string>
+ <string name="pref_enqueue_location_title">Järjekorra asukoht</string>
+ <string name="pref_enqueue_location_sum">Saated lisatakse: %1$s</string>
+ <string name="enqueue_location_back">Algusesse</string>
+ <string name="enqueue_location_front">Lõppu</string>
+ <string name="enqueue_location_after_current">Pärast praegust saadet</string>
<string name="pref_smart_mark_as_played_disabled">Välja lülitatud</string>
- <string name="pref_image_cache_size_title">Pildi vahemälu suurus</string>
+ <string name="pref_image_cache_size_title">Piltide vahemälu suurus</string>
<string name="pref_image_cache_size_sum">Piltide jaoks kasutatava vahemälu suurus</string>
- <string name="crash_report_title">Veateated</string>
- <string name="crash_report_sum">Saada veateated e-postiga</string>
- <string name="send_email">Saata e-post</string>
+ <string name="view_mailing_list">Vaata meililisti</string>
+ <string name="bug_report_title">Raporteeri veast</string>
+ <string name="open_bug_tracker">Ava vigade loetelu</string>
+ <string name="copy_to_clipboard">Kopeeri lõikelauale</string>
+ <string name="copied_to_clipboard">Kopeeritud lõikelauale</string>
<string name="experimental_pref">Katsetamisel</string>
+ <string name="pref_media_player_message">Vali, millise meediaesitajaga faile mängitakse</string>
<string name="pref_current_value">Praegune väärtus: %1$s</string>
<string name="pref_proxy_title">Vaheserver</string>
<string name="pref_proxy_sum">Määra võrgu vaheserver</string>
- <string name="pref_faq">KKK</string>
- <string name="pref_known_issues">Teadaolevad probleemid</string>
+ <string name="pref_faq">Korduma kippuvad küsimused</string>
<string name="pref_no_browser_found">Veebilehitsejat ei leitud.</string>
<string name="pref_cast_title">Chromecasti tugi</string>
+ <string name="pref_cast_message_play_flavor">Luba meedia esitamine kaugseadmetest (Chromecast, kõlarid või Android TV)</string>
+ <string name="pref_cast_message_free_flavor">Chromecast vajab kolmanda osapoole omanduslikke teeke, mis on selles AntennaPodi versioonis välja lülitatud</string>
+ <string name="pref_enqueue_downloaded_title">Järjekord allalaaditud</string>
+ <string name="pref_enqueue_downloaded_summary">Allalaaditud saadete lisamine järjekorda</string>
+ <string name="media_player_builtin">Sisseehitatud Androidi esitaja</string>
+ <string name="pref_skip_silence_title">Audios vaikuse vahele jätmine</string>
+ <string name="pref_videoBehavior_title">Videost väljumisel</string>
+ <string name="pref_videoBehavior_sum">Mida tehakse video esitamisest lahkumisel</string>
+ <string name="stop_playback">Lõpetatakse esitus</string>
+ <string name="continue_playback">Jätkatakse heli esitamist</string>
+ <string name="behavior">Käitumine</string>
+ <string name="pref_back_button_behavior_title">Tagasi nupu käitumine</string>
+ <string name="pref_back_button_behavior_sum">Tagasi nupu käitumine muutmine.</string>
+ <string name="back_button_default">Vaikimisi</string>
+ <string name="back_button_open_drawer">Avab navigeerimise sahtli</string>
+ <string name="back_button_double_tap">Topeltkoputus väljub</string>
+ <string name="back_button_show_prompt">Väljumine kinnitusega</string>
+ <string name="close_prompt">Kas tahad kindlasti sulgeda AntennaPodi?</string>
+ <string name="double_tap_toast">Koputa nuppu väljumiseks uuesti</string>
+ <string name="back_button_go_to_page">Mine lehele...</string>
+ <string name="back_button_go_to_page_title">Vali leht</string>
+ <string name="pref_delete_removes_from_queue_title">Kustutamine eemaldab järjekorrast</string>
+ <string name="pref_delete_removes_from_queue_sum">Saade eemaldatakse järjekorrast pärast kustutamist automaatselt.</string>
+ <!--About screen-->
+ <string name="about_pref">Info</string>
+ <string name="antennapod_version">AntennaPodi versioon</string>
+ <string name="developers">Arendajad</string>
+ <string name="developers_summary">Igaüks võib aidata AntennaPodi paremaks teha</string>
+ <string name="translators">Tõlkijad</string>
+ <string name="translators_summary">Tõlkeid lisavad AntennaPodi kasutajad Transifex keskkonnas</string>
+ <string name="privacy_policy">Privaatsus</string>
+ <string name="licenses">Litsentsid</string>
+ <string name="licenses_summary">AntennaPod kasutab teiste suurepärast tarkvara</string>
<!--Search-->
<string name="search_hint">Otsi saateid</string>
<string name="found_in_shownotes_label">Leitud saate märkmetest</string>
<string name="found_in_chapters_label">Leitud peatükkidest</string>
- <string name="search_status_no_results">ei leitud midagi</string>
+ <string name="found_in_authors_label">Leiti autorite hulgast</string>
+ <string name="found_in_feeds_label">Leiti saadete hulgast</string>
+ <string name="search_status_no_results">Ei leitud midagi</string>
<string name="search_label">Otsi</string>
<string name="found_in_title_label">Leitud pealkirjast</string>
+ <string name="no_results_for_query">\"%1$s\" otsimine ei andnud tulemusi</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML failid võimaldavad sul liigutada oma taskuhäälinguid ühest rakendusest teise.</string>
<string name="opml_import_option">Valik %1$d</string>
- <string name="opml_import_explanation_1">Vali failisüsteemist konktreetne kaust.</string>
- <string name="opml_import_explanation_2">Kasuta OPML-faili avamiseks väliseid rakendusi nagu Dropbox, Google Drive või sinu lemmik failihaldur.</string>
- <string name="start_import_label">Alusta importimist</string>
+ <string name="opml_import_explanation_1">Vali failisüsteemist konkreetne kaust.</string>
+ <string name="opml_import_explanation_3">Paljud rakendused nagu Google Mail, Dropbox, Google Drive ja enamik failihaldureid oskavad <i>avada</i> OPML faile AntennaPodiga.</string>
<string name="opml_import_label">OPML import</string>
- <string name="opml_directory_error">VIGA!</string>
<string name="reading_opml_label">OPML faili lugemine</string>
<string name="opml_reader_error">OPML dokumendi lugemisel tekkis tõrge:</string>
- <string name="opml_import_error_no_file">Ühtegi faili pole avatud!</string>
+ <string name="opml_import_error_no_file">Ühtegi faili pole valitud!</string>
<string name="select_all_label">Vali kõik</string>
<string name="deselect_all_label">Ära vali midagi</string>
- <string name="select_options_label">Vali...</string>
<string name="choose_file_from_filesystem">Kohalikust failisüsteemist</string>
- <string name="choose_file_from_external_application">Kasuta välist rakendust</string>
<string name="opml_export_label">OPML eksport</string>
<string name="html_export_label">HTML eksport</string>
<string name="exporting_label">Eksportimine...</string>
<string name="export_error_label">Viga eksportimisel</string>
+ <string name="export_success_title">Eksportimine edukas</string>
+ <string name="export_success_sum">Eksporditud fail salvestati asukohta:\n\n%1$s</string>
+ <string name="opml_import_ask_read_permission">OPML faili lugemiseks on vajalik ligipääs välisele salvestusruumile</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Määra unetaimer</string>
<string name="disable_sleeptimer_label">Keela unetaimer</string>
<string name="enter_time_here_label">Sisesta aeg</string>
<string name="sleep_timer_label">Unetaimer</string>
- <string name="time_left_label">Aega järgi:\u0020</string>
- <string name="time_dialog_invalid_input">Vigane sisend, aeg peab olema arvuna</string>
+ <string name="time_left_label">Aega jäänud:\u0020</string>
+ <string name="time_dialog_invalid_input">Vigane sisend, aeg peab olema täisarv</string>
+ <string name="timer_about_to_expire_label"><b>Kui taimer on lõppemas:</b></string>
<string name="shake_to_reset_label">Raputa taimeri nullimiseks</string>
<string name="timer_vibration_label">Vibreeri</string>
<string name="time_seconds">sekundit</string>
- <string name="time_minutes">inutit</string>
+ <string name="time_minutes">minutit</string>
<string name="time_hours">tundi</string>
<plurals name="time_seconds_quantified">
<item quantity="one">1 sekund</item>
@@ -344,15 +546,17 @@
<string name="sleep_timer_disabled_label">Unetaimer on välja lülitatud</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGOORIAD</string>
- <string name="gpodnet_toplist_header">POPIMAD TASKUHÄÄLINGUD</string>
+ <string name="gpodnet_toplist_header">POPULAARSED</string>
<string name="gpodnet_suggestions_header">SOOVITUSED</string>
<string name="gpodnet_search_hint">Otsi gpodder.net-ist</string>
<string name="gpodnetauth_login_title">Logi sisse</string>
- <string name="gpodnetauth_login_butLabel">Logi välja</string>
+ <string name="gpodnetauth_login_descr">Tere tulemast gpodder.net-i sisse logima. Kõigepealt sisesta sisselogimise andmed:</string>
+ <string name="gpodnetauth_login_butLabel">Logi sisse</string>
<string name="gpodnetauth_login_register">Kui sul pole veel kontot, siis sa saad selle endale registreerida siin:\nhttps://gpodder.net/register/</string>
<string name="username_label">Kasutajanimi</string>
<string name="password_label">Parool</string>
<string name="gpodnetauth_device_title">Seadme valimine</string>
+ <string name="gpodnetauth_device_descr">Loo oma gpodder.net konto jaoks uus seade või vali olemasolev:</string>
<string name="gpodnetauth_device_deviceID">Seadme ID:\u0020</string>
<string name="gpodnetauth_device_caption">Pealkiri</string>
<string name="gpodnetauth_device_butCreateNewDevice">Loo uus seade</string>
@@ -362,6 +566,7 @@
<string name="gpodnetauth_device_caption_errorEmpty">Pealkiri ei tohi olla tühi</string>
<string name="gpodnetauth_device_butChoose">Vali</string>
<string name="gpodnetauth_finish_title">Sisse logitud!</string>
+ <string name="gpodnetauth_finish_descr">Palju õnne! Sinu gpodder.net konto on nüüd lingitud sinu seadmega. AntennaPod süngib nüüdsest tellimused sinu seadmes gpodder.net-i kontoga.</string>
<string name="gpodnetauth_finish_butsyncnow">Alusta kohe sünkroonimist</string>
<string name="gpodnetauth_finish_butgomainscreen">Mine peaekraanile</string>
<string name="gpodnetsync_auth_error_title">gpodder.net autentimise viga</string>
@@ -370,10 +575,14 @@
<string name="gpodnetsync_error_descr">Sünkroonimise ajal tekkis viga:\u0020</string>
<string name="gpodnetsync_pref_report_successful">Korras</string>
<string name="gpodnetsync_pref_report_failed">Ebaõnnestus</string>
+ <string name="gpodnetsync_username_characters_error">Kasutajanimi võib sisaldada ainult tähti, numbreid, side- ja allkriipsu.</string>
<!--Directory chooser-->
<string name="selected_folder_label">Vali kaust:</string>
<string name="create_folder_label">Loo kaust</string>
<string name="choose_data_directory">Vali andmete kaust</string>
+ <string name="choose_data_directory_message">Palun vali oma andmete kaust. AntennaPod loob vajalikud alamkaustad.</string>
+ <string name="choose_data_directory_permission_rationale">Andmete kausta muutmiseks on vajalik ligipääs välisele salvestusruumile</string>
+ <string name="choose_data_directory_available_space">vaba on %1$s %2$s-st</string>
<string name="create_folder_msg">Loo uus kaust nimega \"%1$s\"?</string>
<string name="create_folder_success">Uus kaust on loodud</string>
<string name="create_folder_error_no_write_access">Sellesse kausta ei saa kirjutada</string>
@@ -383,37 +592,47 @@
<string name="folder_not_readable_error">\"%1$s\" pole loetav</string>
<string name="folder_not_writable_error">\"%1$s\" pole kirjutatav</string>
<string name="folder_not_empty_dialog_title">Kaust pole tühi</string>
+ <string name="folder_not_empty_dialog_msg">Kaust, mille valisid, ei ole tühi. Meedialaadimised ja teised failid paigutatakse otse sellesse kausta. Kas tahad siiski jätkata?</string>
<string name="set_to_default_folder">Vali vaikimisi kaust</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Kui teine äpp tahab helisid mängida, pausi esitus, selle asemel et vähendada helitugevust</string>
<string name="pref_pausePlaybackForFocusLoss_title">Peata kõne korral</string>
<string name="pref_resumeAfterCall_sum">Taasta esitamine pärast telefonikõne lõppu</string>
<string name="pref_resumeAfterCall_title">Taasta pärast kõne lõppu</string>
<string name="pref_restart_required">Selle muudatuse mõjumiseks tuleb AntennaPod taaskäivitada.</string>
<!--Online feed view-->
<string name="subscribe_label">Telli</string>
- <string name="subscribed_label">Tellitud</string>
- <string name="downloading_label">Allalaadimine...</string>
+ <string name="subscribing_label">Tellimine...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Keri tagasi</string>
<string name="fast_forward_label">Keri edasi</string>
<string name="media_type_audio_label">Heli</string>
<string name="media_type_video_label">Video</string>
<string name="navigate_upwards_label">Liigu ülespoole</string>
- <string name="status_downloading_label">Saadet laaditakse alla</string>
+ <string name="status_downloading_label">Saade laaditakse alla</string>
<string name="in_queue_label">Saade on järjekorras</string>
+ <string name="drag_handle_content_description">Kirje asukoha muutmiseks lohista</string>
<string name="load_next_page_label">Laadi järgmine lehekülg</string>
<!--Feed information screen-->
<string name="authentication_label">Autentimine</string>
+ <string name="authentication_descr">Muuda kasutajanime ja parooli selle taskuhäälingu ja saadete jaoks.</string>
<string name="auto_download_settings_label">Automaatse allalaadimise seaded</string>
<string name="episode_filters_label">Saadete filter</string>
+ <string name="episode_filters_description">Terminite loetelu, mille alusel otsustada, kas saade tuleks arvata sisse või välja automaatsel allalaadimisel</string>
<string name="episode_filters_include">Kaasa</string>
<string name="episode_filters_exclude">Jäta välja</string>
<string name="episode_filters_hint">Üksikud sõnad \n\"Mitu sõna\"</string>
<string name="keep_updated">Hoia uuendatuna</string>
+ <string name="keep_updated_summary">Kaasa see voog kui (automaatselt) värskendatakse kõiki voogusid</string>
+ <string name="auto_download_disabled_globally">Automaatne allalaadimine on keelatud AntennaPodi peamistes seadetes</string>
<!--Progress information-->
<string name="progress_upgrading_database">Andmebaasi uuendamine</string>
<!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Tellimuste importimine teistest äppidest...</string>
<!--Add podcast fragment-->
+ <string name="search_podcast_hint">Otsi taskuhäälingut...</string>
<string name="search_itunes_label">Otsi iTunest</string>
+ <string name="search_fyyd_label">Search fyyd-ist</string>
+ <string name="advanced_search">Täpsem otsing</string>
<string name="filter">Filter</string>
<!--Episodes apply actions-->
<string name="all_label">Kõik</string>
@@ -443,12 +662,12 @@
<string name="sort_duration_long_short">Kestus (pikk \u2192 lühike)</string>
<!--Rating dialog-->
<string name="rating_title">Kas sulle meeldib AntennaPod?</string>
- <string name="rating_message">Me hindame, kui sa saaksid võtta aega, et hinnata AntennaPodi.</string>
+ <string name="rating_message">Oleks suur asi, kui sa saaksid võtta aega, et anda AntennaPodile hinnang.</string>
<string name="rating_never_label">Jäta mind rahule</string>
<string name="rating_later_label">Tuleta hiljem meelde</string>
<string name="rating_now_label">Jah, otseloomulikult!</string>
<!--Audio controls-->
- <string name="audio_controls">Audiopleieri nupud</string>
+ <string name="audio_controls">Audioesitaja nupud</string>
<string name="playback_speed">Esitamise kiirus</string>
<string name="volume">Helitugevus</string>
<string name="left_short">L</string>
@@ -456,6 +675,7 @@
<string name="audio_effects">Heliefektid</string>
<string name="stereo_to_mono">Downmix: Stereo monoks</string>
<string name="sonic_only">Ainult Sonic</string>
+ <string name="exoplayer_only">Ainult ExoPlayer</string>
<!--proxy settings-->
<string name="proxy_type_label">Tüüp</string>
<string name="host_label">Hostinimi</string>
@@ -466,18 +686,44 @@
<string name="proxy_test_successful">Kontroll oli edukas</string>
<string name="proxy_test_failed">Kontroll ebaõnnestus</string>
<string name="proxy_host_empty_error">Hostinimi ei saa olla tühi</string>
- <string name="proxy_host_invalid_error">Se pole korrektne IP aadress või domeen</string>
+ <string name="proxy_host_invalid_error">See pole korrektne IP aadress või domeen</string>
<string name="proxy_port_invalid_error">Port pole korrektne</string>
<!--Subscriptions fragment-->
+ <string name="subscription_num_columns">Tulpade arv</string>
<!--Database import/export-->
<string name="import_export">Andmebaasi importimine/eksportimine</string>
+ <string name="import_export_warning">See eksperimentaalne funktsioon võimaldab tellimused ja esitatud saadete loetelu kanda üle teise seadmesse.\n\nEksporditud andmebaase saab importida ainult AntennaPodi samasse versiooni. Muul juhul annab see ettearvamatu tulemuse.\n\nPärast importimist võivad saated paista nagu oleksid allalaaditud, kuigi ei ole. Et AntennaPod sellest aru saaks, tuleb vajutada esitamise nupule.</string>
<string name="label_import">Impordi</string>
<string name="label_export">Ekspordi</string>
<string name="import_select_file">Vali fail, mida importida</string>
- <string name="export_ok">Eksportimine on sooritatud.</string>
+ <string name="export_ok">Eksportimine edukas.</string>
+ <string name="import_ok">Importimine edukas.\n\nAntennaPodi taaskäivitamiseks vajuta OK</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Esita...</string>
+ <string name="cast_disconnect_label">Katkesta kaugesitus</string>
+ <string name="cast_not_castable">Valitud meedia ei ühildu kaugseadmega</string>
+ <string name="cast_failed_to_play">Meedia esitamise alustamine nurjus</string>
+ <string name="cast_failed_to_stop">Meedia esitamise peatamine nurjus</string>
+ <string name="cast_failed_to_pause">Meedia esitamise pausimine nurjus</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
- <string name="cast_failed_media_error_skipping">Tõrge meedia esitamisel. Jätame vahele...</string>
+ <string name="cast_failed_setting_volume">Helivaljuse määramine nurjus</string>
+ <string name="cast_failed_no_connection">Kaugseadmega pole ühendust</string>
+ <string name="cast_failed_no_connection_trans">Ühendus kaugseadmega on katkenud. Rakendus püüab ühenduse taastada. Palun oota mõned sekundid ja proovi uuesti.</string>
+ <string name="cast_failed_status_request">Sünkroniseerimine kaugseadmega nurjus</string>
+ <string name="cast_failed_seek">Kerimine uuele asukohale kaugseadmes nurjus</string>
+ <string name="cast_failed_receiver_player_error">Vastuvõtval esitajal esines tõsine viga</string>
+ <string name="cast_failed_media_error_skipping">Tõrge meedia esitamisel. Jääb vahele...</string>
<!--Notification channels-->
+ <string name="notification_channel_user_action">Vajalik on tegevus</string>
+ <string name="notification_channel_user_action_description">Näidatakse, kui mingi tegevus on vajalik, näiteks tuleb sisestada parool.</string>
+ <string name="notification_channel_downloading">Allalaadimine</string>
+ <string name="notification_channel_downloading_description">Näidatakse allalaadimise ajal.</string>
+ <string name="notification_channel_playing">Praegu esitatakse</string>
+ <string name="notification_channel_playing_description">Võimaldab esitust juhtida. See on saate kuulamise ajal peamine teade.</string>
+ <string name="notification_channel_error">Vead</string>
+ <string name="notification_channel_error_description">Näidatakse, kui miski läks valesti, näiteks allalaadimine või gpodderiga sükroonimine nurjus.</string>
+ <string name="import_bad_file">Vigane/rikutud fail</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">Lisa vidin</string>
+ <string name="widget_opacity">Läbipaistvus</string>
</resources>
diff --git a/core/src/main/res/values-eu/strings.xml b/core/src/main/res/values-eu/strings.xml
new file mode 100644
index 000000000..13cc6b217
--- /dev/null
+++ b/core/src/main/res/values-eu/strings.xml
@@ -0,0 +1,732 @@
+<?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">Eguneratu harpidetzak</string>
+ <string name="feeds_label">Kanalak</string>
+ <string name="statistics_label">Estatistikak</string>
+ <string name="add_feed_label">Gehitu podcasta</string>
+ <string name="episodes_label">Saioak</string>
+ <string name="all_episodes_short_label">Denak</string>
+ <string name="new_episodes_label">Berria</string>
+ <string name="favorite_episodes_label">Gogokoak</string>
+ <string name="new_label">Berriak</string>
+ <string name="settings_label">Ezarpenak</string>
+ <string name="downloads_label">Deskargak</string>
+ <string name="downloads_running_label">Exekutatzen</string>
+ <string name="downloads_completed_label">Osatuta</string>
+ <string name="downloads_log_label">Izena</string>
+ <string name="subscriptions_label">Harpidetzak</string>
+ <string name="subscriptions_list_label">Harpidetza zerrenda</string>
+ <string name="cancel_download_label">Deskarga ezeztatu</string>
+ <string name="playback_history_label">Erreprodukzio historia</string>
+ <string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Sinkronizatzen beste gailu batzuekin</string>
+ <string name="gpodnet_auth_label">Saioa hasi gpodder.net-en</string>
+ <string name="episode_cache_full_title">Saioen katxea beteta</string>
+ <string name="episode_cache_full_message">Saioen katxeta mugara iritsi da. Ezarpenetan katxearen tamaina handitu dezakezu.</string>
+ <!--Statistics fragment-->
+ <string name="total_time_listened_to_podcasts">Ikusitako podcasten denbora osoa:</string>
+ <string name="statistics_details_dialog">%1$d kanpo %2$d hasitako saioetatik. %3$s \n\nErreproduzituak %4$setatik.</string>
+ <string name="statistics_mode">Estatistika modua</string>
+ <string name="statistics_mode_normal">Benetan ikusitakoen iraupena kalkulatzea. Bi aldiz ikustean bi aldiz kontatzen da; ikusita bezala markatzea, berriz, ez da kontatzen</string>
+ <string name="statistics_mode_count_all">Ikusita markatutako podcast guztien laburpena</string>
+ <string name="statistics_speed_not_counted">Oharra: erreprodukzioaren abiadura ez da sekula kontutan hartzen.</string>
+ <string name="statistics_reset_data">Estatistikak ezabatu</string>
+ <string name="statistics_reset_data_msg">Horrek atal guztien iraupenaren historia ezabatuko du. Ziur zaude egin nahi duzula?</string>
+ <!--Main activity-->
+ <string name="drawer_open">Zabaldu menua</string>
+ <string name="drawer_close">Itxi menua</string>
+ <string name="drawer_preferences">Kutxaren lehentasunak</string>
+ <string name="drawer_feed_order_unplayed_episodes">Antolatu kontagailuaren arabera</string>
+ <string name="drawer_feed_order_alphabetical">Antolatu alfabetikoki</string>
+ <string name="drawer_feed_order_last_update">Antolatu argitaratze dataz</string>
+ <string name="drawer_feed_order_most_played">Antolatu ikusitako saioen arabera</string>
+ <string name="drawer_feed_counter_new_unplayed">Saio berri ikusi gabekoen zenbatekoa</string>
+ <string name="drawer_feed_counter_new">Saio berrien zenbatekoa</string>
+ <string name="drawer_feed_counter_unplayed">Ikusi gabeko saioen zenbatekoa</string>
+ <string name="drawer_feed_counter_downloaded">Deskargatutako saioen zenbatekoa</string>
+ <string name="drawer_feed_counter_none">Bat ere ez</string>
+ <!--Webview actions-->
+ <string name="open_in_browser_label">Nabigatzailean ireki</string>
+ <string name="copy_url_label">URLa kopiatu</string>
+ <string name="share_url_label">URLa partekatu</string>
+ <string name="copied_url_msg">URLa arbelean kopiatu da</string>
+ <string name="go_to_position_label">Leku horretara joan</string>
+ <!--Playback history-->
+ <string name="clear_history_label">Historia garbitu</string>
+ <!--Other-->
+ <string name="confirm_label">Baieztatu</string>
+ <string name="cancel_label">Ezeztatu</string>
+ <string name="yes">Bai</string>
+ <string name="no">Ez</string>
+ <string name="reset">Berrezarri</string>
+ <string name="author_label">Egilea(k)</string>
+ <string name="language_label">Hizkuntza</string>
+ <string name="url_label">URL</string>
+ <string name="cover_label">Irudia</string>
+ <string name="error_label">Errorea</string>
+ <string name="error_msg_prefix">Errorea gertatu da:</string>
+ <string name="needs_storage_permission">Baimenak behar dira hori egiteko</string>
+ <string name="refresh_label">Eguneratu</string>
+ <string name="external_storage_error_msg">Ez da kanpo biltegirik aurkitu. Ziurtatu zure kanpo biltegia muntatuta dagoela aplikazioa ondo ibil dadin.</string>
+ <string name="chapters_label">Kapituluak</string>
+ <string name="chapter_duration">Iraupena: %1$s</string>
+ <string name="description_label">Deskribapena</string>
+ <string name="episodes_suffix">\u0020saio</string>
+ <string name="processing_label">Prozesatzen</string>
+ <string name="save_username_password_label">Gorde erabiltzailea eta pasahitza</string>
+ <string name="close_label">Itxi</string>
+ <string name="retry_label">Saiatu berriro</string>
+ <string name="auto_download_label">Deskarga automatikoetan sartu</string>
+ <string name="auto_download_apply_to_items_title">Aplikatu aurreko saioei</string>
+ <string name="auto_download_apply_to_items_message">Aukera berria <i>deskarga automatikoa</i> automatikoki aplikatuko zaie saio berriei. Nahi duzu aurreko saioei ere aplikatzea?</string>
+ <string name="auto_delete_label">Ezabatu saioa automatikoki</string>
+ <string name="parallel_downloads_suffix">\u0020deskarga paraleloak</string>
+ <string name="feed_auto_download_global">Globala aurrez zehaztua</string>
+ <string name="feed_auto_download_always">Beti</string>
+ <string name="feed_auto_download_never">Inoiz ez</string>
+ <string name="send_label">Bidali...</string>
+ <string name="episode_cleanup_never">Inoiz ez</string>
+ <string name="episode_cleanup_queue_removal">Ilaran ez dagoenean</string>
+ <string name="episode_cleanup_after_listening">Bukatu ondoren</string>
+ <plurals name="episode_cleanup_hours_after_listening">
+ <item quantity="one">1 ordu bukatu ondoren</item>
+ <item quantity="other">%d ordu bukatu ondoren</item>
+ </plurals>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="one">1 egun bukatu ondoren</item>
+ <item quantity="other">%d bukatu ondoren</item>
+ </plurals>
+ <string name="num_selected_label">%d aukeratua</string>
+ <!--'Add Feed' Activity labels-->
+ <string name="feedurl_label">Kanalaren URLa</string>
+ <string name="etxtFeedurlHint">www.adibidea.com/feed</string>
+ <string name="txtvfeedurl_label">Gehitu podcaasta URL bidez</string>
+ <string name="browse_gpoddernet_label">Bilatu gpodder.net-en bilatu</string>
+ <string name="discover">Aurkitu</string>
+ <string name="discover_more">gehiago »</string>
+ <!--Actions on feeds-->
+ <string name="mark_all_read_label">Markatu denak ikusita bezala</string>
+ <string name="mark_all_read_msg">Markatu saio denak ikusita bezala</string>
+ <string name="mark_all_read_confirmation_msg">Ziurtatu saio denak ikusita bezala markatu nahi dituzula.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Ziurtatu podcast honen saio guztiak ikusita bezala markatu nahi dituzula.</string>
+ <string name="remove_all_new_flags_label">Kendu \"berria\" ikur denak</string>
+ <string name="removed_all_new_flags_msg">Kendu dira \"berria\" ikur denak</string>
+ <string name="remove_all_new_flags_confirmation_msg">Ziurtatu saio denen \"berria\" ikurra kendu nahi dituzula</string>
+ <string name="show_info_label">Programaren informazioa</string>
+ <string name="show_feed_settings_label">Erakutsi podcastaren ezarpenak</string>
+ <string name="feed_info_label">Podcastaren informazioa</string>
+ <string name="feed_settings_label">Podcastaren ezarpenak</string>
+ <string name="rename_feed_label">Berrizendatu podcasta</string>
+ <string name="remove_feed_label">Kendu podcasta</string>
+ <string name="share_label">Partekatu...</string>
+ <string name="share_link_label">Partekatu URL saioa</string>
+ <string name="share_link_with_position_label">Partekatu URL saioa kokapenaz</string>
+ <string name="share_file_label">Partekatu fitxategia</string>
+ <string name="share_website_url_label">URL web gunea partekatu</string>
+ <string name="share_feed_url_label">Partekatu jarioaren URLa</string>
+ <string name="share_item_url_label">Partekatu media fitxategiaren URLa</string>
+ <string name="share_item_url_with_position_label">Partekatu Media Fitxategiaren URLa kokapenaz</string>
+ <string name="feed_delete_confirmation_msg">Mesedez berretsi \"%1$s\" podcasta eta bere saio \"guztiak\" (deskargatutako saioak barne) ezabatu nahi dituzula.</string>
+ <string name="feed_remover_msg">Podcasta ezabatzen</string>
+ <string name="load_complete_feed">Eguneratu osatutako podcasta</string>
+ <string name="batch_edit">Lotea editatu</string>
+ <string name="select_all_above">Aukeratu dena goian</string>
+ <string name="select_all_below">Aukeratu dena behean</string>
+ <string name="hide_unplayed_episodes_label">Ikusi gabe</string>
+ <string name="hide_paused_episodes_label">Geldirik</string>
+ <string name="hide_played_episodes_label">Ikusita</string>
+ <string name="hide_queued_episodes_label">Ilaran</string>
+ <string name="hide_not_queued_episodes_label">Ez dago ilaran</string>
+ <string name="hide_downloaded_episodes_label">Deskargatuta</string>
+ <string name="hide_not_downloaded_episodes_label">Deskargatu gabe</string>
+ <string name="hide_has_media_label">Media du</string>
+ <string name="hide_is_favorite_label">Gogokoa da</string>
+ <string name="filtered_label">Iragaziak</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Errorea azken eguneraketan</string>
+ <string name="open_podcast">Ireki podcasta</string>
+ <!--actions on feeditems-->
+ <string name="download_label">Deskargatu</string>
+ <plurals name="downloading_batch_label">
+ <item quantity="one">Deskargatzen %d saioa.</item>
+ <item quantity="other">Deskargatzen %d saioak.</item>
+ </plurals>
+ <string name="play_label">Ikusi</string>
+ <string name="pause_label">Pausatu</string>
+ <string name="stream_label">Zuzenean entzun</string>
+ <string name="delete_label">Ezabatu</string>
+ <string name="delete_failed">Ezin da fitxategia ezabatu. Gailua berrabiarazteak lagun dezake.</string>
+ <string name="delete_episode_label">Saioa ezabatu</string>
+ <plurals name="deleted_episode_batch_label">
+ <item quantity="one">%dsaio ezabatua.</item>
+ <item quantity="other">%d saio ezabatuak.</item>
+ </plurals>
+ <string name="remove_new_flag_label">Kendu \"berria\" ikurra</string>
+ <string name="removed_new_flag_label">\"Berria\" ikurra kendu da</string>
+ <string name="mark_read_label">Markatu ikusita bezala</string>
+ <string name="marked_as_read_label">Ikusita bezala markatua</string>
+ <plurals name="marked_read_batch_label">
+ <item quantity="one">%d saio markatuta ikusita bezala.</item>
+ <item quantity="other">%d saio markatuta ikusita bezala.</item>
+ </plurals>
+ <string name="mark_unread_label">Markatu ikusi gabe bezala</string>
+ <plurals name="marked_unread_batch_label">
+ <item quantity="one">%d saio markatuta ikusita bezala.</item>
+ <item quantity="other">%d saio markatuta ez ikusita bezala.</item>
+ </plurals>
+ <string name="add_to_queue_label">Ilaran gehitu</string>
+ <string name="added_to_queue_label">Ilaran gehitua</string>
+ <plurals name="added_to_queue_batch_label">
+ <item quantity="one">%d saio ilaran gehitua.</item>
+ <item quantity="other">%d saio ilaran gehituak.</item>
+ </plurals>
+ <string name="remove_from_queue_label">Kendu ilaratik</string>
+ <plurals name="removed_from_queue_batch_label">
+ <item quantity="one">%d saio ilaratik kenduta.</item>
+ <item quantity="other">%d saio ilaratik kenduta.</item>
+ </plurals>
+ <string name="add_to_favorite_label">Gogokoetara gehitu</string>
+ <string name="added_to_favorites">Gogokoetara gehitua</string>
+ <string name="remove_from_favorite_label">Gogokoetatik kendu</string>
+ <string name="removed_from_favorites">Gogokoetatik kendua</string>
+ <string name="visit_website_label">Bisitatu web gunea</string>
+ <string name="skip_episode_label">Saioa baztertu</string>
+ <string name="activate_auto_download">Aktibatu deskarga automatikoa</string>
+ <string name="deactivate_auto_download">Desaktibatu deskarga automatikoa</string>
+ <string name="reset_position">Berrezarri erreprodukzioaren tokia</string>
+ <string name="removed_item">Ezabatutako elementua</string>
+ <!--Download messages and labels-->
+ <string name="download_successful">arrakastatsua</string>
+ <string name="download_pending">Deskargatzeko zain</string>
+ <string name="download_running">Deskarga abian</string>
+ <string name="download_error_details">Xehetasunak</string>
+ <string name="download_error_details_message">%1$s \n\nartxibategiaren URL:\n%2$s</string>
+ <string name="download_error_device_not_found">Ez da biltegiratze gailurik aurkitu</string>
+ <string name="download_error_insufficient_space">Ez dago nahiko tokirik</string>
+ <string name="download_error_http_data_error">HTTP datuen errorea</string>
+ <string name="download_error_error_unknown">Errore ezezaguna</string>
+ <string name="download_error_parser_exception">Analizatzailearen salbuespena</string>
+ <string name="download_error_unsupported_type">Kanal mota ez onartua</string>
+ <string name="download_error_connection_error">Konexio errorea</string>
+ <string name="download_error_unknown_host">Ostalari ezezaguna</string>
+ <string name="download_error_unauthorized">Egiaztatze errorea</string>
+ <string name="download_error_file_type_type">Artxibategi motaren errorea</string>
+ <string name="download_error_forbidden">Debekaturik</string>
+ <string name="download_canceled_msg">Deskarga ezeztatua</string>
+ <string name="download_canceled_autodownload_enabled_msg">Deskarga ezeztatua\aktibatu da <i>Auto deskarga</i> elementu honetan</string>
+ <string name="download_report_title">Deskargak osatu dira errorea(k) d(it)uela</string>
+ <string name="download_report_content_title">Deskargen txostena</string>
+ <string name="download_error_malformed_url">URL-ren formatu okerra</string>
+ <string name="download_error_io_error">E/S errorea</string>
+ <string name="download_error_request_error">Errorea eskaeran</string>
+ <string name="download_error_db_access">Errorea datu basean sartzean</string>
+ <plurals name="downloads_left">
+ <item quantity="one">%d deskarga zain</item>
+ <item quantity="other">%d deskarga zain</item>
+ </plurals>
+ <string name="downloads_processing">Deskargak prozesatzen</string>
+ <string name="download_notification_title">Podcastaren datuak deskargatzen</string>
+ <string name="download_report_content">%1$d deskarga arrakastatsu, %2$d huts egin dute</string>
+ <string name="download_log_title_unknown">Izenburu ezezaguna</string>
+ <string name="download_type_feed">Kanala</string>
+ <string name="download_type_media">Media artxibategia</string>
+ <string name="download_request_error_dialog_message_prefix">Errore bat gertatu da fitxategia deskargatzen saiatzean:\u0020</string>
+ <string name="null_value_podcast_error">Ez da erakutsi zitekeen podcast-ik eman.</string>
+ <string name="authentication_notification_title">Eskatutako autentifikazioa</string>
+ <string name="authentication_notification_msg">Eskatutako baliabideak erabiltzaile bat eta pasahitza behar ditu</string>
+ <string name="confirm_mobile_download_dialog_title">Baieztatu deskarga sare mugikor bidez</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Datu-sare mugikorren bidezko deskargak desaktibatu dira konfigurazioan.\n\n Aukeratu daiteke saio ilarari eranstea edo deskargak aldi baterako baimentzea.\n\n<small>10 minutura haien aukeraketa gogora araziko da.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Datu mugikorren sareko deskargak desaktibatu dira konfigurazioan.\n\n Deskargak aldi baterako baimendu nahi dituzu?\n\n<small>10 minutura haien aukeraketa gogora araziko da.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Berretsi zuzenekoa mugikorrean</string>
+ <string name="confirm_mobile_streaming_notification_message">Konexioaren datu bidezko zuzenekoa desgaiturik dago ezarpenetan. Sakatu halere ikusteko.</string>
+ <string name="confirm_mobile_streaming_button_always">Baimendu beti</string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Ilaran gehitu</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Baimendu aldi baterako</string>
+ <!--Mediaplayer messages-->
+ <string name="player_error_msg">Errorea!</string>
+ <string name="player_stopped_msg">Ez dago ezer erreproduzitzen</string>
+ <string name="player_preparing_msg">Prestatzen</string>
+ <string name="player_ready_msg">Prest</string>
+ <string name="player_seeking_msg">Bilatzen</string>
+ <string name="playback_error_server_died">Zerbitzaria hil da</string>
+ <string name="playback_error_unsupported">Media mota hori ez da onartzen</string>
+ <string name="playback_error_timeout">Ekintzak denbora muga gainditu du</string>
+ <string name="playback_error_unknown">Akats ezezaguna</string>
+ <string name="no_media_playing_label">Ez dago ezer erreproduzitzen</string>
+ <string name="player_buffering_msg">Biltegiratzen</string>
+ <string name="player_go_to_picture_in_picture">Picture-in-picture modua</string>
+ <string name="unknown_media_key">AntennaPod - Media tekla ezezaguna: %1$d</string>
+ <!--Queue operations-->
+ <string name="lock_queue">Ilara blokeatu</string>
+ <string name="unlock_queue">Ilara desblokeatu</string>
+ <string name="queue_locked">Ilara blokeaturik</string>
+ <string name="queue_unlocked">Ilara desblokeaturik</string>
+ <string name="queue_lock_warning">Ilara blokeatzen baduzu, ezin izango dituzu saioak mugitu edo markatu.</string>
+ <string name="checkbox_do_not_show_again">Ez erakutsi berriro</string>
+ <string name="clear_queue_label">Ilara hustu</string>
+ <string name="undo">Desegin</string>
+ <string name="move_to_top_label">Hasierara eraman</string>
+ <string name="move_to_bottom_label">Bukaerara eraman</string>
+ <string name="sort">Antolatu</string>
+ <string name="keep_sorted">Antolatuta eduki</string>
+ <string name="date">Data</string>
+ <string name="duration">Iraupena</string>
+ <string name="episode_title">Saioaren izenburua</string>
+ <string name="feed_title">Podcastaren izenburua</string>
+ <string name="random">Ausaz</string>
+ <string name="smart_shuffle">Erreprodukzio adimentsua</string>
+ <string name="ascending">Gorantz</string>
+ <string name="descending">Beherantz</string>
+ <string name="clear_queue_confirmation_msg">Ziurtatu ilarako saio DENAK ezabatu nahi dituzula</string>
+ <string name="sort_old_to_new">Berritik zaharrera</string>
+ <string name="sort_new_to_old">Zaharretik berrira</string>
+ <!--Variable Speed-->
+ <string name="download_plugin_label">Deskargatu osagarria</string>
+ <string name="no_playback_plugin_title">Osagarria instalatu gabe</string>
+ <string name="set_playback_speed_label">Erreprodukzio abiadurak</string>
+ <string name="enable_sonic">Gaitu Sonic</string>
+ <!--Empty list labels-->
+ <string name="no_items_header_label">Saiorik ez ilaran</string>
+ <string name="no_items_label">Gehitu saio bat deskargatuz edo sakatu luzaro saio bat eta hautatu \"gehitu ilarari\".</string>
+ <string name="no_shownotes_label">Saio honek ez du oharrik ikusgai.</string>
+ <string name="no_run_downloads_head_label">Ez dago deskargarik abian</string>
+ <string name="no_run_downloads_label">Saioak deskarga ditzakezu podcastaren xehetasun pantailan.</string>
+ <string name="no_comp_downloads_head_label">Ez dago deskargatutako saiorik</string>
+ <string name="no_comp_downloads_label">Saioak deskarga ditzakezu podcastaren xehetasun pantailan.</string>
+ <string name="no_log_downloads_head_label">Ez dago deskargen erregistrorik</string>
+ <string name="no_log_downloads_label">Deskargen erregistroak hemen azalduko dira eskuragarri daudenean.</string>
+ <string name="no_history_head_label">Historiarik ez</string>
+ <string name="no_history_label">Saioa entzundakoan hemen azalduko da.</string>
+ <string name="no_all_episodes_head_label">Saiorik ez</string>
+ <string name="no_all_episodes_label">Podcast bat gehitzen duzunean saioak hemen azalduko dira.</string>
+ <string name="no_new_episodes_head_label">Saio berririk ez</string>
+ <string name="no_new_episodes_label">Saio berriak azaltzean hemen azalduko dira.</string>
+ <string name="no_fav_episodes_head_label">Gogoko saiorik ez</string>
+ <string name="no_fav_episodes_label">Gogokoetan saio berriak gehitu ditzakezu gainean luze sakatuz.</string>
+ <string name="no_chapters_head_label">Kapitulurik ez</string>
+ <string name="no_chapters_label">Saio honek ez du kapitulurik.</string>
+ <string name="no_subscriptions_head_label">Ez dago harpidetzarik</string>
+ <string name="no_subscriptions_label">Podcast batean harpidetzeko, sakatu beheko \"gehi\" ikonoa.</string>
+ <!--Preferences-->
+ <string name="storage_pref">Biltegia</string>
+ <string name="project_pref">Proiektua</string>
+ <string name="queue_label">Ilara</string>
+ <string name="integrations_label">Integrazioak</string>
+ <string name="automation">Automatizazioa</string>
+ <string name="download_pref_details">Xehetasunak</string>
+ <string name="import_export_pref">Inportatu/esportatu</string>
+ <string name="import_export_search_keywords">Babeskopia, berrezarri, backupa, restore</string>
+ <string name="appearance">Itxura</string>
+ <string name="external_elements">Kanpo elementuak</string>
+ <string name="interruptions">Etenaldiak</string>
+ <string name="playback_control">Erreprodukzioaren kontrola</string>
+ <string name="preference_search_hint">Bilatu...</string>
+ <string name="preference_search_no_results">Emaitzarik ez</string>
+ <string name="preference_search_clear_history">Historia ezabatu</string>
+ <string name="media_player">Media erreproduzigailua</string>
+ <string name="pref_episode_cleanup_title">Saioen garbitzea</string>
+ <string name="pref_episode_cleanup_summary">Ilaran edo gogokoetan ez dauden gertakariak ezabatu egin daitezke deskarga automatikoak gertakari berrietarako lekua behar badu.</string>
+ <string name="pref_pauseOnDisconnect_sum">Erreprodukzioa gelditu entzungailu edo bluetootha kentzean</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Erreprodukzioa jarraitu entzungailu edo bluettota berriz konektatzean</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Erreprodukzioa jarraitu bluetootha berriz konektatzean</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Aurrera botoia: jauzi</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Bluetooth bidez konektatutako gailuan aurrera botoia sakatzean, hurrengo saiora egingo du jauzi aurrera egin beharrean</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Atzera botoia: Berrabiarazi</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">Atzera botoia sakatzean saioa berriz hasiko da atzera egin beharrean</string>
+ <string name="pref_followQueue_sum">Erreprodukzioa amaitzean ilarako hurrengo elementura jauzi</string>
+ <string name="pref_auto_delete_sum">Saioa ezabatu erreprodukzioa amaitzean</string>
+ <string name="pref_auto_delete_title">Automatikoki ezabatu</string>
+ <string name="pref_smart_mark_as_played_sum">Erreproduzitutako saioak markatu nahiz bukatzeko segundo batzuk falta</string>
+ <string name="pref_smart_mark_as_played_title">Erreproduzituta marka adimendua</string>
+ <string name="pref_skip_keeps_episodes_sum">Saioak gorde jaustean</string>
+ <string name="pref_skip_keeps_episodes_title">Gorde jauzi egindako saioak</string>
+ <string name="pref_favorite_keeps_episodes_sum">Gorde saioak gogoko gisa markatzean</string>
+ <string name="pref_favorite_keeps_episodes_title">Gorde gogoko saioak</string>
+ <string name="playback_pref">Erreprodukzioa</string>
+ <string name="network_pref">Sarea</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Eguneratzeko tartea edo ordua</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Zehaztu tartea edo eguneko ordu bat kanalak automatikoki eguneratzeko</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Zuk ahal duzu <i>tartea</i> gustuko \"2orduro\" <i>eguneko ordua </i> \"7:00 AM\" adibidez edo <i>desgaitu</i> eguneraketa automatikoak\n\n<small>Oharra: Eguneraketa orduak ez dira zehatzak. Atzerapen txiki bat eman daiteke.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Desgaitu</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Tartea zehaztu</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Zehaztu eguneko ordua</string>
+ <string name="pref_autoUpdateIntervallOrTime_every">%1$sdenak</string>
+ <string name="pref_autoUpdateIntervallOrTime_at">%1$setan</string>
+ <string name="pref_followQueue_title">Etengabeko erreprodukzioa</string>
+ <string name="pref_pauseOnHeadsetDisconnect_title">Deskonektatu entzungailurik gabe</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Birkonektatu entzungailuez</string>
+ <string name="pref_unpauseOnBluetoothReconnect_title">Birkonektatu bluetooth bidez</string>
+ <string name="pref_stream_over_download_title">Nahiago zuzenean</string>
+ <string name="pref_stream_over_download_sum">Zuzenean botoia erakusten du, zerrendetan deskargatzeko botoiaren ordez.</string>
+ <string name="pref_mobileUpdate_title">Sare bidezko eguneraketak</string>
+ <string name="pref_mobileUpdate_sum">Aukeratu datu mugikorrez deskarga daitekeena</string>
+ <string name="pref_mobileUpdate_refresh">Iturria eguneratu</string>
+ <string name="pref_mobileUpdate_images">Azaleko irudiak</string>
+ <string name="pref_mobileUpdate_auto_download">Deskargatu automatikoki</string>
+ <string name="pref_mobileUpdate_episode_download">Saioaren deskarga</string>
+ <string name="pref_mobileUpdate_streaming"> Zuzenean</string>
+ <string name="user_interface_label">Erabiltzailearen interfazea</string>
+ <string name="pref_set_theme_title">Gai bat aukeratu</string>
+ <string name="pref_nav_drawer_items_title">Aldatu nabigatze kutxa</string>
+ <string name="pref_nav_drawer_items_sum">Aldatu nabigatze kutxan azaltzen diren elementuak</string>
+ <string name="pref_nav_drawer_feed_order_title">Doitu harpidetzaren ordena</string>
+ <string name="pref_nav_drawer_feed_order_sum">Aldatu harpidetzen ordena</string>
+ <string name="pref_nav_drawer_feed_counter_title">Doitu harpidetzen kontagailua</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Aldatu harpidetzen kontagailuan erakutsitako informazioa aldatzea. Harpidetzen ordenari ere eragiten dio, \'Harpidetzaren ordena\' \'kontagailu\' moduan badago.</string>
+ <string name="pref_set_theme_sum">Aldatu AntennaPoden itxura</string>
+ <string name="pref_automatic_download_title">Deskarga automatikoa</string>
+ <string name="pref_automatic_download_sum">Konfiguratu saioen deskarga automatikoa</string>
+ <string name="pref_autodl_wifi_filter_title">Gaitu Wifi iragazkia</string>
+ <string name="pref_autodl_wifi_filter_sum">Onartu deskarga automatikoak aukeratutako Wifi sareetan bakarrik</string>
+ <string name="autodl_wifi_filter_permission_title">Baimena beharrezkoa</string>
+ <string name="autodl_wifi_filter_permission_message">Wi-Fi sareen arabera iragazteko kokapen-baimena behar da. Klikatu baimena emateko.</string>
+ <string name="pref_automatic_download_on_battery_title">Deskargatu kargatzen ari dela</string>
+ <string name="pref_automatic_download_on_battery_sum">Onartu deskarga bateria kargatzen ari ez denean</string>
+ <string name="pref_parallel_downloads_title">Deskarga paraleloak</string>
+ <string name="pref_episode_cache_title">Saioak gordetzea</string>
+ <string name="pref_episode_cache_summary">Gailuan katxeatutako saioen zenbatekoa. Deskarga automatikoa bertan behera utziko da zenbaki honetara heltzean.</string>
+ <string name="pref_episode_cover_title">Saioaren azala erabili</string>
+ <string name="pref_episode_cover_summary">Erabili atalaren azala ahal denean. Desaktibatzen bada, aplikazioak podcastaren azala erabiliko du beti.</string>
+ <string name="pref_theme_title_use_system">Sistemaren gaia erabili</string>
+ <string name="pref_theme_title_light">Argia</string>
+ <string name="pref_theme_title_dark">Iluna</string>
+ <string name="pref_theme_title_trueblack">Beltza (AMOLEDentzako)</string>
+ <string name="pref_episode_cache_unlimited">Neurri gabe</string>
+ <string name="pref_update_interval_hours_plural">orduak</string>
+ <string name="pref_update_interval_hours_singular">ordua</string>
+ <string name="pref_update_interval_hours_manual">Eskuliburua</string>
+ <string name="pref_gpodnet_authenticate_title">Hasi saioa</string>
+ <string name="pref_gpodnet_authenticate_sum">Hasi saioa zure gpodder.net kontuan zure harpidetzak sinkronizatzeko</string>
+ <string name="pref_gpodnet_logout_title">Itxi saioa</string>
+ <string name="pref_gpodnet_logout_toast">Saioa ongi itxi duzu</string>
+ <string name="pref_gpodnet_setlogin_information_title">Aldatu saio hasieraren informazioa</string>
+ <string name="pref_gpodnet_setlogin_information_sum">Aldatu saio hasierako datuak zure gpodder.net-en kontuan.</string>
+ <string name="pref_gpodnet_sync_changes_title">Sinkronizatu aldaketak orain</string>
+ <string name="pref_gpodnet_sync_changes_sum">Sinkronizatu harpidetza aldaketak eta saioak gpodder.net-ekin</string>
+ <string name="pref_gpodnet_full_sync_title">Era bat sinkronizaturik</string>
+ <string name="pref_gpodnet_full_sync_sum">Sinkronizatu harpidetza denak eta saioak gpodder.net-ekin</string>
+ <string name="pref_gpodnet_sync_sum_last_sync_line">Azken sinkronizatze saiakera: %1$s (%2$s)</string>
+ <string name="pref_gpodnet_sync_started">Sinkronizatzen hasi da</string>
+ <string name="pref_gpodnet_login_status"><![CDATA[Horrela hasi du saioa <i>%1$s</i> gailu honekin <i>%2$s</i>]]></string>
+ <string name="pref_gpodnet_notifications_title">Erakutsi sinkronizatze erroreak</string>
+ <string name="pref_gpodnet_notifications_sum">Ezarpen honek ez du eraginik saio hasierako erroretan eraginik.</string>
+ <string name="pref_playback_speed_title">Erreproduzitzeko abiadurak</string>
+ <string name="pref_playback_speed_sum">Pertsonalizatu eskura dauden abiadurak audio erreproduzitzeko abiadura aldakorrean</string>
+ <string name="pref_feed_playback_speed_sum">Feed honen saioak zein abiaduratan hasiko diren.</string>
+ <string name="pref_playback_time_respects_speed_title">Doitu medioaren informazioa erreprodukzio-abiaduraren arabera doitzea</string>
+ <string name="pref_playback_time_respects_speed_sum">Posizioa eta iraupena erreprodukzio-abiadurara egokituta daude.</string>
+ <string name="pref_fast_forward">Aurrera egiteko tartea</string>
+ <string name="pref_fast_forward_sum">Pertsonalizatu zenbat segundu egingo duen aurrera aurrera botoia sakatzean
+</string>
+ <string name="pref_rewind">Atzera egiteko tartea</string>
+ <string name="pref_rewind_sum">Pertsonalizatu zenbat segundu egingo duen atzera atzera botoia sakatzean
+</string>
+ <string name="pref_gpodnet_sethostname_title">Ezarri gailuaren izena</string>
+ <string name="pref_gpodnet_sethostname_use_default_host">Erabili lehenetsitako izena</string>
+ <string name="pref_expandNotify_title">Jakinarazpenen lehentasuna</string>
+ <string name="pref_expandNotify_sum">Honek jakinarazpenak zabaltzen ditu erreprodukzio botoiak erakusteko</string>
+ <string name="pref_persistNotify_title">Erreprodukzio kontrol iraunkorrak</string>
+ <string name="pref_persistNotify_sum">Eutsi jakinarazpenak eta kontrolak blokeo pantailan erreprodukzioa gelditzean</string>
+ <string name="pref_compact_notification_buttons_title">Konfiguratu botoiak blokeo pantailan</string>
+ <string name="pref_compact_notification_buttons_sum">Aldatu botoiak blokeatze pantailan. Erreproduzitu/gelditu botoia beti azalduko da.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">Aukeratu gehienez %1$d elementu</string>
+ <string name="pref_compact_notification_buttons_dialog_error">Ezingo duzu %1$d elementu baino gehiago aukeratu.</string>
+ <string name="pref_lockscreen_background_title">Ezarri blokeo pantailaren atzealdea</string>
+ <string name="pref_lockscreen_background_sum">Ezarri saioaren irudia blokeo pantailarako atzealdea moduan. Horren eraginez, hirugarrenen aplikazio irudiak ere azalduko dira.</string>
+ <string name="pref_showDownloadReport_title">Deskarga txostena erakutsi</string>
+ <string name="pref_showDownloadReport_sum">Deskargak huts egiten badu, sortu txostena akatsaren xehetasunekin.</string>
+ <string name="pref_expand_notify_unsupport_toast">Android 4.1 aurreko bertsioek ez dituzte zabaldutako jakinarazpenak jasaten</string>
+ <string name="pref_enqueue_location_title">Ilaran gehitu kokalekuan</string>
+ <string name="pref_enqueue_location_sum">Saioak hemen gehitu: %1$s</string>
+ <string name="enqueue_location_back">Bukaera</string>
+ <string name="enqueue_location_front">Hasiera</string>
+ <string name="enqueue_location_after_current">Oraingo saioaren ondoren</string>
+ <string name="pref_smart_mark_as_played_disabled">Desgaitua</string>
+ <string name="pref_image_cache_size_title">Irudiak biltegiratzeko tamaina</string>
+ <string name="pref_image_cache_size_sum">Diskoko irudien biltegiratze tamaina</string>
+ <string name="view_mailing_list">Posta zerrenda ikusi</string>
+ <string name="bug_report_title">Errorearen berri eman</string>
+ <string name="open_bug_tracker">Erroreen bilatzailea irekirik</string>
+ <string name="copy_to_clipboard">Kopiatu arbelean</string>
+ <string name="copied_to_clipboard">Arbelean kopiatu da</string>
+ <string name="experimental_pref">Esperimentala</string>
+ <string name="pref_media_player_message">Aukeratu zein erreprozitzeko gailua erabili artxibategiak erreproduzitzeko</string>
+ <string name="pref_current_value">Egungo balioa: %1$s</string>
+ <string name="pref_proxy_title">Proxy</string>
+ <string name="pref_proxy_sum">Konfiguratu sareko proxya</string>
+ <string name="pref_faq">Ohiko galderak</string>
+ <string name="pref_no_browser_found">Ez da web nabigatzailea aurkitu</string>
+ <string name="pref_cast_title">Chromecasterako euskarria</string>
+ <string name="pref_cast_message_play_flavor">Gaitu Cast gailuetan urrutira erreproduzitzeko euskarria (chromecast, altabozak edo Android TB modukoak)</string>
+ <string name="pref_cast_message_free_flavor">Chromecastek AntennaPod bertsio honetan desgaiturik dauden hirugarrenen liburutegiak behar ditu</string>
+ <string name="pref_enqueue_downloaded_title">Gehitu deskargatutakoak ilarara</string>
+ <string name="pref_enqueue_downloaded_summary">Gehitu deskargatutako saioak ilarara</string>
+ <string name="media_player_builtin">Integratutako Android erreproduzitzailea</string>
+ <string name="pref_skip_silence_title">Jauzi egin ixilunea audioan</string>
+ <string name="pref_videoBehavior_title">Bideotik irtetean</string>
+ <string name="pref_videoBehavior_sum">Jokaera bideo erreproduzitzetik irtetean</string>
+ <string name="stop_playback">Erreprodukzioa gelditu</string>
+ <string name="continue_playback">Jarraitu audioa erreproduzitzen</string>
+ <string name="behavior">Joera</string>
+ <string name="pref_back_button_behavior_title">Atzera botoiaren jokaera</string>
+ <string name="pref_back_button_behavior_sum">Aldatu atzera botoiaren jokaera</string>
+ <string name="back_button_default">Aurrez zehaztua</string>
+ <string name="back_button_open_drawer">Nabigatze kaxa ireki</string>
+ <string name="back_button_double_tap">Sakatu bi aldiz irteteko</string>
+ <string name="back_button_show_prompt">Baieztatu irteteko</string>
+ <string name="close_prompt">Ziur AntennaPod itxi nahi duzula?</string>
+ <string name="double_tap_toast">Sakatu botoia berriz irteteko</string>
+ <string name="back_button_go_to_page">Jo orrialdera...</string>
+ <string name="back_button_go_to_page_title">Aukeratu orrialdea</string>
+ <string name="pref_delete_removes_from_queue_title">Ezabatu ilaratik kendutakoak</string>
+ <string name="pref_delete_removes_from_queue_sum">Kendu saio bat automatikoki ilaratik ezabatzean.</string>
+ <!--About screen-->
+ <string name="about_pref">Honi buruz</string>
+ <string name="antennapod_version">AntennaPod bertsioa</string>
+ <string name="developers">Garatzaileak</string>
+ <string name="developers_summary">Edonork lagun dezake AntennaPod hobetzen</string>
+ <string name="translators">Itzultzaileak</string>
+ <string name="translators_summary">Itzulpenek AntennaPod erabiltzaile berriak sortzen dituzte Transifex erabiliz</string>
+ <string name="privacy_policy">Pribatutasun politika</string>
+ <string name="licenses">Baimenak</string>
+ <string name="licenses_summary">AntennaPod-ek beste software ezin hobeak erabiltzen ditu</string>
+ <!--Search-->
+ <string name="search_hint">Saioak bilatu</string>
+ <string name="found_in_shownotes_label">Programaren oharretan atzemanda</string>
+ <string name="found_in_chapters_label">Kapituluetan atzemanda</string>
+ <string name="found_in_authors_label">Egile(et)an atzemanda</string>
+ <string name="found_in_feeds_label">Podcastean atzemanda</string>
+ <string name="search_status_no_results">Ez da emaitzarik aurkitu</string>
+ <string name="search_label">Bilatu</string>
+ <string name="found_in_title_label">Tituluan atzemanda</string>
+ <string name="no_results_for_query">Ez da \"%1$s\"emaitzarik aurkitu</string>
+ <!--OPML import and export-->
+ <string name="opml_import_option">Aukera %1$d</string>
+ <string name="opml_import_explanation_1">Aukeratu tokiko fitxategien jarraibidea.</string>
+ <string name="opml_import_explanation_3">Google Mail, Dropbox, Google Dive eta artxibategien gestore gehienek bezalako hainbat aplikazioek ahal dute
+ <i>ireki</i> OPML artxibategiak <i>honekin</i> ,AntennaPod.</string>
+ <string name="opml_import_label">Inportatu OPML Importtik</string>
+ <string name="reading_opml_label">OPML artxibategia irakurtzen</string>
+ <string name="opml_reader_error">Errorea OPML dokkumentua irakurtzean:</string>
+ <string name="opml_import_error_no_file">Artxibategi bat aukeratu behar duzu!</string>
+ <string name="select_all_label">Aukeratu dena</string>
+ <string name="deselect_all_label">Aukera kendu denei</string>
+ <string name="choose_file_from_filesystem">Tokiko fitxategi sistematik</string>
+ <string name="opml_export_label">OPMLra esportatu</string>
+ <string name="html_export_label">HTMLra esportatu</string>
+ <string name="exporting_label">Esportatzen...</string>
+ <string name="export_error_label">Errorea esportatzean</string>
+ <string name="export_success_title">Esportatzea ongi burutu da</string>
+ <string name="export_success_sum">Esportatutako artxibategia hemen gorde da:\n\n%1$s</string>
+ <string name="opml_import_ask_read_permission">Kanpo biltegirako sarrera behar duzu OPML artxibategiak irakurtzeko</string>
+ <!--Sleep timer-->
+ <string name="set_sleeptimer_label">Ezarri tenporizadore bat</string>
+ <string name="disable_sleeptimer_label">Desgaitu tenporizadorea</string>
+ <string name="enter_time_here_label">Ordua ezarri</string>
+ <string name="sleep_timer_label">Tenporizadorea</string>
+ <string name="time_left_label">Gelditzen den denbora:\u0020</string>
+ <string name="time_dialog_invalid_input">Baliogabeko sarrera, denbora zenbaki osoa izan behar du</string>
+ <string name="timer_about_to_expire_label"><b>Tenporizadorea bukatzear dagoeneanWhen timer is about to expire:</b></string>
+ <string name="shake_to_reset_label">Astindu tenporizadorea berrekiteko</string>
+ <string name="timer_vibration_label">Dardar egin</string>
+ <string name="time_seconds">segundoak</string>
+ <string name="time_minutes">minutuak</string>
+ <string name="time_hours">orduak</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">segundo</item>
+ <item quantity="other">%d segundo</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minutu 1</item>
+ <item quantity="other">%d minutu</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">ordu 1</item>
+ <item quantity="other">%d ordu</item>
+ </plurals>
+ <string name="auto_enable_label">Automatikoki gaitu</string>
+ <string name="sleep_timer_enabled_label">Tenporizadorea gaiturik</string>
+ <string name="sleep_timer_disabled_label">Tenporizadorea desgaiturik</string>
+ <!--gpodder.net-->
+ <string name="gpodnet_taglist_header">KATEGORIAK</string>
+ <string name="gpodnet_toplist_header">PODCASTIK ONENAK</string>
+ <string name="gpodnet_suggestions_header">IRADOKIZUNAK</string>
+ <string name="gpodnet_search_hint">Bilatu gpodder.net-en bilatu</string>
+ <string name="gpodnetauth_login_title">Hasi saioa</string>
+ <string name="gpodnetauth_login_descr">Ongi etorri gpodder.net saio hasierara. Hasteko zure saio hasierako datuak sartu:</string>
+ <string name="gpodnetauth_login_butLabel">Hasi saioa</string>
+ <string name="gpodnetauth_login_register">Oraindik konturik ez baduzu hemen sortu dezakezu:\nhttps://gpodder.net/register/</string>
+ <string name="username_label">Erabioltzailea</string>
+ <string name="password_label">Pasahitza</string>
+ <string name="gpodnetauth_device_title">Gailua aukeratzea</string>
+ <string name="gpodnetauth_device_descr">Sortu gailu bat zure gpodder.net kontua erabiltzeko edo dagoenetako bat aukeratu:</string>
+ <string name="gpodnetauth_device_deviceID">Gailuaren ID:\u0020</string>
+ <string name="gpodnetauth_device_caption">Deskribapena</string>
+ <string name="gpodnetauth_device_butCreateNewDevice">Sortu gailu berria</string>
+ <string name="gpodnetauth_device_chooseExistingDevice">Dagoen gailuetako bat aukeratu:</string>
+ <string name="gpodnetauth_device_errorEmpty">Gailuaren ID ezin du hutsik egon</string>
+ <string name="gpodnetauth_device_errorAlreadyUsed">Gailuaren ID dagoeneko erabiltzen da</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">Testuak ezin du hutsik egon</string>
+ <string name="gpodnetauth_device_butChoose">Aukeratu</string>
+ <string name="gpodnetauth_finish_title">Saioa ondo hasi duzu!</string>
+ <string name="gpodnetauth_finish_descr">Zorionak! Zure gpodder.net kontua zure gailuarekin lotuta dago orain. Hemendik aurrera, AntennaPodek automatikoki sinkronizatuko ditu zure gailuaren harpidetzak bzure gpodder.net kontuarekin.</string>
+ <string name="gpodnetauth_finish_butsyncnow">Hasi sinkronizazioa orain</string>
+ <string name="gpodnetauth_finish_butgomainscreen">Joan pantaila nagusira</string>
+ <string name="gpodnetsync_auth_error_title">Egiaztatze errorea gpodder.net-en</string>
+ <string name="gpodnetsync_auth_error_descr">Erabiltzaile edo pasahitz okerrak</string>
+ <string name="gpodnetsync_error_title">Sinkronizatze errorea gpodder.net-en</string>
+ <string name="gpodnetsync_error_descr">Sinkronizazio errorea izan da:\u0020</string>
+ <string name="gpodnetsync_pref_report_successful">Arrakastatsua</string>
+ <string name="gpodnetsync_pref_report_failed">Huts egin du</string>
+ <string name="gpodnetsync_username_characters_error">Erabiltzaile-izenek letrak, digitu, erdiko gidoi eta gidoi baxuak baino ezin dituzte eduki.</string>
+ <!--Directory chooser-->
+ <string name="selected_folder_label">Aukeratutako karpeta:</string>
+ <string name="create_folder_label">Karpeta sortu</string>
+ <string name="choose_data_directory">Aukeratu datu karpeta</string>
+ <string name="choose_data_directory_message">Aukeratu datuen karpetaren erroa. AntennaPod-ek azpidirektorio egokiak sortuko ditu.</string>
+ <string name="choose_data_directory_permission_rationale">Kanpo biltegirako sarrera behar duzu datuen karpeta aldatzeko</string>
+ <string name="choose_data_directory_available_space">%1$s %2$s -tik libre</string>
+ <string name="create_folder_msg">\"%1$s\" izeneko karpeta berria sortu?</string>
+ <string name="create_folder_success">Karpeta berria sortu da</string>
+ <string name="create_folder_error_no_write_access">Ezin da karpeta honetan idatzi</string>
+ <string name="create_folder_error_already_exists">Karpeta hau lehendik ere badago</string>
+ <string name="create_folder_error">Ezin izan da karpeta sortu</string>
+ <string name="folder_does_not_exist_error">\"%1$s\" ez dago</string>
+ <string name="folder_not_readable_error">\"%1$s\" ezin da irakurri</string>
+ <string name="folder_not_writable_error">\"%1$s\" ezin da aldatu</string>
+ <string name="folder_not_empty_dialog_title">Karpeta ez dago hutsik</string>
+ <string name="folder_not_empty_dialog_msg">Aukeratutako karpeta ez dago hutsik. Deskargak eta bestelako fitxategiak zuzenean kopiatuko dira karpeta honetan. Jarraitu hala ere?</string>
+ <string name="set_to_default_folder">Aukeratu karpeta lehenetsia</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Bolumena jaitsi beharrean erreprodukzioa geldiarazi beste aplikazio batek soinuak erreproduzitzen dituenean</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Gelditu etenaldiak daudenean</string>
+ <string name="pref_resumeAfterCall_sum">Jarraitu erreprodukzioa dei baten ondoren</string>
+ <string name="pref_resumeAfterCall_title">Berrekin dei baten ondoren</string>
+ <string name="pref_restart_required">Beharrezkoa da AntennaPod berrabiaraztea aldaketak gauzatzeko.</string>
+ <!--Online feed view-->
+ <string name="subscribe_label">Eman izena</string>
+ <string name="subscribing_label">Harpidetzen...</string>
+ <!--Content descriptions for image buttons-->
+ <string name="rewind_label">Atzera egin</string>
+ <string name="fast_forward_label">Aurrera egin</string>
+ <string name="media_type_audio_label">Audioa</string>
+ <string name="media_type_video_label">Bideoa</string>
+ <string name="navigate_upwards_label">Nabigatu gorantz </string>
+ <string name="status_downloading_label">Saioa deskargatzen ari da</string>
+ <string name="in_queue_label">Saioa ilaran dago</string>
+ <string name="drag_handle_content_description">Arrastatu elementu honen posizioa aldatzeko</string>
+ <string name="load_next_page_label">Hurrengo orria kargatu</string>
+ <!--Feed information screen-->
+ <string name="authentication_label">Egiaztatzea</string>
+ <string name="authentication_descr">Aldatu podcast honen eta bere saioen erabiltzaile izena eta pasahitza.</string>
+ <string name="auto_download_settings_label">Deskargatu automatikoko aukerak</string>
+ <string name="episode_filters_label">Saioen iragazkia</string>
+ <string name="episode_filters_description">Saio bat deskarga automatikotik kanpo utzi behar den edo ez erabakitzeko erabilitako terminoen zerrenda</string>
+ <string name="episode_filters_include">Gehitu</string>
+ <string name="episode_filters_exclude">Baztertu</string>
+ <string name="episode_filters_hint">Hitz solteak \n\"hitz anizkunak\"</string>
+ <string name="keep_updated">Eguneratuta eduki</string>
+ <string name="keep_updated_summary">Iturri hori sartu iturri guztiak (auto-) freskatzen direnean</string>
+ <string name="auto_download_disabled_globally">Deskarga automatikoa desgaituta dago AntennaPod-en lehentasun orokorretan</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Datu basea eguneratzen</string>
+ <!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Berariazko erabilerako aplikazioen harpidetzak inportatuz …</string>
+ <!--Add podcast fragment-->
+ <string name="search_podcast_hint">Bilatu podcasta</string>
+ <string name="search_itunes_label">Bilatu itunes-en</string>
+ <string name="search_fyyd_label">Bilatu fyyd-en</string>
+ <string name="advanced_search">Bilaketa aaurreratua</string>
+ <string name="filter">Iragazia</string>
+ <!--Episodes apply actions-->
+ <string name="all_label">Denak</string>
+ <string name="selected_all_label">Aukeratutako saio denak</string>
+ <string name="none_label">Bat ere ez</string>
+ <string name="deselected_all_label">Ez dago aukeratutako saiorik</string>
+ <string name="played_label">Ikusita</string>
+ <string name="selected_played_label">Ikusitako saio denak</string>
+ <string name="unplayed_label">Ikusi gabe</string>
+ <string name="selected_unplayed_label">Ikusi gabeko saio aukeratuak</string>
+ <string name="downloaded_label">Deskargatuta</string>
+ <string name="selected_downloaded_label">Deskargatutako saio aukeratuak</string>
+ <string name="not_downloaded_label">Deskargatu gabe</string>
+ <string name="selected_not_downloaded_label">Deskargatu gabeko saio aukeratuak</string>
+ <string name="queued_label">Ilaran</string>
+ <string name="selected_queued_label">Ilarako saio aukeratuak</string>
+ <string name="not_queued_label">Ez dago ilaran</string>
+ <string name="selected_not_queued_label">Ilaran ez dauden saio aukeratuak</string>
+ <string name="has_media">Media du</string>
+ <string name="selected_has_media_label">Media duten saio aukeratuak</string>
+ <!--Sort-->
+ <string name="sort_title_a_z">Izenburua (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Izenburua (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Data (berria \u2192 zaharra)</string>
+ <string name="sort_date_old_new">Data (zaharra \u2192 berria)</string>
+ <string name="sort_duration_short_long">Iraupena (motza\u2192 luzea)</string>
+ <string name="sort_duration_long_short">Iraupena (luzea\u2192 motza)</string>
+ <!--Rating dialog-->
+ <string name="rating_title">Gustuko duzu AntennaPod?</string>
+ <string name="rating_message">Oso eskertuta egongo ginateke AntennaPod baloratzeko denbora pixka bat eskainiko bazenigu.</string>
+ <string name="rating_never_label">Bakean utz nazazu</string>
+ <string name="rating_later_label">Gogoratu geroago</string>
+ <string name="rating_now_label">Tira, egin dezagun!</string>
+ <!--Audio controls-->
+ <string name="audio_controls">Audio kontrolak</string>
+ <string name="playback_speed">Erreprodukzio abiadura</string>
+ <string name="volume">Bolumena</string>
+ <string name="left_short">Ezk</string>
+ <string name="right_short">Esk</string>
+ <string name="audio_effects">Audio efektuak</string>
+ <string name="stereo_to_mono">Nahastu: estereotik monora</string>
+ <string name="sonic_only">Sonic baino ez</string>
+ <string name="exoplayer_only">ExoPlayer baino ez</string>
+ <!--proxy settings-->
+ <string name="proxy_type_label">Mota</string>
+ <string name="host_label">Host</string>
+ <string name="port_label">Portua</string>
+ <string name="optional_hint">(Aukerako)</string>
+ <string name="proxy_test_label">Testa</string>
+ <string name="proxy_checking">Egiaztatzen...</string>
+ <string name="proxy_test_successful">Test arrakastatsua</string>
+ <string name="proxy_test_failed">Testak huts egin du</string>
+ <string name="proxy_host_empty_error">Host-ak ezin du hutsik egon</string>
+ <string name="proxy_host_invalid_error">Host-a ez da IP helbide edo domeinu baliodun bat</string>
+ <string name="proxy_port_invalid_error">Balio gabeko Portua</string>
+ <!--Subscriptions fragment-->
+ <string name="subscription_num_columns">Zutabe kopurua</string>
+ <!--Database import/export-->
+ <string name="import_export">Inportatu/esportatu datu basea</string>
+ <string name="import_export_warning">Funtzio esperimental hau bere harpidetzak eta erreproduzitutako saioak beste gailu batera transferitzeko erabil daiteke.\n\nEsportatutako datu-baseak soilik inportatu daitezke AntennaPod bertsio bera erabiltzen denean. Bestela, funtzio honek ustekabeko portaera bat eragin lezake.\n\nInportazioaren ondoren, saioak deskargatu gisa ager daitezke horrela ez daudenean. Sakatu saioak erreproduzitzeko botoia AntennaPodek antzeman dezan.</string>
+ <string name="label_import">Inportatu</string>
+ <string name="label_export">Esportatu</string>
+ <string name="import_select_file">Aukeratu inportatzeko fitxategia</string>
+ <string name="export_ok">Esportatzea ongi burutu da</string>
+ <string name="import_ok">Inportazio arrakastatsua.\n\nSakatu OK, AntennaPod berrabiarazteko</string>
+ <!--Casting-->
+ <string name="cast_media_route_menu_title">Ikusi hemen...</string>
+ <string name="cast_disconnect_label">Cast saioa deskonektatu</string>
+ <string name="cast_not_castable">Hautatutako bitartekoa ez da bateragarria cast gailuarekin</string>
+ <string name="cast_failed_to_play">Errorea medioaren erreprodukzioa hastean</string>
+ <string name="cast_failed_to_stop">Errorea medioaren erreprodukzioa uztean</string>
+ <string name="cast_failed_to_pause">Errorea medioaren erreprodukzioa gelditzean</string>
+ <!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <string name="cast_failed_setting_volume">Errorea bolumena doitzean</string>
+ <string name="cast_failed_no_connection">Ez dago inolako konexiorik cast gailuarekin</string>
+ <string name="cast_failed_no_connection_trans">Cast gailuarekin konexioa galdu da. Aplikazioa konexioa berrezartzen saiatzen ari da. Itxaron pixka batean eta saiatu berriro.</string>
+ <string name="cast_failed_status_request">Errorea cast gailuarekin sinkronizatzean</string>
+ <string name="cast_failed_seek">Errorea cast gailuan posizio berria bilatzean</string>
+ <string name="cast_failed_receiver_player_error">Erreproduzigailuak akats larria aurkitu du</string>
+ <string name="cast_failed_media_error_skipping">Errorea medioa erreproduzitzean. Jauzi egiten …</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Beharrezko ekintza</string>
+ <string name="notification_channel_user_action_description">Zure ekintza beharrezkoa den erakusten da, adibidez, pasahitz bat sartu behar duzun edo ez.</string>
+ <string name="notification_channel_downloading">Deskargatzen</string>
+ <string name="notification_channel_downloading_description">Deskargatu bitartean erakusten da.</string>
+ <string name="notification_channel_playing">Erreproduzitzen</string>
+ <string name="notification_channel_playing_description">Erreprodukzioa kontrolatzeko aukera ematen du. Podcast bat erreproduzitzen den bitartean ikusten den jakinarazpen nagusia da.</string>
+ <string name="notification_channel_error">Erroreak</string>
+ <string name="notification_channel_error_description">Zerbait gaizki atera den erakusten du, adibidez, gpodder deskargak edo sinkronizazioak huts egiten duen.</string>
+ <string name="import_bad_file">Fitxategi baliogabe/ustela</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">widget-a sortu</string>
+ <string name="widget_opacity">Opakutasuna</string>
+</resources>
diff --git a/core/src/main/res/values-fi/strings.xml b/core/src/main/res/values-fi/strings.xml
index 067761579..4a076866b 100644
--- a/core/src/main/res/values-fi/strings.xml
+++ b/core/src/main/res/values-fi/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synkronoi toisiin laitteisiin</string>
<string name="gpodnet_auth_label">gpodder.net Kirjautuminen</string>
- <string name="free_space_label">%1$s vapaa</string>
<string name="episode_cache_full_title">Jaksojen välimuisti täynnä</string>
<string name="episode_cache_full_message">Jaksojen välimuistin rajoitus on ylitetty. Voit lisätä välimuistin kokoa Asetuksissa.</string>
- <string name="synchronizing">Synkronoi...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Podcasteja soitettu yhteensä:</string>
<string name="statistics_details_dialog">%1$d jakso %2$d:sta aloitettu.\n\nSoitettu %3$s jaksoa %4$s:sta.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Tekijä(t)</string>
<string name="language_label">Kieli</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Asetukset</string>
<string name="cover_label">Kuva</string>
<string name="error_label">Virhe</string>
<string name="error_msg_prefix">Tapahtui virhe:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">Ulkoista tallennustilaa ei ole saatavilla. Varmista että ulkoinen tallennustila on aktivoitu, jotta sovellus voi toimia oikeellisesti.</string>
<string name="chapters_label">Kappaleet</string>
<string name="chapter_duration">Kesto: %1$s</string>
- <string name="shownotes_label">Jaksomuistio</string>
<string name="description_label">Kuvaus</string>
- <string name="most_recent_prefix">Uusin jakso:\u0020</string>
<string name="episodes_suffix">\u0020jaksoa</string>
- <string name="length_prefix">Kesto:\u0020</string>
- <string name="size_prefix">Koko:\u0020</string>
<string name="processing_label">Prosessoi</string>
- <string name="loading_label">Lataa...</string>
<string name="save_username_password_label">Tallenna käyttäjätunnus ja salasana</string>
<string name="close_label">Sulje</string>
<string name="retry_label">Yritä uudelleen</string>
@@ -108,8 +100,6 @@
<string name="feedurl_label">Syöte URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Lisää podcast URL</string>
- <string name="podcastdirectories_label">Etsi podcast hakemistosta</string>
- <string name="podcastdirectories_descr">Voit etsiä uusia podcasteja iTunes tai fyyd hakemistosta tai selata gpodder.net nimen, kategorian tai suosittujen mukaan.</string>
<string name="browse_gpoddernet_label">Selaa gpodder.net</string>
<string name="discover">Löydä</string>
<string name="discover_more">lisää »</string>
@@ -137,7 +127,6 @@
<string name="feed_delete_confirmation_msg">Varmista, että haluat poistaa podcastin \"%1$s\" ja KAIKKI sen jaksot (mukaanlukien ladatut jaksot).</string>
<string name="feed_remover_msg">Poistetaan podcast</string>
<string name="load_complete_feed">Päivitä podcast</string>
- <string name="hide_episodes_title">Piilota jaksoja</string>
<string name="batch_edit">Batch editoi</string>
<string name="select_all_above">Valitse kaikki yllä</string>
<string name="select_all_below">Valitse kaikki alla</string>
@@ -161,9 +150,7 @@
</plurals>
<string name="play_label">Soita</string>
<string name="pause_label">Pause</string>
- <string name="stop_label">Stop</string>
<string name="stream_label">Striimaa</string>
- <string name="remove_label">Poista</string>
<string name="delete_label">Deletoi</string>
<string name="delete_failed">Ei voida poistaa tiedostoa. Laitteen uudelleenkäynnistys saattaa auttaa.</string>
<string name="delete_episode_label">Deletoi jakso</string>
@@ -207,14 +194,12 @@
<string name="removed_item">Asia poistettu</string>
<!--Download messages and labels-->
<string name="download_successful">onnistui</string>
- <string name="download_failed">epäonnistui</string>
<string name="download_pending">Lataus odottamassa</string>
<string name="download_running">Lataamassa</string>
<string name="download_error_details">Tiedot</string>
<string name="download_error_details_message">%1$s \n\nTiedoston URL:\n%2$s</string>
<string name="download_error_device_not_found">Tallennuslaitetta ei löytynyt</string>
<string name="download_error_insufficient_space">Ei tarpeeksi tilaa</string>
- <string name="download_error_file_error">Tiedostovirhe</string>
<string name="download_error_http_data_error">HTTP Data virhe</string>
<string name="download_error_error_unknown">Odottamaton virhe</string>
<string name="download_error_parser_exception">Parserin virhe</string>
@@ -224,7 +209,6 @@
<string name="download_error_unauthorized">Autentikaatio virhe</string>
<string name="download_error_file_type_type">Tiedostotyyppivirhe</string>
<string name="download_error_forbidden">Ei sallittu</string>
- <string name="cancel_all_downloads_label">Peruuta kaikki lataukset</string>
<string name="download_canceled_msg">Lataus peruutettu</string>
<string name="download_canceled_autodownload_enabled_msg">Lataus peruutettu\nPoistettu <i>Automaattinen lataus</i> tälle tiedolle </string>
<string name="download_report_title">Lataukset valmistuivat virhe(ide)n kanssa</string>
@@ -243,7 +227,6 @@
<string name="download_log_title_unknown">Tuntematon otsikko</string>
<string name="download_type_feed">Syöte</string>
<string name="download_type_media">Mediatiedosto</string>
- <string name="download_type_image">Kuva</string>
<string name="download_request_error_dialog_message_prefix">Tiedoston latauksen aikana tapahtui virhe:\u0020</string>
<string name="null_value_podcast_error">Ei voida näyttää olematonta podcastia</string>
<string name="authentication_notification_title">Vaaditaan autentikaatio</string>
@@ -264,7 +247,6 @@
<string name="no_media_playing_label">Media ei soi</string>
<string name="player_buffering_msg">Bufferoi</string>
<string name="player_go_to_picture_in_picture">Kuva-kuvassa-tila</string>
- <string name="playbackservice_notification_title">Soittaa podcastia</string>
<string name="unknown_media_key">AntennaPod - Tuntematon media avain: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Lukitse jono</string>
@@ -273,7 +255,6 @@
<string name="queue_unlocked">Jono avattu</string>
<string name="clear_queue_label">Tyhjennä jono</string>
<string name="undo">Peruuta</string>
- <string name="removed_from_queue">Aihe poistettu</string>
<string name="move_to_top_label">Siirrä ylimmäiseksi</string>
<string name="move_to_bottom_label">Siirrä alimmaikseksi</string>
<string name="sort">Lajittele</string>
@@ -291,13 +272,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Lataa plugin</string>
<string name="no_playback_plugin_title">Plugin ei asennettu</string>
- <string name="no_playback_plugin_or_sonic_msg">Muuttuvan soittonopeuden toiminnassa suosittelemme sisäänrakennettua Sonic mediaplayer [Android 4.1+].\n\nVaihtoehtoisesti voit ladata kolmannen osapuolen pluginin <i>Prestissimo</i> Play Storesta.\nOngelmat Prestissimossa eivät ole AntennaPod vastuulla ja tulisi raportoida pluginin tekijöille.</string>
<string name="set_playback_speed_label">Soittonopeudet</string>
<string name="enable_sonic">Aloita Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Ei jaksoja jonossa</string>
<string name="no_items_label">Lisää jakso lataamalla ja painamalla pitkään jaksoa ja valitse \"Lisää jonoon\".</string>
- <string name="no_feeds_label">Et ole tilannut yhtään podcastia vielä.</string>
<string name="no_shownotes_label">Jaksolla ei ole jaksomuistiota</string>
<string name="no_run_downloads_head_label">Ei latauksia</string>
<string name="no_run_downloads_label">Voit ladata jakson podcastin lisätietosivulla.</string>
@@ -318,8 +297,6 @@
<!--Preferences-->
<string name="storage_pref">Tallennus</string>
<string name="project_pref">Projekti</string>
- <string name="other_pref">Muuta</string>
- <string name="about_pref">Tietoja</string>
<string name="queue_label">Jono</string>
<string name="integrations_label">Integraatiot</string>
<string name="automation">Automaatio</string>
@@ -359,18 +336,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Aseta ajankohta</string>
<string name="pref_autoUpdateIntervallOrTime_every">joka %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">aika %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Lataa mediatiedostot vain WiFin kautta</string>
<string name="pref_followQueue_title">Jatkuva soitto</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi medialataus</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Kuulokkeiden irrotus</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Kuulokkeiden uudelleenyhdistyminen</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth uudelleenyhdistyminen</string>
<string name="pref_mobileUpdate_title">Mobiilipäivitykset</string>
- <string name="refreshing_label">Päivittää</string>
<string name="user_interface_label">Käyttöliittymä</string>
<string name="pref_set_theme_title">Valitse teema</string>
- <string name="pref_nav_drawer_title">Kustomoi navogointihylly</string>
- <string name="pref_nav_drawer_sum">Kustomoi näkymä navigointihyllyssä</string>
<string name="pref_nav_drawer_items_title">Aseta navigointihyllyn asiat</string>
<string name="pref_nav_drawer_items_sum">Vaihda navigointihyllyssä esiintyviä asioita.</string>
<string name="pref_nav_drawer_feed_order_title">Aseta tilauksien järjestys</string>
@@ -382,8 +354,6 @@
<string name="pref_automatic_download_sum">Aseta jaksojen automaattinen lataus</string>
<string name="pref_autodl_wifi_filter_title">Aseta Wi-Fi filtteri</string>
<string name="pref_autodl_wifi_filter_sum">Salli automaatiset lataukset vain sallittujen Wi-Fi verkkojen kautta.</string>
- <string name="pref_autodl_allow_on_mobile_title">Lataa mobiiilidatayhteydellä</string>
- <string name="pref_autodl_allow_on_mobile_sum">Salli automaattinen lataus mobiilitdatayhteyden kautta</string>
<string name="pref_automatic_download_on_battery_title">Lataa kun ei ladata akkua</string>
<string name="pref_automatic_download_on_battery_sum">Salli automaattiset lataukset kun akku ei ole latautumassa</string>
<string name="pref_parallel_downloads_title">Yhtäaikaiset lataukset</string>
@@ -408,7 +378,6 @@
<string name="pref_gpodnet_full_sync_sum">Synkronoi kaikki tilaukset ja jaksojen tilastot gpodder.net</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Viimeisin synkronointiyritys: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synkronointi aloitettu</string>
- <string name="pref_gpodnet_full_sync_started">Täysi synkronointi aloitettu</string>
<string name="pref_gpodnet_login_status"><![CDATA[Kirjauduttu <i>%1$s</i> laitteella <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Näytä synkronointivirheet ilmoituksissa</string>
<string name="pref_gpodnet_notifications_sum">Tämä asetus ei vaikuta autentikointivirheisiin.</string>
@@ -435,21 +404,14 @@
<string name="pref_showDownloadReport_title">Näytä latausraportti</string>
<string name="pref_showDownloadReport_sum">Jos lataus epäonnistuu, generoi raportti joka näyttää virheen tiedot.</string>
<string name="pref_expand_notify_unsupport_toast">Android versiot ennen 4.1 eivät tue laajenettuja ilmoituksia.</string>
- <string name="pref_queueAddToFront_sum">Lisää uusia jaksoja jonon eteen.</string>
- <string name="pref_queueAddToFront_title">Lisää jonon eteen.</string>
<string name="pref_smart_mark_as_played_disabled">Poissa käytöstä</string>
<string name="pref_image_cache_size_title">Kuvan välimuistin koko</string>
<string name="pref_image_cache_size_sum">Tallennusmedian välimuistin koko kuville.</string>
- <string name="crash_report_title">Kaatumisen raportti</string>
- <string name="crash_report_sum">Lähetä uusin kaatumisraportti emaililla</string>
- <string name="send_email">Lähteä email</string>
<string name="experimental_pref">Kokeellinen</string>
<string name="pref_media_player_message">Valitse mediasovellus jolla soitetaan tiedostot</string>
<string name="pref_current_value">Nykyinen arvo: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Aseta verkkoproxy</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Tunnetut ongelmat</string>
<string name="pref_no_browser_found">Selainta ei löydetty</string>
<string name="pref_cast_title">Chromecast-tuki</string>
<string name="pref_cast_message_play_flavor">Aseta ulkoisten mediaohjelmien soitto Cast laitteilla (kuten Chromecast, Audio Speakers tai Android TV)</string>
@@ -475,6 +437,8 @@
<string name="back_button_go_to_page_title">Valitse sivu</string>
<string name="pref_delete_removes_from_queue_title">Deletointi poistaa jonosta</string>
<string name="pref_delete_removes_from_queue_sum">Poista jakso automaattisesti jonosta kun se deletoidaan.</string>
+ <!--About screen-->
+ <string name="about_pref">Tietoja</string>
<!--Search-->
<string name="search_hint">Etsi jaksoja</string>
<string name="found_in_shownotes_label">Löydetty jaksomuistiosta</string>
@@ -486,22 +450,16 @@
<string name="found_in_title_label">Löydetty otsikoista</string>
<string name="no_results_for_query">Ei löydetty tuloksia \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML tiedostot mahdollistavat podcastien siirron podcasterista toiseen.</string>
<string name="opml_import_option">Valinta %1$d</string>
<string name="opml_import_explanation_1">Valitse täydellinen tiedostopolku tietojärjestelmästä</string>
- <string name="opml_import_explanation_2">Käytä ulkopuolisia sovelluksia kuten Dropbox, Google Drive tai suosikki tiedostomanageria OPML tiedoston avaamiseen.</string>
<string name="opml_import_explanation_3">Monet sovellukset kuten Google Mail, Dropbox, Google Drive ja useimmat tiedostomanagerit <i>avaavat</i> OPML tiedostoja <i>kanssa</i> AntennaPodiin.</string>
- <string name="start_import_label">Aloita tuonti</string>
<string name="opml_import_label">OPML tuonti</string>
- <string name="opml_directory_error">VIRHE!</string>
<string name="reading_opml_label">Lukee OPML tiedostoa</string>
<string name="opml_reader_error">Virhe lukiessa OPML dokumenttia:</string>
<string name="opml_import_error_no_file">Ei tiedostoa valittuna!</string>
<string name="select_all_label">Valitse kaikki</string>
<string name="deselect_all_label">Poista valinta kaikista</string>
- <string name="select_options_label">Valitse...</string>
<string name="choose_file_from_filesystem">Tiedostojärjestelmästä</string>
- <string name="choose_file_from_external_application">Ulkoinen sovellus</string>
<string name="opml_export_label">OPML vienti</string>
<string name="html_export_label">HTML vienti</string>
<string name="exporting_label">Vienti...</string>
@@ -594,8 +552,6 @@
<string name="pref_restart_required">AntennaPod täytyy uudelleenkäynnistää tämän asetuksen voimaantuloon.</string>
<!--Online feed view-->
<string name="subscribe_label">Tilaa</string>
- <string name="subscribed_label">Tilattu</string>
- <string name="downloading_label">Lataa...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Kelaa takaisin</string>
<string name="fast_forward_label">Hyppää eteen</string>
@@ -703,7 +659,6 @@
<string name="cast_failed_setting_volume">Epäonnistui asettaa äänenvoimakkuus</string>
<string name="cast_failed_no_connection">Yhteyttä cast laitteeseen ei löydy</string>
<string name="cast_failed_no_connection_trans">Yhteys cast laitteeseen menetetty. Sovelllus yrittää muodostaa yhteyden, jos mahdollista. Odota muutama sekunti ja yritä uudelleen.</string>
- <string name="cast_failed_perform_action">Epäonnistui suorittaa toiminto</string>
<string name="cast_failed_status_request">Epäonnistui synkronoida cast laitteeseen</string>
<string name="cast_failed_seek">Epäonnistui löytää uusi osoite cast laitteelta</string>
<string name="cast_failed_receiver_player_error">Vastaanotinlaiteessa on vakava virhe</string>
@@ -718,4 +673,5 @@
<string name="notification_channel_error">Virheet</string>
<string name="notification_channel_error_description">Näytetään kun jotain meni vikaan, esimerkiksi jos lataus tai gpodder synkronointi epäonnistui.</string>
<string name="import_bad_file">Rikkinäinen/Korruptoitunut tiedosto</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml
index d5225a656..c90ea3c91 100644
--- a/core/src/main/res/values-fr/strings.xml
+++ b/core/src/main/res/values-fr/strings.xml
@@ -14,7 +14,7 @@
<string name="downloads_label">Téléchargements</string>
<string name="downloads_running_label">En cours</string>
<string name="downloads_completed_label">Terminé</string>
- <string name="downloads_log_label">Journal d\'activité</string>
+ <string name="downloads_log_label">Journal</string>
<string name="subscriptions_label">Abonnements</string>
<string name="subscriptions_list_label">Liste des abonnements</string>
<string name="cancel_download_label">Annuler les téléchargements</string>
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synchroniser avec d\'autres appareils</string>
<string name="gpodnet_auth_label">Identifiants gpodder.net</string>
- <string name="free_space_label">%1$s d\'espace libre</string>
<string name="episode_cache_full_title">L\'emplacement pour stocker les épisodes est plein</string>
<string name="episode_cache_full_message">Le nombre maximal d\'épisodes téléchargés a été atteint. Vous pouvez changer ce nombre dans les paramètres.</string>
- <string name="synchronizing">Synchronisation...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Temps d\'écoute total</string>
<string name="statistics_details_dialog">%1$d épisodes sur %2$d commencés.\n\nLu %3$s sur %4$s.</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">Calculer la durée réellement écoutée. Les épisodes écoutés deux fois comptent double alors que ceux simplement marqués lus ne sont pas comptés</string>
<string name="statistics_mode_count_all">Additionner tous les podcasts marqués comme lus</string>
<string name="statistics_speed_not_counted">Note : la vitesse de lecture n\'est jamais prise en compte.</string>
+ <string name="statistics_reset_data">Réinitialiser les statistiques </string>
+ <string name="statistics_reset_data_msg">L\'information des temps de lecture va être effacée. Etes-vous sûr de vouloir continuer ?</string>
<!--Main activity-->
<string name="drawer_open">Ouvrir le menu</string>
<string name="drawer_close">Fermer le menu</string>
@@ -63,7 +63,6 @@
<string name="author_label">Auteur(s)</string>
<string name="language_label">Langue</string>
<string name="url_label">Lien</string>
- <string name="podcast_settings_label">Préférences</string>
<string name="cover_label">Image</string>
<string name="error_label">Erreur</string>
<string name="error_msg_prefix">Une erreur a eu lieu :</string>
@@ -72,21 +71,16 @@
<string name="external_storage_error_msg">Aucun stockage externe n\'est disponible. Merci de connecter un volume de stockage externe pour que l\'application puisse fonctionner correctement.</string>
<string name="chapters_label">Chapitres</string>
<string name="chapter_duration">Durée : %1$s</string>
- <string name="shownotes_label">Notes d\'épisode</string>
<string name="description_label">Description</string>
- <string name="most_recent_prefix">Épisode le plus récent :\u0020</string>
<string name="episodes_suffix">\u0020épisodes</string>
- <string name="length_prefix">Durée :\u0020</string>
- <string name="size_prefix">Taille :\u0020</string>
<string name="processing_label">Traitement en cours</string>
- <string name="loading_label">Chargement...</string>
<string name="save_username_password_label">Sauvegarder votre identifiant et votre mot de passe</string>
<string name="close_label">Fermer</string>
<string name="retry_label">Réessayer</string>
<string name="auto_download_label">Télécharger automatiquement</string>
<string name="auto_download_apply_to_items_title">Appliquer aux épisodes précédents</string>
<string name="auto_download_apply_to_items_message">Le nouveau paramètre <i>Téléchargement Automatique</i> sera automatiquement appliqué sur chaque nouvel épisode.\nVoulez-vous faire de même avec les épisodes précédents ?</string>
- <string name="auto_delete_label">Suppression automatique de l\'épisode</string>
+ <string name="auto_delete_label">Suppression automatique des épisodes</string>
<string name="parallel_downloads_suffix">\u0020téléchargements parallèles</string>
<string name="feed_auto_download_global">Option par défaut</string>
<string name="feed_auto_download_always">Toujours</string>
@@ -108,8 +102,6 @@
<string name="feedurl_label">Lien du flux</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Ajouter un podcast à partir de son lien</string>
- <string name="podcastdirectories_label">Trouver un podcast</string>
- <string name="podcastdirectories_descr">Pour de nouveaux podcasts vous pouvez chercher iTunes ou fyyd ou parcourir gpodder.net par nom, catégorie ou popularité.</string>
<string name="browse_gpoddernet_label">Chercher sur gpodder.net</string>
<string name="discover">Découvrir</string>
<string name="discover_more">plus »</string>
@@ -131,13 +123,13 @@
<string name="share_link_label">Partager le lien</string>
<string name="share_link_with_position_label">Partager le lien avec la position</string>
<string name="share_file_label">Partager le fichier</string>
+ <string name="share_website_url_label">Partager le lien du site</string>
<string name="share_feed_url_label">Partager le lien du flux</string>
<string name="share_item_url_label">Partager le lien du fichier</string>
<string name="share_item_url_with_position_label">Partager le lien du fichier avec la position</string>
<string name="feed_delete_confirmation_msg">Confirmer que vous voulez supprimer le podcast \"%1$s\" et TOUS ses épisodes téléchargés.</string>
<string name="feed_remover_msg">Podcast en cours de suppression</string>
<string name="load_complete_feed">Mettre à jour tout le podcast</string>
- <string name="hide_episodes_title">Cacher épisodes</string>
<string name="batch_edit">Edition groupée</string>
<string name="select_all_above">Sélectionner avec ceux au-dessus</string>
<string name="select_all_below">Sélectionner avec ceux en-dessous</string>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Lire</string>
<string name="pause_label">Pause</string>
- <string name="stop_label">Stop</string>
- <string name="stream_label">Lire en ligne</string>
- <string name="remove_label">Supprimer</string>
+ <string name="stream_label">Streaming</string>
<string name="delete_label">Effacer</string>
<string name="delete_failed">Suppression du fichier impossible. Redémarrer pourrait aider.</string>
<string name="delete_episode_label">Suppression de l\'épisode</string>
@@ -207,14 +197,12 @@
<string name="removed_item">Élément retiré</string>
<!--Download messages and labels-->
<string name="download_successful">terminé</string>
- <string name="download_failed">échoué</string>
<string name="download_pending">Téléchargement en attente</string>
<string name="download_running">Téléchargement en cours</string>
<string name="download_error_details">Détails</string>
<string name="download_error_details_message">%1$s \n\nLien du fichier :\n%2$s</string>
<string name="download_error_device_not_found">Volume de stockage non trouvé</string>
<string name="download_error_insufficient_space">Espace insuffisant</string>
- <string name="download_error_file_error">Accès au fichier impossible</string>
<string name="download_error_http_data_error">Erreur de données HTTP</string>
<string name="download_error_error_unknown">Erreur inconnue</string>
<string name="download_error_parser_exception">Message d\'erreur</string>
@@ -224,7 +212,6 @@
<string name="download_error_unauthorized">Erreur d\'authentification</string>
<string name="download_error_file_type_type">Erreur format de fichier</string>
<string name="download_error_forbidden">Interdit</string>
- <string name="cancel_all_downloads_label">Annuler tous les téléchargements</string>
<string name="download_canceled_msg">Téléchargement annulé</string>
<string name="download_canceled_autodownload_enabled_msg">Téléchargement annulé\n <i>Téléchargement Automatique</i> désactivé pour cet élément</string>
<string name="download_report_title">Téléchargements terminés avec des erreurs</string>
@@ -243,7 +230,6 @@
<string name="download_log_title_unknown">Titre inconnu</string>
<string name="download_type_feed">Flux</string>
<string name="download_type_media">Fichier média</string>
- <string name="download_type_image">Image</string>
<string name="download_request_error_dialog_message_prefix">Une erreur s\'est produite durant le téléchargement du fichier :\u0020</string>
<string name="null_value_podcast_error">Aucun podcast correspondant.</string>
<string name="authentication_notification_title">Authentification requise</string>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">Aucune lecture</string>
<string name="player_buffering_msg">Mise en mémoire</string>
<string name="player_go_to_picture_in_picture">Mode Picture-in-Picture</string>
- <string name="playbackservice_notification_title">Lecture de podcast en cours</string>
<string name="unknown_media_key">AntennaPod - Touche média inconnue : %1$d</string>
<!--Queue operations-->
- <string name="lock_queue">Bloquer la liste de lecture</string>
- <string name="unlock_queue">Débloquer la liste de lecture</string>
+ <string name="lock_queue">Verrouiller la liste de lecture</string>
+ <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="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>
- <string name="removed_from_queue">Élément retiré</string>
<string name="move_to_top_label">Déplacer en haut de la liste</string>
<string name="move_to_bottom_label">Déplacer en bas de la liste</string>
<string name="sort">Trier</string>
@@ -297,13 +283,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Télécharger une extension</string>
<string name="no_playback_plugin_title">Extension non installée</string>
- <string name="no_playback_plugin_or_sonic_msg">Pour pouvoir changer la vitesse de lecture il est recommandé d\'activer le lecteur interne Sonic [Android 4.1+].\n\nVous pouvez également télécharger l\'extension <i>Prestissimo</i> depuis le Play Store.\nLes problème rencontrés avec Prestissimo ne sont pas de la responsabilité d\'AntennaPod et sont à communiquer au créateur de l\'extension.</string>
<string name="set_playback_speed_label">Vitesses de lecture</string>
<string name="enable_sonic">Activer Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Aucun épisode dans la liste de lecture</string>
<string name="no_items_label">Pour ajouter un épisode, téléchargez le ou faites une pression longue dessus et taper sur \"Ajouter à la liste de lecture\".</string>
- <string name="no_feeds_label">Vous n\'êtes encore abonné à aucun podcast.</string>
<string name="no_shownotes_label">Aucune notes pour cet épisode.</string>
<string name="no_run_downloads_head_label">Aucun téléchargement en cours</string>
<string name="no_run_downloads_label">Vous pouvez télécharger des épisodes à partir du détail d\'un podcast.</string>
@@ -321,11 +305,11 @@
<string name="no_fav_episodes_label">Vous pouvez marquer des épisodes comme favoris par une pression longue dessus.</string>
<string name="no_chapters_head_label">Pas de chapitres</string>
<string name="no_chapters_label">Cet épisode n\'a pas de chapitre.</string>
+ <string name="no_subscriptions_head_label">Aucun abonnement</string>
+ <string name="no_subscriptions_label">Pour vous abonner à un podcast appuyer sur le bouton +.</string>
<!--Preferences-->
<string name="storage_pref">Stockage</string>
<string name="project_pref">Projet</string>
- <string name="other_pref">Autres</string>
- <string name="about_pref">À propos</string>
<string name="queue_label">Liste de lecture</string>
<string name="integrations_label">Intégrations</string>
<string name="automation">Automatisation</string>
@@ -368,12 +352,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Heure</string>
<string name="pref_autoUpdateIntervallOrTime_every">toutes les %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">à %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Ne télécharger les épisodes que par Wi-Fi</string>
<string name="pref_followQueue_title">Lecture continue</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Téléchargement en Wi-Fi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Déconnexion des écouteurs ou du Bluetooth</string>
<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_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>
@@ -381,11 +365,8 @@
<string name="pref_mobileUpdate_auto_download">Téléchargement automatique</string>
<string name="pref_mobileUpdate_episode_download">Téléchargement d\'épisodes</string>
<string name="pref_mobileUpdate_streaming">Streaming</string>
- <string name="refreshing_label">Mise à jour en cours</string>
<string name="user_interface_label">Interface utilisateur</string>
<string name="pref_set_theme_title">Choisir un thème</string>
- <string name="pref_nav_drawer_title">Personnaliser le volet de navigation</string>
- <string name="pref_nav_drawer_sum">Personnaliser l\'apparence du volet de navigation</string>
<string name="pref_nav_drawer_items_title">Changer les éléments du volet de navigation</string>
<string name="pref_nav_drawer_items_sum">Choisir quels éléments apparaissent dans le volet de navigation</string>
<string name="pref_nav_drawer_feed_order_title">Définir l\'ordre des abonnements</string>
@@ -397,13 +378,16 @@
<string name="pref_automatic_download_sum">Configurer le téléchargement automatique des épisodes.</string>
<string name="pref_autodl_wifi_filter_title">Activer le filtre Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Autoriser le téléchargement automatique uniquement sur les réseaux Wi-Fi sélectionnés.</string>
- <string name="pref_autodl_allow_on_mobile_title">Télécharger avec la connexion mobile</string>
- <string name="pref_autodl_allow_on_mobile_sum">Autoriser le téléchargement automatique avec la connexion mobile</string>
+ <string name="autodl_wifi_filter_permission_title">Autorisation requise</string>
+ <string name="autodl_wifi_filter_permission_message">L\'autorisation de localisation est nécessaire pour filtrer le Wi-Fi. Appuyer pour accorder l\'autorisation.</string>
<string name="pref_automatic_download_on_battery_title">Télécharger lorsque l\'appareil n\'est pas en charge</string>
<string name="pref_automatic_download_on_battery_sum">Autoriser le téléchargement automatique quand l\'appareil n\'est pas en train de charger</string>
<string name="pref_parallel_downloads_title">Téléchargements simultanés</string>
<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_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>
<string name="pref_theme_title_trueblack">Noir (pour écran AMOLED)</string>
@@ -423,12 +407,12 @@
<string name="pref_gpodnet_full_sync_sum">Synchroniser tous les abonnements et tous les états des épisodes avec gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Dernière tentative de synchronisation : %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synchronisation démarrée</string>
- <string name="pref_gpodnet_full_sync_started">Synchronisation totale commencée</string>
<string name="pref_gpodnet_login_status"><![CDATA[Connecté comme <i>%1$s</i> avec l\'appareil <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Notification des erreurs de synchronisation</string>
<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_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>
@@ -440,7 +424,7 @@
<string name="pref_expandNotify_title">Priorité haute de notification</string>
<string name="pref_expandNotify_sum">Permet, généralement, d\'étendre la notification pour montrer les boutons de lecture</string>
<string name="pref_persistNotify_title">Boutons de lecture permanents</string>
- <string name="pref_persistNotify_sum">Garder les notifications et les boutons de lecture sur l\'écran de verouillage quand la lecture est en pause</string>
+ <string name="pref_persistNotify_sum">Garder les notifications et les boutons de lecture sur l\'écran de verrouillage quand la lecture est en pause</string>
<string name="pref_compact_notification_buttons_title">Définir les boutons de l\'écran de verrouillage</string>
<string name="pref_compact_notification_buttons_sum">Change les boutons de lecture sur l\'écran de verrouillage. Le bouton de lecture/pause est toujours affiché.</string>
<string name="pref_compact_notification_buttons_dialog_title">Choisir un maximum de %1$d éléments</string>
@@ -450,21 +434,25 @@
<string name="pref_showDownloadReport_title">Afficher le rapport de téléchargements</string>
<string name="pref_showDownloadReport_sum">Si les téléchargements échouent, générer un rapport détaillé des échecs.</string>
<string name="pref_expand_notify_unsupport_toast">Les versions d\'Android antérieures à 4.1 ne sont pas compatibles avec les notifications élargies</string>
- <string name="pref_queueAddToFront_sum">Ajouter les nouveaux épisodes au début de la liste de lecture</string>
- <string name="pref_queueAddToFront_title">Mettre au début de la liste de lecture</string>
+ <string name="pref_enqueue_location_title">Emplacement des épisodes téléchargés</string>
+ <string name="pref_enqueue_location_sum">Ajouter les épisodes : %1$s</string>
+ <string name="enqueue_location_back">en fin de liste</string>
+ <string name="enqueue_location_front">en début de liste</string>
+ <string name="enqueue_location_after_current">après l\'épisode courant</string>
<string name="pref_smart_mark_as_played_disabled">Désactivé</string>
<string name="pref_image_cache_size_title">Taille du cache de l\'image</string>
<string name="pref_image_cache_size_sum">Taille de l’espace de stockage temporaire des images.</string>
- <string name="crash_report_title">Rapport de crash</string>
- <string name="crash_report_sum">Envoyer le dernier rapport de crash par e-mail</string>
- <string name="send_email">Envoyer e-mail</string>
+ <string name="view_mailing_list">Liste de diffusion</string>
+ <string name="bug_report_title">Signaler un bug</string>
+ <string name="open_bug_tracker">Ouvrir le suivi des bugs</string>
+ <string name="copy_to_clipboard">Copier dans le presse-papier</string>
+ <string name="copied_to_clipboard">Copié dans le presse-papier</string>
<string name="experimental_pref">Expérimental</string>
<string name="pref_media_player_message">Choisir le lecteur à utiliser pour lire les fichiers</string>
<string name="pref_current_value">Valeur actuelle : %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Paramétrer un réseau proxy</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Problèmes connus</string>
+ <string name="pref_faq">Foire aux questions</string>
<string name="pref_no_browser_found">Aucun navigateur trouvé.</string>
<string name="pref_cast_title">Support Chromecast</string>
<string name="pref_cast_message_play_flavor">Activer la lecture à distance sur les appareils Cast (comme Chromecast, Audio Speaker ou Android TV)</string>
@@ -490,6 +478,16 @@
<string name="back_button_go_to_page_title">Sélectionner une page</string>
<string name="pref_delete_removes_from_queue_title">Supprimer retire de la liste de lecture</string>
<string name="pref_delete_removes_from_queue_sum">Retirer de la liste de lecture les épisodes quand ils sont supprimés.</string>
+ <!--About screen-->
+ <string name="about_pref">À propos</string>
+ <string name="antennapod_version">Version d\'AntennaPod</string>
+ <string name="developers">Développeurs</string>
+ <string name="developers_summary">Tout le monde peut améliorer AntennaPod</string>
+ <string name="translators">Traducteurs</string>
+ <string name="translators_summary">La traduction est faite par les utilisateurs d\'AntennaPod sur Transifex</string>
+ <string name="privacy_policy">Politique de confidentialité</string>
+ <string name="licenses">Licences</string>
+ <string name="licenses_summary">AntennaPod s\'appuie sur d\'autres logiciels</string>
<!--Search-->
<string name="search_hint">Chercher les épisodes</string>
<string name="found_in_shownotes_label">Trouvé dans les notes d\'épisodes</string>
@@ -501,22 +499,16 @@
<string name="found_in_title_label">Trouvé dans le titre</string>
<string name="no_results_for_query">Aucun résultat trouvé pour \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Les fichiers OPML vous permettent d\'exporter vos podcasts d\'un logiciel à un autre.</string>
<string name="opml_import_option">Option %1$d</string>
<string name="opml_import_explanation_1">Choisir un chemin de fichier spécifique dans le système de fichiers local</string>
- <string name="opml_import_explanation_2">Utiliser une application tierce comme Dropbox, Google Drive ou votre gestionnaire de fichier favori pour ouvrir un fichier OPML</string>
<string name="opml_import_explanation_3">De nombreuses applications comme Google Mail, Dropbox ou Google Drive et la plupart des gestionnaires de fichiers peuvent <i>ouvrir</i> les fichiers OPML <i>avec</i> AntennaPod.</string>
- <string name="start_import_label">Démarrer l\'importation</string>
<string name="opml_import_label">Import OPML</string>
- <string name="opml_directory_error">ERREUR !</string>
<string name="reading_opml_label">Lecture du fichier OPML en cours</string>
<string name="opml_reader_error">Une erreur s\'est produite pendant la lecture du fichier OPML :</string>
<string name="opml_import_error_no_file">Aucun fichier sélectionné !</string>
<string name="select_all_label">Tout choisir</string>
<string name="deselect_all_label">Ne rien choisir</string>
- <string name="select_options_label">Choisir...</string>
<string name="choose_file_from_filesystem">Depuis le système de fichier local</string>
- <string name="choose_file_from_external_application">Utiliser une application tierce</string>
<string name="opml_export_label">Export OPML</string>
<string name="html_export_label">Export HTML</string>
<string name="exporting_label">Export en cours...</string>
@@ -609,8 +601,7 @@
<string name="pref_restart_required">AntennaPod doit être redémarré afin que ce changement prenne effet</string>
<!--Online feed view-->
<string name="subscribe_label">S\'abonner</string>
- <string name="subscribed_label">Abonné</string>
- <string name="downloading_label">En cours de téléchargement...</string>
+ <string name="subscribing_label">Abonnement en cours...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Saut arrière</string>
<string name="fast_forward_label">Saut avant</string>
@@ -623,7 +614,7 @@
<string name="load_next_page_label">Charger la page suivante</string>
<!--Feed information screen-->
<string name="authentication_label">Authentification</string>
- <string name="authentication_descr">Modifier votre identifiant et mot de passe pour ce podcast et tous ses épisodes</string>
+ <string name="authentication_descr">Identifiant et mot de passe pour ce podcast et ses épisodes.</string>
<string name="auto_download_settings_label">Préférence de téléchargement automatique</string>
<string name="episode_filters_label">Filtre d\'épisode</string>
<string name="episode_filters_description">Liste de mots permettant de décider si un épisode est à inclure ou exclure des téléchargements automatiques</string>
@@ -657,7 +648,7 @@
<string name="not_downloaded_label">Non téléchargés</string>
<string name="selected_not_downloaded_label">Épisodes non téléchargés sélectionnés</string>
<string name="queued_label">Dans la liste de lecture</string>
- <string name="selected_queued_label">Episodes présents dans la liste de lecture sélectionnés</string>
+ <string name="selected_queued_label">Épisodes présents dans la liste de lecture sélectionnés</string>
<string name="not_queued_label">Pas dans la liste de lecture</string>
<string name="selected_not_queued_label">Episodes absents de la liste de lecture sélectionnés</string>
<string name="has_media">Avec média</string>
@@ -718,7 +709,6 @@
<string name="cast_failed_setting_volume">Échec de réglage du volume</string>
<string name="cast_failed_no_connection">Aucune connexion à l\'appareil Cast n\'existe</string>
<string name="cast_failed_no_connection_trans">La connexion à l\'appareil cast a été perdu. L\'application tente de rétablir la connexion. Veuillez patienter quelques secondes et réessayer.</string>
- <string name="cast_failed_perform_action">Échec de l\'action</string>
<string name="cast_failed_status_request">Échec de la synchronisation avec l\'appareil cast</string>
<string name="cast_failed_seek">Échec de la recherche de la nouvelle position sur l\'appareil cast</string>
<string name="cast_failed_receiver_player_error">Le lecteur de réception a rencontré une grave erreur</string>
@@ -733,4 +723,7 @@
<string name="notification_channel_error">Erreurs</string>
<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_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 d3050075e..411d79430 100644
--- a/core/src/main/res/values-gl-rES/strings.xml
+++ b/core/src/main/res/values-gl-rES/strings.xml
@@ -4,7 +4,7 @@
<string name="feed_update_receiver_name">Actualizar subscricións</string>
<string name="feeds_label">Fontes</string>
<string name="statistics_label">Estatísticas</string>
- <string name="add_feed_label">Engadir podcast</string>
+ <string name="add_feed_label">Engadir Podcast</string>
<string name="episodes_label">Episodios</string>
<string name="all_episodes_short_label">Todo</string>
<string name="new_episodes_label">Novo</string>
@@ -13,7 +13,7 @@
<string name="settings_label">Axustes</string>
<string name="downloads_label">Descargas</string>
<string name="downloads_running_label">Activas</string>
- <string name="downloads_completed_label">Completado</string>
+ <string name="downloads_completed_label">Completadas</string>
<string name="downloads_log_label">Rexistro</string>
<string name="subscriptions_label">Subscricións</string>
<string name="subscriptions_list_label">Lista de subscricións</string>
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronizar con outros dispositivos</string>
<string name="gpodnet_auth_label">gpodder.net Conexión</string>
- <string name="free_space_label">%1$s libre</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="synchronizing">Sincronizando...</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>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">Calcular a duración que foi realmente reproducida. Reproducir dúas veces conta dobre, mentres que marcar como lido non conta</string>
<string name="statistics_mode_count_all">Sumar todos os podcast marcados como reproducidos</string>
<string name="statistics_speed_not_counted">Aviso: A velocidade de reprodución non se ten en conta en ningún caso.</string>
+ <string name="statistics_reset_data">Restablecer estatísticas</string>
+ <string name="statistics_reset_data_msg">Esto borrará o histórico de escoita de todos os episodios. Seguro que queres borralo?</string>
<!--Main activity-->
<string name="drawer_open">Abrir menú</string>
<string name="drawer_close">Pechar menú</string>
@@ -63,7 +63,6 @@
<string name="author_label">Autor(es)</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Axustes</string>
<string name="cover_label">Imaxe</string>
<string name="error_label">Fallo</string>
<string name="error_msg_prefix">Produciuse un fallo:</string>
@@ -72,14 +71,9 @@
<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="chapters_label">Capítulos</string>
<string name="chapter_duration">Duración: %1$s</string>
- <string name="shownotes_label">Notas do episodio</string>
<string name="description_label">Descrición</string>
- <string name="most_recent_prefix">Episodio máis recente:\u0020</string>
<string name="episodes_suffix">\u0020episodios</string>
- <string name="length_prefix">Duración:\u0020</string>
- <string name="size_prefix">Tamaño:\u0020</string>
<string name="processing_label">Procesando</string>
- <string name="loading_label">Cargando...</string>
<string name="save_username_password_label">Gardar nome de usuario e contrasinal</string>
<string name="close_label">Pechar</string>
<string name="retry_label">Reintentar</string>
@@ -108,8 +102,6 @@
<string name="feedurl_label">URL da fonte</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Engadir Podcast por URL</string>
- <string name="podcastdirectories_label">Buscar Podcast nun directorio</string>
- <string name="podcastdirectories_descr">Para novos podcasts, pode buscar en iTunes ou fyyd, ou tamén en gpodder.net por nome, categoría ou popularidade.</string>
<string name="browse_gpoddernet_label">Buscar en gpodder.net</string>
<string name="discover">Descubrir</string>
<string name="discover_more">máis »</string>
@@ -118,9 +110,9 @@
<string name="mark_all_read_msg">Marcáronse todos como reproducidos</string>
<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">Eliminar todas as \"novas\" marcas</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 eliminar a marca de \"novo\" de todos os episodios.</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="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>
@@ -131,13 +123,13 @@
<string name="share_link_label">Compartir a URL do episodio</string>
<string name="share_link_with_position_label">Compartir a URL do ficheiro do episodio coa posición</string>
<string name="share_file_label">Compartir ficheiro</string>
+ <string name="share_website_url_label">Compartir URL da web</string>
<string name="share_feed_url_label">Compartir URL da fonte</string>
<string name="share_item_url_label">Compartir a URL do ficheiro multimedia</string>
<string name="share_item_url_with_position_label">Compartir a URL do ficheiro multimedia coa posición</string>
<string name="feed_delete_confirmation_msg">Por favor, confirme que quere eliminar o podcast \"%1$s\" e TODOS os seus episodios (incluídos os xa descargados).</string>
<string name="feed_remover_msg">Eliminando o podcast</string>
<string name="load_complete_feed">Actualizar o podcast completo</string>
- <string name="hide_episodes_title">Ocultar episodios</string>
<string name="batch_edit">Edición por lote</string>
<string name="select_all_above">Seleccionar todo arriba</string>
<string name="select_all_below">Seleccionar todo debaixo</string>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Reproducir</string>
<string name="pause_label">Pausar</string>
- <string name="stop_label">Parar</string>
- <string name="stream_label">Enviar</string>
- <string name="remove_label">Eliminar</string>
+ <string name="stream_label">Difundir</string>
<string name="delete_label">Borrar</string>
<string name="delete_failed">Non se puido eliminar o ficheiro. Reiniciar o dispositivo podería axudar.</string>
<string name="delete_episode_label">Eliminar episodio</string>
@@ -207,14 +197,12 @@
<string name="removed_item">Elmento eleminado</string>
<!--Download messages and labels-->
<string name="download_successful">exitoso</string>
- <string name="download_failed">fallou</string>
<string name="download_pending">Descarga pendente</string>
<string name="download_running">Descarga en proceso</string>
<string name="download_error_details">Detalles</string>
<string name="download_error_details_message">%1$s\n\nURL do ficheiro:\n %2$s </string>
<string name="download_error_device_not_found">Non se atopou dispositivo de almacenamento</string>
<string name="download_error_insufficient_space">Non hai suficiente espacio</string>
- <string name="download_error_file_error">Fallo de ficheiro</string>
<string name="download_error_http_data_error">Fallo de datos HTTP</string>
<string name="download_error_error_unknown">Fallo descoñecido</string>
<string name="download_error_parser_exception">Excepción no procesador</string>
@@ -224,7 +212,6 @@
<string name="download_error_unauthorized">Fallo na autenticación</string>
<string name="download_error_file_type_type">Fallo no tipo de ficheiro</string>
<string name="download_error_forbidden">Non admitido</string>
- <string name="cancel_all_downloads_label">Cancelar todas as descargas</string>
<string name="download_canceled_msg">Descarga cancelada</string>
<string name="download_canceled_autodownload_enabled_msg">Descarga cancelada\nDesactivouse a <i>Descarga Automática</i> para este elemento</string>
<string name="download_report_title">Descargas completadas con erro(s)</string>
@@ -243,7 +230,6 @@
<string name="download_log_title_unknown">Título descoñecido</string>
<string name="download_type_feed">Fonte</string>
<string name="download_type_media">Ficheiro de medios</string>
- <string name="download_type_image">Imaxe</string>
<string name="download_request_error_dialog_message_prefix">Houbo un fallo intentando descargar o ficheiro:\u0020</string>
<string name="null_value_podcast_error">Non se proporcionou ningún podcast que se poida mostrar.</string>
<string name="authentication_notification_title">Precísase autenticación</string>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">Non reproducindo</string>
<string name="player_buffering_msg">Almacenando</string>
<string name="player_go_to_picture_in_picture">Modo imaxe-en-imaxe</string>
- <string name="playbackservice_notification_title">Reproducindo podcast</string>
<string name="unknown_media_key">AntennaPod - chave de medios descoñecida: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Bloquear a cola</string>
<string name="unlock_queue">Desbloquear a cola</string>
<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="clear_queue_label">Limpar cola</string>
<string name="undo">Desfacer</string>
- <string name="removed_from_queue">Elemento eliminado</string>
<string name="move_to_top_label">Mover arriba</string>
<string name="move_to_bottom_label">Mover abaixo</string>
<string name="sort">Ordenar</string>
@@ -297,13 +283,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Descargar engadido</string>
<string name="no_playback_plugin_title">Engadido non instalado</string>
- <string name="no_playback_plugin_or_sonic_msg">Para que funcione a velocidade de reprodución variable recomendamos utilizar o reproductor Sonic incluído[Android 4.1+].\n\nAlternativamente tamén pode descargar o engadido de terceiros de nome <i>Prestissimo</i>desde a Play Store.\nCalquer problema con Prestissimo non é responsabilidade de AntennaPod e debería informar ao responsable do engadido.</string>
<string name="set_playback_speed_label">Velocidade de reproducións</string>
<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_feeds_label">Aínda non está subscrito a ningún podcast.</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>
@@ -321,11 +305,11 @@
<string name="no_fav_episodes_label">Pode engadir episodios aos favoritos manténdoos pulsados.</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>
+ <string name="no_subscriptions_label">Para subscribirte a un podcast, preme o botón máis, abaixo.</string>
<!--Preferences-->
<string name="storage_pref">Almacenamento</string>
<string name="project_pref">Proxecto</string>
- <string name="other_pref">Outro</string>
- <string name="about_pref">Sobre</string>
<string name="queue_label">Cola</string>
<string name="integrations_label">Integracións</string>
<string name="automation">Automatizado</string>
@@ -368,12 +352,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Establecer hora do día</string>
<string name="pref_autoUpdateIntervallOrTime_every">cada %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">as %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Descargar ficheiros de medios so cando rede WiFi</string>
<string name="pref_followQueue_title">Reprodución continua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Descarga de medios WiFi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Desconexión de auriculares</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Reconexión de auriculares</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Reconexión bluetooth</string>
+ <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_refresh">Actualizar fontes</string>
@@ -381,11 +365,8 @@
<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="refreshing_label">Actualizando</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_title">Personalice a Caixa de navegación</string>
- <string name="pref_nav_drawer_sum">Personalice o aspecto da caixa de navegación</string>
<string name="pref_nav_drawer_items_title">Estableza 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>
@@ -397,13 +378,16 @@
<string name="pref_automatic_download_sum">Axuste a descarga automática de episodios.</string>
<string name="pref_autodl_wifi_filter_title">Habilitar o filtro WiFi</string>
<string name="pref_autodl_wifi_filter_sum">Permitir a descarga automática só en redes WiFi selecionadas.</string>
- <string name="pref_autodl_allow_on_mobile_title">Descargar baixo conexión móbil</string>
- <string name="pref_autodl_allow_on_mobile_sum">Permitir a descarga automática estando conectado a rede de datos móbil.</string>
+ <string name="autodl_wifi_filter_permission_title">Precísase permiso</string>
+ <string name="autodl_wifi_filter_permission_message">Precísase o permiso de localización para o filtro Wi-Fi. Toca para concedelo.</string>
<string name="pref_automatic_download_on_battery_title">Descargar elementos cando non esté cargando</string>
<string name="pref_automatic_download_on_battery_sum">Permitir a descarga automática cando a batería non está a cargar</string>
<string name="pref_parallel_downloads_title">Descargas simultáneas</string>
<string name="pref_episode_cache_title">Caché de episodios</string>
<string name="pref_episode_cache_summary">O número total de episodios descargados na caché do dispositivo. A descarga automática suspenderase se se alcanza este número.</string>
+ <string name="pref_episode_cover_title">Utilizar Portada do episodio</string>
+ <string name="pref_episode_cover_summary">Utilizar a portada específica do episodio cando sexa posible. Se non o marcas, a app mostrará sempre a portada do podcast.</string>
+ <string name="pref_theme_title_use_system">Utilizar o decorado do sistema</string>
<string name="pref_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Oscuro</string>
<string name="pref_theme_title_trueblack">Negro (listo para AMOLED)</string>
@@ -423,12 +407,12 @@
<string name="pref_gpodnet_full_sync_sum">Sincronizar todas as subscricións e estados de episodios con gpodder.net</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Último intento de sincronización: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronización iniciada</string>
- <string name="pref_gpodnet_full_sync_started">Sincronización completa iniciada</string>
<string name="pref_gpodnet_login_status"><![CDATA[Conectada como <i>%1$s</i> co dispositivo <i>%2$s</i>]]></string>
<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_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>
@@ -450,21 +434,24 @@
<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_queueAddToFront_sum">Engadir os novos episodios ao inicio da cola.</string>
- <string name="pref_queueAddToFront_title">Por no inicio da 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>
+ <string name="enqueue_location_after_current">Tras o episodio actual</string>
<string name="pref_smart_mark_as_played_disabled">Desactivado</string>
<string name="pref_image_cache_size_title">Tamaño da caché de imaxes</string>
<string name="pref_image_cache_size_sum">Tamaño da caché en disco para as imaxes.</string>
- <string name="crash_report_title">Informe de Desgracias</string>
- <string name="crash_report_sum">Enviar por correo-e o informe de fallo xeral na aplicación</string>
- <string name="send_email">Enviar email</string>
+ <string name="view_mailing_list">Ver lista de correo</string>
+ <string name="bug_report_title">Informar de fallo</string>
+ <string name="open_bug_tracker">Abrir seguimento de fallos</string>
+ <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_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>
- <string name="pref_faq">PMF</string>
- <string name="pref_known_issues">Problemas coñecidos</string>
+ <string name="pref_faq">Preguntas Máis Frecuentes</string>
<string name="pref_no_browser_found">Non se atopou un navegador web</string>
<string name="pref_cast_title">Soporte Chromecast</string>
<string name="pref_cast_message_play_flavor">Habilitar o soporte de reprodución remota nun dispositivo Cast (como o Chromecast, Altofalantes ou Android TV)</string>
@@ -490,6 +477,16 @@
<string name="back_button_go_to_page_title">Seleccionar páxina</string>
<string name="pref_delete_removes_from_queue_title">Eliminar quita da Cola</string>
<string name="pref_delete_removes_from_queue_sum">Eliminar automáticamente un episodio da cola cando se elimina.</string>
+ <!--About screen-->
+ <string name="about_pref">Sobre</string>
+ <string name="antennapod_version">Versión AntennaPod</string>
+ <string name="developers">Desenvolvedoras</string>
+ <string name="developers_summary">Todas podemos axudar a mellorar AntennaPod</string>
+ <string name="translators">Tradutores</string>
+ <string name="translators_summary">As traducións de AntennaPod realízanse utilizando Transifex</string>
+ <string name="privacy_policy">Política de privacidade</string>
+ <string name="licenses">Licenzas</string>
+ <string name="licenses_summary">AntennaPod toma como fonte outro software</string>
<!--Search-->
<string name="search_hint">Buscar episodios</string>
<string name="found_in_shownotes_label">Atopado nas notas do episodio</string>
@@ -501,22 +498,16 @@
<string name="found_in_title_label">Atopado no título</string>
<string name="no_results_for_query">Non hai resultados para \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Os ficheiros OPML permítenlle mover os seus podcast de un xestor a outro.</string>
<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_2">Utilice unha aplicación externa como Dropbox, Google Drive ou o seu xestor de ficheiros para abrir un ficheiro OPML.</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="start_import_label">Iniciar a importación</string>
<string name="opml_import_label">Importar OPML</string>
- <string name="opml_directory_error">FALLO!</string>
<string name="reading_opml_label">Lendo ficheiro OPML</string>
<string name="opml_reader_error">Houbo un fallo ao ler o documento OPML:</string>
<string name="opml_import_error_no_file">Non hai ficheiro seleccionado!</string>
<string name="select_all_label">Seleccionar todo</string>
<string name="deselect_all_label">Deseleccionar todo</string>
- <string name="select_options_label">Seleccionar...</string>
<string name="choose_file_from_filesystem">Desde sistema local de ficheiros</string>
- <string name="choose_file_from_external_application">Utilizar aplicacións externa</string>
<string name="opml_export_label">Exportar OPML</string>
<string name="html_export_label">Exportar HTML</string>
<string name="exporting_label">Exportando...</string>
@@ -609,8 +600,7 @@
<string name="pref_restart_required">AntennaPod debe reiniciarse para que esta opción se aplique.</string>
<!--Online feed view-->
<string name="subscribe_label">Subscribir</string>
- <string name="subscribed_label">Subscrito</string>
- <string name="downloading_label">Descargando...</string>
+ <string name="subscribing_label">Subscribindo...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Rebobinar</string>
<string name="fast_forward_label">Avance rápido</string>
@@ -718,7 +708,6 @@
<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_perform_action">Non se puido completar a acción</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>
@@ -733,4 +722,7 @@
<string name="notification_channel_error">Fallos</string>
<string name="notification_channel_error_description">Mostrado si algo falla, por exemplo si a descarga ou a sincronización con gpodder fallan.</string>
<string name="import_bad_file">Ficheiro non válido/corrupto</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">Crear widget</string>
+ <string name="widget_opacity">Opacidade</string>
</resources>
diff --git a/core/src/main/res/values-hi-rIN/strings.xml b/core/src/main/res/values-hi-rIN/strings.xml
index c36f4662d..82eef3082 100644
--- a/core/src/main/res/values-hi-rIN/strings.xml
+++ b/core/src/main/res/values-hi-rIN/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">अन्य उपकरण के साथ समक्रमिक करें</string>
<string name="gpodnet_auth_label">gpodder.net लॉगिन</string>
- <string name="free_space_label">%1$s खाली</string>
<string name="episode_cache_full_title">एपिसोड कैश भर गया है</string>
<string name="episode_cache_full_message">एपिसोड कैश सीमा तक पहुंच गया है। आप सेटिंग में कैश का आकार बढ़ा सकते हैं।</string>
- <string name="synchronizing">समक्रमिक हो रहा है...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">चलाये गए पॉडकास्ट का कुल समय:</string>
<string name="statistics_details_dialog">%2$d में से %1$d एपिसोड शुरू किए गए है.\n\n%4$s में से %3$sपूरे हुए है। </string>
@@ -61,7 +59,6 @@
<string name="author_label">कलाकार</string>
<string name="language_label">भाषा</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">सेटिंग्स</string>
<string name="cover_label">तस्वीर</string>
<string name="error_label">त्रुटि</string>
<string name="error_msg_prefix">एक त्रुटि हो गई:</string>
@@ -70,14 +67,9 @@
<string name="external_storage_error_msg">कोई बाहरी भंडारण उपलब्ध नहीं है.सुनिश्चित करें कि आपने बाहरी भंडारण मुहिम शुरू की है ताकि अनुप्रयोग ठीक से काम कर सकते हैं</string>
<string name="chapters_label">अध्याय</string>
<string name="chapter_duration">अवधि: %1$s</string>
- <string name="shownotes_label">नोट्स दिखाएँ</string>
<string name="description_label">विवरण</string>
- <string name="most_recent_prefix">सबसे हाल का प्रकरण:\u0020</string>
<string name="episodes_suffix">\u0020एपिसोड</string>
- <string name="length_prefix">लंबाई:\u0020</string>
- <string name="size_prefix">साइज:\u0020</string>
<string name="processing_label">प्रसंस्करण</string>
- <string name="loading_label">लोड हो रहा है...</string>
<string name="save_username_password_label">यूज़रनेम और पासवर्ड सहेजें</string>
<string name="close_label">बंद करें</string>
<string name="retry_label">पुन: प्रयास</string>
@@ -101,8 +93,6 @@
<string name="feedurl_label">यूआरएल फ़ीड</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">यूआरएल द्वारा पॉडकास्ट जोड़ें</string>
- <string name="podcastdirectories_label">पॉडकास्ट निर्देशिका</string>
- <string name="podcastdirectories_descr">नए पॉडकास्ट के लिए, आप iTunes या fyyd खोज सकते हैं, या gpodder.net को नाम, श्रेणी या लोकप्रियता द्वारा ब्राउज़ कर सकते हैं।</string>
<string name="browse_gpoddernet_label">gpodder.net ब्राउज़ करें</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">पढ़ने के रूप में सभी को चिह्नित करें</string>
@@ -121,7 +111,6 @@
<string name="share_item_url_with_position_label"> स्थान के साथ मीडिया फाइल शेयर करे</string>
<string name="feed_remover_msg">पॉडकास्ट हटाया जारहा है</string>
<string name="load_complete_feed">सारे पॉडकास्ट को रिफ्रेश करें</string>
- <string name="hide_episodes_title">एपिसोड छुपाए</string>
<string name="batch_edit">जत्था संपादित करें</string>
<string name="hide_unplayed_episodes_label">नहीं चलाए गए</string>
<string name="hide_paused_episodes_label">रोके गए</string>
@@ -139,9 +128,7 @@
<string name="download_label">डाउनलोड</string>
<string name="play_label">प्ले</string>
<string name="pause_label">रोकें</string>
- <string name="stop_label">रोकें</string>
<string name="stream_label">स्ट्रिम</string>
- <string name="remove_label"> हटाएँ</string>
<string name="delete_label">डिलीट</string>
<string name="delete_failed">फ़ाइल डिलीट करने में असमर्थ। डिवाइस को रिबूट करने से मदद मिल सकती है।</string>
<string name="mark_read_label">चलाया गया के रूप में चिह्नित करें</string>
@@ -162,14 +149,12 @@
<string name="removed_item">आइटम हटया गया</string>
<!--Download messages and labels-->
<string name="download_successful">सफल हुआ</string>
- <string name="download_failed">असफल हुआ</string>
<string name="download_pending">लंबित डाउनलोड</string>
<string name="download_running">डाउनलोड चल रहा है</string>
<string name="download_error_details">विवरण</string>
<string name="download_error_details_message">%1$s \n\nफाइल URL:\n%2$s</string>
<string name="download_error_device_not_found">स्टोरेज डिवाइस नहीं मिला</string>
<string name="download_error_insufficient_space">अपर्याप्त स्थान</string>
- <string name="download_error_file_error">फ़ाइल त्रुटि</string>
<string name="download_error_http_data_error">एचटीटीपी डेटा त्रुटि</string>
<string name="download_error_error_unknown">अज्ञात त्रुटि</string>
<string name="download_error_parser_exception">पार्सर अपवाद</string>
@@ -179,7 +164,6 @@
<string name="download_error_unauthorized">प्रमाणीकरण त्रुटि</string>
<string name="download_error_file_type_type">फ़ाइल प्रकार त्रुटि</string>
<string name="download_error_forbidden">निषिद्ध</string>
- <string name="cancel_all_downloads_label">सभी डाउनलोड रद्द करें</string>
<string name="download_canceled_msg">डाउनलोड रद्द</string>
<string name="download_canceled_autodownload_enabled_msg">डाउनलोड रद्द हुआ\nइस आइटम के लिए <i>ऑटडाउनलोड</i> अक्षम किया गया</string>
<string name="download_report_title">त्रुटि के साथ डाउनलोड संपन्न हुए</string>
@@ -198,7 +182,6 @@
<string name="download_log_title_unknown">अज्ञात शीर्षक</string>
<string name="download_type_feed"> फ़ीड</string>
<string name="download_type_media">मीडिया फ़ाइल</string>
- <string name="download_type_image">छवि</string>
<string name="download_request_error_dialog_message_prefix">फाइल डाउनलोड करने के लिए प्रयास करते समय एक त्रुटि हुई:\u0020</string>
<string name="authentication_notification_title">प्रमाणीकरण आवश्यक है</string>
<string name="authentication_notification_msg">आपके द्वारा अनुरोधित संसाधन के लिए यूजरनाम यूजरनेम और पासवर्ड की आवश्यकता है</string>
@@ -215,12 +198,10 @@
<string name="playback_error_unknown">अज्ञात त्रुटि</string>
<string name="no_media_playing_label">मीडिया नहीं चल रहा</string>
<string name="player_buffering_msg">बफरिंग</string>
- <string name="playbackservice_notification_title">प्लेईंग पॉडकास्ट</string>
<string name="unknown_media_key">एन्टेनापॉड - अनजान मीडिया की: %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">कतार साफ</string>
<string name="undo">पूर्ववत् करें</string>
- <string name="removed_from_queue">आइटम हटाया</string>
<string name="move_to_top_label">शीर्ष पर ले जाएं</string>
<string name="move_to_bottom_label">नीचे जाएं</string>
<string name="date">दिनांक</string>
@@ -238,14 +219,11 @@
<string name="set_playback_speed_label">प्लेबैक गति</string>
<string name="enable_sonic">सॉनिक सक्षम करे</string>
<!--Empty list labels-->
- <string name="no_feeds_label">आपने अभी तक किसी भी पॉडकास्ट को सब्सक्राइब नहीं किया है।</string>
<string name="no_shownotes_label">इस एपिसोड में कोई शोनोट्स नहीं है।</string>
<string name="no_chapters_label">इस एपिसोड में कोई अध्याय नहीं है।</string>
<!--Preferences-->
<string name="storage_pref">स्टोरेज</string>
<string name="project_pref">परियोजना</string>
- <string name="other_pref">अन्य</string>
- <string name="about_pref">के बारे में</string>
<string name="queue_label">पंक्ति</string>
<string name="automation">स्वचालन</string>
<string name="download_pref_details">विवरण</string>
@@ -262,12 +240,9 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">दिन का समय निर्धारित करें</string>
<string name="pref_autoUpdateIntervallOrTime_every">प्रति %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">%1$s बजे </string>
- <string name="pref_downloadMediaOnWifiOnly_sum">केवल वाईफ़ाई पर मीडिया फ़ाइलें डाउनलोड करें</string>
<string name="pref_followQueue_title">सतत प्लेबैक</string>
- <string name="pref_downloadMediaOnWifiOnly_title">वाईफाई मीडिया डाउनलोड करें</string>
<string name="pref_pauseOnHeadsetDisconnect_title">headphones काटना</string>
<string name="pref_mobileUpdate_title">मोबाइल अपडेट</string>
- <string name="refreshing_label">रिफ्रेशिंग</string>
<string name="user_interface_label">यूजर इंटरफेस</string>
<string name="pref_set_theme_title">थीम का चयन करें</string>
<string name="pref_set_theme_sum">AntennaPod का प्रकटन बदलें.</string>
@@ -293,16 +268,15 @@
<string name="pref_playback_speed_sum">चर गति ऑडियो प्लेबैक के लिए उपलब्ध गति बनाइए</string>
<string name="pref_gpodnet_sethostname_title">होस्टनाम सेट</string>
<string name="pref_gpodnet_sethostname_use_default_host">डिफ़ॉल्ट होस्ट का प्रयोग करें</string>
+ <!--About screen-->
+ <string name="about_pref">के बारे में</string>
<!--Search-->
<string name="found_in_chapters_label">अध्यायों में मिला</string>
<string name="search_status_no_results">कोई परिणाम नहीं मिले</string>
<string name="search_label">खोज</string>
<string name="found_in_title_label">शीर्षक में मिला</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML फ़ाइलें आपको एक podcatcher से दूसरे को अपने पॉडकास्ट स्थानांतरित करने के लिए अनुमति देते हैं.</string>
- <string name="start_import_label">आयात प्रारंभ</string>
<string name="opml_import_label">OPML आयात</string>
- <string name="opml_directory_error">त्रुटि!</string>
<string name="reading_opml_label">OPML फ़ाइल पढ़ना</string>
<string name="select_all_label">सभी का चयन करें</string>
<string name="deselect_all_label">सभी का चयन रद्द करें</string>
@@ -358,7 +332,6 @@
<string name="pref_pausePlaybackForFocusLoss_title">रुकावटो के लिए रोकें</string>
<!--Online feed view-->
<string name="subscribe_label">सदस्यता लें</string>
- <string name="subscribed_label">सदस्यता ली गई</string>
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
<!--Progress information-->
@@ -374,4 +347,5 @@
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<!--Notification channels-->
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-hu/strings.xml b/core/src/main/res/values-hu/strings.xml
index 706b13b79..b84d1900a 100644
--- a/core/src/main/res/values-hu/strings.xml
+++ b/core/src/main/res/values-hu/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
<string name="feed_update_receiver_name">Feliratkozások frissítése</string>
- <string name="feeds_label">Idővonalak</string>
+ <string name="feeds_label">Csatornák</string>
<string name="statistics_label">Statisztika</string>
<string name="add_feed_label">Podcast hozzáadása</string>
<string name="episodes_label">Epizódok</string>
@@ -17,28 +17,30 @@
<string name="downloads_log_label">Napló</string>
<string name="subscriptions_label">Feliratkozások</string>
<string name="subscriptions_list_label">Feliratkozások listája</string>
- <string name="cancel_download_label">Letöltés megállítása</string>
- <string name="playback_history_label">Lejátszási Napló</string>
+ <string name="cancel_download_label">Letöltés\nmegállítása</string>
+ <string name="playback_history_label">Lejátszási napló</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Szinkronizáció más eszközökkel</string>
- <string name="gpodnet_auth_label">gpodder.net Bejelentkezés</string>
- <string name="free_space_label">%1$s szabad</string>
+ <string name="gpodnet_auth_label">gpodder.net bejelentkezés</string>
<string name="episode_cache_full_title">Epizód tárhely megtelt</string>
- <string name="episode_cache_full_message">Az epizódok tárolására megadott maximális tárhely megtelt. A méretét a \"Beállítások\" menüpontban tudod megnövelni.</string>
- <string name="synchronizing">Szinkronizálás...</string>
+ <string name="episode_cache_full_message">Az epizódok tárolására megadott maximális tárhely megtelt. A mérete a beállításokban növelhető meg.</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">A lejátszott podcastok összideje:</string>
- <string name="statistics_details_dialog">%1$d-ból %2$d epizód elindítva.\n\n%3$s-ból %4$s lejátszva.</string>
- <string name="statistics_mode">Statisztikai módok</string>
+ <string name="statistics_details_dialog">%1$d/%2$d epizód elindítva.\n\n%3$s/%4$s lejátszva.</string>
+ <string name="statistics_mode">Statisztikai mód</string>
+ <string name="statistics_mode_normal">A tényleges lejátszási idő számítása. A kétszeri lejátszás kétszer lesz számolva, míg a lejátszottként megjelölés nem számít bele</string>
+ <string name="statistics_mode_count_all">Az összes lejátszottnak jelölt podcast összeszámolása</string>
+ <string name="statistics_speed_not_counted">Megjegyzés: a lejátszási sebesség nem lesz beleszámítva.</string>
+ <string name="statistics_reset_data">Statisztikai adatok alaphelyzetbe állítása</string>
<!--Main activity-->
<string name="drawer_open">Menü megnyitása</string>
<string name="drawer_close">Menü bezárása</string>
<string name="drawer_preferences">Oldalsáv beállításai</string>
<string name="drawer_feed_order_unplayed_episodes">Rendezés számláló szerint</string>
- <string name="drawer_feed_order_alphabetical">Rendezés ABC rendben</string>
- <string name="drawer_feed_order_last_update">Rendezés megjelenés dátuma szerint</string>
- <string name="drawer_feed_order_most_played">Rendezés játszott epizódok szerint</string>
- <string name="drawer_feed_counter_new_unplayed">Új és nem játszott epizódok száma</string>
+ <string name="drawer_feed_order_alphabetical">Rendezés betűrendben</string>
+ <string name="drawer_feed_order_last_update">Rendezés közzététel dátuma szerint</string>
+ <string name="drawer_feed_order_most_played">Rendezés lejátszott epizódok szerint</string>
+ <string name="drawer_feed_counter_new_unplayed">Új és nem lejátszott epizódok száma</string>
<string name="drawer_feed_counter_new">Új epizódok száma</string>
<string name="drawer_feed_counter_unplayed">Nem játszott epizódok száma</string>
<string name="drawer_feed_counter_downloaded">Letöltött epizódok száma</string>
@@ -47,41 +49,36 @@
<string name="open_in_browser_label">Megnyitás böngészőben</string>
<string name="copy_url_label">URL másolása</string>
<string name="share_url_label">URL megosztása</string>
- <string name="copied_url_msg">URL másolva a vásólapra</string>
+ <string name="copied_url_msg">URL vágólapra másolva</string>
<string name="go_to_position_label">Ugrás ehhez a pozícióhoz</string>
<!--Playback history-->
<string name="clear_history_label">Napló törlése</string>
<!--Other-->
- <string name="confirm_label">Megerősít</string>
- <string name="cancel_label">Mégsem</string>
+ <string name="confirm_label">Megerősítés</string>
+ <string name="cancel_label">Mégse</string>
<string name="yes">Igen</string>
<string name="no">Nem</string>
<string name="reset">Visszaállítás</string>
<string name="author_label">Szerző(k)</string>
<string name="language_label">Nyelv</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Beállítások</string>
<string name="cover_label">Kép</string>
<string name="error_label">Hiba</string>
<string name="error_msg_prefix">Hiba történt:</string>
+ <string name="needs_storage_permission">A művelethez tároló jogosultság szükséges</string>
<string name="refresh_label">Frissítés</string>
- <string name="external_storage_error_msg">Nem található külső tárhely. Biztosíts egy külső tárhelyet hogy az alkalmazás működni tudjon.</string>
+ <string name="external_storage_error_msg">Nem található külső tárhely. Az alkalmazás helyes működése érdekében győződjön meg róla, hogy csatolva van-e.</string>
<string name="chapters_label">Fejezetek</string>
<string name="chapter_duration">Hossz: %1$s</string>
- <string name="shownotes_label">Jegyzetek</string>
<string name="description_label">Leírás</string>
- <string name="most_recent_prefix">Legfrissebb epizód:\u0020</string>
<string name="episodes_suffix">\u0020epizód</string>
- <string name="length_prefix">Hossz:\u0020</string>
- <string name="size_prefix">Méret:\u0020</string>
<string name="processing_label">Feldolgozás</string>
- <string name="loading_label">Betöltés...</string>
<string name="save_username_password_label">Felhasználónév és jelszó mentése</string>
<string name="close_label">Bezárás</string>
<string name="retry_label">Újra</string>
- <string name="auto_download_label">Automatikus letöltésekhez hozzáfűzés</string>
- <string name="auto_download_apply_to_items_title">Alkalmazás előző epizódokra</string>
- <string name="auto_download_apply_to_items_message">A beállított <i>Automatikus letöltés</i> az új epizódokra érvényes.\nVonatkozzon a korábban kibocsátott epizódokra is?</string>
+ <string name="auto_download_label">Hozzáadás az automatikus letöltésekhez</string>
+ <string name="auto_download_apply_to_items_title">Alkalmazás az előző epizódokra</string>
+ <string name="auto_download_apply_to_items_message">A beállított <i>Automatikus letöltés</i> az új epizódokra lesz érvényes.\nAlkalmazza a korábban közzétett epizódokra is?</string>
<string name="auto_delete_label">Epizód automatikus törlése</string>
<string name="parallel_downloads_suffix">\u0020párhuzamos letöltés</string>
<string name="feed_auto_download_global">Globális alapértelmezett</string>
@@ -96,23 +93,28 @@
<item quantity="other">befejezés után %d órával</item>
</plurals>
<plurals name="episode_cleanup_days_after_listening">
- <item quantity="one">1 nappal a befejezés után</item>
- <item quantity="other">%d nappal a befejezés után</item>
+ <item quantity="one">befejezés után 1 nappal</item>
+ <item quantity="other">befejezés után %d nappal</item>
</plurals>
<string name="num_selected_label">%d kiválasztva</string>
<!--'Add Feed' Activity labels-->
- <string name="feedurl_label">Idővonal URL</string>
- <string name="etxtFeedurlHint">www.example.com/feed</string>
+ <string name="feedurl_label">Csatorna URL</string>
+ <string name="etxtFeedurlHint">www.example.com/csatorna</string>
<string name="txtvfeedurl_label">Podcast hozzáadása URL alapján</string>
- <string name="podcastdirectories_label">Podcast keresése mappában</string>
- <string name="podcastdirectories_descr">Új podcastokat kereshetsz iTunes-on vagy fyyd-en, vagy a gpodder.net-en név, kategória vagy cím alapján.</string>
- <string name="browse_gpoddernet_label">gpodder.net böngészése</string>
+ <string name="browse_gpoddernet_label">A gpodder.net böngészése</string>
<string name="discover">Felfedezés</string>
+ <string name="discover_more">több »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Az összes megjelölése lejátszottként</string>
- <string name="mark_all_read_msg">Az összes epizód lejátszottként megjelölve</string>
- <string name="mark_all_read_confirmation_msg">Biztosan megjelölöd az összes epizódot lejátszottként?</string>
- <string name="show_info_label">Információ mutatása</string>
+ <string name="mark_all_read_msg">Az összes epizód lejátszottnak jelölve</string>
+ <string name="mark_all_read_confirmation_msg">Biztos, hogy lejátszottnak jelöli az összes epizódot?</string>
+ <string name="mark_all_read_feed_confirmation_msg">Biztos, hogy a podcast összes epizódját lejátszottnak jelöli?</string>
+ <string name="remove_all_new_flags_label">Az összes „új” jelző eltávolítása</string>
+ <string name="removed_all_new_flags_msg">Az összes „új” jelző eltávolítva</string>
+ <string name="remove_all_new_flags_confirmation_msg">Biztos, hogy eltávolítja az „új” jelzőt az összes epizódról?</string>
+ <string name="show_info_label">Információ megjelenítése</string>
+ <string name="show_feed_settings_label">Podcast beállítások megjelenítése</string>
+ <string name="feed_info_label">Podcast információk</string>
<string name="feed_settings_label">Podcast beállítások</string>
<string name="rename_feed_label">Podcast átnevezése</string>
<string name="remove_feed_label">Podcast eltávolítása</string>
@@ -120,20 +122,24 @@
<string name="share_link_label">Epizód URL megosztása</string>
<string name="share_link_with_position_label">Epizód URL megosztása pozícióval</string>
<string name="share_file_label">Fájl megosztása</string>
- <string name="share_feed_url_label">Idővonal URL megosztása</string>
+ <string name="share_website_url_label">Honlap URL megosztása</string>
+ <string name="share_feed_url_label">Csatorna URL megosztása</string>
<string name="share_item_url_label">Médiafájl URL megosztása</string>
<string name="share_item_url_with_position_label">Médiafájl URL megosztása pozícióval</string>
- <string name="feed_delete_confirmation_msg">Kérlek erősítsd meg, hogy törlöd a \"%1$s\" podcastot, az ÖSSZES epizóddal együtt (a letöltötteket is beleértve).</string>
- <string name="feed_remover_msg">Podcast eltávolítása folyamatban</string>
- <string name="hide_episodes_title">Epizódok elrejtése</string>
+ <string name="feed_delete_confirmation_msg">Erősítse meg, hogy törli a(z) „%1$s” podcastot, és az ÖSSZES epizódját (a letöltött epizódokat is beleértve).</string>
+ <string name="feed_remover_msg">Podcast eltávolítása</string>
+ <string name="load_complete_feed">Teljes podcast frissítése</string>
+ <string name="batch_edit">Tömeges szerkesztés</string>
+ <string name="select_all_above">Az összes felette lévő kiválasztása</string>
+ <string name="select_all_below">Az összes alatta lévő kiválasztása</string>
<string name="hide_unplayed_episodes_label">Nem lejátszott</string>
<string name="hide_paused_episodes_label">Szüneteltetett</string>
<string name="hide_played_episodes_label">Lejátszott</string>
- <string name="hide_queued_episodes_label">Sorbaállítva</string>
- <string name="hide_not_queued_episodes_label">Nincs sorbaállítva</string>
+ <string name="hide_queued_episodes_label">Sorbaállított</string>
+ <string name="hide_not_queued_episodes_label">Nem sorbaállított</string>
<string name="hide_downloaded_episodes_label">Letöltött</string>
<string name="hide_not_downloaded_episodes_label">Nem letöltött</string>
- <string name="hide_has_media_label">További tartalma van</string>
+ <string name="hide_has_media_label">Médiát tartalmaz</string>
<string name="hide_is_favorite_label">Kedvenc</string>
<string name="filtered_label">Szűrt</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} A legutóbbi frissítés sikertelen</string>
@@ -146,34 +152,42 @@
</plurals>
<string name="play_label">Lejátszás</string>
<string name="pause_label">Szüneteltetés</string>
- <string name="stop_label">Megállítás</string>
<string name="stream_label">Stream-elés</string>
- <string name="remove_label">Eltávolítás</string>
<string name="delete_label">Törlés</string>
- <string name="delete_failed">A fájl törlése nem sikerült. Az eszköz újraindítása segíthet a probléma megoldásában.</string>
+ <string name="delete_failed">A fájl nem törölhető. Az eszköz újraindítása segíthet a probléma megoldásában.</string>
<string name="delete_episode_label">Epizód törlése</string>
<plurals name="deleted_episode_batch_label">
<item quantity="one">%d epizód törölve. </item>
<item quantity="other">%d epizód törölve.</item>
</plurals>
- <string name="mark_read_label">Jelölés játszottnak</string>
- <string name="marked_as_read_label">Lejátszottként megjelölve</string>
+ <string name="remove_new_flag_label">Az „új” jelző eltávolítása</string>
+ <string name="removed_new_flag_label">Az „új” jelző eltávolítva</string>
+ <string name="mark_read_label">Megjelölés lejátszottként</string>
+ <string name="marked_as_read_label">Megjelölve lejátszottként</string>
<plurals name="marked_read_batch_label">
- <item quantity="one">%d epizód lejátszottként megjelölve. </item>
- <item quantity="other">%d epizód lejátszottként megjelölve.</item>
+ <item quantity="one">%d epizód megjelölve lejátszottként.</item>
+ <item quantity="other">%d epizód megjelölve lejátszottként.</item>
</plurals>
- <string name="mark_unread_label">Jelölés nem játszottnak</string>
+ <string name="mark_unread_label">Megjelölés nem lejátszottként</string>
<plurals name="marked_unread_batch_label">
- <item quantity="one">%d epizód nem játszottként megjelölve. </item>
- <item quantity="other">%d epizód nem játszottként megjelölve.</item>
+ <item quantity="one">%d epizód megjelölve nem lejátszottként.</item>
+ <item quantity="other">%d epizód megjelölve nem lejátszottként.</item>
</plurals>
<string name="add_to_queue_label">Sorbaállítás</string>
<string name="added_to_queue_label">Hozzáadva a lejátszási sorhoz</string>
- <string name="remove_from_queue_label">Eltávolítás lejátszási sorból</string>
- <string name="add_to_favorite_label">Kedvencekhez adás</string>
- <string name="added_to_favorites">Kedvencekhez adva</string>
- <string name="remove_from_favorite_label">Kedvencekből eltávolítás</string>
- <string name="removed_from_favorites">Kedvencekből eltávolítva</string>
+ <plurals name="added_to_queue_batch_label">
+ <item quantity="one">%d epizód sorbaállítva.</item>
+ <item quantity="other">%d epizód sorbaállítva.</item>
+ </plurals>
+ <string name="remove_from_queue_label">Eltávolítás a lejátszási sorból</string>
+ <plurals name="removed_from_queue_batch_label">
+ <item quantity="one">%d epizód eltávolítva a lejátszási sorból.</item>
+ <item quantity="other">%d epizód eltávolítva a lejátszási sorból.</item>
+ </plurals>
+ <string name="add_to_favorite_label">Hozzáadás a kedvencekhez</string>
+ <string name="added_to_favorites">Hozzáadva a kedvencekhez</string>
+ <string name="remove_from_favorite_label">Eltávolítás a kedvencekből</string>
+ <string name="removed_from_favorites">Eltávolítva a kedvencekből</string>
<string name="visit_website_label">Honlap megtekintése</string>
<string name="skip_episode_label">Epizód átugrása</string>
<string name="activate_auto_download">Automatikus letöltés bekapcsolása</string>
@@ -182,30 +196,29 @@
<string name="removed_item">Elem eltávolítva</string>
<!--Download messages and labels-->
<string name="download_successful">sikeres</string>
- <string name="download_failed">sikertelen</string>
<string name="download_pending">Letöltés várakozik</string>
<string name="download_running">Letöltés fut</string>
<string name="download_error_details">Részletek</string>
<string name="download_error_details_message">%1$s \n\nFájl URL:\n%2$s</string>
- <string name="download_error_device_not_found">Táreszköz nem található</string>
+ <string name="download_error_device_not_found">Tárolóeszköz nem található</string>
<string name="download_error_insufficient_space">Túl kevés tárhely</string>
- <string name="download_error_file_error">Fájl Hiba</string>
- <string name="download_error_http_data_error">HTTP Adat Hiba</string>
- <string name="download_error_error_unknown">Ismeretlen Hiba</string>
- <string name="download_error_parser_exception">Értelmező Kivétel</string>
- <string name="download_error_unsupported_type">Nem Támogatott Idővonal Típus</string>
- <string name="download_error_connection_error">Kapcsolódási Hiba</string>
- <string name="download_error_unknown_host">Ismeretlen Hoszt</string>
- <string name="download_error_unauthorized">Azonosítási Hiba</string>
- <string name="download_error_file_type_type">Fájl Típus Hiba</string>
- <string name="download_error_forbidden">Tiltott</string>
- <string name="cancel_all_downloads_label">Az összes letöltés visszavonása</string>
- <string name="download_canceled_msg">Letöltés visszavonva</string>
- <string name="download_report_title">Letöltés befejeződött, hibák léptek fel</string>
+ <string name="download_error_http_data_error">HTTP adathiba</string>
+ <string name="download_error_error_unknown">Ismeretlen hiba</string>
+ <string name="download_error_parser_exception">Értelmező által dobott kivétel</string>
+ <string name="download_error_unsupported_type">Nem támogatott csatornatípus</string>
+ <string name="download_error_connection_error">Kapcsolódási hiba</string>
+ <string name="download_error_unknown_host">Ismeretlen kiszolgál</string>
+ <string name="download_error_unauthorized">Hitelesítési hiba</string>
+ <string name="download_error_file_type_type">Fájltípus-hiba</string>
+ <string name="download_error_forbidden">Megtiltva</string>
+ <string name="download_canceled_msg">Letöltés megszakítva</string>
+ <string name="download_canceled_autodownload_enabled_msg">Letöltés megszakítva\n<i>Automatikus letöltése</i> letiltva az elemnél</string>
+ <string name="download_report_title">A letöltések hibákkal fejeződtek be</string>
<string name="download_report_content_title">Letöltési jelentés</string>
<string name="download_error_malformed_url">Hibás URL</string>
+ <string name="download_error_io_error">Ki-/bemeneti hiba</string>
<string name="download_error_request_error">Lekérési hiba</string>
- <string name="download_error_db_access">Adatbázis hozzáférési hiba</string>
+ <string name="download_error_db_access">Adatbázis-hozzáférési hiba</string>
<plurals name="downloads_left">
<item quantity="one">%d letöltés van hátra</item>
<item quantity="other">%d letöltés van hátra</item>
@@ -215,195 +228,498 @@
<string name="download_report_content">%1$d letöltés sikeres, %2$d sikertelen</string>
<string name="download_log_title_unknown">Ismeretlen cím</string>
<string name="download_type_feed">Csatorna</string>
- <string name="download_type_media">Média fájl</string>
- <string name="download_type_image">Kép</string>
- <string name="authentication_notification_title">Bejelentkezés szükséges</string>
- <string name="authentication_notification_msg">A kért forrás felhasználónevet és jelszót kér</string>
- <string name="confirm_mobile_download_dialog_enable_temporarily">Átmenetileg engedélyez</string>
+ <string name="download_type_media">Médiafájl</string>
+ <string name="download_request_error_dialog_message_prefix">Hiba történt a fájl letöltésekor:\u0020</string>
+ <string name="null_value_podcast_error">Nem lett megjeleníthető podcast megadva.</string>
+ <string name="authentication_notification_title">Hitelesítés szükséges</string>
+ <string name="authentication_notification_msg">A kért erőforráshoz felhasználónév és jelszó szükséges</string>
+ <string name="confirm_mobile_download_dialog_title">Mobil letöltés megerősítése</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">A mobil adatkapcsolaton történő letöltés ki van kapcsolva a beállításokban.\n\nVálaszthat, hogy hozzáadja az epizódot a lejátszási sorhoz, vagy ideiglenesen engedélyezheti a letöltést.\n\n<small>A választási 10 percig lesz megjegyezve.</small></string>
+ <string name="confirm_mobile_download_dialog_message">A mobil adatkapcsolaton történő letöltés ki van kapcsolva a beállításokban.\n\nIdeiglenesen engedélyezi a letöltést?\n\n<small>A választása 10 percig lesz megjegyezve.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Mobil adatátvitel megerősítése</string>
+ <string name="confirm_mobile_streaming_notification_message">A mobil adatkapcsolaton történő adatátvitel ki van kapcsolva a beállításokban. Koppintson ha mindenképp szeretné.</string>
+ <string name="confirm_mobile_streaming_button_always">Engedélyezés mindig</string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Sorbaállítás</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Engedélyezés átmenetileg</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Hiba!</string>
+ <string name="player_stopped_msg">Nincs médialejátszás</string>
<string name="player_preparing_msg">Előkészítés</string>
<string name="player_ready_msg">Kész</string>
- <string name="player_seeking_msg">Tekerés</string>
- <string name="playback_error_server_died">Szerver kapcsolat megszakadt</string>
+ <string name="player_seeking_msg">Pozícionálás</string>
+ <string name="playback_error_server_died">A kiszolgálókapcsolat megszakadt</string>
+ <string name="playback_error_unsupported">Nem támogatott médiatípus</string>
+ <string name="playback_error_timeout">Műveleti időtúllépés</string>
<string name="playback_error_unknown">Ismeretlen hiba</string>
+ <string name="no_media_playing_label">Nincs médialejátszás</string>
<string name="player_buffering_msg">Pufferelés</string>
- <string name="playbackservice_notification_title">Podcast lejátszása</string>
+ <string name="player_go_to_picture_in_picture">Kép a képben mód</string>
+ <string name="unknown_media_key">AntennaPod – Ismeretlen médiabillentyű: %1$d</string>
<!--Queue operations-->
- <string name="lock_queue">Lejátszási sor lezárása</string>
+ <string name="lock_queue">Lejátszási sor zárolása</string>
<string name="unlock_queue">Lejátszási sor feloldása</string>
- <string name="queue_locked">Lejátszási sor lezárva</string>
+ <string name="queue_locked">Lejátszási sor zárolva</string>
<string name="queue_unlocked">Lejátszási sor feloldva</string>
- <string name="clear_queue_label">Lejátszási sor tisztítása</string>
+ <string name="checkbox_do_not_show_again">Ne mutassa újra</string>
+ <string name="clear_queue_label">Lejátszási sor kiürítése</string>
<string name="undo">Visszavonás</string>
- <string name="removed_from_queue">Elem eltávolítva</string>
<string name="move_to_top_label">Mozgatás az elejére</string>
<string name="move_to_bottom_label">Mozgatás a végére</string>
<string name="sort">Rendezés</string>
+ <string name="keep_sorted">Rendezés megtartása</string>
<string name="date">Dátum</string>
<string name="duration">Hossz</string>
- <string name="episode_title">Epizód cím</string>
- <string name="feed_title">Podcast cím</string>
+ <string name="episode_title">Epizódcím</string>
+ <string name="feed_title">Podcastcím</string>
+ <string name="random">Véletlenszerű</string>
+ <string name="smart_shuffle">Okos keverés</string>
<string name="ascending">Növekvő</string>
<string name="descending">Csökkenő</string>
+ <string name="clear_queue_confirmation_msg">Erősítse meg, hogy törölni akarja az ÖSSZES epizódot a sorból</string>
+ <string name="sort_old_to_new">Régitől az újig</string>
+ <string name="sort_new_to_old">Újtól a régiig</string>
<!--Variable Speed-->
- <string name="download_plugin_label">Kiegészítő letöltése</string>
- <string name="no_playback_plugin_title">Kiegészítő nincs telepítve</string>
+ <string name="download_plugin_label">Bővítmény letöltése</string>
+ <string name="no_playback_plugin_title">A bővítmény nincs telepítve</string>
<string name="set_playback_speed_label">Lejátszási sebesség</string>
<string name="enable_sonic">Sonic engedélyezése</string>
<!--Empty list labels-->
- <string name="no_history_head_label">Napló üres</string>
+ <string name="no_items_header_label">Nincsenek epizódok a sorban</string>
+ <string name="no_items_label">Adjon hozzá egy epizód azzal, hogy letölti, vagy nyomjon meg hosszan egy epizódot, és válassza a „Hozzáadás a sorhoz” lehetőséget.</string>
+ <string name="no_shownotes_label">Ehhez az epizódhoz nem tartoznak műsorjegyzetek.</string>
+ <string name="no_run_downloads_head_label">Nincs futó letöltés</string>
+ <string name="no_run_downloads_label">Epizódokat a podcast részletező képernyőn tölthet le.</string>
+ <string name="no_comp_downloads_head_label">Nincsenek letöltött epizódok</string>
+ <string name="no_comp_downloads_label">Epizódokat a podcast részletező képernyőn tölthet le.</string>
+ <string name="no_log_downloads_head_label">Nincs letöltési napló</string>
+ <string name="no_log_downloads_label">A letöltési napló itt fog megjelenni, ha elérhető lesz.</string>
+ <string name="no_history_head_label">A napló üres</string>
+ <string name="no_history_label">Ha meghallgat egy epizódot, meg fog itt jelenni.</string>
+ <string name="no_all_episodes_head_label">Nincsenek epizódok</string>
+ <string name="no_all_episodes_label">Ha hozzáad egy podcastot, az epizódok itt fognak megjelenni.</string>
+ <string name="no_new_episodes_head_label">Nincsenek új epizódok</string>
+ <string name="no_new_episodes_label">Ha új epizódok érkeznek, meg fognak itt jelenni.</string>
+ <string name="no_fav_episodes_head_label">Nincsenek kedvenc epizódok</string>
+ <string name="no_fav_episodes_label">Epizódokat hosszú megnyomással adhat a kedvencekhez.</string>
+ <string name="no_chapters_head_label">Nincsenek fejezetek</string>
+ <string name="no_chapters_label">Ennek az epizódnak nincsenek fejezetei.</string>
+ <string name="no_subscriptions_head_label">Nincsenek feliratkozások</string>
+ <string name="no_subscriptions_label">Podcastra való feliratkozáshoz nyomja meg az alábbi plusz ikont.</string>
<!--Preferences-->
<string name="storage_pref">Tároló</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Egyebek</string>
- <string name="about_pref">Rólam</string>
<string name="queue_label">Lejátszási sor</string>
+ <string name="integrations_label">Integrációk</string>
+ <string name="automation">Automatizálás</string>
<string name="download_pref_details">Részletek</string>
<string name="import_export_pref">Importálás/exportálás</string>
+ <string name="import_export_search_keywords">biztonsági mentés, helyreállítás</string>
<string name="appearance">Megjelenés</string>
- <string name="pref_pauseOnDisconnect_sum">Lejátszás szüneteltetése fejhallgató és bluetooth leválasztásakor</string>
- <string name="pref_hardwareForwardButtonSkips_title">Előre gomb átugor</string>
- <string name="pref_hardwarePreviousButtonRestarts_title">Előző gomb újraindít</string>
+ <string name="external_elements">Külső elemek</string>
+ <string name="interruptions">Megszakítások</string>
+ <string name="playback_control">Lejátszásvezérlés </string>
+ <string name="preference_search_hint">Keresés…</string>
+ <string name="preference_search_no_results">Nincsenek találatok</string>
+ <string name="preference_search_clear_history">Napló törlése</string>
+ <string name="media_player">Médialejátszó</string>
+ <string name="pref_episode_cleanup_title">Epizódok tisztítása</string>
+ <string name="pref_episode_cleanup_summary">Azon epizódok, melyek nincsenek a sorban és nem kedvencek, azok törölhetőek, ha az Automatikus letöltéshez helyre van szüksége az új epizódok miatt.</string>
+ <string name="pref_pauseOnDisconnect_sum">Lejátszás szüneteltetése fejhallgató vagy bluetooth leválasztásakor</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Lejátszás folytatása a fejhallgatók újracsatlakoztatásakor</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Lejátszás folytatása a bluetooth újracsatlakozásakor</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Az előre gomb átugorja</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Az bluetooth kapcsolaton csatlakozó eszköz előre gombjának megnyomásakor előretekerés helyett a következő számra ugrik</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Az előző gomb újraindítja</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">A hardveres előző gomb megnyomásakor visszatekerés helyett újraindítja a jelenlegi epizódot</string>
+ <string name="pref_followQueue_sum">A lejátszás befejeztével ugrás a sor következő elemére</string>
+ <string name="pref_auto_delete_sum">Az epizód törlése, ha a lejátszás véget ért</string>
<string name="pref_auto_delete_title">Automatikus törlés</string>
- <string name="pref_smart_mark_as_played_title">Intelligens játszottnak jelölés</string>
+ <string name="pref_smart_mark_as_played_sum">Az epizód megjelölése lejátszottként, ha legfeljebb már csak adott számú másodperc van hátra</string>
+ <string name="pref_smart_mark_as_played_title">Lejátszottnak jelölés intelligensen</string>
+ <string name="pref_skip_keeps_episodes_sum">Epizódok megtartása, ha kihagyásra kerülnek</string>
+ <string name="pref_skip_keeps_episodes_title">Kihagyott epizódok megtartása</string>
+ <string name="pref_favorite_keeps_episodes_sum">Epizódok megtartása, ha kedvencnek vannak jelölve</string>
+ <string name="pref_favorite_keeps_episodes_title">Kedvenc epizódok megtartása</string>
<string name="playback_pref">Lejátszás</string>
<string name="network_pref">Hálózat</string>
- <string name="pref_autoUpdateIntervallOrTime_Interval">Intervallum</string>
- <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Időpont</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Frissítési intervallum vagy időpont</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Adjon meg egy intervallumot vagy egy adott időpontot, amikor a csatornák automatikusan frissítésre kerülnek</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Megadhat egy &lt;i&gt;intervallumot&lt;/i&gt;, mint „2 óránként”, megadhat egy konkrét &lt;i&gt;időpontot&lt;/i&gt;, mint „de. 7:00” vagy letilthatja &lt;i&gt;letilthatja&lt;/i&gt; a frissítéseket.\n\nMegjegyzés: A frissítési idők nem pontosak. Rövid késleltetést tapasztalhat.&lt;small&gt;</string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Letiltás</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Intervallum megadása</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Időpont megadása</string>
<string name="pref_autoUpdateIntervallOrTime_every">minden %1$s</string>
- <string name="pref_autoUpdateIntervallOrTime_at">%1$s-kor</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Média fájlok letöltése csak WiFi-n</string>
+ <string name="pref_autoUpdateIntervallOrTime_at">ekkor: %1$s</string>
<string name="pref_followQueue_title">Folyamatos lejátszás</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi média lejátszás</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Fejhallgató leválasztása</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Fejhallgató újracsatlakoztatása</string>
- <string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth újracsatlakozás</string>
- <string name="pref_mobileUpdate_title">Frissítések mobiladat-kapcsolaton</string>
+ <string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth újracsatlakoztatás</string>
+ <string name="pref_stream_over_download_title">Adatátvitel előnyben részesítése</string>
+ <string name="pref_stream_over_download_sum">Letöltés gomb helyett adatátvitel gomb megjelenítése listákban.</string>
+ <string name="pref_mobileUpdate_title">Frissítések mobil adatkapcsolaton</string>
+ <string name="pref_mobileUpdate_sum">Válassza ki, hogy mit engedélyez mobil adatkapcsolaton keresztül</string>
+ <string name="pref_mobileUpdate_refresh">Csatorna frissítése</string>
+ <string name="pref_mobileUpdate_images">Borítóképek</string>
+ <string name="pref_mobileUpdate_auto_download">Automatikus letöltés</string>
+ <string name="pref_mobileUpdate_episode_download">Epizódletöltés</string>
+ <string name="pref_mobileUpdate_streaming">Adatátvitel</string>
<string name="user_interface_label">Felhasználói felület</string>
<string name="pref_set_theme_title">Téma kiválasztása</string>
- <string name="pref_nav_drawer_title">Navigációs fiók testreszabása</string>
- <string name="pref_nav_drawer_sum">Navigációs fiók kinézetének testreszabás</string>
- <string name="pref_nav_drawer_items_title">Navigációs fiók elemeinek kiválasztása</string>
- <string name="pref_set_theme_sum">AntennaPod kinézetének megváltoztatása</string>
+ <string name="pref_nav_drawer_items_title">Navigációs fiók elemeinek beállítása</string>
+ <string name="pref_nav_drawer_items_sum">A navigációs fiókban megjelenő elemek módosítása.</string>
+ <string name="pref_nav_drawer_feed_order_title">Feliratkozási sorrend beállítása</string>
+ <string name="pref_nav_drawer_feed_order_sum">A feliratkozásai sorrendjének módosítása</string>
+ <string name="pref_nav_drawer_feed_counter_title">Feliratkozásszámláló beállítása</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Módosítja a feliratkozásszámlálóban megjelenített információt. A feliratkozások sorrendjére is hatással van, ha a „Feliratkozási sorrend” a „Számláló” értékre van állítva.</string>
+ <string name="pref_set_theme_sum">AntennaPod kinézetének módosítása.</string>
<string name="pref_automatic_download_title">Automatikus letöltés</string>
- <string name="pref_automatic_download_sum">Epizódok automatikus letöltésének beállítása</string>
- <string name="pref_autodl_wifi_filter_title">Wi-Fi szűrő beállítása</string>
- <string name="pref_autodl_allow_on_mobile_title">Letöltés mobiladat-kapcsolaton keresztül</string>
+ <string name="pref_automatic_download_sum">Az epizódok automatikus letöltésének beállítása.</string>
+ <string name="pref_autodl_wifi_filter_title">Wi-Fi szűrő engedélyezése</string>
+ <string name="pref_autodl_wifi_filter_sum">Automatikus letöltés engedélyezése csak a kiválasztott Wi-Fi hálózatok esetén.</string>
+ <string name="autodl_wifi_filter_permission_title">Jogosultság szükséges</string>
+ <string name="autodl_wifi_filter_permission_message">A Wi-Fi szűrőhöz helymeghatározási jogosultság szükséges. Koppintson a jogosultság engedélyezéséhez.</string>
+ <string name="pref_automatic_download_on_battery_title">Letöltés, ha nincs akkumulátortöltés</string>
+ <string name="pref_automatic_download_on_battery_sum">Automatikus letöltés engedélyezése, ha az akkumulátor nem töltődik</string>
<string name="pref_parallel_downloads_title">Párhuzamos letöltések</string>
- <string name="pref_episode_cache_title">Epizód gyorsítótár</string>
+ <string name="pref_episode_cache_title">Epizód tárhely</string>
+ <string name="pref_episode_cache_summary">Az eszközön tárolt letöltött epizódok száma. Az automatikus letöltés felfüggesztésre kerül, ha eléri ezt a számot.</string>
+ <string name="pref_episode_cover_title">Epizód borítókép használata</string>
+ <string name="pref_theme_title_use_system">Rendszertéma használata</string>
<string name="pref_theme_title_light">Világos</string>
<string name="pref_theme_title_dark">Sötét</string>
<string name="pref_theme_title_trueblack">Fekete (AMOLED-hez alkalmas)</string>
+ <string name="pref_episode_cache_unlimited">Korlátlan</string>
<string name="pref_update_interval_hours_plural">óra</string>
<string name="pref_update_interval_hours_singular">óra</string>
- <string name="pref_update_interval_hours_manual">Manuális</string>
+ <string name="pref_update_interval_hours_manual">Kézi</string>
<string name="pref_gpodnet_authenticate_title">Bejelentkezés</string>
- <string name="pref_gpodnet_authenticate_sum">Jelentkezz be a gpodder.net fiókodba a feliratkozások szinkronizálásához.</string>
+ <string name="pref_gpodnet_authenticate_sum">Jelentkezzen be a gpodder.net fiókjába a feliratkozásai szinkronizálásához.</string>
<string name="pref_gpodnet_logout_title">Kijelentkezés</string>
<string name="pref_gpodnet_logout_toast">Kijelentkezés sikeres</string>
+ <string name="pref_gpodnet_setlogin_information_title">Bejelentkezési adatok módosítása</string>
+ <string name="pref_gpodnet_setlogin_information_sum">A gpodder.net fiókjához használt bejelentkezési adatok módosítása.</string>
<string name="pref_gpodnet_sync_changes_title">Változások szinkronizálása most</string>
+ <string name="pref_gpodnet_sync_changes_sum">A feliratkozásai és epizódállapotainak szinkronizálása a gpodder.nettel.</string>
<string name="pref_gpodnet_full_sync_title">Teljes szinkronizálás most</string>
- <string name="pref_playback_speed_title">Lejátszási sebesség</string>
+ <string name="pref_gpodnet_full_sync_sum">Az összes feliratkozásának és epizódállapotainak szinkronizálása a gpodder.nettel.</string>
+ <string name="pref_gpodnet_sync_sum_last_sync_line">Legutóbbi szinkronizálási kísérlet: %1$s (%2$s)</string>
+ <string name="pref_gpodnet_sync_started">Szinkronizálás elkezdődött</string>
+ <string name="pref_gpodnet_login_status"><![CDATA[Bejelentkezve mint <i>%1$s</i>, a(z) <i>%2$s</i> eszközzel]]></string>
+ <string name="pref_gpodnet_notifications_title">Szinkronizálási hibaértesítések megjelenítése</string>
+ <string name="pref_gpodnet_notifications_sum">Ez a beállítás a hitelesítési hibákra nem érvényes.</string>
+ <string name="pref_playback_speed_title">Lejátszási sebességek</string>
+ <string name="pref_playback_speed_sum">A változó sebességű hanglejátszáshoz elérhető sebességek testreszabása</string>
+ <string name="pref_playback_time_respects_speed_title">Médiainformáció hozzáigazítása a lejátszási sebességhez</string>
+ <string name="pref_playback_time_respects_speed_sum">A megjelenített pozíció és hossz a lejátszási sebességhez lesz igazítva</string>
+ <string name="pref_fast_forward">Előretekerés mértéke</string>
+ <string name="pref_fast_forward_sum">Szabja testre, hogy hány másodperccel ugorjon előre az előretekerés gomb megnyomásakor</string>
+ <string name="pref_rewind">Visszatekerés mértéke</string>
+ <string name="pref_rewind_sum">Szabja testre, hogy hány másodperccel ugorjon vissza a visszatekerés gomb megnyomásakor</string>
+ <string name="pref_gpodnet_sethostname_title">Gépnév megadása</string>
+ <string name="pref_gpodnet_sethostname_use_default_host">Alapértelmezett gépnév használata</string>
+ <string name="pref_expandNotify_title">Magas értesítési prioritás</string>
+ <string name="pref_expandNotify_sum">Ez általában kibővíti az értesítést, hogy megjelenítse a lejátszási gombokat.</string>
+ <string name="pref_persistNotify_title">Állandó lejátszásvezérlők</string>
+ <string name="pref_persistNotify_sum">Az értesítés és a zárképernyőn megjelenő vezérlők megtartása a lejátszás szüneteltetésekor.</string>
<string name="pref_compact_notification_buttons_title">Zárképernyő gombok beállítása</string>
- <string name="pref_lockscreen_background_title">Zárképernyő háttérkép</string>
- <string name="pref_lockscreen_background_sum">Zárképernyő háttérkép átállítása az epizód képeként.</string>
+ <string name="pref_compact_notification_buttons_sum">A zárképernyőn megjelenő lejátszási gombok módosítása. A lejátszás/szüneteltetés gombok mindig szerepel.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">Válasszon legfeljebb %1$d elemet</string>
+ <string name="pref_compact_notification_buttons_dialog_error">Legfeljebb %1$d elemet választhat.</string>
+ <string name="pref_lockscreen_background_title">Képernyőzár háttérkép beállítása</string>
+ <string name="pref_lockscreen_background_sum">A képernyőzár háttérképének beállítása a jelenlegi epizód képére. Mellékhatásként, ez a harmadik féltől származó alkalmazásokban is megjeleníti a képet.</string>
<string name="pref_showDownloadReport_title">Letöltési jelentés megtekintése</string>
+ <string name="pref_showDownloadReport_sum">Ha a letöltések sikertelenek, előállít egy jelentést, amely részletezi a hibát</string>
+ <string name="pref_expand_notify_unsupport_toast">A 4.1 előtti Android verziók nem támogatják a bővített értesítéseket.</string>
+ <string name="pref_enqueue_location_title">Hely sorbaállítása</string>
+ <string name="pref_enqueue_location_sum">Epizódok hozzáadása ehhez: %1$s</string>
+ <string name="enqueue_location_back">Végére</string>
+ <string name="enqueue_location_front">Elejére</string>
+ <string name="enqueue_location_after_current">A jelenlegi epizód után</string>
+ <string name="pref_smart_mark_as_played_disabled">Letiltva</string>
<string name="pref_image_cache_size_title">Kép gyorsítótár mérete</string>
- <string name="pref_image_cache_size_sum">Kép gyorsítótár méretének a lemezen</string>
- <string name="crash_report_title">Hibajelentés</string>
+ <string name="pref_image_cache_size_sum">Kép gyorsítótár mérete a lemezen.</string>
+ <string name="view_mailing_list">Levelezőlista megtekintése</string>
+ <string name="bug_report_title">Hibajelentés</string>
+ <string name="open_bug_tracker">Hibakövető megnyitása</string>
+ <string name="copy_to_clipboard">Másolás vágólapra</string>
+ <string name="copied_to_clipboard">Vágólapra másolva</string>
<string name="experimental_pref">Kísérleti</string>
+ <string name="pref_media_player_message">Válassza ki a fájlok lejátszásához használt médialejátszót</string>
+ <string name="pref_current_value">Jelenlegi érték: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Hálózati proxy beállítása</string>
- <string name="pref_faq">GYIK</string>
- <string name="pref_known_issues">Ismert hibák</string>
- <string name="pref_no_browser_found">Nem található webböngésző</string>
+ <string name="pref_faq">Gyakran ismételt kérdések</string>
+ <string name="pref_no_browser_found">Nem található webböngésző.</string>
<string name="pref_cast_title">Chromecast támogatás</string>
+ <string name="pref_cast_message_play_flavor">A távoli médialejátszás engedélyezése a Cast eszközökön (mint a Chromecast, hangfalak vagy Android TV)</string>
+ <string name="pref_cast_message_free_flavor">A Chromecast harmadik féltől származó zárt programkönyvtárakat igényel, amelyek le vannak tiltva az AntennaPod jelen verziójában.</string>
+ <string name="pref_enqueue_downloaded_title">Letöltött elemek sorbaállítása</string>
+ <string name="pref_enqueue_downloaded_summary">Letöltött epizódok sorhoz adása</string>
+ <string name="media_player_builtin">Beépített androidos lejátszó</string>
+ <string name="pref_skip_silence_title">Csend kihagyása a hangokban</string>
+ <string name="pref_videoBehavior_title">A videóból kilépéskor</string>
+ <string name="pref_videoBehavior_sum">Viselkedés a videolejátszás elhagyásakor</string>
+ <string name="stop_playback">Lejátszás befejezése</string>
+ <string name="continue_playback">Hanglejátszás folytatása</string>
+ <string name="behavior">Viselkedés</string>
+ <string name="pref_back_button_behavior_title">Vissza gomb viselkedése</string>
+ <string name="pref_back_button_behavior_sum">A vissza gomb viselkedésének módosítása</string>
+ <string name="back_button_default">Alapértelmezett</string>
+ <string name="back_button_open_drawer">Navigációs fiók megnyitása</string>
+ <string name="back_button_double_tap">Dupla koppintás a kilépéshez</string>
+ <string name="back_button_show_prompt">Megerősítés a kilépéshez</string>
+ <string name="close_prompt">Biztos, hogy bezárja az AntennaPodot?</string>
+ <string name="double_tap_toast">A kilépéshez érintse meg újra a vissza gombot</string>
+ <string name="back_button_go_to_page">Ugrás a laphoz…</string>
+ <string name="back_button_go_to_page_title">Válasszon lapot</string>
+ <string name="pref_delete_removes_from_queue_title">A törlés eltávolítja a sorból</string>
+ <string name="pref_delete_removes_from_queue_sum">Törléskor az epizód automatikus eltávolítása a sorból.</string>
+ <!--About screen-->
+ <string name="about_pref">Névjegy</string>
+ <string name="antennapod_version">AntennaPod verzió</string>
+ <string name="developers">Fejlesztők</string>
+ <string name="developers_summary">Bárki segíthet az AntennaPod jobbá tételében</string>
+ <string name="translators">Fordítók</string>
+ <string name="translators_summary">A fordításokat AntennaPod felhasználók készítik a Transifex segítségével</string>
+ <string name="privacy_policy">Adatvédelmi nyilatkozat</string>
+ <string name="licenses">Licencek</string>
+ <string name="licenses_summary">Az AntennaPod más remek szoftvereket használ</string>
<!--Search-->
<string name="search_hint">Epizódok keresése</string>
+ <string name="found_in_shownotes_label">Megtalálva az adásjegyzetekben</string>
+ <string name="found_in_chapters_label">Megtalálva a fejezetekben</string>
+ <string name="found_in_authors_label">Megtalálva a szerzőkben</string>
+ <string name="found_in_feeds_label">Megtalálva a podcastban</string>
+ <string name="search_status_no_results">Nincsenek találatok</string>
<string name="search_label">Keresés</string>
+ <string name="found_in_title_label">Megtalálva a címben</string>
+ <string name="no_results_for_query">Nincs találat a következőre: „%1$s”</string>
<!--OPML import and export-->
- <string name="start_import_label">Importálás indítása</string>
+ <string name="opml_import_option">%1$d. lehetőség</string>
+ <string name="opml_import_explanation_1">Válasszon egy konkrét útvonalat a helyi fájlrendszerről.</string>
+ <string name="opml_import_explanation_3">Számos alkalmazás meg tudja <i>nyitni</i> az OPML fájlokat az AntennaPod <i>segítségével</i>, például a Google Mail, a Dropbox és a legtöbb fájlkezelő.</string>
<string name="opml_import_label">OPML importálása</string>
- <string name="opml_directory_error">HIBA!</string>
<string name="reading_opml_label">OPML fájl olvasása</string>
+ <string name="opml_reader_error">Hiba történt az OPML dokumentum olvasásakor:</string>
+ <string name="opml_import_error_no_file">Nincs fájl kiválasztva!</string>
+ <string name="select_all_label">Összes kiválasztása</string>
+ <string name="deselect_all_label">Összes kiválasztásának megszüntetése</string>
+ <string name="choose_file_from_filesystem">A helyi fájlrendszerről</string>
+ <string name="opml_export_label">OPML exportálása</string>
+ <string name="html_export_label">HTML exportálása</string>
+ <string name="exporting_label">Exportálás…</string>
+ <string name="export_error_label">Exportálási hiba</string>
+ <string name="export_success_title">Exportálás sikeres</string>
+ <string name="export_success_sum">Az exportált fájl ide lett kiírva:\n\n%1$s</string>
+ <string name="opml_import_ask_read_permission">A külső tároló elérése szükséges az OPML fájl olvasásához</string>
<!--Sleep timer-->
- <string name="sleep_timer_enabled_label">Elalvás időzítő engedélyezése</string>
- <string name="sleep_timer_disabled_label">Elalvás időzítő kikapcsolása</string>
+ <string name="set_sleeptimer_label">Alvási időzítő beállítása</string>
+ <string name="disable_sleeptimer_label">Alvási időzítő letiltása</string>
+ <string name="enter_time_here_label">Adja meg az időt</string>
+ <string name="sleep_timer_label">Alvási időzít</string>
+ <string name="time_left_label">Hátralévő idő:\u0020</string>
+ <string name="time_dialog_invalid_input">Érvénytelen bemenet, az időnek egész számnak kell lennie</string>
+ <string name="timer_about_to_expire_label"><b>Ha az idő hamarosan lejár:</b></string>
+ <string name="shake_to_reset_label">Rázás az időzítő visszaállításához</string>
+ <string name="timer_vibration_label">Rezgés</string>
+ <string name="time_seconds">másodperc</string>
+ <string name="time_minutes">perc</string>
+ <string name="time_hours">óra</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 másodperc</item>
+ <item quantity="other">%d másodperc</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 perc</item>
+ <item quantity="other">%d perc</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 óra</item>
+ <item quantity="other">%d óra</item>
+ </plurals>
+ <string name="auto_enable_label">Automatikus bekapcsolás</string>
+ <string name="sleep_timer_enabled_label">Alvási időzítő bekapcsolva</string>
+ <string name="sleep_timer_disabled_label">Alvási időzítő kikapcsolva</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGÓRIÁK</string>
- <string name="gpodnet_suggestions_header">AJÁNLÁSOK</string>
+ <string name="gpodnet_toplist_header">LEGJOBB PODCASTOK</string>
+ <string name="gpodnet_suggestions_header">JAVASLATOK</string>
+ <string name="gpodnet_search_hint">Keresés a gpodder.neten</string>
<string name="gpodnetauth_login_title">Bejelentkezés</string>
+ <string name="gpodnetauth_login_descr">Üdvözli a gpodder.net bejelentkezési folyamat. Először írja be a bejelentkezési információit:</string>
<string name="gpodnetauth_login_butLabel">Bejelentkezés</string>
+ <string name="gpodnetauth_login_register">Ha még nincs fiókja, itt létrehozhat egyet:\nhttps://gpodder.net/register/</string>
<string name="username_label">Felhasználónév</string>
<string name="password_label">Jelszó</string>
<string name="gpodnetauth_device_title">Eszköz kiválasztása</string>
+ <string name="gpodnetauth_device_descr">Hozzon létre egy új eszközt a gpodder.net fiókjához, vagy válasszon egy meglévőt:</string>
+ <string name="gpodnetauth_device_deviceID">Eszközazonosító:\u0020</string>
+ <string name="gpodnetauth_device_caption">Felirat</string>
+ <string name="gpodnetauth_device_butCreateNewDevice">Új eszköz létrehozása</string>
+ <string name="gpodnetauth_device_chooseExistingDevice">Létező eszköz kiválasztása:</string>
+ <string name="gpodnetauth_device_errorEmpty">Az eszközazonosító nem lehet üres</string>
+ <string name="gpodnetauth_device_errorAlreadyUsed">Az eszközazonosító már használatban van</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">A felirat nem lehet üres</string>
+ <string name="gpodnetauth_device_butChoose">Kiválasztás</string>
+ <string name="gpodnetauth_finish_title">Bejelentkezés sikertelen</string>
+ <string name="gpodnetauth_finish_descr">Gratulálunk! A gpodder.net fiókja most már össze van kapcsolva az eszközével. Az AntennaPod automatikusan szinkronizálja az eszközén lévő feliratkozásait a gpodder.net fiókjával.</string>
+ <string name="gpodnetauth_finish_butsyncnow">Szinkronizálás indítása most</string>
+ <string name="gpodnetauth_finish_butgomainscreen">Ugrás a főképernyőre</string>
+ <string name="gpodnetsync_auth_error_title">gpodder.net hitelesítési hiba</string>
+ <string name="gpodnetsync_auth_error_descr">Hibás felhasználónév vagy jelszó</string>
+ <string name="gpodnetsync_error_title">gpodder.net szinkronizálási hiba</string>
+ <string name="gpodnetsync_error_descr">Hiba történt a szinkronizálás során:\u0020</string>
<string name="gpodnetsync_pref_report_successful">Sikeres</string>
<string name="gpodnetsync_pref_report_failed">Sikertelen</string>
+ <string name="gpodnetsync_username_characters_error">A felhasználónevek csak betűket, számokat, kötőjeleket és aláhúzásokat tartalmazhatnak.</string>
<!--Directory chooser-->
<string name="selected_folder_label">Kiválasztott mappa:</string>
<string name="create_folder_label">Mappa létrehozása</string>
- <string name="create_folder_error_already_exists">Mappa már létezik</string>
- <string name="folder_not_empty_dialog_title">Mappa nem üres</string>
+ <string name="choose_data_directory">Válasszon adatmappát</string>
+ <string name="choose_data_directory_message">Válassza ki az adatmappája gyökerét. Az AntennaPod létre fogja hozni a megfelelő alkönyvtárakat.</string>
+ <string name="choose_data_directory_permission_rationale">A külső tároló elérése szükséges az adatmappa módosításához</string>
+ <string name="choose_data_directory_available_space">%1$s / %2$s szabad</string>
+ <string name="create_folder_msg">Létrehozza az új „%1$s” mappát?</string>
+ <string name="create_folder_success">Új mappa létrehozva</string>
+ <string name="create_folder_error_no_write_access">Nem lehet ebbe a mappába írni</string>
+ <string name="create_folder_error_already_exists">A mappa már létezik</string>
+ <string name="create_folder_error">Nem lehet létrehozni a mappát</string>
+ <string name="folder_does_not_exist_error">A(z) „%1$s” még nem létezik</string>
+ <string name="folder_not_readable_error">A(z) „%1$s” nem olvasható</string>
+ <string name="folder_not_writable_error">A(z) „%1$s” nem írható</string>
+ <string name="folder_not_empty_dialog_title">A mappa nem üres</string>
+ <string name="folder_not_empty_dialog_msg">A kiválasztott mappa nem üres. A médialetöltések és az egyéb fájlok közvetlenül a mappába fognak kerülni. Mindenképp folytatja?</string>
<string name="set_to_default_folder">Alapértelmezett mappa kiválasztása</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Lejátszás szüneteltetése a hangerő csökkentése helyett, ha egy másik alkalmazás akar hangot lejátszani</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Megszakítások esetén szüneteltetés</string>
+ <string name="pref_resumeAfterCall_sum">Lejátszás folytatása a telefonhívás befejeztekor</string>
+ <string name="pref_resumeAfterCall_title">Folytatás hívás után</string>
+ <string name="pref_restart_required">A változás életbe lépéséhez az AntennaPod újraindítása szükséges.</string>
<!--Online feed view-->
<string name="subscribe_label">Feliratkozás</string>
- <string name="subscribed_label">Feliratkozva</string>
- <string name="downloading_label">Letöltés...</string>
+ <string name="subscribing_label">Feliratkozás…</string>
<!--Content descriptions for image buttons-->
+ <string name="rewind_label">Visszatekerés</string>
+ <string name="fast_forward_label">Előretekerés</string>
<string name="media_type_audio_label">Hang</string>
- <string name="media_type_video_label">Video</string>
+ <string name="media_type_video_label">Videó</string>
+ <string name="navigate_upwards_label">Navigálás felfelé</string>
+ <string name="status_downloading_label">Az epizód letöltés alatt van</string>
+ <string name="in_queue_label">Az epizód sorba van állítva </string>
+ <string name="drag_handle_content_description">Húzza az elem pozíciójának módosításához</string>
<string name="load_next_page_label">Következő oldal betöltése</string>
<!--Feed information screen-->
+ <string name="authentication_label">Hitelesítés</string>
+ <string name="authentication_descr">A felhasználónév és jelszó módosítása ennél a podcastnál és az epizódoknál.</string>
+ <string name="auto_download_settings_label">Automatikus letöltési beállítások</string>
+ <string name="episode_filters_label">Epizódszűrő</string>
+ <string name="episode_filters_description">A kulcsszavak, mely alapján eldől, hogy az automatikus letöltés beleveszi-e az epizódot, vagy sem</string>
+ <string name="episode_filters_include">Belevétel</string>
+ <string name="episode_filters_exclude">Kihagyás</string>
+ <string name="episode_filters_hint">Különálló szavak\n„Több szó”</string>
+ <string name="keep_updated">Frissítse</string>
+ <string name="keep_updated_summary">Vegye bele ezt a csatornát is az összes csatorna (automatikus) frissítésébe</string>
+ <string name="auto_download_disabled_globally">Az automatikus letöltés ki van kapcsolva a fő AntennaPod beállításokban</string>
<!--Progress information-->
+ <string name="progress_upgrading_database">Az adatbázis frissítése</string>
<!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Feliratkozások importálása az egycélú alkalmazásokból…</string>
<!--Add podcast fragment-->
- <string name="search_itunes_label">iTunes keresés</string>
- <string name="search_fyyd_label">fyyd keresés</string>
+ <string name="search_podcast_hint">Podcast keresése…</string>
+ <string name="search_itunes_label">Keresés az iTunes-on</string>
+ <string name="search_fyyd_label">Keresés a fyyden</string>
+ <string name="advanced_search">Speciális keresés</string>
+ <string name="filter">Szűrő</string>
<!--Episodes apply actions-->
- <string name="all_label">Mind</string>
- <string name="downloaded_label">Letöltve</string>
- <string name="not_downloaded_label">Nincs letöltve</string>
- <string name="queued_label">Sorbaállítva</string>
- <string name="not_queued_label">Nincs sorbaállítva</string>
+ <string name="all_label">Összes</string>
+ <string name="selected_all_label">Összes epizód kiválasztva</string>
+ <string name="none_label">Egyik sem</string>
+ <string name="deselected_all_label">Az összes epizód kiválasztásának megszüntetése</string>
+ <string name="played_label">Lejátszott</string>
+ <string name="selected_played_label">A lejátszott epizódok kiválasztva</string>
+ <string name="unplayed_label">Nem játszott</string>
+ <string name="selected_unplayed_label">A nem játszott epizódok kiválasztása</string>
+ <string name="downloaded_label">Letöltött</string>
+ <string name="selected_downloaded_label">A letöltött epizódok kiválasztása</string>
+ <string name="not_downloaded_label">Nem letöltött</string>
+ <string name="selected_not_downloaded_label">A nem letöltött epizódok kiválasztása</string>
+ <string name="queued_label">Sorba állított</string>
+ <string name="selected_queued_label">A sorba állított epizódok kiválasztva</string>
+ <string name="not_queued_label">Nem sorba állított</string>
+ <string name="selected_not_queued_label">A nem sorba állított epizódok kiválasztva</string>
+ <string name="has_media">Médiát tartalmaz</string>
+ <string name="selected_has_media_label">Médiát tartalmazó kiválasztott epizódok</string>
<!--Sort-->
<string name="sort_title_a_z">Cím (A \u2192 Z)</string>
<string name="sort_title_z_a">Cím (Z \u2192 A)</string>
- <string name="sort_date_new_old">Dátum (Új \u2192 Régi)</string>
- <string name="sort_date_old_new">Dátum (Régi \u2192 Új)</string>
- <string name="sort_duration_short_long">Hossz (Rövid \u2192 Hosszú)</string>
- <string name="sort_duration_long_short">Hossz (Hosszú \u2192 Rövid)</string>
+ <string name="sort_date_new_old">Dátum (új \u2192 régi)</string>
+ <string name="sort_date_old_new">Dátum (régi \u2192 új)</string>
+ <string name="sort_duration_short_long">Hossz (rövid \u2192 hosszú)</string>
+ <string name="sort_duration_long_short">Hossz (hosszú \u2192 rövid)</string>
<!--Rating dialog-->
- <string name="rating_later_label">Kérdezz rákésőbb</string>
+ <string name="rating_title">Kedveli az AntennaPodot?</string>
+ <string name="rating_message">Értékelnénk, ha időt szánna az AntennaPod értékelésére.</string>
+ <string name="rating_never_label">Hagyjon békén</string>
+ <string name="rating_later_label">Emlékeztessen később</string>
+ <string name="rating_now_label">Rendben, csináljuk!</string>
<!--Audio controls-->
- <string name="audio_controls">Hang vezérlők</string>
+ <string name="audio_controls">Hangvezérlők</string>
<string name="playback_speed">Lejátszási sebesség</string>
<string name="volume">Hangerő</string>
<string name="left_short">B</string>
<string name="right_short">J</string>
<string name="audio_effects">Hangeffektek</string>
- <string name="stereo_to_mono">Lekeverés: Sztereót Monora</string>
- <string name="sonic_only">Csak Sonic</string>
+ <string name="stereo_to_mono">Lekeverés: sztereót monóra</string>
+ <string name="sonic_only">Csak Sonic esetén</string>
+ <string name="exoplayer_only">Csak ExoPlayer esetén</string>
<!--proxy settings-->
<string name="proxy_type_label">Típus</string>
<string name="host_label">Kiszolgáló</string>
<string name="port_label">Port</string>
- <string name="optional_hint">(Opcionális)</string>
+ <string name="optional_hint">(Nem kötelező)</string>
<string name="proxy_test_label">Teszt</string>
+ <string name="proxy_checking">Ellenőrzés…</string>
<string name="proxy_test_successful">Teszt sikeres</string>
<string name="proxy_test_failed">Teszt sikertelen</string>
- <string name="proxy_host_empty_error">Kiszolgáló nem lehet üres</string>
- <string name="proxy_port_invalid_error">Port nem helyes</string>
+ <string name="proxy_host_empty_error">A kiszolgáló nem lehet üres</string>
+ <string name="proxy_host_invalid_error">A kiszolgáló nem érvényes IP-cím vagy domain</string>
+ <string name="proxy_port_invalid_error">A port nem helyes</string>
<!--Subscriptions fragment-->
+ <string name="subscription_num_columns">Oszlopok száma</string>
<!--Database import/export-->
<string name="import_export">Adatbázis importálása/exportálása</string>
+ <string name="import_export_warning">Ez a kísérleti funkció arra használható, hogy átküldje a feliratkozásait és a lejátszott epizódokat egy másik eszközre.\n\nAz exportált adatbázisok csak az AntennaPod ugyanazon verziójába importálhatóak. Különben ez a funkció váratlan működést okozhat.\n\nImportálás után az epizódok letöltöttként jelenhetnek meg, pedig nincsenek letöltve. Csak nyomja meg a lejátszás gombot az epizódokon, hogy az AntennaPod észrevegye ezt.</string>
<string name="label_import">Importálás</string>
<string name="label_export">Exportálás</string>
- <string name="import_select_file">Fájl kiálasztás importáláshoz</string>
- <string name="export_ok">Exportálás sikeres</string>
+ <string name="import_select_file">Importálandó fájl kiválasztása</string>
+ <string name="export_ok">Exportálás sikeres.</string>
+ <string name="import_ok">Importálás sikeres.\n\nNyomja meg az OK gombot az AntennaPod újraindításához</string>
<!--Casting-->
+ <string name="cast_media_route_menu_title">Lejátszás ezen…</string>
+ <string name="cast_disconnect_label">A továbbítási munkamenet bontása</string>
+ <string name="cast_not_castable">A kiválasztott média nem kompatibilis a továbbításhoz használt eszközzel</string>
+ <string name="cast_failed_to_play">A médialejátszás indítása sikertelen</string>
+ <string name="cast_failed_to_stop">A médialejátszás leállítása sikertelen</string>
+ <string name="cast_failed_to_pause">A médialejátszás szüneteltetése sikertelen</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
- <string name="cast_failed_setting_volume">Hiba a hangerő beállítása közben</string>
- <string name="cast_failed_media_error_skipping">Hiba a lejátszás közben. Átugrás...</string>
+ <string name="cast_failed_setting_volume">Hiba a hangerő beállításakor</string>
+ <string name="cast_failed_no_connection">Nincs kapcsolat a továbbításhoz használandó eszközzel</string>
+ <string name="cast_failed_no_connection_trans">A kapcsolat megszakadt a továbbításhoz használandó eszközzel. Az alkalmazás próbál újrakapcsolódni, ha lehetséges. Várjon néhány másodpercet, és próbálja újra.</string>
+ <string name="cast_failed_status_request">Nem sikerült szinkronizálni a továbbításhoz használt eszközön</string>
+ <string name="cast_failed_seek">Nem sikerült az új pozícióra tekerni a továbbításhoz használt eszközön</string>
+ <string name="cast_failed_receiver_player_error">A fogadó lejátszó súlyos hibát tapasztalt</string>
+ <string name="cast_failed_media_error_skipping">Hiba médialejátszáskor. Átugrás…</string>
<!--Notification channels-->
+ <string name="notification_channel_user_action">Művelet szükséges</string>
+ <string name="notification_channel_user_action_description">Akkor látszik, ha művelet szükséges, például ha meg kell adnia egy jelszót.</string>
+ <string name="notification_channel_downloading">Letöltés</string>
+ <string name="notification_channel_downloading_description">Letöltés közben jelenik meg</string>
+ <string name="notification_channel_playing">Most játszott</string>
+ <string name="notification_channel_playing_description">Lehetővé teszi a lejátszás vezérlését. Ez a fő értesítés, amit a podcast lejátszásakor lát.</string>
+ <string name="notification_channel_error">Hibák</string>
+ <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_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 9ec2ad151..111e9932a 100644
--- a/core/src/main/res/values-it/strings.xml
+++ b/core/src/main/res/values-it/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronizza con altri dispositivi</string>
<string name="gpodnet_auth_label">Accesso a gpodder.net</string>
- <string name="free_space_label">%1$s disponibili</string>
<string name="episode_cache_full_title">Cache degli episodi piena</string>
<string name="episode_cache_full_message">Lo spazio di memoria della cache degli episodi è esaurito. Puoi aumentarlo nelle Impostazioni</string>
- <string name="synchronizing">Sincronizzazione...</string>
<!--Statistics fragment-->
<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>
@@ -33,6 +31,8 @@
<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_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_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>
<!--Main activity-->
<string name="drawer_open">Apri il menù</string>
<string name="drawer_close">Chiudi il menù</string>
@@ -63,7 +63,6 @@
<string name="author_label">Autore(i)</string>
<string name="language_label">Lingua</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Impostazioni</string>
<string name="cover_label">Immagine</string>
<string name="error_label">Errore</string>
<string name="error_msg_prefix">È stato rilevato un errore:</string>
@@ -72,14 +71,9 @@
<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="chapters_label">Capitoli</string>
<string name="chapter_duration">Durata: %1$s</string>
- <string name="shownotes_label">Note dell\'episodio</string>
<string name="description_label">Descrizione</string>
- <string name="most_recent_prefix">Episodio più recente:\u0020</string>
<string name="episodes_suffix">\u0020episodi</string>
- <string name="length_prefix">Durata:\u0020</string>
- <string name="size_prefix">Dimensione:\u0020</string>
<string name="processing_label">Elaborazione in corso</string>
- <string name="loading_label">Caricamento...</string>
<string name="save_username_password_label">Salva nome utente e password</string>
<string name="close_label">Chiudi</string>
<string name="retry_label">Riprova</string>
@@ -108,8 +102,6 @@
<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="podcastdirectories_label">Trova un podcast nella directory</string>
- <string name="podcastdirectories_descr">Per trovare podcasts puoi cercare su iTunes o fyyd, oppure puoi esplorare gpodder.net per nome, categoria oppure popolarità.</string>
<string name="browse_gpoddernet_label">Esplora gpodder.net</string>
<string name="discover">Scopri</string>
<string name="discover_more">altro »</string>
@@ -131,13 +123,13 @@
<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_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="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="load_complete_feed">Aggiorna podcast completo</string>
- <string name="hide_episodes_title">Nascondi gli episodi</string>
<string name="batch_edit">Modifica in gruppo</string>
<string name="select_all_above">Seleziona tutti sopra</string>
<string name="select_all_below">Seleziona tutti sotto</string>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Riproduci</string>
<string name="pause_label">Pausa</string>
- <string name="stop_label">Ferma</string>
<string name="stream_label">Stream</string>
- <string name="remove_label">Rimuovi</string>
<string name="delete_label">Elimina</string>
<string name="delete_failed">Impossibile eliminare il file. Il riavvio del dispositivo potrebbe aiutare.</string>
<string name="delete_episode_label">Elimina episodio</string>
@@ -207,14 +197,12 @@
<string name="removed_item">Elemento rimosso</string>
<!--Download messages and labels-->
<string name="download_successful">successo</string>
- <string name="download_failed">fallito</string>
<string name="download_pending">Download in attesa</string>
<string name="download_running">Download in corso</string>
<string name="download_error_details">Dettagli</string>
<string name="download_error_details_message">%1$s \n\nURL file:\n%2$s</string>
<string name="download_error_device_not_found">Spazio di archiviazione non trovato</string>
<string name="download_error_insufficient_space">Spazio insufficiente</string>
- <string name="download_error_file_error">Errore del file</string>
<string name="download_error_http_data_error">Errore dei dati HTTP</string>
<string name="download_error_error_unknown">Errore sconosciuto</string>
<string name="download_error_parser_exception">Eccezione del decodificatore</string>
@@ -224,11 +212,10 @@
<string name="download_error_unauthorized">Errore di autenticazione</string>
<string name="download_error_file_type_type">Errore Formato FIle</string>
<string name="download_error_forbidden">Proibito</string>
- <string name="cancel_all_downloads_label">Annulla tutti i download</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_report_title">Download completato con un errore (o errori)</string>
- <string name="download_report_content_title">Rapporto del downoad</string>
+ <string name="download_report_content_title">Rapporto del download</string>
<string name="download_error_malformed_url">URL malformato</string>
<string name="download_error_io_error">Errore IO</string>
<string name="download_error_request_error">Errore della richiesta</string>
@@ -243,7 +230,6 @@
<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_type_image">Immagine</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>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">Nessun elemento multimediale in riproduzione</string>
<string name="player_buffering_msg">Buffer in corso</string>
<string name="player_go_to_picture_in_picture">Modalità picture-in-picture</string>
- <string name="playbackservice_notification_title">Riproduzione del podcast in corso</string>
<string name="unknown_media_key">AntennaPod - Chiave dell\'elemento multimediale sconosciuta: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Blocca la coda</string>
<string name="unlock_queue">Sblocca la coda</string>
<string name="queue_locked">Coda bloccata</string>
<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="undo">Undo</string>
- <string name="removed_from_queue">Oggetto rimosso</string>
<string name="move_to_top_label">Sposta all\'inizio</string>
<string name="move_to_bottom_label">Sposta in fondo</string>
<string name="sort">Ordina</string>
@@ -297,13 +283,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Scarica plugin</string>
<string name="no_playback_plugin_title">Plugin non installato</string>
- <string name="no_playback_plugin_or_sonic_msg">Per far funzionare la riproduzione a velocità variabile raccomandiamo di usare il Sonic mediaplayer integrato [Android 4.1+].\n\nAltrimenti è possibile scaricare il plugin di terze parti <i>Prestissimo</i> dal Play Store.\nQualsiasi problema riscontrato con Prestissimo non dipende da AntennaPod e dovrebbe esser segnalato allo sviluppatore del plugin.</string>
<string name="set_playback_speed_label">Velocità di riproduzione</string>
<string name="enable_sonic">Abilita Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Nessun episodio in coda</string>
<string name="no_items_label">Aggiungi un episodio scaricandolo o tenendo premuto e selezionando \"Aggiungi alla coda\"</string>
- <string name="no_feeds_label">Non sei ancora abbonato a nessun podcast. </string>
<string name="no_shownotes_label">Questo episodio non ha note.</string>
<string name="no_run_downloads_head_label">Nessun download in corso</string>
<string name="no_run_downloads_label">Puoi scaricare gli episodi dalla vista dettagliata del podcast.</string>
@@ -321,11 +305,11 @@
<string name="no_fav_episodes_label">Puoi aggiungere episodi ai preferiti tenendo premuto su di essi.</string>
<string name="no_chapters_head_label">Nessun capitolo</string>
<string name="no_chapters_label">Questo episodio non ha capitoli.</string>
+ <string name="no_subscriptions_head_label">Nessuna sottoscrizione</string>
+ <string name="no_subscriptions_label">Per aggiungere un podcast, premi il tasto + in basso.</string>
<!--Preferences-->
<string name="storage_pref">Memoria</string>
<string name="project_pref">Progetto</string>
- <string name="other_pref">Altro</string>
- <string name="about_pref">Informazioni</string>
<string name="queue_label">Coda</string>
<string name="integrations_label">Integrazioni</string>
<string name="automation">Automazione</string>
@@ -368,12 +352,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Imposta orario</string>
<string name="pref_autoUpdateIntervallOrTime_every">ogni %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">alle %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Abilita il download dei media solo tramite WiFi</string>
<string name="pref_followQueue_title">Riproduzione continua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Download dei media su WiFi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Disconnessione cuffie</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Riconnessione cuffie</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Riconnessione Bluetooth</string>
+ <string name="pref_stream_over_download_title">Preferisci streaming</string>
+ <string name="pref_stream_over_download_sum">Mostra il tasto stream nelle liste al posto del tasto download.</string>
<string name="pref_mobileUpdate_title">Aggiornamenti su rete mobile</string>
<string name="pref_mobileUpdate_sum">Seleziona quali operazioni sono consentite su reti mobili a consumo</string>
<string name="pref_mobileUpdate_refresh">Aggiornamento feed</string>
@@ -381,11 +365,8 @@
<string name="pref_mobileUpdate_auto_download">Download automatici</string>
<string name="pref_mobileUpdate_episode_download">Download episodi</string>
<string name="pref_mobileUpdate_streaming">Streaming</string>
- <string name="refreshing_label">Aggiornamento</string>
<string name="user_interface_label">Interfaccia utente</string>
<string name="pref_set_theme_title">Seleziona un tema</string>
- <string name="pref_nav_drawer_title">Personalizza menù di navigazione</string>
- <string name="pref_nav_drawer_sum">Personalizza l\'aspetto del menù di navigazione</string>
<string name="pref_nav_drawer_items_title">Seleziona elementi del menù</string>
<string name="pref_nav_drawer_items_sum">Aggiungi o rimuovi gli elementi che appaiono nel menù laterale.</string>
<string name="pref_nav_drawer_feed_order_title">Imposta l\'ordine delle sottoscrizioni</string>
@@ -397,13 +378,16 @@
<string name="pref_automatic_download_sum">Configura il download automatico degli episodi</string>
<string name="pref_autodl_wifi_filter_title">Abilita il filtro Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Abilita il download automatico solo per alcune reti Wi-Fi selezionate.</string>
- <string name="pref_autodl_allow_on_mobile_title">Scarica da rete mobile</string>
- <string name="pref_autodl_allow_on_mobile_sum">Abilita il download automatico anche tramite la rete mobile.</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_sum">Permetti il download automatico quando la batteria non è in carica</string>
<string name="pref_parallel_downloads_title">Download simulanei</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>
+ <string name="pref_episode_cover_summary">Usa l\'immagine dell\'episodio se disponibile. Se disattivata, verrà usata sempre l\'immagine del podcast.</string>
+ <string name="pref_theme_title_use_system">Usa tema di sistema</string>
<string name="pref_theme_title_light">Chiaro</string>
<string name="pref_theme_title_dark">Scuro</string>
<string name="pref_theme_title_trueblack">Nero (AMOLED ready)</string>
@@ -423,12 +407,12 @@
<string name="pref_gpodnet_full_sync_sum">Sincronizza tutte le sottoscrizioni e gli stati degli episodi con gpodder.net</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Ultimo tentativo: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronizzazione avviata</string>
- <string name="pref_gpodnet_full_sync_started">Sincronizzazione avviata</string>
<string name="pref_gpodnet_login_status"><![CDATA[Accesso come <i>%1$s</i> con il dispositivo <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Notifica gli errori di sincronizzazione</string>
<string name="pref_gpodnet_notifications_sum">Non si applica agli errori di autenticazione.</string>
<string name="pref_playback_speed_title">Velocità di riproduzione</string>
<string name="pref_playback_speed_sum">Personalizza le velocità disponibili per la riproduzione audio a velocità variabile</string>
+ <string name="pref_feed_playback_speed_sum">Velocità di riproduzione da usare per gli episodi di questa sottoscrizione</string>
<string name="pref_playback_time_respects_speed_title">Adatta info alla velocità di riproduzione</string>
<string name="pref_playback_time_respects_speed_sum">La posizione del cursore e la durata si adattano alla velocità di riproduzione scelta.</string>
<string name="pref_fast_forward">Tempo di salto in avanti</string>
@@ -450,21 +434,25 @@
<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_queueAddToFront_sum">Aggiungi i nuovi episodi in cima alla coda.</string>
- <string name="pref_queueAddToFront_title">Aggiungi in cima alla coda</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>
+ <string name="enqueue_location_front">Fronte</string>
+ <string name="enqueue_location_after_current">Dopo l\'episodio attuale</string>
<string name="pref_smart_mark_as_played_disabled">Disabilitato</string>
<string name="pref_image_cache_size_title">Dimensione cache delle immagini</string>
<string name="pref_image_cache_size_sum">Spazio su disco usato per la cache delle immagini.</string>
- <string name="crash_report_title">Report dei crash</string>
- <string name="crash_report_sum">Invia il report dell\'ultimo crash via e-mail</string>
- <string name="send_email">Invia e-mail</string>
+ <string name="view_mailing_list">Visualizza mailing list</string>
+ <string name="bug_report_title">Segnala un problema</string>
+ <string name="open_bug_tracker">Apri il bug tracker</string>
+ <string name="copy_to_clipboard">Copia degli appunti</string>
+ <string name="copied_to_clipboard">Copiato negli appunti</string>
<string name="experimental_pref">Sperimentale</string>
<string name="pref_media_player_message">Seleziona il media player da usare per riprodurre i file</string>
<string name="pref_current_value">Impostazione attuale: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Imposta proxy di rete</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Problemi noti</string>
+ <string name="pref_faq">Domande frequenti - FAQ</string>
<string name="pref_no_browser_found">Nessun browser web trovato.</string>
<string name="pref_cast_title">Supporto a Chromecast</string>
<string name="pref_cast_message_play_flavor">Abilita il supporto per la riproduzione multimediale remota sui device Cast (Chromecast, casse esterne o Android TV)</string>
@@ -490,6 +478,16 @@
<string name="back_button_go_to_page_title">Seleziona pagina</string>
<string name="pref_delete_removes_from_queue_title">Rimuovi dalla coda gli eliminati</string>
<string name="pref_delete_removes_from_queue_sum">Rimuovi automaticamente un episodio dalla coda quando viene eliminato.</string>
+ <!--About screen-->
+ <string name="about_pref">Informazioni</string>
+ <string name="antennapod_version">Versione di AntennaPod</string>
+ <string name="developers">Sviluppatori</string>
+ <string name="developers_summary">Chiunque può contribuire a rendere AntennaPod migliore</string>
+ <string name="translators">Traduttori</string>
+ <string name="translators_summary">Le traduzioni sono create dagli utenti di AntennaPod grazie a Transifex</string>
+ <string name="privacy_policy">Privacy policy</string>
+ <string name="licenses">Licenze</string>
+ <string name="licenses_summary">AntennaPod usa altri ottimi software</string>
<!--Search-->
<string name="search_hint">Cerca negli episodi</string>
<string name="found_in_shownotes_label">Trovato nelle note dell\'episodio</string>
@@ -501,22 +499,16 @@
<string name="found_in_title_label">Trovato nel titolo</string>
<string name="no_results_for_query">Nessun risultato trovato per \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">I file OPML ti permettono di spostare i tuoi podcast da un programma ad un altro.</string>
<string name="opml_import_option">Opzione %1$d</string>
<string name="opml_import_explanation_1">Scegli un percorso specifico dal filesystem locale.</string>
- <string name="opml_import_explanation_2">Usa un\'applicazione esterna come Dropbox, Google Drive o il tuo gestore file preferito per aprire un file OPML.</string>
<string name="opml_import_explanation_3">Molte applicazioni come Google Mail, Dropbox, Google Drive e i gestori di file possono <i>aprire</i> i file OPML <i>con</i> AntennaPod.</string>
- <string name="start_import_label">Avvio importazione</string>
<string name="opml_import_label">Importa da OPML</string>
- <string name="opml_directory_error">ERRORE!</string>
<string name="reading_opml_label">Lettura OPML file in corso</string>
<string name="opml_reader_error">E\' stato riscontrato un errore nell\'apertura del documento OPML</string>
<string name="opml_import_error_no_file">Nessun file selezionato!</string>
<string name="select_all_label">Seleziona tutti</string>
<string name="deselect_all_label">Deseleziona tutti</string>
- <string name="select_options_label">Seleziona...</string>
<string name="choose_file_from_filesystem">Dal filesystem locale</string>
- <string name="choose_file_from_external_application">Utilizza un\'applicazione esterna</string>
<string name="opml_export_label">Esporta in OPML</string>
<string name="html_export_label">Esporta in HTML</string>
<string name="exporting_label">Esportazione in corso...</string>
@@ -609,8 +601,7 @@
<string name="pref_restart_required">AntennaPod deve essere riavviato per applicare le modifiche.</string>
<!--Online feed view-->
<string name="subscribe_label">Abbonati</string>
- <string name="subscribed_label">Abbonato</string>
- <string name="downloading_label">Download in corso...</string>
+ <string name="subscribing_label">Sottoscrizione...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Riavvolgi</string>
<string name="fast_forward_label">Avanti veloce</string>
@@ -718,7 +709,6 @@
<string name="cast_failed_setting_volume">Impostazione del volume fallita</string>
<string name="cast_failed_no_connection">Nessuna connessione al dispositivo di ricezione</string>
<string name="cast_failed_no_connection_trans">Connessione al dispositivo persa. L\'applicazione sta cercando di ristabilire la connessione, se possibile. Per favore attendi qualche secondo e riprova.</string>
- <string name="cast_failed_perform_action">Esecuzione dell\'operazione fallita</string>
<string name="cast_failed_status_request">Sincronizzazione con il dispositivo ricevente fallita</string>
<string name="cast_failed_seek">Ricerca della nuova posizione sul dispositivo ricevente fallita</string>
<string name="cast_failed_receiver_player_error">Il dispositivo ricevente ha restituito un errore grave</string>
@@ -733,4 +723,7 @@
<string name="notification_channel_error">Errori</string>
<string name="notification_channel_error_description">Mostrato se qualcosa è andato storto, per esempio se fallisce il download o la sincronizzazione di gpodder.</string>
<string name="import_bad_file">File non valido/corrotto</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">Crea widget</string>
+ <string name="widget_opacity">Opacità</string>
</resources>
diff --git a/core/src/main/res/values-iw-rIL/strings.xml b/core/src/main/res/values-iw-rIL/strings.xml
index d688b3935..dde0220dc 100644
--- a/core/src/main/res/values-iw-rIL/strings.xml
+++ b/core/src/main/res/values-iw-rIL/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">סנכרון עם מכשירים אחרים</string>
<string name="gpodnet_auth_label">כניסה אל gpodder.net</string>
- <string name="free_space_label">%1$s פנויים</string>
<string name="episode_cache_full_title">מטמון הפרקים מלא</string>
<string name="episode_cache_full_message">מטמון הפרקים התמלא. ניתן להגדיל את גודל המטמון בהגדרות.</string>
- <string name="synchronizing">מתבצע סנכרון…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">זמן ניגון הפודקאסטים הכולל:</string>
<string name="statistics_details_dialog">%1$d מתוך %2$d פרקים החלו.\n\nנוגנו %3$s מתוך %4$s.</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">חישוב זמן שנוגן בפועל. נגינה כפולה נספרת פעמיים, בעוד שסימון כנוגן לא נחשב.</string>
<string name="statistics_mode_count_all">סיכום כל הפודקאסטים שסומנו כנוגנו</string>
<string name="statistics_speed_not_counted">לתשומת לבך: אין התייחסות למהירות הנגינה.</string>
+ <string name="statistics_reset_data">איפוס נתונים סטטיסטיים</string>
+ <string name="statistics_reset_data_msg">פעולה זו תמחק את משך הנגינה של כל הפרקים. להמשיך?</string>
<!--Main activity-->
<string name="drawer_open">פתיחת תפריט</string>
<string name="drawer_close">סגירת תפריט</string>
@@ -63,7 +63,6 @@
<string name="author_label">יוצרים</string>
<string name="language_label">שפה</string>
<string name="url_label">כתובת</string>
- <string name="podcast_settings_label">הגדרות</string>
<string name="cover_label">תמונה</string>
<string name="error_label">שגיאה</string>
<string name="error_msg_prefix">אירעה שגיאה:</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">אין אחסון חיצוני זמין. נא לוודא שיש אחסון חיצוני מעוגן כדי שהיישומון יוכל לפעול כראוי.</string>
<string name="chapters_label">פרקים</string>
<string name="chapter_duration">משך: %1$s</string>
- <string name="shownotes_label">הערות פרק</string>
<string name="description_label">תיאור</string>
- <string name="most_recent_prefix">הפרק האחרון:\u0020</string>
<string name="episodes_suffix">\u0020פרקים</string>
- <string name="length_prefix">אורך:\u0020</string>
- <string name="size_prefix">גודל:\u0020</string>
<string name="processing_label">מתבצע עיבוד</string>
- <string name="loading_label">בטעינה...</string>
<string name="save_username_password_label">שמירת שם משתמש וססמה</string>
<string name="close_label">סגירה</string>
<string name="retry_label">לנסות שוב</string>
@@ -112,8 +106,6 @@
<string name="feedurl_label">כתובת הזנה</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">הוספת פודקאסט לפי כתובת</string>
- <string name="podcastdirectories_label">חפש פודקאסט בספריה</string>
- <string name="podcastdirectories_descr">לאיתור פודקאסטים חדשים, ניתן לחפש ב־iTunes או ב־fyyd או לעיין ב־gpodder.net לפי שם, קטגוריה או פופולריות.</string>
<string name="browse_gpoddernet_label">עיון ב־gpodder.net</string>
<string name="discover">עיון</string>
<string name="discover_more">עוד »</string>
@@ -135,13 +127,13 @@
<string name="share_link_label">שיתוף כתובת פרק</string>
<string name="share_link_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>
- <string name="hide_episodes_title">הסתרת פרקים</string>
<string name="batch_edit">עריכה כמותית</string>
<string name="select_all_above">בחירת כל אלו שלעיל</string>
<string name="select_all_below">בחירת כל אלו שלהלן</string>
@@ -167,9 +159,7 @@
</plurals>
<string name="play_label">ניגון</string>
<string name="pause_label">השהיה</string>
- <string name="stop_label">עצירה</string>
<string name="stream_label">הזרמה</string>
- <string name="remove_label">הסרה</string>
<string name="delete_label">מחיקה</string>
<string name="delete_failed">לא ניתן למחוק קובץ. הפעלת המכשיר מחדש עשויה לסייע.</string>
<string name="delete_episode_label">מחיקת פרק</string>
@@ -223,14 +213,12 @@
<string name="removed_item">פריט הוסר</string>
<!--Download messages and labels-->
<string name="download_successful">הצלחה</string>
- <string name="download_failed">כשל</string>
<string name="download_pending">הורדה ממתינה</string>
<string name="download_running">הורדה מתבצעת</string>
<string name="download_error_details">פרטים</string>
<string name="download_error_details_message">%1$s \n\nכתובת הקובץ:\n%2$s</string>
<string name="download_error_device_not_found">התקן האחסון לא נמצא</string>
<string name="download_error_insufficient_space">אין די שטח אחסון</string>
- <string name="download_error_file_error">שגיאת קובץ</string>
<string name="download_error_http_data_error">שגיאת נתוני HTTP</string>
<string name="download_error_error_unknown">שגיאה לא ידועה</string>
<string name="download_error_parser_exception">שגיאת מפענח</string>
@@ -240,7 +228,6 @@
<string name="download_error_unauthorized">שגיאת אימות</string>
<string name="download_error_file_type_type">שגיאת סוג קובץ</string>
<string name="download_error_forbidden">אסור</string>
- <string name="cancel_all_downloads_label">ביטול כל ההורדות</string>
<string name="download_canceled_msg">הורדה בוטלה</string>
<string name="download_canceled_autodownload_enabled_msg">ההורדה בוטלה\nה<i>הורדה האוטומטית</i> הושבתה עבור פריט זה</string>
<string name="download_report_title">הורדות הושלמו עם שגיאה אחת או יותר</string>
@@ -261,7 +248,6 @@
<string name="download_log_title_unknown">כותרת לא ידועה</string>
<string name="download_type_feed">הזנה</string>
<string name="download_type_media">קובץ מדיה</string>
- <string name="download_type_image">תמונה</string>
<string name="download_request_error_dialog_message_prefix">אירעה שגיאה בעת הניסיון הורדת הקובץ:\u0020</string>
<string name="null_value_podcast_error">לא סופקו פרקים שניתן להציג.</string>
<string name="authentication_notification_title">נדרש אימות</string>
@@ -287,16 +273,16 @@
<string name="no_media_playing_label">אין מדיה מתנגנת</string>
<string name="player_buffering_msg">החוצץ מתמלא</string>
<string name="player_go_to_picture_in_picture">מצב תמונה בתוך תמונה</string>
- <string name="playbackservice_notification_title">פודקאסט מתנגן</string>
<string name="unknown_media_key">אנטנה־פּוֹד - מפתח מדיה לא ידוע: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">נעילת תור</string>
<string name="unlock_queue">שחרור תור</string>
<string name="queue_locked">התור ננעל</string>
<string name="queue_unlocked">התור שוחרר מנעילה</string>
+ <string name="queue_lock_warning">נעילת התור אינה מונעת את ההחלקה או את סידור הפרקים מחדש.</string>
+ <string name="checkbox_do_not_show_again">לא להציג שוב</string>
<string name="clear_queue_label">ניקוי תור</string>
<string name="undo">ביטול</string>
- <string name="removed_from_queue">פריט הוסר</string>
<string name="move_to_top_label">העברה למעלה</string>
<string name="move_to_bottom_label">העברה למטה</string>
<string name="sort">מיון</string>
@@ -315,13 +301,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">הורדת תוסף</string>
<string name="no_playback_plugin_title">תוסף לא מותקן</string>
- <string name="no_playback_plugin_or_sonic_msg">כדי שתכונת המהירות המשתנה תפעל, מומלץ להפעיל את נגן המדיה המובנה בשם Sonic [מ־Android 4.1 ואילך].\n\nלחלופין, ניתן להוריד תוסף צד שלישי בשם <i>Prestissimo</i> מהחנות Play.\nכל תקלה שמופיעה ב־Prestissimo אינה באחריות אנטנה־פּוֹד ויש לדווח עליה לבעלים על התוסף.</string>
<string name="set_playback_speed_label">מהירויות ניגון</string>
<string name="enable_sonic">הפעלת Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">אין פרקים בתור</string>
<string name="no_items_label">ניתן להוסיף פרק על ידי הורדתו או ללחוץ על פרק לחיצה ארוכה ולבחור ב־„הוספה לתור”.</string>
- <string name="no_feeds_label">לא נרשמת לאף פודקאסט עדיין.</string>
<string name="no_shownotes_label">לפרק זה אין הערות פרק.</string>
<string name="no_run_downloads_head_label">אין הורדות פעילו</string>
<string name="no_run_downloads_label">ניתן להוריד פרקים במסך הפרטים של הפודקאסט.</string>
@@ -339,11 +323,11 @@
<string name="no_fav_episodes_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>
- <string name="other_pref">אחר</string>
- <string name="about_pref">על אודות</string>
<string name="queue_label">תור</string>
<string name="integrations_label">שילובים</string>
<string name="automation">אוטומציה</string>
@@ -386,12 +370,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">הגדרת הזמן ביום</string>
<string name="pref_autoUpdateIntervallOrTime_every">כל %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">ב־%1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">הורדת קובצי מדיה רק דרך רשת אלחוטית</string>
<string name="pref_followQueue_title">ניגון מתמשך</string>
- <string name="pref_downloadMediaOnWifiOnly_title">הורדת מדיה דרך רשת אלחוטית</string>
<string name="pref_pauseOnHeadsetDisconnect_title">ניתוק אוזניות</string>
<string name="pref_unpauseOnHeadsetReconnect_title">חיבור אוזניות מחדש</string>
<string name="pref_unpauseOnBluetoothReconnect_title">חיבור Bluetooth מחדש</string>
+ <string name="pref_stream_over_download_title">הזרמה עדיפה</string>
+ <string name="pref_stream_over_download_sum">הצגת כפתור הזרמה במקום כפתור הורדה ברשימות.</string>
<string name="pref_mobileUpdate_title">עדכון דרך רשת סלולרית</string>
<string name="pref_mobileUpdate_sum">לבחור מה מותר דרך חיבור רשת סלולרית</string>
<string name="pref_mobileUpdate_refresh">רענון הזנה</string>
@@ -399,11 +383,8 @@
<string name="pref_mobileUpdate_auto_download">הורדה אוטומטית</string>
<string name="pref_mobileUpdate_episode_download">הורדת פרקים</string>
<string name="pref_mobileUpdate_streaming">הזרמה</string>
- <string name="refreshing_label">מתבצע רענון</string>
<string name="user_interface_label">מנשק משתמש</string>
<string name="pref_set_theme_title">בחירת ערכת עיצוב</string>
- <string name="pref_nav_drawer_title">התאמת מגירת הניווט</string>
- <string name="pref_nav_drawer_sum">התאמת תצוגת מגירת הניווט.</string>
<string name="pref_nav_drawer_items_title">הגדרת פריטי מגירת ניווט</string>
<string name="pref_nav_drawer_items_sum">החלפת הפריטים שמופיעים במגירת הניווט.</string>
<string name="pref_nav_drawer_feed_order_title">הגדרת סדר מינויים</string>
@@ -415,13 +396,16 @@
<string name="pref_automatic_download_sum">הגדרת הורדה אטומטית של פרקים.</string>
<string name="pref_autodl_wifi_filter_title">הפעלת סינון רשתות אלחוטיות</string>
<string name="pref_autodl_wifi_filter_sum">לאפשר הורדה אוטומטית רק דרך רשתות אלחוטיות נבחרות.</string>
- <string name="pref_autodl_allow_on_mobile_title">הורדה דרך רשת סלולרית</string>
- <string name="pref_autodl_allow_on_mobile_sum">לאפשר הורדה אוטומטית דרך חיבור נתונים של רשת סלולרית.</string>
+ <string name="autodl_wifi_filter_permission_title">נדרשת הרשאה</string>
+ <string name="autodl_wifi_filter_permission_message">נדרשת הרשאת מיקום כדי לסנן רשתות אלחוטיות. יש לגעת כדי להעניק את ההרשאה.</string>
<string name="pref_automatic_download_on_battery_title">להוריד שלא בזמן טעינה</string>
<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_episode_cache_summary">המספר הכולל של פרקים שהורדו ונשמרים במכשיר. הורדה אוטומטית תושבת אם הכמות הזאת הושגה.</string>
+ <string name="pref_episode_cover_title">להשתמש בעטיפת הפרק</string>
+ <string name="pref_episode_cover_summary">להשתמש בעטיפת הפרק כאשר ניתן. אם האפשרות לא סומנה היישומון ישתמש בתמונת העטיפה של הפודקאסט.</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>
@@ -441,12 +425,12 @@
<string name="pref_gpodnet_full_sync_sum">סנכרון כל המינויים ומצבי הפרקים עם gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">ניסיון הסנכרון האחרון: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">הסנכרון התחיל</string>
- <string name="pref_gpodnet_full_sync_started">החל סנכרון מלא</string>
<string name="pref_gpodnet_login_status"><![CDATA[נכנסת בשם <i>%1$s</i> עם ההתקן <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">הצגת התרעות שגיאות סנכרון</string>
<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_playback_time_respects_speed_title">להתאים את פרטי המדיה למהירות הנגינה</string>
<string name="pref_playback_time_respects_speed_sum">המיקום המוצג והמשך מותאמים למהירות הנגינה</string>
<string name="pref_fast_forward">זמן דילוג בהאצה קדימה</string>
@@ -468,21 +452,25 @@
<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_queueAddToFront_sum">הוספת פרקים חדשים לראש התור.</string>
- <string name="pref_queueAddToFront_title">הוספה לראש התור.</string>
+ <string name="pref_enqueue_location_title">הוספת המיקום לתור</string>
+ <string name="pref_enqueue_location_sum">הוספת פרקים אל: %1$s</string>
+ <string name="enqueue_location_back">גב</string>
+ <string name="enqueue_location_front">חזית</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="crash_report_title">דיווח על קריסה</string>
- <string name="crash_report_sum">שליחת דיווח הקריסה העדכני ביותר דרך דוא״ל</string>
- <string name="send_email">שליחת דוא״ל</string>
+ <string name="view_mailing_list">צפייה ברשימת הדיוור</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_known_issues">תקלות ידועות</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>
@@ -508,6 +496,16 @@
<string name="back_button_go_to_page_title">בחירת עמוד</string>
<string name="pref_delete_removes_from_queue_title">מחיקה מסירה מהתור</string>
<string name="pref_delete_removes_from_queue_sum">הסרת פרק מהתור אוטומטית עם מחיקתו.</string>
+ <!--About screen-->
+ <string name="about_pref">על אודות</string>
+ <string name="antennapod_version">גרסת אנטנהפּוֹד</string>
+ <string name="developers">מפתחים</string>
+ <string name="developers_summary">כולם יכולים לסייע בשיפור אנטנהפּוֹד</string>
+ <string name="translators">מתרגמים</string>
+ <string name="translators_summary">תרגומים נוצרים על ידי משתמשים של אנטנהפּוֹד בעזרת Transifex</string>
+ <string name="privacy_policy">מדיניות פרטיות</string>
+ <string name="licenses">רישיונות</string>
+ <string name="licenses_summary">היישומון אנטנהפּוֹד משתמש בתכניות נהדרות נוספות</string>
<!--Search-->
<string name="search_hint">חיפוש אחר פרקים</string>
<string name="found_in_shownotes_label">נמצא בהערות הפרק</string>
@@ -519,22 +517,16 @@
<string name="found_in_title_label">נמצא בכותרת</string>
<string name="no_results_for_query">לא נמצאו תוצאות עבור „%1$s”</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">קובצי OPML מאפשרים לך להעביר את הפודקאסטים שלך ממכשיר אחד למשנהו.</string>
<string name="opml_import_option">אפשרות %1$d</string>
<string name="opml_import_explanation_1">נא לבחור נתיב קובץ מסוים ממערכת הקבצים המקומית.</string>
- <string name="opml_import_explanation_2">ניתן להשתמש ביישומים חיצוניים כמו Dropbox,‏ Google Drive או מנהל הקבצים המועדף עליך כדי לפתוח קובץ OPML.</string>
<string name="opml_import_explanation_3">יישומים רבים כגון Google Mail,‏ Dropbox,‏ Google Drive ורוב מנהלי הקבצים יכולים <i>לפתוח</i> קובצי OPML <i>עם</i> אנטנה־פּוֹד.</string>
- <string name="start_import_label">התחלת ייבוא</string>
<string name="opml_import_label">ייבוא OPML</string>
- <string name="opml_directory_error">שגיאה!</string>
<string name="reading_opml_label">קריאת קובץ OPML</string>
<string name="opml_reader_error">אירעה שגיאה בעת קריאת מסמך ה־OPML:</string>
<string name="opml_import_error_no_file">לא נבחר קובץ!</string>
<string name="select_all_label">בחירת הכול</string>
<string name="deselect_all_label">ביטול הבחירה</string>
- <string name="select_options_label">בחירה…</string>
<string name="choose_file_from_filesystem">ממערכת הקבצים המקומית</string>
- <string name="choose_file_from_external_application">שימוש ביישומון חיצוני</string>
<string name="opml_export_label">ייצוא OPML</string>
<string name="html_export_label">ייצוא HTML</string>
<string name="exporting_label">מתבצע ייצוא…</string>
@@ -633,8 +625,7 @@
<string name="pref_restart_required">יש להפעיל את אנטנה־פּוֹד מחדש כדי שהשינויים ייכנסו לתוקף.</string>
<!--Online feed view-->
<string name="subscribe_label">הרשמה</string>
- <string name="subscribed_label">נרשמת</string>
- <string name="downloading_label">מתבצעת הורדה…</string>
+ <string name="subscribing_label">מתבצע רישום…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">חזרה לאחור</string>
<string name="fast_forward_label">הרצה קדימה</string>
@@ -742,7 +733,6 @@
<string name="cast_failed_setting_volume">הגדרת עצמת השמע נכשלה</string>
<string name="cast_failed_no_connection">אין חיבור להתקן שידור</string>
<string name="cast_failed_no_connection_trans">החיבור להתקן שידור אבד. היישומון מנסה להתחבר שוב, אם ניתן. נא להמתין מספר שניות ואז לנסות שוב.</string>
- <string name="cast_failed_perform_action">ביצוע הפעולה נכשל</string>
<string name="cast_failed_status_request">הסנכרון עם התקן השידור נכשל</string>
<string name="cast_failed_seek">ההקפצה למיקום נגינה אחר בהתקן השידור נכשלה</string>
<string name="cast_failed_receiver_player_error">הנגן המקבל נתקל בשגיאה חמורה</string>
@@ -757,4 +747,7 @@
<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_create_button">יצירת וידג׳</string>
+ <string name="widget_opacity">אטימות</string>
</resources>
diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml
index 5c78ea4c9..8f0933564 100644
--- a/core/src/main/res/values-ja/strings.xml
+++ b/core/src/main/res/values-ja/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">他のデバイスと同期</string>
<string name="gpodnet_auth_label">gpodder.net ログイン</string>
- <string name="free_space_label">%1$s 空き</string>
<string name="episode_cache_full_title">エピソードキャッシュが一杯です</string>
<string name="episode_cache_full_message">エピソードキャッシュが制限に達しました。設定でキャッシュサイズを増やすことができます。</string>
- <string name="synchronizing">同期しています…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">ポッドキャストを再生した合計時間:</string>
<string name="statistics_details_dialog">%2$d から %1$d のエピソードが開始しました。\n\n%4$s から%3$s を再生しました。</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">実際に再生された時間を計算します。 2回再生すると2回カウントされ、再生済のマークはカウントされません</string>
<string name="statistics_mode_count_all">再生済みとマークされたすべてのポッドキャストを集計する</string>
<string name="statistics_speed_not_counted">注意: 再生速度は考慮されません。</string>
+ <string name="statistics_reset_data">統計情報のデータをリセット</string>
+ <string name="statistics_reset_data_msg">これにより、すべてのエピソードを再生した期間の履歴が消去されます。 続行しますか?</string>
<!--Main activity-->
<string name="drawer_open">メニューを開く</string>
<string name="drawer_close">メニューを閉じる</string>
@@ -63,7 +63,6 @@
<string name="author_label">作者</string>
<string name="language_label">言語</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">設定</string>
<string name="cover_label">映像</string>
<string name="error_label">エラー</string>
<string name="error_msg_prefix">エラーが発生しました。</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">外部ストレージが利用できません。アプリが正常に動作できるように、外部ストレージがマウントされていることを確認してください。</string>
<string name="chapters_label">チャプター</string>
<string name="chapter_duration">時間: %1$s</string>
- <string name="shownotes_label">ショーノート</string>
<string name="description_label">説明</string>
- <string name="most_recent_prefix">最も最近のエピソード:\u0020</string>
<string name="episodes_suffix">\u0020エピソード</string>
- <string name="length_prefix">長さ:\u0020</string>
- <string name="size_prefix">サイズ:\u0020</string>
<string name="processing_label">処理中</string>
- <string name="loading_label">ロード中…</string>
<string name="save_username_password_label">ユーザ名とパスワードを保存する</string>
<string name="close_label">閉じる</string>
<string name="retry_label">再試行</string>
@@ -106,8 +100,6 @@
<string name="feedurl_label">フィードURL</string>
<string name="etxtFeedurlHint">フィードまたはWebサイトのURL</string>
<string name="txtvfeedurl_label">URLでポッドキャストを追加</string>
- <string name="podcastdirectories_label">ディレクトリでポッドキャストを検索</string>
- <string name="podcastdirectories_descr">新しいポッドキャストでは、名前、カテゴリー、人気でiTunesやfyydを検索したり、gpodder.netを参照することができます。</string>
<string name="browse_gpoddernet_label">gpodder.netを参照</string>
<string name="discover">発見</string>
<string name="discover_more">さらに »</string>
@@ -129,13 +121,13 @@
<string name="share_link_label">エピソード URLを共有</string>
<string name="share_link_with_position_label">エピソード URL を位置と共に共有</string>
<string name="share_file_label">ファイルを共有</string>
+ <string name="share_website_url_label">ウェブサイト URLを共有</string>
<string name="share_feed_url_label">フィード URLを共有</string>
<string name="share_item_url_label">メディアファイル URLを共有</string>
<string name="share_item_url_with_position_label">メディアファイル URL を位置と共に共有</string>
<string name="feed_delete_confirmation_msg">ポッドキャスト \"%1$s\" とすべての(ダウンロード済を含む)エピソードを削除することを確認してください。</string>
<string name="feed_remover_msg">ポッドキャストを削除しています</string>
<string name="load_complete_feed">ポッドキャストをすべて更新</string>
- <string name="hide_episodes_title">エピソードを非表示にする</string>
<string name="batch_edit">一括編集</string>
<string name="select_all_above">上のすべてを選択</string>
<string name="select_all_below">下のすべてを選択</string>
@@ -158,9 +150,7 @@
</plurals>
<string name="play_label">再生</string>
<string name="pause_label">一時停止</string>
- <string name="stop_label">停止</string>
<string name="stream_label">ストリーム</string>
- <string name="remove_label">削除</string>
<string name="delete_label">削除</string>
<string name="delete_failed">ファイルを削除できません。デバイスを再起動してみてください。</string>
<string name="delete_episode_label">エピソードを削除</string>
@@ -199,14 +189,12 @@
<string name="removed_item">アイテムを削除しました</string>
<!--Download messages and labels-->
<string name="download_successful">完成</string>
- <string name="download_failed">失敗</string>
<string name="download_pending">ダウンロードは保留中</string>
<string name="download_running">ダウンロード実行中</string>
<string name="download_error_details">詳細</string>
<string name="download_error_details_message">%1$s \n\nファイル URL:\n%2$s</string>
<string name="download_error_device_not_found">ストレージ デバイスが見つかりません</string>
<string name="download_error_insufficient_space">スペースが不足しています</string>
- <string name="download_error_file_error">ファイルエラー</string>
<string name="download_error_http_data_error">HTTPデータエラー</string>
<string name="download_error_error_unknown">不明なエラー</string>
<string name="download_error_parser_exception">解析エラー</string>
@@ -216,7 +204,6 @@
<string name="download_error_unauthorized">認証エラー</string>
<string name="download_error_file_type_type">ファイルタイプ エラー</string>
<string name="download_error_forbidden">禁止</string>
- <string name="cancel_all_downloads_label">すべてのダウンロードをキャンセル</string>
<string name="download_canceled_msg">ダウンロードをキャンセルしました</string>
<string name="download_canceled_autodownload_enabled_msg">ダウンロードをキャンセルしました\nこのアイテムの <i>自動ダウンロード</i> を無効にしました</string>
<string name="download_report_title">ダウンロードがエラーで完了しました</string>
@@ -234,7 +221,6 @@
<string name="download_log_title_unknown">タイトル不明</string>
<string name="download_type_feed">フィード</string>
<string name="download_type_media">メディアファイル</string>
- <string name="download_type_image">画像</string>
<string name="download_request_error_dialog_message_prefix">ファイルのダウンロード中にエラーが発生しました:\u0020</string>
<string name="null_value_podcast_error">表示できるポッドキャストがありません。</string>
<string name="authentication_notification_title">認証が必要です</string>
@@ -260,16 +246,16 @@
<string name="no_media_playing_label">再生するメディアがありません</string>
<string name="player_buffering_msg">バッファー中</string>
<string name="player_go_to_picture_in_picture">ピクチャ-イン-ピクチャ モード</string>
- <string name="playbackservice_notification_title">ポッドキャストを再生中</string>
<string name="unknown_media_key">AntennaPod - 不明なメディアキー: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">キューをロック</string>
<string name="unlock_queue">キューのロックを解除</string>
<string name="queue_locked">キューをロックしました</string>
<string name="queue_unlocked">キューのロックを解除しました</string>
+ <string name="queue_lock_warning">キューをロックすると、エピソードのスワイプや並べ替えができなくなります。</string>
+ <string name="checkbox_do_not_show_again">今後表示しない</string>
<string name="clear_queue_label">キューをクリア</string>
<string name="undo">元に戻す</string>
- <string name="removed_from_queue">アイテムを削除しました</string>
<string name="move_to_top_label">先頭に移動</string>
<string name="move_to_bottom_label">最後に移動</string>
<string name="sort">並べ替え</string>
@@ -288,13 +274,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">プラグインをダウンロード</string>
<string name="no_playback_plugin_title">プラグイン はインストールされていません</string>
- <string name="no_playback_plugin_or_sonic_msg">再生速度の変更を機能させるには、内蔵の Sonic メディアプレーヤーを有効にすることを推奨します [Android 4.1+]。代わりに、Google Play からサードパーティのプラグイン <i>Prestissimo</i> をダウンロードすることができます。\nPrestissimo での問題は AntennaPod の責任ではなく、プラグインのオーナーに報告すべきです。</string>
<string name="set_playback_speed_label">再生速度</string>
<string name="enable_sonic">Sonic を有効にする</string>
<!--Empty list labels-->
<string name="no_items_header_label">エピソードはキューにありません</string>
<string name="no_items_label">エピソードをダウンロードして追加するか、エピソードを長押しして \"キューに追加\" を選択してください。</string>
- <string name="no_feeds_label">まだポッドキャストを何も購読していません。</string>
<string name="no_shownotes_label">このエピソードにショーノートはありません。</string>
<string name="no_run_downloads_head_label">実行中のダウンロードはありません</string>
<string name="no_run_downloads_label">ポッドキャスト詳細画面でエピソードをダウンロードできます。</string>
@@ -312,11 +296,11 @@
<string name="no_fav_episodes_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>
- <string name="other_pref">その他</string>
- <string name="about_pref">について</string>
<string name="queue_label">キュー</string>
<string name="integrations_label">統合</string>
<string name="automation">自動</string>
@@ -359,12 +343,12 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">時間をセット</string>
<string name="pref_autoUpdateIntervallOrTime_every">%1$s ごと</string>
<string name="pref_autoUpdateIntervallOrTime_at">%1$s に</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">WiFi接続時のみメディアファイルをダウンロードします</string>
<string name="pref_followQueue_title">連続再生</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFiメディアダウンロード</string>
<string name="pref_pauseOnHeadsetDisconnect_title">ヘッドフォン切断</string>
<string name="pref_unpauseOnHeadsetReconnect_title">ヘッドフォン再接続</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth再接続</string>
+ <string name="pref_stream_over_download_title">ストリーミングを優先する</string>
+ <string name="pref_stream_over_download_sum">ダウンロードボタンの代わりにストリームボタンをリストに表示します。</string>
<string name="pref_mobileUpdate_title">モバイル更新</string>
<string name="pref_mobileUpdate_sum">モバイルデータ接続時に許可するものを選択します</string>
<string name="pref_mobileUpdate_refresh">フィード更新</string>
@@ -372,11 +356,8 @@
<string name="pref_mobileUpdate_auto_download">自動ダウンロード</string>
<string name="pref_mobileUpdate_episode_download">エピソードのダウンロード</string>
<string name="pref_mobileUpdate_streaming">ストリーミング</string>
- <string name="refreshing_label">更新中</string>
<string name="user_interface_label">インターフェース</string>
<string name="pref_set_theme_title">テーマを選択</string>
- <string name="pref_nav_drawer_title">ナビゲーションドロワーをカスタマイズ</string>
- <string name="pref_nav_drawer_sum">ナビゲーションドロワーの外観をカスタマイズします。</string>
<string name="pref_nav_drawer_items_title">ナビゲーションドロワーを変更</string>
<string name="pref_nav_drawer_items_sum">ナビゲーションドロワーに表示するアイテムを変更します。</string>
<string name="pref_nav_drawer_feed_order_title">購読注文をセット</string>
@@ -388,13 +369,16 @@
<string name="pref_automatic_download_sum">エピソードの自動ダウンロードを構成します。</string>
<string name="pref_autodl_wifi_filter_title">Wi-Fiフィルターを有効にする</string>
<string name="pref_autodl_wifi_filter_sum">選択したWi-Fiネットワークに対してのみ自動ダウンロードを許可します。</string>
- <string name="pref_autodl_allow_on_mobile_title">モバイルデータ接続時にダウンロード</string>
- <string name="pref_autodl_allow_on_mobile_sum">モバイルデータ接続時にダウンロードを許可します</string>
+ <string name="autodl_wifi_filter_permission_title">アクセス許可が必要です</string>
+ <string name="autodl_wifi_filter_permission_message">Wi-Fi フィルターには位置情報のアクセス許可が必要です。 タップして許可を付与します。</string>
<string name="pref_automatic_download_on_battery_title">充電中以外の時にダウンロード</string>
<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_episode_cache_summary">デバイスにキャッシュされたダウンロード済エピソードの合計数。この数に達すると自動ダウンロードが抑制されます。</string>
+ <string name="pref_episode_cover_title">エピソードカバーを使用する</string>
+ <string name="pref_episode_cover_summary">エピソード固有のカバーが利用できる場合は常に使用します。 チェックされていない場合、アプリは常にポッドキャストのカバー画像を使用します。</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>
@@ -414,12 +398,12 @@
<string name="pref_gpodnet_full_sync_sum">すべての購読とエピソードの状態を gpodder.net で同期します。</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">最後に同期を試行: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">同期を開始しました</string>
- <string name="pref_gpodnet_full_sync_started">完全な同期を開始しました</string>
<string name="pref_gpodnet_login_status"><![CDATA[<i>%1$s</i> としてデバイス <i>%2$s</i> でログインしました]]></string>
<string name="pref_gpodnet_notifications_title">同期エラーの通知を表示</string>
<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_playback_time_respects_speed_title">メディア情報を再生速度に調整</string>
<string name="pref_playback_time_respects_speed_sum">表示される位置と時間が再生速度に調整されます</string>
<string name="pref_fast_forward">早送りのスキップ時間</string>
@@ -441,21 +425,25 @@
<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_queueAddToFront_sum">新しいエピソードをキューの先頭に追加します。</string>
- <string name="pref_queueAddToFront_title">キューの先頭に入れる</string>
+ <string name="pref_enqueue_location_title">キューに入れる場所</string>
+ <string name="pref_enqueue_location_sum">エピソードを追加: %1$s</string>
+ <string name="enqueue_location_back">背面</string>
+ <string name="enqueue_location_front">前面</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="crash_report_title">クラッシュレポート</string>
- <string name="crash_report_sum">メールで最新のクラッシュレポートを送信します</string>
- <string name="send_email">メールを送信</string>
+ <string name="view_mailing_list">メーリングリストを表示</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">FAQ</string>
- <string name="pref_known_issues">既知の問題</string>
+ <string name="pref_faq">よくある質問と答え</string>
<string name="pref_no_browser_found">Webブラウザーが見つかりません。</string>
<string name="pref_cast_title">Chromecast サポート</string>
<string name="pref_cast_message_play_flavor">(Chromecast、オーディオスピーカー、Android TV など) キャストデバイス上でリモートメディア再生のサポートを有効にします</string>
@@ -481,6 +469,16 @@
<string name="back_button_go_to_page_title">ページの選択</string>
<string name="pref_delete_removes_from_queue_title">キューから削除済を削除</string>
<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">翻訳は、Transifex を使用してAntennaPod のユーザーが作成します</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>
@@ -492,22 +490,16 @@
<string name="found_in_title_label">タイトルで見つかりました</string>
<string name="no_results_for_query">\"%1$s\" の結果は見つかりませんでした</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPMLファイルで、あるポッドキャッチャーから別のものにポッドキャストを移動することができます。</string>
<string name="opml_import_option">オプション %1$d</string>
<string name="opml_import_explanation_1">ローカルのファイルシステムから指定するファイルパスを選択してください。</string>
- <string name="opml_import_explanation_2">Dropbox、Google ドライブ、またはお好みのファイルマネージャなどの外部アプリケーションを使用して、OPML ファイルを開いてください。</string>
<string name="opml_import_explanation_3">Google メール、Dropbox、Google ドライブ、および多くのファイルマネージャーなどのアプリケーションで、AntennaPod <i>の</i> OPML ファイルを <i>開く</i> ことができます。</string>
- <string name="start_import_label">インポート開始</string>
<string name="opml_import_label">OPMLインポート</string>
- <string name="opml_directory_error">エラー!</string>
<string name="reading_opml_label">OPMLファイルを読み込み中</string>
<string name="opml_reader_error">OPMLドキュメントの読み込み中にエラーが発生しました:</string>
<string name="opml_import_error_no_file">ファイルが選択されていません!</string>
<string name="select_all_label">すべてを選択</string>
<string name="deselect_all_label">選択解除</string>
- <string name="select_options_label">選択…</string>
<string name="choose_file_from_filesystem">ローカル ファイルシステムから</string>
- <string name="choose_file_from_external_application">外部アプリケーションを使用する</string>
<string name="opml_export_label">OPMLエクスポート</string>
<string name="html_export_label">HTML エクスポート</string>
<string name="exporting_label">エクスポート中…</string>
@@ -597,8 +589,7 @@
<string name="pref_restart_required">この変更を有効にするには AntennaPod を再起動する必要があります。</string>
<!--Online feed view-->
<string name="subscribe_label">購読</string>
- <string name="subscribed_label">購読しました</string>
- <string name="downloading_label">ダウンロード中…</string>
+ <string name="subscribing_label">購読中…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">巻き戻し</string>
<string name="fast_forward_label">早送り</string>
@@ -706,7 +697,6 @@
<string name="cast_failed_setting_volume">音量の設定に失敗しました</string>
<string name="cast_failed_no_connection">キャストデバイスへの接続が存在しません</string>
<string name="cast_failed_no_connection_trans">キャストデバイスへの接続が失われました。可能な場合、アプリケーションは接続を再確立しようとします。数秒待ってから、もう一度お試しください。</string>
- <string name="cast_failed_perform_action">操作の実行に失敗しました</string>
<string name="cast_failed_status_request">キャストデバイスとの同期に失敗しました</string>
<string name="cast_failed_seek">キャストデバイスの新しい位置への移動に失敗しました</string>
<string name="cast_failed_receiver_player_error">レシーバープレーヤーで深刻なエラーが発生しました</string>
@@ -721,4 +711,7 @@
<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_create_button">ウィジェットを作成</string>
+ <string name="widget_opacity">透明度</string>
</resources>
diff --git a/core/src/main/res/values-lt/strings.xml b/core/src/main/res/values-lt/strings.xml
index 6abb40435..211e8412d 100644
--- a/core/src/main/res/values-lt/strings.xml
+++ b/core/src/main/res/values-lt/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sinchronizuojama su kitais įrenginiais</string>
<string name="gpodnet_auth_label">gpodder.net prisijungimas</string>
- <string name="free_space_label">%1$s laisva</string>
<string name="episode_cache_full_title">Epizodų podėlis pilnas</string>
<string name="episode_cache_full_message">Pasiektas epizodų podėlio dydžio limitas. Nustatymuose galite padidinti podėlio dydį.</string>
- <string name="synchronizing">Sinchronizuojama…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Bendra perklausytų tinklalaidžių trukmė:</string>
<string name="statistics_details_dialog">Paleisti %1$d iš %2$d epizodų.\n\nPerklausyta %3$s iš %4$s.</string>
@@ -33,6 +31,7 @@
<string name="statistics_mode_normal">Apskaičiuoti tikrąją atkūrimo trukmę. Perklausa dukart skaičiuojama dvigubai, o pažymėjimas perklausytu neskaičiuojamas</string>
<string name="statistics_mode_count_all">Sumuoti visas tinklalaides, pažymėtas kaip perklausytas</string>
<string name="statistics_speed_not_counted">Atkreipkite dėmesį: į atkūrimo spartą neatsižvelgiama.</string>
+ <string name="statistics_reset_data">Nustatyti statistikos duomenis iš naujo</string>
<!--Main activity-->
<string name="drawer_open">Atverti meniu</string>
<string name="drawer_close">Užverti meniu</string>
@@ -63,7 +62,6 @@
<string name="author_label">Autorius (-iai)</string>
<string name="language_label">Kalba</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Nustatymai</string>
<string name="cover_label">Paveikslėlis</string>
<string name="error_label">Klaida</string>
<string name="error_msg_prefix">Įvyko klaida:</string>
@@ -72,14 +70,9 @@
<string name="external_storage_error_msg">Nėra prieinamų išorinių laikmenų. Patikrinkite, jog išorinės laikmenos yra prijungtos, kad programėlė tinkamai veiktų.</string>
<string name="chapters_label">Skyriai</string>
<string name="chapter_duration">Trukmė: %1$s</string>
- <string name="shownotes_label">Laidos užrašai</string>
<string name="description_label">Aprašymas</string>
- <string name="most_recent_prefix">Naujausias epizodas:\u0020</string>
<string name="episodes_suffix">\u0020epizodai</string>
- <string name="length_prefix">Trukmė:\u0020</string>
- <string name="size_prefix">Dydis:\u0020</string>
<string name="processing_label">Apdorojama</string>
- <string name="loading_label">Kraunama...</string>
<string name="save_username_password_label">Išsaugoti vartotojo vardą ir slaptažodį</string>
<string name="close_label">Užverti</string>
<string name="retry_label">Bandyti vėl</string>
@@ -105,9 +98,9 @@
<string name="feedurl_label">Sklaidos kanalo URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Pridėti tinklalaidę pagal URL adresą</string>
- <string name="podcastdirectories_label">Rasti tinklalaidę aplanke</string>
- <string name="podcastdirectories_descr">Naujų tinklalaidžių atrasti galite ieškodami „iTunes“ ar „fyyd“ arba naršydami „gpodder.net“ kataloge pagal pavadinimą, kategoriją ar populiarumą.</string>
<string name="browse_gpoddernet_label">Naršyti gpodder.net svetainėje</string>
+ <string name="discover">Atraskite</string>
+ <string name="discover_more">daugiau »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Pažymėti visus kaip perklausytus</string>
<string name="mark_all_read_msg">Visi epizodai pažymėti kaip perklausyti</string>
@@ -129,7 +122,6 @@
<string name="feed_delete_confirmation_msg">Patvirtinkite, jog norite ištrinti tinklalaidę „%1$s“ ir VISUS jos epizodus (įskaitant atsiųstus epizodus).</string>
<string name="feed_remover_msg">Tinklalaidė šalinama</string>
<string name="load_complete_feed">Atnaujinti visą tinklalaidę</string>
- <string name="hide_episodes_title">Slėpti epizodus</string>
<string name="batch_edit">Keisti iš karto kelis</string>
<string name="select_all_above">Pažymėti visus aukščiau</string>
<string name="select_all_below">Pažymėti visus žemiau</string>
@@ -147,13 +139,24 @@
<string name="open_podcast">Atverti tinklalaidę</string>
<!--actions on feeditems-->
<string name="download_label">Atsisiųsti</string>
+ <plurals name="downloading_batch_label">
+ <item quantity="one">Atsiunčiamas %d epizodas.</item>
+ <item quantity="few">Atsiunčiami %d epizodai.</item>
+ <item quantity="many">Atsiunčiami %d epizodai.</item>
+ <item quantity="other">Atsiunčiami %d epizodai.</item>
+ </plurals>
<string name="play_label">Atkurti</string>
<string name="pause_label">Pristabdyti</string>
- <string name="stop_label">Sustabdyti</string>
<string name="stream_label">Klausytis tiesiogiai</string>
- <string name="remove_label">Pašalinti</string>
<string name="delete_label">Ištrinti</string>
<string name="delete_failed">Nepavyksta ištrinti failo. Įrenginio paleidimas iš naujo gali padėti.</string>
+ <string name="delete_episode_label">Ištrinti epizodą</string>
+ <plurals name="deleted_episode_batch_label">
+ <item quantity="one">%d epizodas ištrintas.</item>
+ <item quantity="few">%d epizodai ištrinti.</item>
+ <item quantity="many">%d epizodai ištrinti.</item>
+ <item quantity="other">%d epizodai ištrinti.</item>
+ </plurals>
<string name="mark_read_label">Pažymėti kaip perklausytą</string>
<string name="marked_as_read_label">Pažymėtas kaip perklausytas</string>
<string name="mark_unread_label">Pažymėti kaip neperklausytą</string>
@@ -172,14 +175,12 @@
<string name="removed_item">Elementas pašalintas</string>
<!--Download messages and labels-->
<string name="download_successful">sėkmingi</string>
- <string name="download_failed">nepavyko</string>
<string name="download_pending">Laukia atsiuntimo</string>
<string name="download_running">Atsiuntimas vyksta</string>
<string name="download_error_details">Išsami informacija</string>
<string name="download_error_details_message">%1$s \n\nFailo URL:\n%2$s</string>
<string name="download_error_device_not_found">Nerastas laikmenos įrenginys</string>
<string name="download_error_insufficient_space">Trūksta laisvos vietos</string>
- <string name="download_error_file_error">Failo klaida</string>
<string name="download_error_http_data_error">HTTP duomenų klaida</string>
<string name="download_error_error_unknown">Nežinoma klaida</string>
<string name="download_error_parser_exception">Išimtinė situacija analizatoriuje</string>
@@ -189,7 +190,6 @@
<string name="download_error_unauthorized">Tapatumo nustatymo klaida</string>
<string name="download_error_file_type_type">Failo tipo klaida</string>
<string name="download_error_forbidden">Uždrausta</string>
- <string name="cancel_all_downloads_label">Atšaukti visus atsiuntimus</string>
<string name="download_canceled_msg">Atsiuntimas atšauktas</string>
<string name="download_canceled_autodownload_enabled_msg">Atsiuntimas atšauktas\n<i>Automatinis atsiuntimas</i> šiam elementui išjungtas</string>
<string name="download_report_title">Atsiuntimai užbaigti su klaida (-omis)</string>
@@ -210,13 +210,13 @@
<string name="download_log_title_unknown">Nežinomas pavadinimas</string>
<string name="download_type_feed">Sklaidos kanalas</string>
<string name="download_type_media">Medijos failas</string>
- <string name="download_type_image">Paveikslėlis</string>
<string name="download_request_error_dialog_message_prefix">Įvyko klaida bandant atsisiųsti failą:\u0020</string>
<string name="authentication_notification_title">Reikalingas tapatumo nustatymas</string>
<string name="authentication_notification_msg">Šis išteklius reikalauja vartotojo vardo bei slaptažodžio</string>
<string name="confirm_mobile_download_dialog_title">Patvirtinkite atsiuntimą mobiliuoju internetu</string>
<string name="confirm_mobile_download_dialog_message_not_in_queue">Atsiuntimas mobiliuoju internetu išjungtas nustatymuose.\n\nGalite pasirinkti tik pridėti epizodą į eilę arba laikinai leisti atsiuntimą.\n\n<small>Jūsų pasirinkimas bus prisimenamas 10 minučių.</small></string>
<string name="confirm_mobile_download_dialog_message">Atsiuntimas mobiliuoju internetu išjungtas nustatymuose.\n\nAr norite laikinai leisti atsiuntimą?\n\n<small>Jūsų pasirinkimas bus prisimenamas 10 minučių.</small></string>
+ <string name="confirm_mobile_streaming_button_always">Leisti visada</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Pridėti į eilę</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Laikinai leisti</string>
<!--Mediaplayer messages-->
@@ -230,16 +230,15 @@
<string name="no_media_playing_label">Atkūrimas nevyksta</string>
<string name="player_buffering_msg">Buferizuojama</string>
<string name="player_go_to_picture_in_picture">„Picture-in-picture“ režimas</string>
- <string name="playbackservice_notification_title">Atkuriama tinklalaidė</string>
<string name="unknown_media_key">„AntennaPod“ – nežinomas medijos klavišas: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Užrakinti eilę</string>
<string name="unlock_queue">Atrakinti eilę</string>
<string name="queue_locked">Eilė užrakinta</string>
<string name="queue_unlocked">Eilė atrakinta</string>
+ <string name="checkbox_do_not_show_again">Daugiau neberodyti</string>
<string name="clear_queue_label">Išvalyti eilę</string>
<string name="undo">Atšaukti</string>
- <string name="removed_from_queue">Elementas pašalintas</string>
<string name="move_to_top_label">Perkelti į viršų</string>
<string name="move_to_bottom_label">Perkelti į apačią</string>
<string name="sort">Rikiuoti</string>
@@ -255,18 +254,22 @@
<!--Variable Speed-->
<string name="download_plugin_label">Atsiųsti įskiepį</string>
<string name="no_playback_plugin_title">Įskiepis neįdiegtas</string>
- <string name="no_playback_plugin_or_sonic_msg">Jei norite, jog kintamos atkūrimo spartos funkcija veiktų, patartina įjungti įtaisytąją „Sonic“ medijos leistuvę [„Android“ 4.1+].\n\nKaip pakaitalą, naudodamiesi „Play Store“, galite atsisiųsti trečiųjų šalių įskiepį <i>Prestissimo</i>.\nBet kokie nesklandumai dėl „Prestissimo“ veikimo yra ne „AntennaPod“ atsakomybė, todėl apie juos reikėtų pranešti įskiepio autoriams.</string>
<string name="set_playback_speed_label">Atkūrimo spartos</string>
<string name="enable_sonic">Įjungti „Sonic“</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Kol kas neprenumeruojate jokios tinklalaidės.</string>
<string name="no_shownotes_label">Šis epizodas neturi užrašų.</string>
+ <string name="no_run_downloads_head_label">Nėra vykstančių atsiuntimų</string>
+ <string name="no_comp_downloads_head_label">Atsisiųstų epizodų nėra</string>
+ <string name="no_history_head_label">Istorijos nėra</string>
+ <string name="no_all_episodes_head_label">Epizodų nėra</string>
+ <string name="no_new_episodes_head_label">Naujų epizodų nėra</string>
+ <string name="no_fav_episodes_head_label">Mėgiamų epizodų nėra</string>
+ <string name="no_chapters_head_label">Skyrių nėra</string>
<string name="no_chapters_label">Šis epizodas neturi skyrių.</string>
+ <string name="no_subscriptions_head_label">Prenumeratų nėra</string>
<!--Preferences-->
<string name="storage_pref">Laikmena</string>
<string name="project_pref">Projektas</string>
- <string name="other_pref">Kita</string>
- <string name="about_pref">Apie</string>
<string name="queue_label">Eilė</string>
<string name="integrations_label">Integracijos</string>
<string name="automation">Automatizacija</string>
@@ -275,6 +278,8 @@
<string name="appearance">Išvaizda</string>
<string name="external_elements">Išoriniai elementai</string>
<string name="interruptions">Pertraukimai</string>
+ <string name="playback_control">Atkūrimo valdymas</string>
+ <string name="preference_search_clear_history">Išvalyti istoriją</string>
<string name="media_player">Medijos grotuvas</string>
<string name="pref_episode_cleanup_title">Epizodų valymas</string>
<string name="pref_episode_cleanup_summary">Epizodai, nesantys eilėje ar tarp mėgiamųjų, gali būti ištrinti automatinio atsiuntimo metu pritrūkus laisvos vietos naujiems epizodams </string>
@@ -304,18 +309,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Nustatyti dienos metą</string>
<string name="pref_autoUpdateIntervallOrTime_every">kas %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">lygiai %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Medijos failus atsiųsti tik naudojantis belaidžiu tinklu</string>
<string name="pref_followQueue_title">Nenutrūkstamas atkūrimas</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Atsisiųsti tik belaidžiu tinklu</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Ausinių atjungimas</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Pakartotinai prijungus ausines</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Pakartotinai prisijungus prie „Bluetooth“</string>
<string name="pref_mobileUpdate_title">Atnaujinimai mobiliuoju internetu</string>
- <string name="refreshing_label">Atnaujinama</string>
<string name="user_interface_label">Vartotojo sąsaja</string>
<string name="pref_set_theme_title">Pasirinkti temą</string>
- <string name="pref_nav_drawer_title">Derinti naršymo stalčių</string>
- <string name="pref_nav_drawer_sum">Derinti naršymo stalčiaus išvaizdą.</string>
<string name="pref_nav_drawer_items_title">Nustatyti naršymo stalčiaus elementus</string>
<string name="pref_nav_drawer_items_sum">Keisti naršymo stalčiuje rodomus elementus.</string>
<string name="pref_nav_drawer_feed_order_title">Nustatyti prenumeratų tvarką</string>
@@ -327,8 +327,6 @@
<string name="pref_automatic_download_sum">Konfigūruoti automatinį epizodų atsiuntimą.</string>
<string name="pref_autodl_wifi_filter_title">Įjungti belaidžių tinklų filtrą</string>
<string name="pref_autodl_wifi_filter_sum">Leisti automatinį atsiuntimą tik prisijungus prie nurodytų belaidžių tinklų.</string>
- <string name="pref_autodl_allow_on_mobile_title">Leisti atsiuntimus mobiliuoju internetu</string>
- <string name="pref_autodl_allow_on_mobile_sum">Leisti automatinį atsiuntimą naudojantis mobiliuoju internetu.</string>
<string name="pref_automatic_download_on_battery_title">Atsiuntimas ne įkrovimo metu</string>
<string name="pref_automatic_download_on_battery_sum">Leisti automatinį atsiuntimą kai baterija nėra įkraunama</string>
<string name="pref_parallel_downloads_title">Lygiagretūs atsiuntimai</string>
@@ -352,7 +350,6 @@
<string name="pref_gpodnet_full_sync_sum">Sinchronizuoti visas prenumeratas bei epizodų būsenas su „gpodder.net“.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Paskutinis sinchronizavimo bandymas: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Pradėtas sinchronizavimas</string>
- <string name="pref_gpodnet_full_sync_started">Pradėtas pilnas sinchronizavimas</string>
<string name="pref_gpodnet_login_status"><![CDATA[Prisijungta kaip <i>%1$s</i> naudojant įrenginį <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Rodyti sinchronizavimo klaidų pranešimus</string>
<string name="pref_gpodnet_notifications_sum">Šis nustatymas negalioja tapatumo nustatymo klaidoms.</string>
@@ -377,21 +374,17 @@
<string name="pref_showDownloadReport_title">Rodyti atsiuntimų ataskaitą</string>
<string name="pref_showDownloadReport_sum">Atsiuntimui nepavykus, sukurti ataskaitą su išsamiu klaidų aprašymu.</string>
<string name="pref_expand_notify_unsupport_toast">Ankstesnės nei 4.1 „Android“ versijos nepalaiko išplėstų programos pranešimų.</string>
- <string name="pref_queueAddToFront_sum">Naujus epizodus dėti į eilės priekį.</string>
- <string name="pref_queueAddToFront_title">Dėti į eilės priekį</string>
<string name="pref_smart_mark_as_played_disabled">Išjungtas</string>
<string name="pref_image_cache_size_title">Paveikslėlių podėlio dydis</string>
<string name="pref_image_cache_size_sum">Paveikslėlių podėlio diske dydis.</string>
- <string name="crash_report_title">Strigties pranešimas</string>
- <string name="crash_report_sum">Siųsti paskiausios strigties pranešimą el. paštu</string>
- <string name="send_email">Siųsti el. laišką</string>
+ <string name="copy_to_clipboard">Kopijuoti į iškarpinę</string>
+ <string name="copied_to_clipboard">Nukopijuotas į iškarpinę</string>
<string name="experimental_pref">Eksperimentinis</string>
<string name="pref_media_player_message">Pasirinkite medijos grotuvą, kuris bus naudojamas atkūrimui</string>
<string name="pref_current_value">Dabartinė reikšmė: %1$s</string>
<string name="pref_proxy_title">Įgaliotasis serveris</string>
<string name="pref_proxy_sum">Nustatyti įgaliotąjį tinklo serverį</string>
- <string name="pref_faq">DUK</string>
- <string name="pref_known_issues">Žinomos problemos</string>
+ <string name="pref_faq">Dažniausiai užduodami klausimai</string>
<string name="pref_no_browser_found">Nerasta jokia interneto naršyklė.</string>
<string name="pref_cast_title">„Chromecast“ palaikymas</string>
<string name="pref_cast_message_play_flavor">Įjungti nuotolinio medijos atkūrimo „Cast“ įrenginiuose (pvz. „Chromecast“, garso kolonėlės ar „Android TV“) palaikymą</string>
@@ -414,6 +407,8 @@
<string name="double_tap_toast">Jei norite išeiti, dar kartą paspauskite grįžimo mygtuką</string>
<string name="back_button_go_to_page">Eiti į puslapį…</string>
<string name="back_button_go_to_page_title">Pažymėti puslapį</string>
+ <!--About screen-->
+ <string name="about_pref">Apie</string>
<!--Search-->
<string name="search_hint">Ieškoti epizodų</string>
<string name="found_in_shownotes_label">Rasta laidos užrašuose</string>
@@ -425,22 +420,16 @@
<string name="found_in_title_label">Rasta pavadinime</string>
<string name="no_results_for_query">Rezultatų paieškai \"%1$s\" nėra.</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML failai leidžia perkelti tinklalaides iš vienos tvarkytuvės į kitą.</string>
<string name="opml_import_option">Parinktis %1$d</string>
<string name="opml_import_explanation_1">Pasirinkite tikslų kelią iki failo vietinėje failų sistemoje.</string>
- <string name="opml_import_explanation_2">Naudokite išorinę programėlę, pvz. „Dropbox“, „Google Drive“ ar mėgiamą failų tvarkytuvę OPML failui atverti.</string>
<string name="opml_import_explanation_3">Dauguma programėlių, pvz. „Google Mail“, „Dropbox“, „Google Drive“ ir dauguma failų tvarkytuvių leidžia <i>atverti</i> OPML failus <i>naudojant</i> „AntennaPod“.</string>
- <string name="start_import_label">Pradėti importą</string>
<string name="opml_import_label">OPML importas</string>
- <string name="opml_directory_error">KLAIDA!</string>
<string name="reading_opml_label">Skaitomas OPML failas</string>
<string name="opml_reader_error">Skaitant OPML failą įvyko klaida:</string>
<string name="opml_import_error_no_file">Nepasirinktas failas!</string>
<string name="select_all_label">Pažymėti visus</string>
<string name="deselect_all_label">Nepažymėti nieko</string>
- <string name="select_options_label">Pasirinkti...</string>
<string name="choose_file_from_filesystem">Iš vietinės failų sistemos</string>
- <string name="choose_file_from_external_application">Naudoti išorinę programėlę</string>
<string name="opml_export_label">OPML eksportas</string>
<string name="html_export_label">HTML eksportas</string>
<string name="exporting_label">Eksportuojama...</string>
@@ -537,8 +526,6 @@
<string name="pref_restart_required">Kad pakeitimas įsigaliotų, reikia paleisti „AntennaPod“ iš naujo.</string>
<!--Online feed view-->
<string name="subscribe_label">Prenumeruoti</string>
- <string name="subscribed_label">Prenumeruojama</string>
- <string name="downloading_label">Atsiunčiama...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Atsukti</string>
<string name="fast_forward_label">Persukti į priekį</string>
@@ -646,7 +633,6 @@ Spauskite „OK“, kad paleisti „AntennaPod“ iš naujo.</string>
<string name="cast_failed_setting_volume">Nepavyko nustatyti garsio</string>
<string name="cast_failed_no_connection">Nėra ryšio su „Chromecast“ įrenginiu</string>
<string name="cast_failed_no_connection_trans">Prarastas ryšys su „Chromecast“ įrenginiu. Jei įmanoma, programėlė pabandys atkurti ryšį. Palaukite keletą sekundžių ir pabandykite dar kartą.</string>
- <string name="cast_failed_perform_action">Nepavyko atlikti veiksmo.</string>
<string name="cast_failed_status_request">Sinchronizavimas su „Chromecast“ įrenginiu nepavyko</string>
<string name="cast_failed_seek">Peršokti į naują poziciją „Chromecast“ įrenginyje nepavyko</string>
<string name="cast_failed_receiver_player_error">Imtuvo leistuvę ištiko rimta klaida</string>
@@ -660,4 +646,5 @@ Spauskite „OK“, kad paleisti „AntennaPod“ iš naujo.</string>
<string name="notification_channel_playing_description">Leidžia valdyti atkūrimą. Tai pagrindinis pranešimas matomas tinklalaidės atkūrimo metu.</string>
<string name="notification_channel_error">Klaidos</string>
<string name="notification_channel_error_description">Rodomas, kai įvykstą kažkas nenumatyto, pavyzdžiui, nepavykus atsiuntimui ar „gpodder“ sinchronizavimui.</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-nb/strings.xml b/core/src/main/res/values-nb/strings.xml
index da6e1c0ae..32d7234f7 100644
--- a/core/src/main/res/values-nb/strings.xml
+++ b/core/src/main/res/values-nb/strings.xml
@@ -21,7 +21,6 @@
<string name="playback_history_label">Avspillingshistorikk</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">gpodder.net-innlogging</string>
- <string name="free_space_label">%1$s ledig</string>
<string name="episode_cache_full_title">Hurtigbuffer for episoder er full</string>
<string name="episode_cache_full_message">Grensen for størrelse på hurtigbuffer er nådd. Du kan øke størrelsen på hurtigbuffer i instillingene.</string>
<!--Statistics fragment-->
@@ -58,7 +57,6 @@
<string name="reset">Tilbakestill</string>
<string name="language_label">Språk</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Innstillinger</string>
<string name="cover_label">Bilde</string>
<string name="error_label">Feil</string>
<string name="error_msg_prefix">Det oppsto en feil:</string>
@@ -66,14 +64,9 @@
<string name="external_storage_error_msg">Finner ikke ekstern lagring. Sjekk at det eksterne lagringsmediet er montert.</string>
<string name="chapters_label">Kapittel</string>
<string name="chapter_duration">Varighet: %1$s</string>
- <string name="shownotes_label">Shownotater</string>
<string name="description_label">Beskrivelse</string>
- <string name="most_recent_prefix">Nyeste episode:\u0020</string>
<string name="episodes_suffix">\u0020episoder</string>
- <string name="length_prefix">Lengde:\u0020</string>
- <string name="size_prefix">Størrelse:\u0020</string>
<string name="processing_label">Behandler</string>
- <string name="loading_label">Laster ...</string>
<string name="save_username_password_label">Lagre brukernavn og passord</string>
<string name="close_label">Lukk</string>
<string name="retry_label">Prøv igjen</string>
@@ -96,8 +89,6 @@
<string name="feedurl_label">Strømmens URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Legg til en podcast via URL</string>
- <string name="podcastdirectories_label">Finn podcast i katalog</string>
- <string name="podcastdirectories_descr">Søk etter nye podcaster i iTunes eller fyyd, eller utforsk gpodder.net etter navn, kategori eller popularitet.</string>
<string name="browse_gpoddernet_label">Bla gjennom gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marker alle som avspilt</string>
@@ -107,7 +98,6 @@
<string name="share_label">Del ...</string>
<string name="share_file_label">Del fil</string>
<string name="share_feed_url_label">Del strømmens URL</string>
- <string name="hide_episodes_title">Skjul episoder</string>
<string name="hide_unplayed_episodes_label">Ikke avspilt</string>
<string name="hide_paused_episodes_label">Pauset</string>
<string name="hide_played_episodes_label">Avspilt</string>
@@ -123,9 +113,7 @@
<string name="download_label">Last ned</string>
<string name="play_label">Spill</string>
<string name="pause_label">Pause</string>
- <string name="stop_label">Stopp</string>
<string name="stream_label">Strøm</string>
- <string name="remove_label">Fjern</string>
<string name="delete_label">Slett</string>
<string name="delete_failed">Kan ikke slette filen. Omstart av enheten kan hjelpe.</string>
<string name="mark_read_label">Marker som avspilt</string>
@@ -146,12 +134,10 @@
<string name="removed_item">Element fjernet</string>
<!--Download messages and labels-->
<string name="download_successful">vellykket</string>
- <string name="download_failed">mislyktes</string>
<string name="download_pending">Nedlastning venter</string>
<string name="download_running">Nedlasting pågår</string>
<string name="download_error_device_not_found">Lagringsenhet ikke funnet</string>
<string name="download_error_insufficient_space">Ikke nok plass</string>
- <string name="download_error_file_error">Fil-feil</string>
<string name="download_error_http_data_error">HTTP-datafeil</string>
<string name="download_error_error_unknown">Ukjent feil</string>
<string name="download_error_parser_exception">Parser-unntak</string>
@@ -161,7 +147,6 @@
<string name="download_error_unauthorized">Autentiseringsfeil</string>
<string name="download_error_file_type_type">Filtype-feil</string>
<string name="download_error_forbidden">Ikke tillatt</string>
- <string name="cancel_all_downloads_label">Avbryt alle nedlastninger</string>
<string name="download_canceled_msg">Nedlasting avbrutt</string>
<string name="download_canceled_autodownload_enabled_msg">Nedlasting avbrutt\n<i>Automatisk nedlasting</i> for dette elementet er deaktivert</string>
<string name="download_report_title">Nedlasting fullført med feilmeldinger</string>
@@ -176,7 +161,6 @@
<string name="download_log_title_unknown">Ukjent tittel</string>
<string name="download_type_feed">Strøm</string>
<string name="download_type_media">Mediafil</string>
- <string name="download_type_image">Bilde</string>
<string name="download_request_error_dialog_message_prefix">En feil oppsto under nedlastningen av filen:\u0020</string>
<string name="authentication_notification_title">Autentisering påkreves</string>
<string name="authentication_notification_msg">Ressursen du forespør krever brukernavn og passord</string>
@@ -195,7 +179,6 @@
<string name="playback_error_unknown">Ukjent feil</string>
<string name="no_media_playing_label">Ingen media spillende for øyeblikket.</string>
<string name="player_buffering_msg">Bufrer</string>
- <string name="playbackservice_notification_title">Spiller podcast</string>
<string name="unknown_media_key">AntennaPod - Ukjent medienøkkel: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Lås køen</string>
@@ -204,7 +187,6 @@
<string name="queue_unlocked">Kø opplåst</string>
<string name="clear_queue_label">Slett køen</string>
<string name="undo">Angre</string>
- <string name="removed_from_queue">Objekt er fjernet</string>
<string name="move_to_top_label">Gå til toppen</string>
<string name="move_to_bottom_label">Gå til bunnen</string>
<string name="sort">Sortér</string>
@@ -225,8 +207,6 @@
<!--Preferences-->
<string name="storage_pref">Lagring</string>
<string name="project_pref">Prosjekt</string>
- <string name="other_pref">Annet</string>
- <string name="about_pref">Om</string>
<string name="queue_label">Kø</string>
<string name="pref_episode_cleanup_title">Episodeopprydding</string>
<string name="pref_pauseOnDisconnect_sum">Sett playback på pause når hodetelefoner eller bluetooth er frakoblet</string>
@@ -252,18 +232,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Angi klokkeslett</string>
<string name="pref_autoUpdateIntervallOrTime_every">hver %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">ved %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Last ned mediafiler eksklusivt med WiFi</string>
<string name="pref_followQueue_title">Kontinuerlig avspilling</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi media nedlastning</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Frakobling av hodetelefoner</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Gjeninnkopling av hodetelefoner</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Blutetooth tilkoblet igjen</string>
<string name="pref_mobileUpdate_title">Mobiloppdateringer</string>
- <string name="refreshing_label">Oppdaterer</string>
<string name="user_interface_label">Brukergrensesnitt</string>
<string name="pref_set_theme_title">Velg tema</string>
- <string name="pref_nav_drawer_title">Skreddersy navigasjonsskuff</string>
- <string name="pref_nav_drawer_sum">Velg utseendet til navigasjonsskuffen.</string>
<string name="pref_nav_drawer_items_title">Velg elementer i navigasjonsskuffen</string>
<string name="pref_nav_drawer_items_sum">Endre hvilke elementer som vises i navigeringsfanen.</string>
<string name="pref_nav_drawer_feed_order_title">Velg rekkefølge på abbonement</string>
@@ -274,8 +249,6 @@
<string name="pref_automatic_download_sum">Konfigurer automatisk nedlasting av episoder.</string>
<string name="pref_autodl_wifi_filter_title">Skru på Wi-Fi-filter</string>
<string name="pref_autodl_wifi_filter_sum">Tillat automatisk nedlasting kun for valgte Wi-Fi-nettverk.</string>
- <string name="pref_autodl_allow_on_mobile_title">Last ned via mobildata</string>
- <string name="pref_autodl_allow_on_mobile_sum">Tillat oppdateringer over kobling via mobildata.</string>
<string name="pref_automatic_download_on_battery_title">Last ned når enheten ikke lader</string>
<string name="pref_automatic_download_on_battery_sum">Tillat automatisk nedlasting når enheten ikke står til lading</string>
<string name="pref_parallel_downloads_title">Parallelle nedlastinger</string>
@@ -296,7 +269,6 @@
<string name="pref_gpodnet_full_sync_title">Synkroniser alt nå</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Siste synkroniseringsforsøk: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synkronisering startet</string>
- <string name="pref_gpodnet_full_sync_started">Full synkronisering startet</string>
<string name="pref_gpodnet_notifications_title">Vis synkroniseringsfeil varsler</string>
<string name="pref_gpodnet_notifications_sum">Denne instillingen gjelder ikke autentiseringfeil.</string>
<string name="pref_playback_speed_title">Avspillingshastigheter</string>
@@ -311,20 +283,15 @@
<string name="pref_showDownloadReport_title">Vis nedlastingsrapport</string>
<string name="pref_showDownloadReport_sum">Generer en rapport som viser detaljer dersom nedlastinger feiler.</string>
<string name="pref_expand_notify_unsupport_toast">Android-versjoner tidligere enn 4.1 støtter ikke utvidede varsler.</string>
- <string name="pref_queueAddToFront_sum">Legg til nye episoder i begynnelsen av køen.</string>
- <string name="pref_queueAddToFront_title">Legg til foran i køen</string>
<string name="pref_smart_mark_as_played_disabled">Deaktivert</string>
<string name="pref_image_cache_size_title">Størrelse for bildemellomlager</string>
<string name="pref_image_cache_size_sum">Størrelsen på mellomlageret for bilder.</string>
- <string name="crash_report_title">Kræsj-rapport</string>
- <string name="crash_report_sum">Send den siste kræsj-rapporten via e-post</string>
- <string name="send_email">Send e-post</string>
<string name="experimental_pref">Eksperimentell</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Kjente problemer</string>
<string name="pref_no_browser_found">Ingen nettleser funnet.</string>
<string name="pref_cast_title">Chromecast støtte</string>
<string name="pref_enqueue_downloaded_summary">Legg til nedlastede episoder i køen</string>
+ <!--About screen-->
+ <string name="about_pref">Om</string>
<!--Search-->
<string name="search_hint">Søk etter episoder</string>
<string name="found_in_chapters_label">Funnet i kapitler</string>
@@ -333,22 +300,16 @@
<string name="found_in_title_label">Funnet i tittel</string>
<string name="no_results_for_query">Ingen resultalter for \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML-filer lar deg flytte podcastene dine fra en podcatcher til en annen.</string>
<string name="opml_import_option">Valg %1$d</string>
<string name="opml_import_explanation_1">Velg en spesifikk filbane fra det lokale filsystemet.</string>
- <string name="opml_import_explanation_2">Bruk en ektern applikasjon som Dropbox, Google Drive eller favoritt-filbehandleren din for å åpne en OPML-fil.</string>
<string name="opml_import_explanation_3">Mange applikasjoner som Gmail, Dropbox, Google Drive og de fleste filbehandlere kan <i>åpne</i> OPML-filer <i>med</i> AntennaPod.</string>
- <string name="start_import_label">Start importering</string>
<string name="opml_import_label">OPML-import</string>
- <string name="opml_directory_error">FEIL</string>
<string name="reading_opml_label">Leser OPML-fil</string>
<string name="opml_reader_error">En feil oppstod ved lesing av OPML dokument: </string>
<string name="opml_import_error_no_file">Ingen fil er valgt.</string>
<string name="select_all_label">Velg alle</string>
<string name="deselect_all_label">Opphev alle markeringene</string>
- <string name="select_options_label">Velg...</string>
<string name="choose_file_from_filesystem">Fra lokalt filsystem</string>
- <string name="choose_file_from_external_application">Bruk ekstern applikasjon</string>
<string name="opml_export_label">OPML-eksportering</string>
<string name="html_export_label">HTML eksport</string>
<string name="exporting_label">Eksporterer...</string>
@@ -437,8 +398,6 @@
<string name="pref_restart_required">AntennaPod må startes om for at denne endringen skal lagres.</string>
<!--Online feed view-->
<string name="subscribe_label">Abonner</string>
- <string name="subscribed_label">Abonnert</string>
- <string name="downloading_label">Laster ned...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Spol tilbake</string>
<string name="fast_forward_label">Spol fremover</string>
@@ -525,4 +484,5 @@
<string name="cast_failed_to_pause">Feilet å pause avspillingen av media</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<!--Notification channels-->
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml
index d406b13e8..caa2c9ce7 100644
--- a/core/src/main/res/values-nl/strings.xml
+++ b/core/src/main/res/values-nl/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synchroniseren met andere apparaten</string>
<string name="gpodnet_auth_label">Inloggen op gpodder.net</string>
- <string name="free_space_label">%1$s beschikbaar</string>
<string name="episode_cache_full_title">Afleveringscache is vol</string>
<string name="episode_cache_full_message">Het maximum aantal gecachete afleveringen is bereikt. Je kunt het maximum verhogen in de instellingen.</string>
- <string name="synchronizing">Bezig met synchroniseren…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Totale duur van afgespeelde podcasts:</string>
<string name="statistics_details_dialog">%1$d van %2$d afleveringen gestart.\n\n%3$s van %4$s afgespeeld.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Maker(s)</string>
<string name="language_label">Taal</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Instellingen</string>
<string name="cover_label">Beeld</string>
<string name="error_label">Fout</string>
<string name="error_msg_prefix">Er is een fout opgetreden:</string>
@@ -72,14 +69,9 @@
<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="chapters_label">Hoofdstukken</string>
<string name="chapter_duration">Duur: %1$s</string>
- <string name="shownotes_label">Notities</string>
<string name="description_label">Omschrijving</string>
- <string name="most_recent_prefix">Meest recente aflevering:\u0020</string>
<string name="episodes_suffix">\u0020afleveringen</string>
- <string name="length_prefix">Duur:\u0020</string>
- <string name="size_prefix">Grootte:\u0020</string>
<string name="processing_label">Bezig met verwerken...</string>
- <string name="loading_label">Bezig met laden...</string>
<string name="save_username_password_label">Gebruikersnaam en wachtwoord opslaan</string>
<string name="close_label">Sluiten</string>
<string name="retry_label">Opnieuw</string>
@@ -108,8 +100,6 @@
<string name="feedurl_label">Feed-URL</string>
<string name="etxtFeedurlHint">www.voorbeeld.nl/feed</string>
<string name="txtvfeedurl_label">Podcast toevoegen via URL</string>
- <string name="podcastdirectories_label">Podcast zoeken in de gids</string>
- <string name="podcastdirectories_descr">Je kunt nieuwe podcasts vinden op naam, categorie of populariteit in de gpodder.net-databank of via iTunes of fyyd.</string>
<string name="browse_gpoddernet_label">Blader door gpodder.net</string>
<string name="discover">Ontdekken</string>
<string name="discover_more">meer »</string>
@@ -137,7 +127,6 @@
<string name="feed_delete_confirmation_msg">Bevestig dat je de podcast \"%1$s\" en ALLE (ook gedownloade) bijbehorende afleveringen wilt verwijderen.</string>
<string name="feed_remover_msg">Podcast verwijderen</string>
<string name="load_complete_feed">Gehele podcast verversen</string>
- <string name="hide_episodes_title">Afleveringen verbergen</string>
<string name="batch_edit">Bulkbewerking</string>
<string name="select_all_above">Alles hierboven selecteren</string>
<string name="select_all_below">Alles hieronder selecteren</string>
@@ -161,9 +150,7 @@
</plurals>
<string name="play_label">Afspelen</string>
<string name="pause_label">Pauzeren</string>
- <string name="stop_label">Stoppen</string>
<string name="stream_label">Streamen</string>
- <string name="remove_label">Verwijderen</string>
<string name="delete_label">Verwijderen</string>
<string name="delete_failed">Kan bestand niet verwijderen; start je apparaat opnieuw op.</string>
<string name="delete_episode_label">Aflevering verwijderen</string>
@@ -207,14 +194,12 @@
<string name="removed_item">Aflevering verwijderd</string>
<!--Download messages and labels-->
<string name="download_successful">voltooid</string>
- <string name="download_failed">mislukt</string>
<string name="download_pending">Download staat in wachtrij</string>
<string name="download_running">Bezig met downloaden</string>
<string name="download_error_details">Details</string>
<string name="download_error_details_message">%1$s \n\nURL van bestand:\n%2$s</string>
<string name="download_error_device_not_found">Opslagmedium niet aangetroffen</string>
<string name="download_error_insufficient_space">Onvoldoende ruimte</string>
- <string name="download_error_file_error">Bestandsfout</string>
<string name="download_error_http_data_error">HTTP-gegevensfout</string>
<string name="download_error_error_unknown">Onbekende fout</string>
<string name="download_error_parser_exception">Verwerkingsuitzondering</string>
@@ -224,7 +209,6 @@
<string name="download_error_unauthorized">Authenticatiefout</string>
<string name="download_error_file_type_type">Bestandssoortfout</string>
<string name="download_error_forbidden">Niet mogelijk</string>
- <string name="cancel_all_downloads_label">Alle downloads afbreken</string>
<string name="download_canceled_msg">Download afgebroken</string>
<string name="download_canceled_autodownload_enabled_msg">Download afgebroken\n<i>Automatisch downloaden</i> uitgeschakeld voor deze aflevering</string>
<string name="download_report_title">Downloads afgerond, maar met fout(en)</string>
@@ -243,7 +227,6 @@
<string name="download_log_title_unknown">Onbekende titel</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Mediabestand</string>
- <string name="download_type_image">Afbeelding</string>
<string name="download_request_error_dialog_message_prefix">Er is een fout opgetreden bij het ​​downloaden van het bestand:\u0020</string>
<string name="null_value_podcast_error">Er is geen te tonen podcast opgegeven.</string>
<string name="authentication_notification_title">Authenticatie vereist</string>
@@ -269,7 +252,6 @@
<string name="no_media_playing_label">Geen media aan het afspelen</string>
<string name="player_buffering_msg">Bezig met bufferen</string>
<string name="player_go_to_picture_in_picture">Picture-in-picture-modus</string>
- <string name="playbackservice_notification_title">Bezig met afspelen van podcast</string>
<string name="unknown_media_key">AntennaPod - onbekende mediatoets: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Wachtrij vergrendelen</string>
@@ -278,7 +260,6 @@
<string name="queue_unlocked">Wachtrij ontgrendeld</string>
<string name="clear_queue_label">Wachtrij leegmaken</string>
<string name="undo">Ongedaan maken</string>
- <string name="removed_from_queue">Item verwijderd</string>
<string name="move_to_top_label">Naar boven verplaatsen</string>
<string name="move_to_bottom_label">Naar beneden verplaatsen</string>
<string name="sort">Sorteren</string>
@@ -297,13 +278,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Plug-in downloaden</string>
<string name="no_playback_plugin_title">Plug-in niet geïnstalleerd</string>
- <string name="no_playback_plugin_or_sonic_msg">Om variabele afspeelsnelheid te kunnen gebruiken, bevelen wij aan om de ingebouwde Sonic-mediaspeler te activeren [Android 4.1 en hoger]\n\nEvt. kun je de externe plug-in <i>Prestissimo</i> downloaden via de Play Store.\nWij zijn niet verantwoordelijk voor problemen met deze plug-in zijn; meld ze bij de plug-inontwikkelaar.</string>
<string name="set_playback_speed_label">Afspeelsnelheden</string>
<string name="enable_sonic">Sonic inschakelen</string>
<!--Empty list labels-->
<string name="no_items_header_label">Geen afleveringen in wachtrij</string>
<string name="no_items_label">Voeg een aflevering toe door deze te downloaden of houd een aflevering lang ingedrukt en kies \'Toevoegen aan wachtrij\'.</string>
- <string name="no_feeds_label">Je hebt nog geen abonnement op een podcast.</string>
<string name="no_shownotes_label">Deze aflevering bevat geen shownotities.</string>
<string name="no_run_downloads_head_label">Geen actieve downloads</string>
<string name="no_run_downloads_label">Je kunt afleveringen downloaden op het podcastinformatiescherm.</string>
@@ -324,8 +303,6 @@
<!--Preferences-->
<string name="storage_pref">Opslag</string>
<string name="project_pref">Project</string>
- <string name="other_pref">Overig</string>
- <string name="about_pref">Over AntennaPod</string>
<string name="queue_label">Wachtrij</string>
<string name="integrations_label">Integraties</string>
<string name="automation">Automatische acties</string>
@@ -368,9 +345,7 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Tijdstip instellen</string>
<string name="pref_autoUpdateIntervallOrTime_every">elke %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">om %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Mediabestanden alleen downloaden via Wi-Fi</string>
<string name="pref_followQueue_title">Doorlopend afspelen</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Media downloaden via Wi-Fi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Loskoppelen van hoofdtelefoon</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Opnieuw aansluiten van hoofdtelefoon</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Opnieuw verbinden met Bluetooth</string>
@@ -381,11 +356,8 @@
<string name="pref_mobileUpdate_auto_download">Automatisch downloaden</string>
<string name="pref_mobileUpdate_episode_download">Hoofdstuk downloaden</string>
<string name="pref_mobileUpdate_streaming">Streamen</string>
- <string name="refreshing_label">Bezig met verversen...</string>
<string name="user_interface_label">Uiterlijk</string>
<string name="pref_set_theme_title">Thema kiezen</string>
- <string name="pref_nav_drawer_title">Navigatiemenu aanpassen</string>
- <string name="pref_nav_drawer_sum">Pas het uiterlijk van het navigatiemenu aan.</string>
<string name="pref_nav_drawer_items_title">Menu-items kiezen</string>
<string name="pref_nav_drawer_items_sum">Kies welke items moeten worden getoond in het navigatiemenu.</string>
<string name="pref_nav_drawer_feed_order_title">Abonnementsvolgorde instellen</string>
@@ -397,8 +369,6 @@
<string name="pref_automatic_download_sum">Stel het automatisch downloaden van afleveringen in.</string>
<string name="pref_autodl_wifi_filter_title">Wi-Fi-filter inschakelen</string>
<string name="pref_autodl_wifi_filter_sum">Automatisch downloaden alleen toestaan via gekozen Wi-Fi-netwerken.</string>
- <string name="pref_autodl_allow_on_mobile_title">Downloaden via mobiel internet</string>
- <string name="pref_autodl_allow_on_mobile_sum">Automatisch downloaden toestaan via een mobiele internetverbinding.</string>
<string name="pref_automatic_download_on_battery_title">Downloaden als het apparaat niet wordt opgeladen</string>
<string name="pref_automatic_download_on_battery_sum">Downloaden toestaan als het apparaat niet wordt opgeladen</string>
<string name="pref_parallel_downloads_title">Gelijktijdige downloads</string>
@@ -423,7 +393,6 @@
<string name="pref_gpodnet_full_sync_sum">Synchroniseer alle abonnementen en afleveringsstatussen met gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Vorige synchronisatie: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synchroniseren gestart</string>
- <string name="pref_gpodnet_full_sync_started">Volledige synchronisatie gestart</string>
<string name="pref_gpodnet_login_status"><![CDATA[Ingelogd als <i>%1$s</i> met apparaat <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Melding tonen bij synchronisatiefouten</string>
<string name="pref_gpodnet_notifications_sum">Deze instelling is niet van toepassing op inlogfouten.</string>
@@ -450,21 +419,14 @@
<string name="pref_showDownloadReport_title">Downloadrapportage tonen</string>
<string name="pref_showDownloadReport_sum">Genereer een rapport met foutdetails als downloads mislukken.</string>
<string name="pref_expand_notify_unsupport_toast">Android-versies lager dan 4.1 ondersteunen geen knoppen op meldingen.</string>
- <string name="pref_queueAddToFront_sum">Nieuwe afleveringen aan het begin van de wachtrij toevoegen.</string>
- <string name="pref_queueAddToFront_title">Bovenaan wachtrij toevoegen</string>
<string name="pref_smart_mark_as_played_disabled">Uitgeschakeld</string>
<string name="pref_image_cache_size_title">Grootte van afbeeldingscache</string>
<string name="pref_image_cache_size_sum">Pas de grootte aan van het cachegeheugen voor afbeeldingen.</string>
- <string name="crash_report_title">Crashrapportage</string>
- <string name="crash_report_sum">Verstuur laatste crashrapportage via e-mail</string>
- <string name="send_email">E-mail versturen</string>
<string name="experimental_pref">Experimenteel</string>
<string name="pref_media_player_message">Kies welke mediaspeler gebruikt moet worden voor het afspelen van bestanden</string>
<string name="pref_current_value">Huidige instelling: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Netwerkproxy instellen</string>
- <string name="pref_faq">Veelgestelde vragen</string>
- <string name="pref_known_issues">Bekende problemen</string>
<string name="pref_no_browser_found">Geen browser aangetroffen.</string>
<string name="pref_cast_title">Chromecast-ondersteuning</string>
<string name="pref_cast_message_play_flavor">Ondersteuning activeren voor draadloos afspelen via Cast-apparaten (zoals Chromecast, luidsprekers of Android TV)</string>
@@ -490,6 +452,8 @@
<string name="back_button_go_to_page_title">Kies een pagina</string>
<string name="pref_delete_removes_from_queue_title">Afleveringen verwijderen uit wachtrij na wissen</string>
<string name="pref_delete_removes_from_queue_sum">Verwijder een aflevering uit de wachtrij als de aflevering in kwestie is verwijderd.</string>
+ <!--About screen-->
+ <string name="about_pref">Over AntennaPod</string>
<!--Search-->
<string name="search_hint">Zoeken naar afleveringen</string>
<string name="found_in_shownotes_label">Gevonden in de shownotities</string>
@@ -501,22 +465,16 @@
<string name="found_in_title_label">Gevonden in de titel</string>
<string name="no_results_for_query">Geen resultaten gevonden voor \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Met OPML-bestanden kun je podcasts van de ene naar de andere podcatcher verplaatsen.</string>
<string name="opml_import_option">Optie %1$d</string>
<string name="opml_import_explanation_1">Kies een bestand via de bestandsbeheer.</string>
- <string name="opml_import_explanation_2">Kies een OPML-bestand via een andere app, zoals Dropbox of Google Drive, of via je bestandsbeheerder.</string>
<string name="opml_import_explanation_3">Apps as Gmail, Dropbox, Google Drive en de meeste bestandsbeheerders kunnen OPML-bestanden <i>openen</i> <i>met</i> AntennaPod.</string>
- <string name="start_import_label">Importeren starten</string>
<string name="opml_import_label">OPML importeren</string>
- <string name="opml_directory_error">FOUT!</string>
<string name="reading_opml_label">Bezig met uitlezen van OPML-bestand...</string>
<string name="opml_reader_error">Er is iets misgegaan tijdens het lezen van het OPML-bestand:</string>
<string name="opml_import_error_no_file">Geen bestand gekozen!</string>
<string name="select_all_label">Alles selecteren</string>
<string name="deselect_all_label">Alles de-selecteren</string>
- <string name="select_options_label">Selecteren...</string>
<string name="choose_file_from_filesystem">Via bestandsbeheer</string>
- <string name="choose_file_from_external_application">Via externe app</string>
<string name="opml_export_label">OPML exporteren</string>
<string name="html_export_label">HTML exporteren</string>
<string name="exporting_label">Bezig met exporteren...</string>
@@ -609,8 +567,6 @@
<string name="pref_restart_required">AntennaPod moet opnieuw worden gestart om deze wijziging toe te passen.</string>
<!--Online feed view-->
<string name="subscribe_label">Abonneren</string>
- <string name="subscribed_label">Geabonneerd</string>
- <string name="downloading_label">Bezig met downloaden...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Terugspoelen</string>
<string name="fast_forward_label">Vooruitspoelen</string>
@@ -718,7 +674,6 @@
<string name="cast_failed_setting_volume">Volume aanpassen mislukt</string>
<string name="cast_failed_no_connection">Er is geen verbinding met het Cast-apparaat</string>
<string name="cast_failed_no_connection_trans">De verbinding met het Cast-apparaat is verbroken; Antennapod probeert de verbinding te herstellen. Wacht een paar seconden en probeer het opnieuw.</string>
- <string name="cast_failed_perform_action">Actie kan niet worden uitgevoerd</string>
<string name="cast_failed_status_request">Synchroniseren met Cast-apparaat mislukt</string>
<string name="cast_failed_seek">Opzoeken van nieuwe positie op Cast-apparaat mislukt</string>
<string name="cast_failed_receiver_player_error">Ernstige fout opgetreden op het afspelende Cast-apparaat</string>
@@ -733,4 +688,5 @@
<string name="notification_channel_error">Foutmeldingen</string>
<string name="notification_channel_error_description">Tonen als er iets is misgegaan, bijvoorbeeld als downloaden of synchroniseren mislukt is.</string>
<string name="import_bad_file">Ongeldig/Foutief bestand</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-pl-rPL/strings.xml b/core/src/main/res/values-pl-rPL/strings.xml
index 3ebdbe487..22f0cc64e 100644
--- a/core/src/main/res/values-pl-rPL/strings.xml
+++ b/core/src/main/res/values-pl-rPL/strings.xml
@@ -20,11 +20,10 @@
<string name="cancel_download_label">Anuluj pobieranie</string>
<string name="playback_history_label">Historia odtwarzania</string>
<string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Synchronizuj z innymi urządzeniami</string>
<string name="gpodnet_auth_label">gpodder.net login</string>
- <string name="free_space_label">%1$s wolnego miejsca</string>
<string name="episode_cache_full_title">Pełna pamięć cache</string>
<string name="episode_cache_full_message">Limit pamięci cache został osiągnięty. Możesz zwiększyć pojemność cache w ustawieniach aplikacji.</string>
- <string name="synchronizing">Synchronizowanie...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Całkowity czas trwania podcastów:</string>
<string name="statistics_details_dialog">%1$d z %2$d odcinków rozpoczęto.\n\nZagrano %3$s z %4$s.</string>
@@ -32,6 +31,8 @@
<string name="statistics_mode_normal">Oblicz łączny czas rzeczywistego odtwarzania. Dwukrotne odtworzenie będzie policzone ale zaznaczenie jako odtworzone nie będzie policzone.</string>
<string name="statistics_mode_count_all">Podsumuj wszystkie podcasty oznaczone jako odtworzone</string>
<string name="statistics_speed_not_counted">Powiadomienie: Prędkość odtwarzania nigdy nie jest uwzględniana.</string>
+ <string name="statistics_reset_data">Resetuj statystyki</string>
+ <string name="statistics_reset_data_msg">Operacja skasuje historię długości odtwarzania wszystkich odcinków. Czy na pewno chcesz kontynuować?</string>
<!--Main activity-->
<string name="drawer_open">Otwórz menu</string>
<string name="drawer_close">Zamknij menu</string>
@@ -59,24 +60,20 @@
<string name="yes">Tak</string>
<string name="no">Nie</string>
<string name="reset">Reset</string>
+ <string name="author_label">Autor(rzy)</string>
<string name="language_label">Język</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Ustawienia</string>
<string name="cover_label">Obraz</string>
<string name="error_label">Błąd</string>
<string name="error_msg_prefix">Wystąpił błąd:</string>
+ <string name="needs_storage_permission">Wymagane uprawnienie dostępu do pamięci w celu ukończenia tej operacji</string>
<string name="refresh_label">Odśwież</string>
<string name="external_storage_error_msg">Brak zewnętrznej pamięci. Sprawdź czy jest ona podłączona żeby aplikacja mogła pracować poprawnie.</string>
<string name="chapters_label">Rozdziały</string>
<string name="chapter_duration">Czas trwania: %1$s</string>
- <string name="shownotes_label">Opis odcinka</string>
<string name="description_label">Opis</string>
- <string name="most_recent_prefix">Najnowszy odcinek:\u0020</string>
<string name="episodes_suffix">:\u0020odcinków</string>
- <string name="length_prefix">Długość:\u0020</string>
- <string name="size_prefix">Rozmiar:\u0020</string>
<string name="processing_label">Przetwarzanie</string>
- <string name="loading_label">Ładowanie</string>
<string name="save_username_password_label">Zapisz nazwę użytkownika i hasło</string>
<string name="close_label">Zamknij</string>
<string name="retry_label">Spróbuj ponownie</string>
@@ -92,27 +89,37 @@
<string name="episode_cleanup_never">Nigdy</string>
<string name="episode_cleanup_queue_removal">Kiedy nie są w kolejce</string>
<string name="episode_cleanup_after_listening">Po odtworzeniu</string>
+ <plurals name="episode_cleanup_hours_after_listening">
+ <item quantity="one">1 godzinę po odtworzeniu</item>
+ <item quantity="few"> %d godzin(y) po odtworzeniu</item>
+ <item quantity="many">%d godzin(y) po odtworzeniu</item>
+ <item quantity="other">%d godzin(y) po odtworzeniu</item>
+ </plurals>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 dzień po odtworzeniu</item>
<item quantity="few">%d dni po odtworzeniu</item>
<item quantity="many">%d dni po odtworzeniu</item>
<item quantity="other">%d dni po odtworzeniu</item>
</plurals>
+ <string name="num_selected_label">zaznaczono %d</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Adres kanału</string>
<string name="etxtFeedurlHint">Adres URL kanału lub strony internetowej</string>
<string name="txtvfeedurl_label">Dodaj podcast przez adres</string>
- <string name="podcastdirectories_label">Znajdź podcast w folderze</string>
- <string name="podcastdirectories_descr">Możesz wyszukiwać nowe podcasty ze względu na nazwę, kategorię lub popularność na gpodder.net, Itunes lub fyyd</string>
<string name="browse_gpoddernet_label">Przeglądaj gpodder.net</string>
+ <string name="discover">Odkryj</string>
<string name="discover_more">więcej »</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Oznacz wszystkie jako odtworzone</string>
<string name="mark_all_read_msg">Wszystkie odcinki zaznaczono jako odtworzone</string>
- <string name="mark_all_read_confirmation_msg">Proszę potwierdzić, że chcesz oznaczyć wszystkie odcinki jako odtworzone.</string>
- <string name="mark_all_read_feed_confirmation_msg">Proszę potwierdzić, że chcesz oznaczyć wszystkie odcinki tego podcastu jako odtworzone.</string>
+ <string name="mark_all_read_confirmation_msg">Potwierdź, że chcesz oznaczyć wszystkie odcinki jako odtworzone.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Potwierdź, że chcesz oznaczyć wszystkie odcinki tego podcastu jako odtworzone.</string>
+ <string name="remove_all_new_flags_label">Usuń wszystkie flagi \"nowy\"</string>
+ <string name="removed_all_new_flags_msg">Usunięto wszystkie flagi \"nowy\"</string>
+ <string name="remove_all_new_flags_confirmation_msg">Potwierdź, że chcesz usunąć flagę \"nowy\" ze wszystkich odcinków.</string>
<string name="show_info_label">Pokaż informacje</string>
<string name="show_feed_settings_label">Pokaż ustawienia podcastu</string>
+ <string name="feed_info_label">Informacje o podcaście</string>
<string name="feed_settings_label">Ustawienia podcastu</string>
<string name="rename_feed_label">Zmień nazwę podcastu</string>
<string name="remove_feed_label">Usuń podcast</string>
@@ -120,10 +127,14 @@
<string name="share_link_label">Udostępnij adres URL odcinka</string>
<string name="share_link_with_position_label">Udostępnij adres URL odcinka z aktualną pozycją</string>
<string name="share_file_label">Udostępnij plik</string>
+ <string name="share_website_url_label">Udostępnij adres URL</string>
<string name="share_feed_url_label">Udostępnij adres kanału</string>
+ <string name="share_item_url_label">Udostępnij adres URL odcinka</string>
+ <string name="share_item_url_with_position_label">Udostępnij adres URL odcinka z pozycją</string>
+ <string name="feed_delete_confirmation_msg">Potwierdź, że chcesz usunąć kanał \"%1$s\" oraz WSZYSTKIE jego odcinki (również te pobrane).</string>
<string name="feed_remover_msg">Usuwanie podcastu</string>
<string name="load_complete_feed">Odśwież cały podcast</string>
- <string name="hide_episodes_title">Ukryj odcinki</string>
+ <string name="batch_edit">Edycja grupowa</string>
<string name="select_all_above">Zaznacz wszystkie powyżej</string>
<string name="select_all_below">Zaznacz wszystkie poniżej</string>
<string name="hide_unplayed_episodes_label">Nieodtworzone</string>
@@ -134,24 +145,62 @@
<string name="hide_downloaded_episodes_label">Pobrane</string>
<string name="hide_not_downloaded_episodes_label">Nie pobrane</string>
<string name="hide_has_media_label">Ma media</string>
+ <string name="hide_is_favorite_label">Ulubione</string>
<string name="filtered_label">Przefiltrowany</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Ostatnie odświerzanie nie powiodło się</string>
<string name="open_podcast">Otwórz Podcast</string>
<!--actions on feeditems-->
<string name="download_label">Pobierz</string>
+ <plurals name="downloading_batch_label">
+ <item quantity="one">Pobieranie %d odcinka.</item>
+ <item quantity="few">Pobieranie %d odcinków.</item>
+ <item quantity="many">Pobieranie %d odcinków.</item>
+ <item quantity="other">Pobieranie %d odcinków.</item>
+ </plurals>
<string name="play_label">Odtwórz</string>
<string name="pause_label">Pauza</string>
- <string name="stop_label">Stop</string>
<string name="stream_label">Strumień</string>
- <string name="remove_label">Usuń</string>
<string name="delete_label">Usuń</string>
<string name="delete_failed">Nie można usunąć pliku. Restart urządzenia może w tym pomóc.</string>
+ <string name="delete_episode_label">Usuń odcinek</string>
+ <plurals name="deleted_episode_batch_label">
+ <item quantity="one">Usunięto %d odcinek. </item>
+ <item quantity="few">Usunięto %d odcinki(ów). </item>
+ <item quantity="many">Usunięto %d odcinki(ów). </item>
+ <item quantity="other">Usunięto %d odcinki(ów). </item>
+ </plurals>
+ <string name="remove_new_flag_label">Usuń flagę \"nowy\"</string>
+ <string name="removed_new_flag_label">Usunięto flagę \"nowy\"</string>
<string name="mark_read_label">Oznacz jako odtworzone</string>
<string name="marked_as_read_label">Oznaczone jako odtworzone</string>
+ <plurals name="marked_read_batch_label">
+ <item quantity="one">Oznaczono %d odcinek jako odtworzony.</item>
+ <item quantity="few">Oznaczono %d odcinki(ów) jako odtworzone.</item>
+ <item quantity="many">Oznaczono %d odcinki(ów) jako odtworzone.</item>
+ <item quantity="other">Oznaczono %d odcinki(ów) jako odtworzone.</item>
+ </plurals>
<string name="mark_unread_label">Oznacz jako nieodtworzone</string>
+ <plurals name="marked_unread_batch_label">
+ <item quantity="one">Oznaczono %d odcinek jako nieodtworzony.</item>
+ <item quantity="few">Oznaczono %d odcinki(ów) jako nieodtworzone.</item>
+ <item quantity="many">Oznaczono %d odcinki(ów) jako nieodtworzone.</item>
+ <item quantity="other">Oznaczono %d odcinki(ów) jako nieodtworzone.</item>
+ </plurals>
<string name="add_to_queue_label">Dodaj do kolejki</string>
<string name="added_to_queue_label">Dodano do Kolejki</string>
+ <plurals name="added_to_queue_batch_label">
+ <item quantity="one">%d odcinek dodano do kolejki.</item>
+ <item quantity="few">%d odcinki(ów) dodano do kolejki.</item>
+ <item quantity="many">%d odcinki(ów) dodano do kolejki.</item>
+ <item quantity="other">%d odcinki(ów) dodano do kolejki.</item>
+ </plurals>
<string name="remove_from_queue_label">Usuń z kolejki</string>
+ <plurals name="removed_from_queue_batch_label">
+ <item quantity="one">%d odcinek usunięto z kolejki.</item>
+ <item quantity="few">%d odcinki(ów) usunięto z kolejki.</item>
+ <item quantity="many">%d odcinki(ów) usunięto z kolejki.</item>
+ <item quantity="other">%d odcinki(ów) usunięto z kolejki.</item>
+ </plurals>
<string name="add_to_favorite_label">Dodaj do Ulubionych</string>
<string name="added_to_favorites">Dodaj do ulubionych</string>
<string name="remove_from_favorite_label">Usuń z Ulubionych</string>
@@ -164,7 +213,6 @@
<string name="removed_item">Pozycja usunięta</string>
<!--Download messages and labels-->
<string name="download_successful">Operacja zakończona sukcesem</string>
- <string name="download_failed">Operacja nie powiodła się</string>
<string name="download_pending">Pobieranie w toku</string>
<string name="download_running">Pobieram</string>
<string name="download_error_details">Szczegóły</string>
@@ -172,7 +220,6 @@
</string>
<string name="download_error_device_not_found">Nie znaleziono urządzenia docelowego</string>
<string name="download_error_insufficient_space">Niewystarczająca ilość pamięci</string>
- <string name="download_error_file_error">Błąd pliku</string>
<string name="download_error_http_data_error">Błąd danych HTTP</string>
<string name="download_error_error_unknown">Nieznany błąd</string>
<string name="download_error_parser_exception">Wyjątek parsera</string>
@@ -182,7 +229,6 @@
<string name="download_error_unauthorized">Błąd autoryzacji</string>
<string name="download_error_file_type_type">Błąd rodzaju pliku</string>
<string name="download_error_forbidden">Zabronione</string>
- <string name="cancel_all_downloads_label">Anuluj wszystkie pobierania</string>
<string name="download_canceled_msg">Pobieranie anulowane</string>
<string name="download_canceled_autodownload_enabled_msg">Pobieranie zatrzymane\nWyłączone <i>Automatyczne pobieranie</i> dla tego elementu</string>
<string name="download_report_title">Pobieranie ukończone</string>
@@ -203,13 +249,16 @@
<string name="download_log_title_unknown">Nieznany tytuł</string>
<string name="download_type_feed">Kanał</string>
<string name="download_type_media">Plik multimedialny</string>
- <string name="download_type_image">Obraz</string>
<string name="download_request_error_dialog_message_prefix">Wystąpił błąd przy próbie pobierania:\u0020</string>
+ <string name="null_value_podcast_error">Nie podano podcastu, który można wyświetlić.</string>
<string name="authentication_notification_title">Wymagana autoryzacja</string>
<string name="authentication_notification_msg">Żądany zasób wymaga podania nazwy użytkownika oraz hasła</string>
<string name="confirm_mobile_download_dialog_title">Potwierdź pobieranie przez sieć komórkową</string>
<string name="confirm_mobile_download_dialog_message_not_in_queue">Pobieranie przez sieć komórkową jest wyłączone w ustawieniach.\n\nMożesz dodać odcinek do kolejki lub tymczasowo zezwolić na pobieranie.\n\n<small>Twój wybór zostanie zapamiętany na 10 minut.</small></string>
<string name="confirm_mobile_download_dialog_message">Pobieranie przez sieć komórkową jest wyłączone w ustawieniach.\n\nCzy chcesz tymczasowo zezwolić na pobieranie?\n\n<small>Twój wybór zostanie zapamiętany na 10 minut.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Potwierdź streamowanie przy użyciu danych komórkowych</string>
+ <string name="confirm_mobile_streaming_notification_message">Streamowanie przy użyciu danych komórkowych jest wyłączone w ustawieniach. Naciśnij aby streamować mimo to.</string>
+ <string name="confirm_mobile_streaming_button_always">Pozwalaj zawsze</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Dodaj do kolejki</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Zezwól tymczasowo</string>
<!--Mediaplayer messages-->
@@ -219,57 +268,88 @@
<string name="player_ready_msg">Gotowe</string>
<string name="player_seeking_msg">Szukam</string>
<string name="playback_error_server_died">Serwer zdechł</string>
+ <string name="playback_error_unsupported">Nieobsługiwany typ danych</string>
+ <string name="playback_error_timeout">Upłynął czas łączenia</string>
<string name="playback_error_unknown">Nieznany błąd</string>
<string name="no_media_playing_label">Żadne media nie odtwarzane </string>
<string name="player_buffering_msg">Buferowanie</string>
- <string name="playbackservice_notification_title">Odtwarzenie podcastu </string>
+ <string name="player_go_to_picture_in_picture">Tryb obraz w obrazie</string>
<string name="unknown_media_key">AntennaPod - Nieznany klawisz: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Zablokuj Kolejkę</string>
<string name="unlock_queue">Odblokuj Kolejkę</string>
<string name="queue_locked">Kolejka zablokowana</string>
<string name="queue_unlocked">Kolejka odblokowana</string>
+ <string name="queue_lock_warning">Jeśli zablokujesz kolejkę, nie będzie można usuwać z niej odcinków przez przesunięcie i zmieniać ich kolejności.</string>
+ <string name="checkbox_do_not_show_again">Nie pokazuj więcej</string>
<string name="clear_queue_label">Wyczyść kolejkę</string>
<string name="undo">Cofnij</string>
- <string name="removed_from_queue">Element usunięty</string>
<string name="move_to_top_label">Przesuń na górę</string>
<string name="move_to_bottom_label">Przesuń na dół</string>
<string name="sort">Sortuj</string>
+ <string name="keep_sorted">Zachowaj</string>
<string name="date">Według daty</string>
<string name="duration">Według długości</string>
<string name="episode_title">Tytuł odcinka</string>
<string name="feed_title">Tytuł podcastu</string>
+ <string name="random">Losowo</string>
+ <string name="smart_shuffle">Losowo</string>
<string name="ascending">Rosnąco</string>
<string name="descending">Malejąco</string>
<string name="clear_queue_confirmation_msg">Potwierdź usunięcie WSZYSTKICH pozycji z kolejki</string>
+ <string name="sort_old_to_new">od najstarszych</string>
+ <string name="sort_new_to_old">od najnowszych</string>
<!--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">Dla odtwarzania w różnych prędkościach zalecamy odblokowania wbudowanego odtwarzacza Sonic [Android 4.1+].\n\nW innym przypadku możesz użycz zewnętrznego dodatku. <i>Prestissimo</i> ze Sklepu Play.\nAntennaPod nie ponosi odpowiedzialności za problemy z Prestissimo, powinny one być zgłaszane do właściciela dodatku.</string>
<string name="set_playback_speed_label">Prędkość odtwarzania</string>
<string name="enable_sonic">Włącz Sonic</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Nie subskrybowałeś jeszcze żadnego podcastu.</string>
+ <string name="no_items_header_label">Brak odcinków w kolejce</string>
+ <string name="no_items_label">Dodaj odcinek, pobierając go, lub przytrzymaj dłużej na odcinku i wybierz \"Dodaj do kolejki\".</string>
<string name="no_shownotes_label">Ten epizod nie ma notatek.</string>
+ <string name="no_run_downloads_head_label">Brak aktywnych pobierań</string>
+ <string name="no_run_downloads_label">Możesz pobrać odcinki z ekranu informacji o podcaście</string>
<string name="no_comp_downloads_head_label">Brak pobranych odcinków</string>
+ <string name="no_comp_downloads_label">Możesz pobrać odcinki z ekranu informacji o podcaście</string>
+ <string name="no_log_downloads_head_label">Brak dziennika pobierań</string>
+ <string name="no_log_downloads_label">Tutaj pojawi się dziennik pobierań</string>
+ <string name="no_history_head_label">Brak historii</string>
+ <string name="no_history_label">Tutaj pojawi się lista odcinków, po ich przesłuchaniu.</string>
+ <string name="no_all_episodes_head_label">Brak odcinków</string>
+ <string name="no_all_episodes_label">Tutaj pojawi się lista odcinków, gdy dodasz podcast.</string>
+ <string name="no_new_episodes_head_label">Brak nowych odcinków.</string>
+ <string name="no_new_episodes_label">Tutaj pojawią się nowe odcinki, gdy zostaną opublikowane.</string>
<string name="no_fav_episodes_head_label">Brak ulubionych odcinków</string>
+ <string name="no_fav_episodes_label">Możesz dodać odcinki do ulubionych przytrzymując dłużej na ich nazwie.</string>
+ <string name="no_chapters_head_label">Brak rozdziałów</string>
<string name="no_chapters_label">Ten odcinek nie ma rozdziałów.</string>
+ <string name="no_subscriptions_head_label">Brak subskrypcji</string>
+ <string name="no_subscriptions_label">Aby subskrybować podcast naciśnij znak plus poniżej</string>
<!--Preferences-->
<string name="storage_pref">Pamięć</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Inne</string>
- <string name="about_pref">O...</string>
<string name="queue_label">Kolejka</string>
+ <string name="integrations_label">Integracje</string>
+ <string name="automation">Automatyzacja</string>
<string name="download_pref_details">Szczegóły</string>
+ <string name="import_export_pref">Import/Eksport</string>
+ <string name="import_export_search_keywords">backup, restore, kopia, kopia zapasowa, przywracanie, przywróć</string>
<string name="appearance">Wygląd</string>
+ <string name="external_elements">Elementy zewnętrzne</string>
+ <string name="interruptions">Przerwy</string>
+ <string name="playback_control">Sterowanie odtwarzaniem</string>
+ <string name="preference_search_hint">Szukaj…</string>
<string name="preference_search_no_results">Brak wyników</string>
<string name="preference_search_clear_history">Wyczyść historię</string>
+ <string name="media_player">Odtwarzacz</string>
<string name="pref_episode_cleanup_title">Usuwanie odcinków</string>
<string name="pref_episode_cleanup_summary">Odcinki niebędące w kolejce i niebędące na liście ulubiobych powinny nadawać się do usunięcia, jeśli Automatyczne Pobieranie potrzebuje miejsca na nowe odcinki.</string>
<string name="pref_pauseOnDisconnect_sum">Wstrzymaj odtwarzanie po rozłączeniu słuchawek lub Bluetooth</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Wznów odtwarzanie kiedy słuchawki zostaną podłączone ponownie</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Wznów odtwarzanie po przywróceniu połączenia Bluetooth</string>
<string name="pref_hardwareForwardButtonSkips_title">Przycisk \'Do przodu\' pomija odcinek</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Naciśnięcie przycisku \'Do przodu\' na urządzeniu bluetooth skacze do następnego odcinka, zamiast przewijać</string>
<string name="pref_hardwarePreviousButtonRestarts_title">Przycisk wstecz restartuje</string>
<string name="pref_hardwarePreviousButtonRestarts_sum">Podczas odtwarzania przycisk wstecz restartuje zamiast przewijać</string>
<string name="pref_followQueue_sum">Przeskocz do następnego elementu kolejki po zakończeniu odtwarzania</string>
@@ -291,37 +371,45 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Ustaw czas dnia</string>
<string name="pref_autoUpdateIntervallOrTime_every">każdy %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">o %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Pobieraj pliki tylko przez WiFi</string>
<string name="pref_followQueue_title">Odtwarzanie ciągłe</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi media pobrane</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Słuchawki odłączone</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Słuchawki podłączone ponownie</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth podłączony ponownie</string>
+ <string name="pref_stream_over_download_title">Preferuj streamowanie</string>
+ <string name="pref_stream_over_download_sum">Wyświetlaj na listach przycisk streamowania zamiast przycisku pobierania.</string>
<string name="pref_mobileUpdate_title">Aktualizacje mobilne</string>
+ <string name="pref_mobileUpdate_sum">Wybierz co ma być dozwolone przy połączeniu poprzez sieć komórkową</string>
+ <string name="pref_mobileUpdate_refresh">Odświeżanie kanałów</string>
+ <string name="pref_mobileUpdate_images">Okładki</string>
<string name="pref_mobileUpdate_auto_download">Automatyczne pobieranie</string>
- <string name="refreshing_label">Odświeżanie</string>
+ <string name="pref_mobileUpdate_episode_download">Pobieranie odcinków</string>
+ <string name="pref_mobileUpdate_streaming">Streaming</string>
<string name="user_interface_label">Interfejs użytkownika</string>
<string name="pref_set_theme_title">Wybierz motyw</string>
- <string name="pref_nav_drawer_title">Dopasuj panel nawigacyjny</string>
- <string name="pref_nav_drawer_sum">Dopasuj wygląd panelu nawigacyjnego.</string>
<string name="pref_nav_drawer_items_title">Wybierz pozycje panelu nawigacyjnego</string>
<string name="pref_nav_drawer_items_sum">Zmienia pozycje widoczne w panelu nawigacyjnym.</string>
<string name="pref_nav_drawer_feed_order_title">Ustaw kolejność subskrypcji</string>
<string name="pref_nav_drawer_feed_order_sum">Zmień kolejność subskrybowanych kanałów</string>
<string name="pref_nav_drawer_feed_counter_title">Ustaw licznik subskrypcji</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Zmień informacje wyświetlane przez licznik subskrypcji. Zmienia również sortowanie subskrypcji, jeśli kolejność ustawiona jest na \'Sortuj wg liczby\'</string>
<string name="pref_set_theme_sum">Zmień wygląd AntennaPod.</string>
<string name="pref_automatic_download_title">Automatyczne pobieranie</string>
<string name="pref_automatic_download_sum">Skonfiguruj automatyczne pobieranie odcinków.</string>
<string name="pref_autodl_wifi_filter_title">Włącz filtr Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Zezwól na automatyczne pobieranie tylko dla określonych sieci Wi-Fi.</string>
- <string name="pref_autodl_allow_on_mobile_title">Pobierz przy użyciu komórkowego transferu danych</string>
- <string name="pref_autodl_allow_on_mobile_sum">Zezwól na automatyczne pobieranie przy użyciu komórkowego transferu danych</string>
+ <string name="autodl_wifi_filter_permission_title">Wymagane uprawnienie</string>
+ <string name="autodl_wifi_filter_permission_message">Wymagane jest zezwolenie na dostęp do lokalizacji, aby filtrować Wi-Fi. Naciśnij, aby zezwolić.</string>
<string name="pref_automatic_download_on_battery_title">Pobieraj, gdy nie ładuje</string>
<string name="pref_automatic_download_on_battery_sum">Zezwól na automatyczne pobieranie, gdy bateria nie jest ładowana.</string>
<string name="pref_parallel_downloads_title">Liczba równoległych pobierań</string>
<string name="pref_episode_cache_title">Pamięć podręczna odcinków</string>
+ <string name="pref_episode_cache_summary">Całkowita liczba odcinków zapisanych na urządzeniu. Automatyczne pobieranie zostanie przerwane, jeśli zostanie ona osiągnięta.</string>
+ <string name="pref_episode_cover_title">Użyj okładek odcinków</string>
+ <string name="pref_episode_cover_summary">Użyj okładek konkretnych odcinków kiedy to możliwe. Odznaczenie spowoduje, że aplikacja zawsze będzie używała okładki kanału.</string>
+ <string name="pref_theme_title_use_system">Użyj motywu systemowego</string>
<string name="pref_theme_title_light">Jasny</string>
<string name="pref_theme_title_dark">Ciemny</string>
+ <string name="pref_theme_title_trueblack">Czarny (dla ekranów AMOLED)</string>
<string name="pref_episode_cache_unlimited">Nielimitowane</string>
<string name="pref_update_interval_hours_plural">godziny</string>
<string name="pref_update_interval_hours_singular">godzina</string>
@@ -338,18 +426,22 @@
<string name="pref_gpodnet_full_sync_sum">Synchronizuj wszystkie subskrypcje oraz stan odcinków z pomocą gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Ostatnia próba synchronizacji: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synchronizacja uruchomiona</string>
- <string name="pref_gpodnet_full_sync_started">Pełna synchronizacja rozpoczęta</string>
<string name="pref_gpodnet_login_status"><![CDATA[Zalogowano jako <i>%1$s</i> na urządzeniu <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Pokaż powiadomienia błędów synchronizacji</string>
<string name="pref_gpodnet_notifications_sum">To ustawienie nie dotyczy błędów autoryzacji.</string>
<string name="pref_playback_speed_title">Prędkość odtwarzania</string>
<string name="pref_playback_speed_sum">Dostosuj prędkości dostępne dla odtwarzania audio o zmiennej prędkości</string>
+ <string name="pref_feed_playback_speed_sum">Prędkość używana przy starcie odtwarzania dla odcinków z tego kanału.</string>
+ <string name="pref_playback_time_respects_speed_title">Dostosuj informacje do prędkości odtwarzania</string>
+ <string name="pref_playback_time_respects_speed_sum">Wyświetlana pozycja i czas trwania są dostosowane do prędkości odtwarzania</string>
<string name="pref_fast_forward">Szybkie przewijanie do przodu</string>
<string name="pref_fast_forward_sum">Dostosuj liczbę sekund do przeskoczenia przy kliknięciu szybkiego przewijania do przodu</string>
<string name="pref_rewind">Przewijanie do tyłu</string>
<string name="pref_rewind_sum">Dostosuj liczbę sekund do przeskoczenia przy kliknięciu przewijania do tyłu</string>
<string name="pref_gpodnet_sethostname_title">Ustaw nazwę hosta</string>
<string name="pref_gpodnet_sethostname_use_default_host">Użyj domyślnego hosta</string>
+ <string name="pref_expandNotify_title">Wysoki priorytet powiadomienia</string>
+ <string name="pref_expandNotify_sum">Rozwija powiadomienie, aby pokazać przyciski odtwarzania</string>
<string name="pref_persistNotify_title">Stałe przyciski odtwarzacza</string>
<string name="pref_persistNotify_sum">Utrzymuj powiadomienie i przyciski odtwarzania na ekranie blokady gdy odtwarzanie jest wstrzymane.</string>
<string name="pref_compact_notification_buttons_title">Ustaw przyciski ekranu blokowania</string>
@@ -361,54 +453,81 @@
<string name="pref_showDownloadReport_title">Pokaż raport z pobierania</string>
<string name="pref_showDownloadReport_sum">Jeżeli pobieranie się nie powiedzie, pokaż raport ze szczegółami błędu.</string>
<string name="pref_expand_notify_unsupport_toast">Android starszy niż 4.1 nie wspiera rozszerzonych powiadomień.</string>
- <string name="pref_queueAddToFront_sum">Dodaj nowe odcinki na początku kolejki.</string>
- <string name="pref_queueAddToFront_title">Dodaj na początku</string>
+ <string name="pref_enqueue_location_title">Pozycja w kolejce</string>
+ <string name="pref_enqueue_location_sum">Dodaj odcinki do: %1$s</string>
+ <string name="enqueue_location_back">Koniec</string>
+ <string name="enqueue_location_front">Początek</string>
+ <string name="enqueue_location_after_current">Za bieżącym odcinkiem</string>
<string name="pref_smart_mark_as_played_disabled">Wyłączone</string>
<string name="pref_image_cache_size_title">Pamięć podręczna obrazów</string>
<string name="pref_image_cache_size_sum">Rozmiar przestrzeni na dysku dla pamięci podręcznej obrazów</string>
- <string name="crash_report_title">Raport o błędach</string>
- <string name="crash_report_sum">Wyślij ostatni raport o błędach przez e-mail</string>
- <string name="send_email">Wyślij e-mail</string>
+ <string name="view_mailing_list">Zobacz listę mailingową</string>
+ <string name="bug_report_title">Zgłoś błąd</string>
+ <string name="open_bug_tracker">Otwórz bugtracker</string>
+ <string name="copy_to_clipboard">Kopiuj do schowka</string>
+ <string name="copied_to_clipboard">Skopiowano do schowka</string>
<string name="experimental_pref">Eksperymentalne</string>
+ <string name="pref_media_player_message">Wybierz który odtwarzacz używać do odtwarzania plików</string>
<string name="pref_current_value">Aktualna wartość: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Ustaw proxy sieciowe</string>
<string name="pref_faq">Najczęściej zadawane pytania</string>
- <string name="pref_known_issues">Znane problemy</string>
<string name="pref_no_browser_found">Nie znaleziono przeglądarki.</string>
<string name="pref_cast_title">Obsługa Chromecast</string>
<string name="pref_cast_message_play_flavor">Uruchom obsługę dla zdalnego odtwarzania mediów na innych urządzeniach (takich jak Chromecast, Audio Speakers albo Android TV)</string>
<string name="pref_cast_message_free_flavor">Chromecast wymagadodatkowych bibliotek, które są zablokowane w tej wersji AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Rzeczy z kolejki pobrane</string>
+ <string name="pref_enqueue_downloaded_title">Kolejkuj pobrane</string>
<string name="pref_enqueue_downloaded_summary">Dodaj pobrane odcinki do kolejki</string>
+ <string name="media_player_builtin">Wbudowany odtwarzacz Androida</string>
+ <string name="pref_skip_silence_title">Pomiń ciszę w plikach audio</string>
+ <string name="pref_videoBehavior_title">Kończenie odtwarzania wideo</string>
+ <string name="pref_videoBehavior_sum">Zachowanie przy wyjściu z odtwarzania wideo</string>
+ <string name="stop_playback">Zakończ odtwarzanie</string>
+ <string name="continue_playback">Kontynuuj odtwarzanie audio</string>
<string name="behavior">Zachowanie</string>
+ <string name="pref_back_button_behavior_title">Zachowanie przycisku \'Wstecz\'</string>
+ <string name="pref_back_button_behavior_sum">Zmień funkcję przycisku \'Wstecz\'/</string>
<string name="back_button_default">Domyślne</string>
<string name="back_button_open_drawer">Otwórz panel nawigacyjny</string>
+ <string name="back_button_double_tap">Podwójne naciśnięcie wychodzi</string>
+ <string name="back_button_show_prompt">Potwierdź aby wyjść</string>
+ <string name="close_prompt">Czy na pewno chcesz zamknąć AntennaPod?</string>
+ <string name="double_tap_toast">Naciśnij ponownie przycisk \'Wstecz\' aby wyjść</string>
+ <string name="back_button_go_to_page">Idź do strony...</string>
+ <string name="back_button_go_to_page_title">Wybierz stronę</string>
+ <string name="pref_delete_removes_from_queue_title">Kasowanie usuwa z kolejki</string>
+ <string name="pref_delete_removes_from_queue_sum">Automatycznie usuwaj odcinek z kolejki gdy go kasujesz.</string>
+ <!--About screen-->
+ <string name="about_pref">O...</string>
+ <string name="antennapod_version">Wersja AntennaPod</string>
+ <string name="developers">Twórcy</string>
+ <string name="developers_summary">Każdy może pomóc ulepszyć AntennaPod</string>
+ <string name="translators">Tłumacze</string>
+ <string name="translators_summary">Tłumaczenie tworzone przez użytkowników AntennaPod używających Transifex</string>
+ <string name="privacy_policy">Polityka prywatności</string>
+ <string name="licenses">Licencje</string>
+ <string name="licenses_summary">AntennaPod używa różnego świetnego oprogramowania/bibliotek</string>
<!--Search-->
<string name="search_hint">Szukaj odcinków</string>
<string name="found_in_shownotes_label">Znalezione w notatkach dotyczących show</string>
<string name="found_in_chapters_label">Znaleziono w rozdziałach</string>
+ <string name="found_in_authors_label">Znaleziono wśród autorów</string>
+ <string name="found_in_feeds_label">Znaleziono w podcastach</string>
<string name="search_status_no_results">Brak wyników</string>
<string name="search_label">Szukaj</string>
<string name="found_in_title_label">Znaleziono w tytułach</string>
<string name="no_results_for_query">Brak wyników dla \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Pliki OPML pozwalają na przenoszenie podcastów między aplikacjami.</string>
<string name="opml_import_option">Opcja %1$d</string>
<string name="opml_import_explanation_1">Wybierz konkretą ścieżkę z lokalnego systemu plików.</string>
- <string name="opml_import_explanation_2">Użyj zewnętrznej aplikacji takiej jak Dropbox, Google Drive lub ulubionego menedżera plików, aby otworzyć plik OPML.</string>
<string name="opml_import_explanation_3">Wiele aplikacji takich jak Google Mail, Dropbox, Google Drive i większość menedżerów plików potrafi <i>otworzyć</i>pliki OPML <i>przy użyciu</i>AntennaPod</string>
- <string name="start_import_label">Rozpocznij import</string>
<string name="opml_import_label">Import OPML</string>
- <string name="opml_directory_error">BŁĄD!</string>
<string name="reading_opml_label">Odczytuję plik OPML</string>
<string name="opml_reader_error">Wystąpił błąd podczas odczytywania pliku OPML:</string>
<string name="opml_import_error_no_file">Nie wbrano pliku!</string>
<string name="select_all_label">Zaznacz wszystko</string>
<string name="deselect_all_label">Odznacz wszystko</string>
- <string name="select_options_label">Wybierz...</string>
<string name="choose_file_from_filesystem">Z lokalnego systemu plików</string>
- <string name="choose_file_from_external_application">Użyj zewnętrznej aplikacji</string>
<string name="opml_export_label">Eksport OPML</string>
<string name="html_export_label">Eksport HTML</string>
<string name="exporting_label">Eksportowanie...</string>
@@ -482,12 +601,14 @@ https://gpodder.net/register/</string>
<string name="gpodnetsync_error_descr">Wystąpił błąd podczas synchronizacji:\u0020</string>
<string name="gpodnetsync_pref_report_successful">Sukces</string>
<string name="gpodnetsync_pref_report_failed">Porażka</string>
+ <string name="gpodnetsync_username_characters_error">Nazwa użytkownika może zawierać tylko litery, cyfry, myślniki i podkreślniki.</string>
<!--Directory chooser-->
<string name="selected_folder_label">Wybrany folder:</string>
<string name="create_folder_label">Utwórz folder</string>
<string name="choose_data_directory">Wybierz folder danych</string>
<string name="choose_data_directory_message">Wybierz główny folder dla danych. AntennaPod utworzy odpowiednie podkatalogi.</string>
<string name="choose_data_directory_permission_rationale">Dostęp do pamięci zewnętrznej jest wymagany do zmiany folderu danych.</string>
+ <string name="choose_data_directory_available_space">%1$s z %2$s wolnych </string>
<string name="create_folder_msg">Utworzyć nowy folder o nazwie \"%1$s\"?</string>
<string name="create_folder_success">Utworzono nowy folder</string>
<string name="create_folder_error_no_write_access">Nie można zapisać do tego folderu</string>
@@ -506,8 +627,7 @@ https://gpodder.net/register/</string>
<string name="pref_restart_required">AntennaPod musi zostać uruchomiony ponownie, by zmiany odniosły skutek.</string>
<!--Online feed view-->
<string name="subscribe_label">Subskrybuj</string>
- <string name="subscribed_label">Subskrybowane</string>
- <string name="downloading_label">Pobieranie...</string>
+ <string name="subscribing_label">Subskrybuję...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Cofnij</string>
<string name="fast_forward_label">Przewiń</string>
@@ -528,13 +648,17 @@ https://gpodder.net/register/</string>
<string name="episode_filters_exclude">Wyłączając</string>
<string name="episode_filters_hint">Pojedyncze słowa \n\"Kilka słów\"</string>
<string name="keep_updated">Utrzymuj zaktualizowane</string>
+ <string name="keep_updated_summary">Uwzględnij ten kanał przy (automatycznym) odświeżaniu wszystkich kanałów</string>
+ <string name="auto_download_disabled_globally">Automatyczne pobieranie jest wyłączone w globalnych ustawieniach AntennaPod</string>
<!--Progress information-->
<string name="progress_upgrading_database">Aktualizacja bazy danych</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importowanie subskrybcji z jednozadaniowych aplikacji</string>
<!--Add podcast fragment-->
+ <string name="search_podcast_hint">Szukaj podcast…</string>
<string name="search_itunes_label">Szukaj w iTunes</string>
<string name="search_fyyd_label">Szukaj w fyyd</string>
+ <string name="advanced_search">Zaawansowane wyszukiwanie</string>
<string name="filter">Filtruj</string>
<!--Episodes apply actions-->
<string name="all_label">Wszystkie</string>
@@ -577,6 +701,7 @@ https://gpodder.net/register/</string>
<string name="audio_effects">Efekty audio</string>
<string name="stereo_to_mono">Downmix: Stereo na mono</string>
<string name="sonic_only">Tylko Sonic</string>
+ <string name="exoplayer_only">Tylko ExoPlayer</string>
<!--proxy settings-->
<string name="proxy_type_label">Typ</string>
<string name="host_label">Host</string>
@@ -590,6 +715,7 @@ https://gpodder.net/register/</string>
<string name="proxy_host_invalid_error">Host nie jest prawidłowym adresem IP albo domeną</string>
<string name="proxy_port_invalid_error">Błędny port</string>
<!--Subscriptions fragment-->
+ <string name="subscription_num_columns">Liczba kolumn</string>
<!--Database import/export-->
<string name="import_export">Import/Export bazy danych</string>
<string name="import_export_warning">Ta eksperymentalna funkcja może zostać użyta do przeniesienia Twoich subskrypcji i już obejrzanych odcinków na inne urządzenie.\n\n Wyeksportowana baza może zostać zaimportowana jedynie taką samą wersją programu AntennaPod. W innym przypadku może to spowodować nieprzewidziane zachowanie programu.\n\n Po zaimportowaniu, odcinki mogą być pokazane jako pobrane pomimo tego iż nie zostały pobrane. Wystarczy kliknąć odtwórz odcinek aby AntennaPod wykrył poprawny stan pliku.</string>
@@ -609,10 +735,21 @@ https://gpodder.net/register/</string>
<string name="cast_failed_setting_volume">Wystąpił błąd podczas zmiany poziomu głośności</string>
<string name="cast_failed_no_connection">Brak połączenia z urządzeniem nadającym</string>
<string name="cast_failed_no_connection_trans">Połączenie z urządzeniem nadającym zostało przerwane. Aplikacja próbuje przywrócić połączenie, jeśli jest to możliwe. Proszę poczekać kilka sekund i spróbować ponownie.</string>
- <string name="cast_failed_perform_action">Błąd podczas wykonywania akcji.</string>
<string name="cast_failed_status_request">Wystąpił błąd podczas synchronizacji z urządzeniem nadającym.</string>
<string name="cast_failed_seek">Wystąpił błąd podczas szukania nowej pozycji na urządzeniu nadającym.</string>
<string name="cast_failed_receiver_player_error">Urządzenie odbierające napotkało poważny problem</string>
<string name="cast_failed_media_error_skipping">Błąd podczas odtwarzania, pomijanie...</string>
<!--Notification channels-->
+ <string name="notification_channel_user_action">Wymagana akcja</string>
+ <string name="notification_channel_user_action_description">Pokazywane, gdy wymagane jest twoje działanie, na przykład jeśli musisz wpisać hasło.</string>
+ <string name="notification_channel_downloading">Pobieranie</string>
+ <string name="notification_channel_downloading_description">Pokazywane podczas aktywnego pobierania.</string>
+ <string name="notification_channel_playing">Teraz odtwarzane</string>
+ <string name="notification_channel_playing_description">Pozwala na kontrolowanie odtwarzania. To jest główne powiadomienie, które zobaczysz podczas odtwarzania podcastu.</string>
+ <string name="notification_channel_error">Błędy</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_create_button">Dodaj widżet</string>
+ <string name="widget_opacity">Nieprzezroczystość</string>
</resources>
diff --git a/core/src/main/res/values-pt-rBR/strings.xml b/core/src/main/res/values-pt-rBR/strings.xml
index 7c318c941..5283887cd 100644
--- a/core/src/main/res/values-pt-rBR/strings.xml
+++ b/core/src/main/res/values-pt-rBR/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Sincronizar com outros dispositivos</string>
<string name="gpodnet_auth_label">gpodder.net login</string>
- <string name="free_space_label">%1$s livre(s)</string>
<string name="episode_cache_full_title">Cache de episódios cheio</string>
<string name="episode_cache_full_message">O limite de cache de episódios foi alcançado. Você pode aumentar o tamanho do cache em Configurações.</string>
- <string name="synchronizing">Sincronizando...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Tempo total de podcasts reproduzidos:</string>
<string name="statistics_details_dialog">%1$d de %2$d episódios iniciados.\n\nReproduzidos %3$s de %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Autor(es)</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Configurações</string>
<string name="cover_label">Capa</string>
<string name="error_label">Erro</string>
<string name="error_msg_prefix">Um erro ocorreu:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">Não há dispositivos de armazenamento externo disponíveis. Por favor, certifique-se de que um dispositivo de armazenamento externo está montado para que o aplicativo possa funcionar adequadamente.</string>
<string name="chapters_label">Capítulos</string>
<string name="chapter_duration">Duração: %1$s</string>
- <string name="shownotes_label">Notas do podcast</string>
<string name="description_label">Descrição</string>
- <string name="most_recent_prefix">Episódio mais recente:\u0020</string>
<string name="episodes_suffix">\u0020episódios</string>
- <string name="length_prefix">Duração:\u0020</string>
- <string name="size_prefix">Tamanho:\u0020</string>
<string name="processing_label">Processando</string>
- <string name="loading_label">Carregando...</string>
<string name="save_username_password_label">Salvar nome do usuário e senha</string>
<string name="close_label">Fechar</string>
<string name="retry_label">Tentar novamente</string>
@@ -103,8 +95,6 @@
<string name="feedurl_label">URL do Feed</string>
<string name="etxtFeedurlHint">www.exemplo.com/feed</string>
<string name="txtvfeedurl_label">Adicionar podcast por URL</string>
- <string name="podcastdirectories_label">Procurar Podcast na Pasta</string>
- <string name="podcastdirectories_descr">Você pode procurar novos episódios no iTunes ou fyyd, ou buscar no gpodder.net pelo nome, categoria ou popularidade.</string>
<string name="browse_gpoddernet_label">Pesquisar no gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Marcar todos como reproduzidos</string>
@@ -127,7 +117,6 @@
<string name="feed_delete_confirmation_msg">Confirme se deseja deletar o podcast \"%1$s\" e todos seus episódios (incluindo os baixados).</string>
<string name="feed_remover_msg">Removendo podcast</string>
<string name="load_complete_feed">Atualizar podcast completamente</string>
- <string name="hide_episodes_title">Ocultar Episódios</string>
<string name="batch_edit">Edição em lote</string>
<string name="select_all_above">Selecionar tudo acima</string>
<string name="select_all_below">Selecionar tudo abaixo</string>
@@ -151,9 +140,7 @@
</plurals>
<string name="play_label">Reproduzir</string>
<string name="pause_label">Pausar</string>
- <string name="stop_label">Parar</string>
<string name="stream_label">Stream</string>
- <string name="remove_label">Remover</string>
<string name="delete_label">Deletar</string>
<string name="delete_failed">Não foi possível deletar o arquivo. Experimente reiniciar o dispositivo.</string>
<string name="delete_episode_label">Apagar Episódio</string>
@@ -195,14 +182,12 @@
<string name="removed_item">Item removido</string>
<!--Download messages and labels-->
<string name="download_successful">com sucesso</string>
- <string name="download_failed">falhou</string>
<string name="download_pending">Download pendente</string>
<string name="download_running">Download em execução</string>
<string name="download_error_details">Detalhes</string>
<string name="download_error_details_message">%1$s \n\nFile URL:\n%2$s</string>
<string name="download_error_device_not_found">Dispositivo de armazenamento não encontrado</string>
<string name="download_error_insufficient_space">Espaço insuficiente</string>
- <string name="download_error_file_error">Erro de arquivo</string>
<string name="download_error_http_data_error">Erro de HTTP Data</string>
<string name="download_error_error_unknown">Erro desconhecido</string>
<string name="download_error_parser_exception">Parser Exception</string>
@@ -212,7 +197,6 @@
<string name="download_error_unauthorized">Erro de autenticação</string>
<string name="download_error_file_type_type">Erro de Tipo de Arquivo</string>
<string name="download_error_forbidden">Proibido</string>
- <string name="cancel_all_downloads_label">Cancelar todos os downloads</string>
<string name="download_canceled_msg">Download cancelado</string>
<string name="download_canceled_autodownload_enabled_msg">Download cancelado\nDesabilitado <i>Download Automático</i> para este item</string>
<string name="download_report_title">Downloads finalizados com erro(s)</string>
@@ -231,7 +215,6 @@
<string name="download_log_title_unknown">Título desconhecido</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Arquivo de mídia</string>
- <string name="download_type_image">Imagem</string>
<string name="download_request_error_dialog_message_prefix">Ocorreu um erro durante download do arquivo:\u0020</string>
<string name="authentication_notification_title">Autenticação requerida</string>
<string name="authentication_notification_msg">O recurso que você solicitou requer um usuário e uma senha</string>
@@ -251,7 +234,6 @@
<string name="no_media_playing_label">Nenhuma mídia tocando</string>
<string name="player_buffering_msg">Armazenando</string>
<string name="player_go_to_picture_in_picture">Modo PIP</string>
- <string name="playbackservice_notification_title">Reproduzindo podcast</string>
<string name="unknown_media_key">AntennaPod - Chave de mídia desconhecida: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Bloquear Fila</string>
@@ -260,7 +242,6 @@
<string name="queue_unlocked">Fila desbloqueada</string>
<string name="clear_queue_label">Limpar fila</string>
<string name="undo">Desfazer</string>
- <string name="removed_from_queue">Item removido</string>
<string name="move_to_top_label">Mover para o topo</string>
<string name="move_to_bottom_label">Mover para o fim</string>
<string name="sort">Ordenar</string>
@@ -276,18 +257,14 @@
<!--Variable Speed-->
<string name="download_plugin_label">Download Plugin</string>
<string name="no_playback_plugin_title">Plugin Não Instalado</string>
- <string name="no_playback_plugin_or_sonic_msg">Para alterar a velocidade da reprodução, nós recomendamos que você abilite o reprodutor de mídias Sonic [Android 4.1+].\n\nOu então, vocÊ pode baixar o plugin de terceiros <i>Prestissimo</i> da Play Store.\nQualquer problema com Prestissimo não é responsabilidade do AntennaPod e deverá ser reportado para o proprietário do plugin.</string>
<string name="set_playback_speed_label">Velocidades de Reprodução</string>
<string name="enable_sonic">Habilitar Sonic</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Você ainda não se inscreveu a nenhum podcast.</string>
<string name="no_shownotes_label">Este episódio não possui notas.</string>
<string name="no_chapters_label">Este episódio não possui capítulos</string>
<!--Preferences-->
<string name="storage_pref">Armazenamento</string>
<string name="project_pref">Projeto</string>
- <string name="other_pref">Outros</string>
- <string name="about_pref">Sobre</string>
<string name="queue_label">Fila</string>
<string name="integrations_label">Integrações</string>
<string name="automation">Automação</string>
@@ -325,18 +302,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Configurar Tempo do dia</string>
<string name="pref_autoUpdateIntervallOrTime_every">cada %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">às %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Fazer download dos arquivos apenas via rede WiFi</string>
<string name="pref_followQueue_title">Reprodução contínua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Download de mídia via WiFi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Fones de ouvido desconectados</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Fones de ouvido reconectados</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth reconectado</string>
<string name="pref_mobileUpdate_title">Atualizações via Rede de Dados Celular</string>
- <string name="refreshing_label">Atualizando</string>
<string name="user_interface_label">Interface com usuário</string>
<string name="pref_set_theme_title">Selecionar tema</string>
- <string name="pref_nav_drawer_title">Customizar Gaveta de Navegação</string>
- <string name="pref_nav_drawer_sum">Customizar a aparencia da gaveta de navegação.</string>
<string name="pref_nav_drawer_items_title">Configurar itens da Gaveta de Navegação</string>
<string name="pref_nav_drawer_items_sum">Escolher quais itens irão aparecer na gaveta de navegação.</string>
<string name="pref_nav_drawer_feed_order_title">Configurar Ordem de Assinaturas</string>
@@ -348,8 +320,6 @@
<string name="pref_automatic_download_sum">Configurar download automático de episódios.</string>
<string name="pref_autodl_wifi_filter_title">Habilitar filtro Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Permitir download automático somente pelas redes Wi-Fi selecionadas.</string>
- <string name="pref_autodl_allow_on_mobile_title">Baixar com conexões móveis</string>
- <string name="pref_autodl_allow_on_mobile_sum">Permite baixar automaticamente com conexões de dados móveis.</string>
<string name="pref_automatic_download_on_battery_title">Baixar enquanto não está carregando</string>
<string name="pref_automatic_download_on_battery_sum">Permitir download automático enquanto a bateria não está carregando</string>
<string name="pref_parallel_downloads_title">Downloads paralelos</string>
@@ -373,7 +343,6 @@
<string name="pref_gpodnet_full_sync_sum">Sincronizar os estados das inscrições e episódios com o gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Última tentativa de sincronização: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronização iniciada</string>
- <string name="pref_gpodnet_full_sync_started">Sincronização total iniciada</string>
<string name="pref_gpodnet_login_status"><![CDATA[Entrou como <i>%1$s</i> com o dispositivo <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Exibir notificações de erros de sincronismo</string>
<string name="pref_gpodnet_notifications_sum">Essa configuração não se aplica a erros de autenticação.</string>
@@ -398,21 +367,14 @@
<string name="pref_showDownloadReport_title">Mostrar Relatório de Downloads</string>
<string name="pref_showDownloadReport_sum">Se os downloads falharem, gerar um relatório que mostra os detalhes da falha.</string>
<string name="pref_expand_notify_unsupport_toast">Versões do Android inferiores a 4.1 não suportam notificações expansíveis</string>
- <string name="pref_queueAddToFront_sum">Adicionar um novo episódio para a frente da fila.</string>
- <string name="pref_queueAddToFront_title">Adicionar ao topo da fila</string>
<string name="pref_smart_mark_as_played_disabled">Desabilitado</string>
<string name="pref_image_cache_size_title">Tamanho da Imagem em Cache</string>
<string name="pref_image_cache_size_sum">Tamanho do cache de disco para imagens.</string>
- <string name="crash_report_title">Relatório de Falha</string>
- <string name="crash_report_sum">Enviar o relatório da última falha por e-mail</string>
- <string name="send_email">Enviar e-mail</string>
<string name="experimental_pref">Experimental</string>
<string name="pref_media_player_message">Selecione qual reprodutor de mídia usar para reproduzir os arquivos</string>
<string name="pref_current_value">Valor atual: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Configurar um proxy da rede</string>
- <string name="pref_faq">Perguntas Frequentes</string>
- <string name="pref_known_issues">Problemas conhecidos</string>
<string name="pref_no_browser_found">Nenhum navegador web encontrado.</string>
<string name="pref_cast_title">Suporte ao Chromecast</string>
<string name="pref_cast_message_play_flavor">Habilitar o suporte para reprodução remota de mídia em dispositivos Cast (como Chromecast, Caixa de som ou Android TV)</string>
@@ -436,6 +398,8 @@
<string name="double_tap_toast">Clique novamente no botão voltar para sair</string>
<string name="back_button_go_to_page">Vá para a página...</string>
<string name="back_button_go_to_page_title">Selecione a página</string>
+ <!--About screen-->
+ <string name="about_pref">Sobre</string>
<!--Search-->
<string name="search_hint">Buscar por episódios</string>
<string name="found_in_shownotes_label">Encontrados em exibir descrição</string>
@@ -447,22 +411,16 @@
<string name="found_in_title_label">Encontrado no título</string>
<string name="no_results_for_query">Nenhum resultado para \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Arquivos OPML permitem que você mova seus podcasts de um programa de podcasts para outro.</string>
<string name="opml_import_option">Opção %1$d</string>
<string name="opml_import_explanation_1">Escolha o caminho específico de um arquivo no sistema.</string>
- <string name="opml_import_explanation_2">Utilizar uma aplicação externa como Dropbox, Google Drive ou o seu gerenciador de arquivos favoritos para abrir um arquivo OPML.</string>
<string name="opml_import_explanation_3">Muitos aplicativos como Google Mail, Dropbox, Google Drive e outros gerenciadores podem <i>abrir</i> arquivos OPML <i>com</i> AntennaPod.</string>
- <string name="start_import_label">Iniciar importação</string>
<string name="opml_import_label">Importação de OPML</string>
- <string name="opml_directory_error">ERRO!</string>
<string name="reading_opml_label">Lendo arquivo OPML</string>
<string name="opml_reader_error">Um erro ocorreu ao ler o documento OPML:</string>
<string name="opml_import_error_no_file">Nenhum arquivo selecionado!</string>
<string name="select_all_label">Selecionar todos</string>
<string name="deselect_all_label">Remover seleção</string>
- <string name="select_options_label">Selecionar...</string>
<string name="choose_file_from_filesystem">Dos arquivos locais do sistema</string>
- <string name="choose_file_from_external_application">Utilizar aplicação externa</string>
<string name="opml_export_label">Exportar OPML</string>
<string name="html_export_label">Exportar HTML</string>
<string name="exporting_label">Exportando...</string>
@@ -553,8 +511,6 @@
<string name="pref_restart_required">AntennaPod deve ser reiniciado para que esta mudanças tenha efeito.</string>
<!--Online feed view-->
<string name="subscribe_label">Assinar</string>
- <string name="subscribed_label">Assinado</string>
- <string name="downloading_label">Baixando...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Voltar</string>
<string name="fast_forward_label">Avançar</string>
@@ -657,7 +613,6 @@
<string name="cast_failed_setting_volume">Falha ao configurar o volume</string>
<string name="cast_failed_no_connection">Sem conexão com o dispositivo cast</string>
<string name="cast_failed_no_connection_trans">A conexão com o dispositivo cast foi perdida. A aplicação está tentando reestabelecer a conexão. Por favor espere por alguns segundos e tente novamente.</string>
- <string name="cast_failed_perform_action">Falha ao executar a ação</string>
<string name="cast_failed_status_request">Falha ao sincronizar com o dispositivo cast</string>
<string name="cast_failed_seek">Falha ao buscar uma nova posição no dispositivo cast</string>
<string name="cast_failed_receiver_player_error">O receptor de reprodução encontrou um erro grave</string>
@@ -671,4 +626,5 @@
<string name="notification_channel_playing_description">Permite controlar a reprodução. Essa é a principal notificação vista ao reproduzir um podcast.</string>
<string name="notification_channel_error">Erros</string>
<string name="notification_channel_error_description">Exibido caso algo dê errado, por exemplo se houver falha no download ou na sincronização com o gpodder.</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml
index ff9249671..d6b227f6c 100644
--- a/core/src/main/res/values-pt/strings.xml
+++ b/core/src/main/res/values-pt/strings.xml
@@ -20,12 +20,10 @@
<string name="cancel_download_label">Cancelar\ndescarga</string>
<string name="playback_history_label">Histórico de reprodução</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_summary">Sincronizar com outros dispositivos</string>
+ <string name="gpodnet_summary">Sincronizar com outros dispositivos.</string>
<string name="gpodnet_auth_label">Dados gpodder.net</string>
- <string name="free_space_label">%1$s disponível</string>
<string name="episode_cache_full_title">Cache de episódios cheia</string>
<string name="episode_cache_full_message">Atingido o limite máximo de itens em cache. Pode aumentar o tamanho de cache nas definições.</string>
- <string name="synchronizing">A sincronizar...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Tempo total dos podcasts reproduzidos:</string>
<string name="statistics_details_dialog">%1$d de %2$d episódios iniciados.\n\nReproduzidos %3$s de %4$s.</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">Calcula a duração dos podcasts reproduzidos. Se os reproduzir duas vezes, serão contados duas vezes mas se os marcar como reproduzidos não serão contados.</string>
<string name="statistics_mode_count_all">Somar todos os podcasts marcados como reproduzidos</string>
<string name="statistics_speed_not_counted">Aviso: a velocidade de reprodução nunca será considerada.</string>
+ <string name="statistics_reset_data">Reiniciar estatísticas dos dados</string>
+ <string name="statistics_reset_data_msg">Isto irá apagar o histórico do tempo de reprodução pata todos os episódios. Tem a certeza de que deseja continuar?</string>
<!--Main activity-->
<string name="drawer_open">Abrir menu</string>
<string name="drawer_close">Fechar menu</string>
@@ -63,7 +63,6 @@
<string name="author_label">Autor(es)</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Definições</string>
<string name="cover_label">Imagem</string>
<string name="error_label">Erro</string>
<string name="error_msg_prefix">Ocorreu um erro:</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">Cartão SD não disponível. Certifique-se de que inseriu o cartão corretamente para que a aplicação funcione corretamente.</string>
<string name="chapters_label">Capítulos</string>
<string name="chapter_duration">Duração: %1$s</string>
- <string name="shownotes_label">Notas</string>
<string name="description_label">Descrição</string>
- <string name="most_recent_prefix">Episódio mais recente:\u0020</string>
<string name="episodes_suffix">\u0020episódios</string>
- <string name="length_prefix">Duração:\u0020</string>
- <string name="size_prefix">Tamanho:\u0020</string>
<string name="processing_label">A processar...</string>
- <string name="loading_label">A carregar...</string>
<string name="save_username_password_label">Guardar utilizador e palavra-passe</string>
<string name="close_label">Fechar</string>
<string name="retry_label">Tentar novamente</string>
@@ -87,7 +81,7 @@
<string name="auto_download_apply_to_items_title">Aplicar aos episódios anteriores</string>
<string name="auto_download_apply_to_items_message">A definição <i>Descarga automática</i> será aplicada a todos os novos episódios.\nGostaria de também a aplicar aos episódios anteriores?</string>
<string name="auto_delete_label">Eliminar episódio automaticamente</string>
- <string name="parallel_downloads_suffix">\u0020descargas simultâneas</string>
+ <string name="parallel_downloads_suffix">\u0020descargas simultâneas.</string>
<string name="feed_auto_download_global">Predefinições</string>
<string name="feed_auto_download_always">Sempre</string>
<string name="feed_auto_download_never">Nunca</string>
@@ -108,8 +102,6 @@
<string name="feedurl_label">URL da fonte</string>
<string name="etxtFeedurlHint">URL da fonte ou do sítio web</string>
<string name="txtvfeedurl_label">Adicionar podcast via URL</string>
- <string name="podcastdirectories_label">Localizar podcasts no diretório</string>
- <string name="podcastdirectories_descr">Para novos podcasts, pode pesquisar no iTunes ou no fyyd. Também pode explorar no gpoder.net por nome, categoria ou popularidade.</string>
<string name="browse_gpoddernet_label">Procurar no gPodder.net</string>
<string name="discover">Descobrir</string>
<string name="discover_more">mais »</string>
@@ -131,13 +123,13 @@
<string name="share_link_label">Partilhar URL do episódio</string>
<string name="share_link_with_position_label">Partilhar URL do episódio (com posição)</string>
<string name="share_file_label">Partilhar ficheiro</string>
+ <string name="share_website_url_label">Partilhar URL do site</string>
<string name="share_feed_url_label">Partilhar URL da fonte</string>
<string name="share_item_url_label">Partilhar URL do ficheiro multimédia</string>
<string name="share_item_url_with_position_label">Partilhar URL do ficheiro multimédia (com posição)</string>
<string name="feed_delete_confirmation_msg">Tem a certeza de que deseja eliminar o podcast \"%1$s\" e TODOS os seus episódios (inclusive descarregados)?</string>
<string name="feed_remover_msg">A remover podcast</string>
<string name="load_complete_feed">Atualizar podcast</string>
- <string name="hide_episodes_title">Ocultar episódios</string>
<string name="batch_edit">Edição em lote</string>
<string name="select_all_above">Marcar tudo acima</string>
<string name="select_all_below">Marcar tudo abaixo</string>
@@ -161,9 +153,7 @@
</plurals>
<string name="play_label">Reproduzir</string>
<string name="pause_label">Pausa</string>
- <string name="stop_label">Parar</string>
<string name="stream_label">Emitir</string>
- <string name="remove_label">Remover</string>
<string name="delete_label">Eliminar</string>
<string name="delete_failed">Episódio não eliminado. Tente reiniciar o dispositivo.</string>
<string name="delete_episode_label">Eliminar episódio</string>
@@ -207,14 +197,12 @@
<string name="removed_item">Item removido</string>
<!--Download messages and labels-->
<string name="download_successful">sucesso</string>
- <string name="download_failed">falha</string>
<string name="download_pending">Descarga pendente</string>
<string name="download_running">Descarga atual</string>
<string name="download_error_details">Detalhes</string>
<string name="download_error_details_message">%1$s \n\Ficheiro URL:\n%2$s</string>
<string name="download_error_device_not_found">Cartão SD não encontrado</string>
<string name="download_error_insufficient_space">Espaço insuficiente</string>
- <string name="download_error_file_error">Erro no ficheiro</string>
<string name="download_error_http_data_error">Erro HTTP</string>
<string name="download_error_error_unknown">Erro desconhecido</string>
<string name="download_error_parser_exception">Exceção do processador</string>
@@ -224,7 +212,6 @@
<string name="download_error_unauthorized">Erro de autenticação</string>
<string name="download_error_file_type_type">Erro do tipo de ficheiro</string>
<string name="download_error_forbidden">Proibido</string>
- <string name="cancel_all_downloads_label">Cancelar descargas</string>
<string name="download_canceled_msg">Descarga cancelada</string>
<string name="download_canceled_autodownload_enabled_msg">Descarga cancelada\n<i>Descarga automática</i> desativada para este item</string>
<string name="download_report_title">Descargas terminadas com erros</string>
@@ -243,7 +230,6 @@
<string name="download_log_title_unknown">Título desconhecido</string>
<string name="download_type_feed">Fonte</string>
<string name="download_type_media">Ficheiro multimédia</string>
- <string name="download_type_image">Imagem</string>
<string name="download_request_error_dialog_message_prefix">Ocorreu um erro ao tentar descarregar o ficheiro:\u0020</string>
<string name="null_value_podcast_error">Não foi possível mostrar o podcast.</string>
<string name="authentication_notification_title">Requer autenticação</string>
@@ -269,16 +255,16 @@
<string name="no_media_playing_label">Nada em reprodução</string>
<string name="player_buffering_msg">A processar...</string>
<string name="player_go_to_picture_in_picture">Modo \'picture-in-picture\'</string>
- <string name="playbackservice_notification_title">Reproduzir podcast</string>
<string name="unknown_media_key">Tecla multimédia desconhecida: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Bloquear fila</string>
<string name="unlock_queue">Desbloquear fila</string>
<string name="queue_locked">Fila bloqueada</string>
<string name="queue_unlocked">Fila desbloqueada</string>
+ <string name="queue_lock_warning">Se bloquear a fila, deixará de poder deslizar ou reordenar episódios.</string>
+ <string name="checkbox_do_not_show_again">Não mostrar novamente</string>
<string name="clear_queue_label">Limpar fila</string>
<string name="undo">Anular</string>
- <string name="removed_from_queue">Item removido</string>
<string name="move_to_top_label">Mover para o topo</string>
<string name="move_to_bottom_label">Mover para o fundo</string>
<string name="sort">Organização</string>
@@ -297,13 +283,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Descarregar extra</string>
<string name="no_playback_plugin_title">Extra não instalado</string>
- <string name="no_playback_plugin_or_sonic_msg">Para que a velocidade variável de reprodução funcione, recomendamos que ative o Sonic Media Player incorporado [Android 4.1+].\n\nEm alternativa, pode descarregar o extra <i>Prestissimo</i>, disponível na Google Play.\nQuaisquer problemas que ocorram com o Prestissimo não são da responsabilidade dos programadores do AntennaPod e devem ser reportados ao dono do extra.</string>
<string name="set_playback_speed_label">Velocidades de reprodução</string>
<string name="enable_sonic">Ativar Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Não existem episódios na fila</string>
<string name="no_items_label">Pode adicionar um episódio se o descarregar ou com um toque longo no episódio e escolher \"Adicionar à fila\".</string>
- <string name="no_feeds_label">Ainda não tem quaisquer podcasts subscritos.</string>
<string name="no_shownotes_label">Este episódio não tem notas.</string>
<string name="no_run_downloads_head_label">Não existem descargas em curso</string>
<string name="no_run_downloads_label">Pode descarregar os episódios no ecrã de detalhes do podcast.</string>
@@ -321,11 +305,11 @@
<string name="no_fav_episodes_label">Pode adicionar episódios aos favoritos com um toque longo.</string>
<string name="no_chapters_head_label">Não há capítulos</string>
<string name="no_chapters_label">Este episódio não tem capítulos.</string>
+ <string name="no_subscriptions_head_label">Não tem subscrições</string>
+ <string name="no_subscriptions_label">Para subscrever um podcast, prima no ícone +</string>
<!--Preferences-->
<string name="storage_pref">Armazenamento</string>
<string name="project_pref">Projeto</string>
- <string name="other_pref">Outras</string>
- <string name="about_pref">Sobre</string>
<string name="queue_label">Fila</string>
<string name="integrations_label">Integrações</string>
<string name="automation">Automatização</string>
@@ -342,21 +326,21 @@
<string name="media_player">Reprodutor multimédia</string>
<string name="pref_episode_cleanup_title">Limpeza de episódios</string>
<string name="pref_episode_cleanup_summary">Os episódios que não estejam na fila e não sejam favoritos podem ser elegíveis para serem removidos se a Descarga automática necessitar de espaço para novos episódios.</string>
- <string name="pref_pauseOnDisconnect_sum">Pausa na reprodução ao desligar os auscultadores ou o bluetooth</string>
- <string name="pref_unpauseOnHeadsetReconnect_sum">Continuar reprodução ao ligar os auscultadores</string>
- <string name="pref_unpauseOnBluetoothReconnect_sum">Continuar reprodução ao estabelecer a ligação bluetooth</string>
- <string name="pref_hardwareForwardButtonSkips_title">Botão para avançar</string>
- <string name="pref_hardwareForwardButtonSkips_sum">Ao premir o botão Avançar no dispositivo bluetooth, ir para o episódio seguinte em vez de avançar a reprodução</string>
- <string name="pref_hardwarePreviousButtonRestarts_title">Botão Anterior para reiniciar</string>
- <string name="pref_hardwarePreviousButtonRestarts_sum">Ao premir o botão do telefone, reiniciar o episódio atual em vez de recuar para o episódio anterior</string>
- <string name="pref_followQueue_sum">Ir para a episódio seguinte ao terminar a reprodução</string>
- <string name="pref_auto_delete_sum">Eliminar episódio ao terminar a reprodução</string>
+ <string name="pref_pauseOnDisconnect_sum">Pausa na reprodução ao desligar os auscultadores ou o bluetooth.</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Continuar reprodução ao ligar os auscultadores.</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Continuar reprodução ao estabelecer a ligação bluetooth.</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Botão \'Seguinte\' para avançar</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Ao premir o botão Avançar no dispositivo bluetooth, ir para o episódio seguinte em vez de avançar a reprodução.</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Botão \'Anterior\' para reiniciar</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">Ao premir o botão Anterior no dispositivo, reiniciar o episódio atual em vez de recuar para o episódio anterior.</string>
+ <string name="pref_followQueue_sum">Ir para a episódio seguinte ao terminar a reprodução.</string>
+ <string name="pref_auto_delete_sum">Eliminar episódio ao terminar a reprodução.</string>
<string name="pref_auto_delete_title">Eliminação automática</string>
- <string name="pref_smart_mark_as_played_sum">Marcar episódio como reproduzido mesmo que restem alguns segundos de reprodução</string>
+ <string name="pref_smart_mark_as_played_sum">Marcar episódio como reproduzido mesmo que restem alguns segundos de reprodução.</string>
<string name="pref_smart_mark_as_played_title">Marcar como reproduzido (inteligente)</string>
- <string name="pref_skip_keeps_episodes_sum">Manter episódios mesmo que tenham sido ignorados</string>
+ <string name="pref_skip_keeps_episodes_sum">Manter episódios mesmo se tiverem sido ignorados.</string>
<string name="pref_skip_keeps_episodes_title">Manter episódios ignorados</string>
- <string name="pref_favorite_keeps_episodes_sum">Manter episódios se forem assinalados como favoritos</string>
+ <string name="pref_favorite_keeps_episodes_sum">Manter episódios se forem assinalados como favoritos.</string>
<string name="pref_favorite_keeps_episodes_title">Manter episódios favoritos</string>
<string name="playback_pref">Reprodução</string>
<string name="network_pref">Rede</string>
@@ -368,42 +352,42 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Definir hora do dia</string>
<string name="pref_autoUpdateIntervallOrTime_every">a cada %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">às %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Apenas descarregar através de redes sem fios</string>
<string name="pref_followQueue_title">Reprodução contínua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Descarregar por Wi-Fi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Auscultadores removidos</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Auscultadores inseridos</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Ligação bluetooth</string>
+ <string name="pref_stream_over_download_title">Preferir transmissão</string>
+ <string name="pref_stream_over_download_sum">Nas listas, mostrar botão de transmissão em vez do botão de descarga.</string>
<string name="pref_mobileUpdate_title">Dados móveis</string>
- <string name="pref_mobileUpdate_sum">Selecione o que pode ser permitido através de dados móveis</string>
+ <string name="pref_mobileUpdate_sum">Selecione o que pode ser permitido através de dados móveis.</string>
<string name="pref_mobileUpdate_refresh">Atualização de fontes</string>
<string name="pref_mobileUpdate_images">Descarga de imagens</string>
<string name="pref_mobileUpdate_auto_download">Descarga automática</string>
<string name="pref_mobileUpdate_episode_download">Descarga de episódios</string>
<string name="pref_mobileUpdate_streaming">Reprodução</string>
- <string name="refreshing_label">A atualizar</string>
<string name="user_interface_label">Interface</string>
<string name="pref_set_theme_title">Tema</string>
- <string name="pref_nav_drawer_title">Menu de navegação</string>
- <string name="pref_nav_drawer_sum">Personalizar a aparência do menu de navegação</string>
<string name="pref_nav_drawer_items_title">Alterar elementos do menu</string>
- <string name="pref_nav_drawer_items_sum">Alterar os itens que aparecem no menu de navegação</string>
+ <string name="pref_nav_drawer_items_sum">Alterar os itens que aparecem no menu de navegação.</string>
<string name="pref_nav_drawer_feed_order_title">Definir ordem de subscrição</string>
- <string name="pref_nav_drawer_feed_order_sum">Alterar a ordem das suas subscrições</string>
+ <string name="pref_nav_drawer_feed_order_sum">Alterar a ordem das suas subscrições.</string>
<string name="pref_nav_drawer_feed_counter_title">Definir contador de subscrições</string>
<string name="pref_nav_drawer_feed_counter_sum">Alterar a informação mostrada no contador de subscrições. Também afeta as subscrições se \'Ordem de subscrição\' estiver definida como \'Contador\'.</string>
- <string name="pref_set_theme_sum">Alterar a aparência do AntennaPod</string>
+ <string name="pref_set_theme_sum">Alterar a aparência do AntennaPod.</string>
<string name="pref_automatic_download_title">Descarga automática</string>
- <string name="pref_automatic_download_sum">Configurar a descarga automática dos episódios</string>
+ <string name="pref_automatic_download_sum">Configurar a descarga automática de episódios.</string>
<string name="pref_autodl_wifi_filter_title">Ativar filtro Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Apenas permitir descargas automáticas através de redes sem fios</string>
- <string name="pref_autodl_allow_on_mobile_title">Descarregar através de dados móveis</string>
- <string name="pref_autodl_allow_on_mobile_sum">Permitir descargas automáticas através de ligações de dados móveis.</string>
+ <string name="autodl_wifi_filter_permission_title">Permissão necessária</string>
+ <string name="autodl_wifi_filter_permission_message">É necessária a permissão de localização para o filtro Wi-Fi. Toque para conceder a permissão.</string>
<string name="pref_automatic_download_on_battery_title">Descarregar se não estiver a carregar</string>
<string name="pref_automatic_download_on_battery_sum">Permitir descarga automática se a bateria não estiver a ser carregada</string>
<string name="pref_parallel_downloads_title">Descargas simultâneas</string>
<string name="pref_episode_cache_title">Cache de episódios</string>
<string name="pref_episode_cache_summary">Número máximo de episódios descarregados para colocar em cache. A descarga automática será suspensa se este número for atingido.</string>
+ <string name="pref_episode_cover_title">Utilizar capa do episódio</string>
+ <string name="pref_episode_cover_summary">Utilizar imagem do episódio se disponível. Se desativar esta opção, será utilizada a imagem do podcast.</string>
+ <string name="pref_theme_title_use_system">Utilizar tema do sistema</string>
<string name="pref_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Escuro</string>
<string name="pref_theme_title_trueblack">Preto (AMOLED)</string>
@@ -423,12 +407,12 @@
<string name="pref_gpodnet_full_sync_sum">Sincronizar todas as subscrições e estados dos episódios com o gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Última tentativa de sincronização: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Sincronização iniciada</string>
- <string name="pref_gpodnet_full_sync_started">Sincronização iniciada</string>
<string name="pref_gpodnet_login_status"><![CDATA[Sessão iniciada como <i>%1$s</i> com o dispositivo <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Mostrar erros de sincronização</string>
<string name="pref_gpodnet_notifications_sum">Esta definição não é aplicável aos erros de autenticação.</string>
<string name="pref_playback_speed_title">Velocidades de reprodução</string>
- <string name="pref_playback_speed_sum">Personalizar as velocidades de reprodução disponíveis</string>
+ <string name="pref_playback_speed_sum">Personalizar as velocidades de reprodução disponíveis.</string>
+ <string name="pref_feed_playback_speed_sum">A velocidade utilizada para iniciar a reprodução áudio dos episódios desta fonte</string>
<string name="pref_playback_time_respects_speed_title">Ajustar informações multimédia à velocidade de reprodução</string>
<string name="pref_playback_time_respects_speed_sum">A posição e a duração serão ajustadas automaticamente à velocidade de reprodução</string>
<string name="pref_fast_forward">Tempo a avançar</string>
@@ -440,41 +424,45 @@
<string name="pref_expandNotify_title">Prioridade da notificação</string>
<string name="pref_expandNotify_sum">Normalmente, esta opção é utilizada para expandir a notificação e mostrar os botões de reprodução.</string>
<string name="pref_persistNotify_title">Controlos de reprodução</string>
- <string name="pref_persistNotify_sum">Manter controlos de notificação e ecrã de bloqueio ao colocar a reprodução em pausa</string>
+ <string name="pref_persistNotify_sum">Manter controlos de notificação e ecrã de bloqueio ao colocar a reprodução em pausa.</string>
<string name="pref_compact_notification_buttons_title">Definir botões do ecrã de bloqueio</string>
<string name="pref_compact_notification_buttons_sum">Alterar botões de reprodução do ecrã de bloqueio. Os botões Reproduzir/Pausa serão sempre incluídos.</string>
<string name="pref_compact_notification_buttons_dialog_title">Selecione no máximo %1$d itens</string>
<string name="pref_compact_notification_buttons_dialog_error">Apenas pode selecionar um máximo de %1$d itens.</string>
<string name="pref_lockscreen_background_title">Definir fundo do ecrã de bloqueio</string>
- <string name="pref_lockscreen_background_sum">Define a imagem do episódio como fundo do ecrã de bloqueio. Efeito colateral: também será mostrada em outras aplicações</string>
+ <string name="pref_lockscreen_background_sum">Define a imagem do episódio como fundo do ecrã de bloqueio. Efeito colateral: também será mostrada em outras aplicações.</string>
<string name="pref_showDownloadReport_title">Mostrar relatório de erros</string>
- <string name="pref_showDownloadReport_sum">Se a descarga falhar, gera um relatório que mostra os detalhes do erro</string>
+ <string name="pref_showDownloadReport_sum">Se a descarga falhar, gera um relatório que mostra os detalhes do erro.</string>
<string name="pref_expand_notify_unsupport_toast">As versões Android anteriores à 4.1 não possuem suporte à expansão de notificações</string>
- <string name="pref_queueAddToFront_sum">Colocar novos episódios no início da fila</string>
- <string name="pref_queueAddToFront_title">Novos episódios no início</string>
+ <string name="pref_enqueue_location_title">Colocar localização na fila</string>
+ <string name="pref_enqueue_location_sum">Adicionar episódios a: %1$s</string>
+ <string name="enqueue_location_back">Recuar</string>
+ <string name="enqueue_location_front">Avançar</string>
+ <string name="enqueue_location_after_current">Após o episódio atual</string>
<string name="pref_smart_mark_as_played_disabled">Desativada</string>
<string name="pref_image_cache_size_title">Cache de imagens</string>
- <string name="pref_image_cache_size_sum">O tamanho da cache de imagens</string>
- <string name="crash_report_title">Relatório de erro</string>
- <string name="crash_report_sum">Enviar o relatório de erros por e-mail</string>
- <string name="send_email">Enviar e-mail</string>
+ <string name="pref_image_cache_size_sum">Tamanho para a cache de imagens.</string>
+ <string name="view_mailing_list">Ver lista de correio</string>
+ <string name="bug_report_title">Reporte de erros</string>
+ <string name="open_bug_tracker">Abrir rastreador de erros</string>
+ <string name="copy_to_clipboard">Copiar para a área de transferência</string>
+ <string name="copied_to_clipboard">Copiado para a área de transferência</string>
<string name="experimental_pref">Experimental</string>
- <string name="pref_media_player_message">Selecione o reprodutor multimédia a utilizar</string>
+ <string name="pref_media_player_message">Selecione o reprodutor multimédia a utilizar.</string>
<string name="pref_current_value">Valor atual: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
- <string name="pref_proxy_sum">Definir um proxy de rede</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Problemas conhecidos</string>
+ <string name="pref_proxy_sum">Definir um proxy de rede.</string>
+ <string name="pref_faq">Questões frequentes</string>
<string name="pref_no_browser_found">Navegador web não encontrado</string>
<string name="pref_cast_title">Suporte Chromecast</string>
<string name="pref_cast_message_play_flavor">Ativar suporte a reprodução multimédia em dispositivos Cast (tais como Chromecast, Android TV...)</string>
- <string name="pref_cast_message_free_flavor">O Chromecast necessita de bibliotecas proprietárias de terceiros que estão desativadas nesta versão do AntennaPod</string>
+ <string name="pref_cast_message_free_flavor">Chromecast necessita de bibliotecas proprietárias de terceiros que estão desativadas nesta versão do AntennaPod.</string>
<string name="pref_enqueue_downloaded_title">Colocar descargas na fila</string>
- <string name="pref_enqueue_downloaded_summary">Adicionar à fila os episódios descarregados</string>
+ <string name="pref_enqueue_downloaded_summary">Adicionar à fila os episódios descarregados.</string>
<string name="media_player_builtin">Reprodutor nativo Android</string>
<string name="pref_skip_silence_title">Ignorar silêncio no áudio</string>
<string name="pref_videoBehavior_title">Ao sair do vídeo</string>
- <string name="pref_videoBehavior_sum">Comportamento ao sair da reprodução do vídeo</string>
+ <string name="pref_videoBehavior_sum">Comportamento ao sair da reprodução do vídeo.</string>
<string name="stop_playback">Parar reprodução</string>
<string name="continue_playback">Continuar reprodução de áudio</string>
<string name="behavior">Comportamento</string>
@@ -490,6 +478,16 @@
<string name="back_button_go_to_page_title">Selecionar página</string>
<string name="pref_delete_removes_from_queue_title">Remover da fila</string>
<string name="pref_delete_removes_from_queue_sum">Remover episódio da fila se este for eliminado.</string>
+ <!--About screen-->
+ <string name="about_pref">Acerca</string>
+ <string name="antennapod_version">Versão do AntennaPod</string>
+ <string name="developers">Programadores</string>
+ <string name="developers_summary">Todos podem ajudar a tornar o AntennaPod melhor</string>
+ <string name="translators">Tradutores</string>
+ <string name="translators_summary">As traduções são criadas pelos utilizadores do AntennaPod, através da plataforma Transifex</string>
+ <string name="privacy_policy">Política de privacidade</string>
+ <string name="licenses">Licenças</string>
+ <string name="licenses_summary">AntennaPod utiliza outro software grandioso</string>
<!--Search-->
<string name="search_hint">Pesquisar por episódios</string>
<string name="found_in_shownotes_label">Encontrado nas notas</string>
@@ -501,22 +499,16 @@
<string name="found_in_title_label">Encontrado no título</string>
<string name="no_results_for_query">Não existem resultados para \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">Os ficheiros OPML permitem-lhe mover os podcasts entre aplicações</string>
<string name="opml_import_option">Opção %1$d</string>
<string name="opml_import_explanation_1">Escolha um caminho especifico no sistema local de ficheiros</string>
- <string name="opml_import_explanation_2">Utilize aplicações externas como o Dropbox, Google Drive ou o seu gestor de ficheiros preferido para abrir o ficheiro OPML</string>
<string name="opml_import_explanation_3">As aplicações como o Google Mail, Dropbox, Google Drive ou gestores de ficheiros podem <i>abrir</i> os ficheiros OPML <i>através</i> do AntennaPod</string>
- <string name="start_import_label">Iniciar importação</string>
<string name="opml_import_label">Importação OPML</string>
- <string name="opml_directory_error">Erro!</string>
<string name="reading_opml_label">A ler ficheiro OPML</string>
<string name="opml_reader_error">Ocorreu um erro ao ler o ficheiro OPML:</string>
<string name="opml_import_error_no_file">Nenhum ficheiro selecionado!</string>
<string name="select_all_label">Marcar tudo</string>
<string name="deselect_all_label">Desmarcar tudo</string>
- <string name="select_options_label">Selecionar...</string>
<string name="choose_file_from_filesystem">Do sistema local de ficheiros</string>
- <string name="choose_file_from_external_application">Utilizar aplicação externa</string>
<string name="opml_export_label">Exportação OPML</string>
<string name="html_export_label">Exportação HTML</string>
<string name="exporting_label">A exportar...</string>
@@ -602,15 +594,14 @@
<string name="folder_not_empty_dialog_title">A pasta não está vazia</string>
<string name="folder_not_empty_dialog_msg">A pasta escolhida não está vazia. Os dados multimédia e os outros ficheiros serão colocados nesta pasta. Continuar?</string>
<string name="set_to_default_folder">Escolha a pasta pré-definida</string>
- <string name="pref_pausePlaybackForFocusLoss_sum">Pausa na reprodução em vez de baixar o volume se outra aplicação quiser reproduzir sons</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Pausa na reprodução em vez de baixar o volume se outra aplicação quiser reproduzir sons.</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pausa nas interrupções</string>
- <string name="pref_resumeAfterCall_sum">Continuar reprodução ao terminar a chamada</string>
+ <string name="pref_resumeAfterCall_sum">Continuar reprodução ao terminar a chamada.</string>
<string name="pref_resumeAfterCall_title">Continuar após a chamada</string>
<string name="pref_restart_required">Tem que reiniciar o AntennaPod para aplicar as alterações</string>
<!--Online feed view-->
<string name="subscribe_label">Subscrever</string>
- <string name="subscribed_label">Subscrito</string>
- <string name="downloading_label">A descarregar...</string>
+ <string name="subscribing_label">Subscrição em curso...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Recuo rápido</string>
<string name="fast_forward_label">Avanço rápido</string>
@@ -718,7 +709,6 @@
<string name="cast_failed_setting_volume">Falha ao definir o volume</string>
<string name="cast_failed_no_connection">Não existe uma ligação ao dispositivo \'cast\'</string>
<string name="cast_failed_no_connection_trans">Falha de ligação ao dispositivo. A aplicação está a tentar estabelecer uma nova ligação. Por favor aguarde alguns segundos e tente novamente.</string>
- <string name="cast_failed_perform_action">Falha ao executar a ação</string>
<string name="cast_failed_status_request">Falha de sincronização com o dispositivo</string>
<string name="cast_failed_seek">Falha ao procurar a nova posição no dispositivo</string>
<string name="cast_failed_receiver_player_error">O reprodutor encontrou um erro crítico</string>
@@ -733,4 +723,7 @@
<string name="notification_channel_error">Erros</string>
<string name="notification_channel_error_description">Mostrar se ocorrerem erros como, por exemplo, não for possível a descarga.</string>
<string name="import_bad_file">Ficheiro inválido/danificado</string>
+ <!--Widget settings-->
+ <string name="widget_create_button">Criar widget</string>
+ <string name="widget_opacity">Opacidade</string>
</resources>
diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml
index 36d69fd6f..5c4753f41 100644
--- a/core/src/main/res/values-ru/strings.xml
+++ b/core/src/main/res/values-ru/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Синхронизировать с другими устройствами</string>
<string name="gpodnet_auth_label">Войти на gpodder.net</string>
- <string name="free_space_label">свободно %1$s</string>
<string name="episode_cache_full_title">Кэш выпусков заполнен</string>
<string name="episode_cache_full_message">Достигнут предел кэша выпусков. Объём кэша можно увеличить в Настройках.</string>
- <string name="synchronizing">Синхронизация…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Общее время прослушивания подкастов:</string>
<string name="statistics_details_dialog">%1$d из %2$d выпусков начато.\n\nПрослушано %3$s из %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Автор(ы)</string>
<string name="language_label">Язык</string>
<string name="url_label">Адрес</string>
- <string name="podcast_settings_label">Настройки</string>
<string name="cover_label">Обложка</string>
<string name="error_label">Ошибка</string>
<string name="error_msg_prefix">Произошла ошибка:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">Внешний носитель недоступен. Убедитесь что внешний носитель установлен, иначе приложение не сможет нормально работать.</string>
<string name="chapters_label">Главы</string>
<string name="chapter_duration">Длительность: %1$s</string>
- <string name="shownotes_label">Примечания к выпуску</string>
<string name="description_label">Описание</string>
- <string name="most_recent_prefix">Последний выпуск:\u0020</string>
<string name="episodes_suffix">\u0020выпуск(ов)</string>
- <string name="length_prefix">Продолжительность:\u0020</string>
- <string name="size_prefix">Размер:\u0020</string>
<string name="processing_label">Обработка</string>
- <string name="loading_label">Загрузка…</string>
<string name="save_username_password_label">Сохранить имя пользователя и пароль</string>
<string name="close_label">Закрыть</string>
<string name="retry_label">Повторить</string>
@@ -112,8 +104,6 @@
<string name="feedurl_label">URL канала</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Добавить подкаст по URL</string>
- <string name="podcastdirectories_label">Найти подкаст в каталоге</string>
- <string name="podcastdirectories_descr">Вы можете искать новые подкасты в каталогах iTunes, fyyd или gpodder.net по имени, категории или популярности.</string>
<string name="browse_gpoddernet_label">Просмотр gpodder.net</string>
<string name="discover">Найти каналы</string>
<string name="discover_more">ещё »</string>
@@ -141,7 +131,6 @@
<string name="feed_delete_confirmation_msg">Подтвердите желание удалить подкаст \"%1$s\" и ВСЕ его эпизоды, в том числе и скачанные.</string>
<string name="feed_remover_msg">Удаление подкаста</string>
<string name="load_complete_feed">Обновить весь подкаст</string>
- <string name="hide_episodes_title">Скрыть выпуски</string>
<string name="batch_edit">Групповая обработка</string>
<string name="select_all_above">Выбрать всё выше</string>
<string name="select_all_below">Выбрать всё ниже</string>
@@ -167,9 +156,7 @@
</plurals>
<string name="play_label">Воспроизвести</string>
<string name="pause_label">Пауза</string>
- <string name="stop_label">Остановить</string>
<string name="stream_label">Воспроизвести из сети</string>
- <string name="remove_label">Удалить</string>
<string name="delete_label">Удалить</string>
<string name="delete_failed">Невозможно удалить файл. Попробуйте перезагрузить устройство.</string>
<string name="delete_episode_label">Удалить выпуск</string>
@@ -223,7 +210,6 @@
<string name="removed_item">Удалено</string>
<!--Download messages and labels-->
<string name="download_successful">успешно</string>
- <string name="download_failed">не удалось</string>
<string name="download_pending">Загрузка в ожидании</string>
<string name="download_running">Загрузка в процессе</string>
<string name="download_error_details">Подробнее</string>
@@ -233,7 +219,6 @@ URL файла:
%2$s</string>
<string name="download_error_device_not_found">Устройство хранения не найдено</string>
<string name="download_error_insufficient_space">Недостаточно места</string>
- <string name="download_error_file_error">Ошибка файла</string>
<string name="download_error_http_data_error">Ошибка данных HTTP</string>
<string name="download_error_error_unknown">Неизвестная ошибка</string>
<string name="download_error_parser_exception">Ошибка обработки</string>
@@ -243,7 +228,6 @@ URL файла:
<string name="download_error_unauthorized">Ошибка авторизации</string>
<string name="download_error_file_type_type">Ошибка типа файла</string>
<string name="download_error_forbidden">Доступ запрещён</string>
- <string name="cancel_all_downloads_label">Отменить все загрузки</string>
<string name="download_canceled_msg">Загрузка отменена</string>
<string name="download_canceled_autodownload_enabled_msg">Загрузка отменена\nОтключена <i>Автоматическая загрузка</i> для этого эпизода</string>
<string name="download_report_title">Загрузки завершились с ошибкой</string>
@@ -264,7 +248,6 @@ URL файла:
<string name="download_log_title_unknown">Неизвестное название</string>
<string name="download_type_feed">Канал</string>
<string name="download_type_media">Медиафайл</string>
- <string name="download_type_image">Изображение</string>
<string name="download_request_error_dialog_message_prefix">Ошибка при загрузке файла:\u0020</string>
<string name="null_value_podcast_error">Не предоставлен подкаст, который можно было бы показать.</string>
<string name="authentication_notification_title">Необходима авторизация</string>
@@ -290,7 +273,6 @@ URL файла:
<string name="no_media_playing_label">Ничего не воспроизводится</string>
<string name="player_buffering_msg">Буферизация</string>
<string name="player_go_to_picture_in_picture">Картинка в картинке</string>
- <string name="playbackservice_notification_title">Воспроизведение подкаста</string>
<string name="unknown_media_key">AntennaPod - неизвестный ключ носителя: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Заблокировать очередь</string>
@@ -299,7 +281,6 @@ URL файла:
<string name="queue_unlocked">Очередь разблокирована</string>
<string name="clear_queue_label">Очистить очередь</string>
<string name="undo">Отмена</string>
- <string name="removed_from_queue">Удалено</string>
<string name="move_to_top_label">Переместить вверх</string>
<string name="move_to_bottom_label">Переместить вниз</string>
<string name="sort">Сортировать</string>
@@ -318,13 +299,11 @@ URL файла:
<!--Variable Speed-->
<string name="download_plugin_label">Загрузить плагин</string>
<string name="no_playback_plugin_title">Плагин не установлен</string>
- <string name="no_playback_plugin_or_sonic_msg">Чтобы воспользоваться изменением скорости воспроизведения, рекомендуется включить встроенный медиапроигрыватель Sonic [Android 4.1+].\n\nИли же можно загрузить сторонний плагин <i>Prestissimo</i> из Play Store.\nОбо всех проблемах, относящихся к Prestissimo стоит сообщать владельцу плагина, AntennaPod к этому не причастен.</string>
<string name="set_playback_speed_label">Скорость воспроизведения</string>
<string name="enable_sonic">Включить Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Нет эпизодов в очереди</string>
<string name="no_items_label">Добавьте выпуск, загрузив его, или долго удерживайте выпуск и выберите «Добавить в очередь».</string>
- <string name="no_feeds_label">Вы ещё не подписаны ни на один подкаст.</string>
<string name="no_shownotes_label">Этот выпуск не содержит примечаний.</string>
<string name="no_run_downloads_head_label">Нет запущенных загрузок</string>
<string name="no_run_downloads_label">Вы можете загрузить выпуски на экране сведений о подкасте.</string>
@@ -345,8 +324,6 @@ URL файла:
<!--Preferences-->
<string name="storage_pref">Хранилище</string>
<string name="project_pref">Проект</string>
- <string name="other_pref">Прочее</string>
- <string name="about_pref">О программе</string>
<string name="queue_label">Очередь</string>
<string name="integrations_label">Интеграция</string>
<string name="automation">Автоматизация</string>
@@ -389,9 +366,7 @@ URL файла:
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Задать время</string>
<string name="pref_autoUpdateIntervallOrTime_every">каждые %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">в %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Загружать файлы только через Wi-Fi</string>
<string name="pref_followQueue_title">Непрерывное воспроизведение</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Загрузка по Wi-Fi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Наушники отсоединены</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Наушники подключены обратно</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth-соединение восстановлено</string>
@@ -402,11 +377,8 @@ URL файла:
<string name="pref_mobileUpdate_auto_download">Автозагрузка</string>
<string name="pref_mobileUpdate_episode_download">Загрузка выпусков</string>
<string name="pref_mobileUpdate_streaming">Трансляции по сети</string>
- <string name="refreshing_label">Обновление</string>
<string name="user_interface_label">Интерфейс</string>
<string name="pref_set_theme_title">Выбор темы</string>
- <string name="pref_nav_drawer_title">Настроить боковую панель</string>
- <string name="pref_nav_drawer_sum">Настроить вид боковой панели</string>
<string name="pref_nav_drawer_items_title">Выбрать пункты боковой панели</string>
<string name="pref_nav_drawer_items_sum">Изменение отображения пунктов меню боковой панели</string>
<string name="pref_nav_drawer_feed_order_title">Упорядочить подписки</string>
@@ -418,8 +390,6 @@ URL файла:
<string name="pref_automatic_download_sum">Настроить автоматическую загрузку выпусков.</string>
<string name="pref_autodl_wifi_filter_title">Включить фильтр Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Разрешать автоматическую загрузку только для выбранных сетей Wi-Fi.</string>
- <string name="pref_autodl_allow_on_mobile_title">Загрузка через мобильное подключение к интернету</string>
- <string name="pref_autodl_allow_on_mobile_sum">Позволить автоматические загрузки через мобильное подключение к интернету.</string>
<string name="pref_automatic_download_on_battery_title">Загружать без зарядки</string>
<string name="pref_automatic_download_on_battery_sum">Разрешать автоматическую загрузку когда батарея не заряжается</string>
<string name="pref_parallel_downloads_title">Одновременные загрузки</string>
@@ -444,7 +414,6 @@ URL файла:
<string name="pref_gpodnet_full_sync_sum">Синхронизировать состояния всех подписок и выпусков при помощи gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Предыдущая попытка синхронизации: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Синхронизация запущена</string>
- <string name="pref_gpodnet_full_sync_started">Запущена полная синхронизация</string>
<string name="pref_gpodnet_login_status"><![CDATA[Вход как <i>%1$s</i> с устройства <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Показывать уведомления об ошибках синхронизации</string>
<string name="pref_gpodnet_notifications_sum">Не затрагивает ошибки авторизации.</string>
@@ -471,21 +440,15 @@ URL файла:
<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_queueAddToFront_sum">Добавлять новые выпуски в начало очереди</string>
- <string name="pref_queueAddToFront_title">В начало очереди</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="crash_report_title">Отчёт о сбое</string>
- <string name="crash_report_sum">Отослать последний отчёт о сбое по e-mail</string>
- <string name="send_email">Отправить Email</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_known_issues">Известные проблемы</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">Включить поддержку удалённого воспроизведения на устройствах с Google Cast (таких как Chromecast, динамики или ТВ на ОС Android)</string>
@@ -511,6 +474,10 @@ URL файла:
<string name="back_button_go_to_page_title">Выбрать страницу</string>
<string name="pref_delete_removes_from_queue_title">Убирать удаленные из очереди</string>
<string name="pref_delete_removes_from_queue_sum">Автоматически убирать выпуск из очереди при его удалении.</string>
+ <!--About screen-->
+ <string name="about_pref">О программе</string>
+ <string name="developers">Разработчики</string>
+ <string name="translators">Переводчики</string>
<!--Search-->
<string name="search_hint">Найти выпуски</string>
<string name="found_in_shownotes_label">Найдено в примечаниях к выпуску</string>
@@ -522,22 +489,16 @@ URL файла:
<string name="found_in_title_label">Найдено в заголовке</string>
<string name="no_results_for_query">Для \"%1$s\" ничего не найдено</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML файлы позволяют перемещать ваши подкасты из одного менеджера подкастов в другой.</string>
<string name="opml_import_option">Настройка %1$d</string>
<string name="opml_import_explanation_1">Укажите путь к файлу на устройстве</string>
- <string name="opml_import_explanation_2">Откройте OPML-файл с помощью внешних приложений: Dropbox, Google Drive или любого файлового менеджера.</string>
<string name="opml_import_explanation_3">Множество приложений умеют <i>открывать</i> OPML-файлы <i>в</i> AntennaPod, например: Google Mail, Dropbox, Google Drive и большинство файловых менеджеров.</string>
- <string name="start_import_label">Начать импорт</string>
<string name="opml_import_label">Импорт OPML</string>
- <string name="opml_directory_error">Ошибка</string>
<string name="reading_opml_label">Чтение файла OPML</string>
<string name="opml_reader_error">При чтении OPML произошла ошибка:</string>
<string name="opml_import_error_no_file">Файл не выбран!</string>
<string name="select_all_label">Отметить все</string>
<string name="deselect_all_label">Снять все отметки</string>
- <string name="select_options_label">Отметить…</string>
<string name="choose_file_from_filesystem">Из файловой системы</string>
- <string name="choose_file_from_external_application">С помощью внешнего приложения</string>
<string name="opml_export_label">Экспорт в OPML</string>
<string name="html_export_label">Экспорт в HTML</string>
<string name="exporting_label">Экспортируется...</string>
@@ -638,8 +599,6 @@ URL файла:
<string name="pref_restart_required">Следует перезапустить AntennaPod для применения настроек.</string>
<!--Online feed view-->
<string name="subscribe_label">Подписаться</string>
- <string name="subscribed_label">Подписка оформлена</string>
- <string name="downloading_label">Идёт загрузка…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Назад</string>
<string name="fast_forward_label">Вперёд</string>
@@ -747,7 +706,6 @@ URL файла:
<string name="cast_failed_setting_volume">Невозможно установить громкость</string>
<string name="cast_failed_no_connection">Нет соединений с устройствами Google cast</string>
<string name="cast_failed_no_connection_trans">Соединение с устройством Google cast потеряно. Идёт попытка восстановления соединения. Пожалуйста, подождите пару секунд и попробуйте снова.</string>
- <string name="cast_failed_perform_action">Не удалось выполнить действие</string>
<string name="cast_failed_status_request">Не удалось синхронизироваться с устройством Google cast</string>
<string name="cast_failed_seek">Не удалось выполнить перемотку на устройстве Google cast</string>
<string name="cast_failed_receiver_player_error">Серьёзная ошибка воспроизведения в устройстве Google cast</string>
@@ -762,4 +720,6 @@ URL файла:
<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_create_button">Создать виджет</string>
</resources>
diff --git a/core/src/main/res/values-sv-rSE/strings.xml b/core/src/main/res/values-sv-rSE/strings.xml
index 6e7fd18ef..52f3f063d 100644
--- a/core/src/main/res/values-sv-rSE/strings.xml
+++ b/core/src/main/res/values-sv-rSE/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Synkronisera med andra enheter</string>
<string name="gpodnet_auth_label">Inloggning till gpodder.net</string>
- <string name="free_space_label">%1$s kvar</string>
<string name="episode_cache_full_title">Episodcachen är full</string>
<string name="episode_cache_full_message">Episodcachens gräns har nåtts. Du kan öka cachens storlek i inställningarna.</string>
- <string name="synchronizing">Synkroniserar...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Total uppspelningstid:</string>
<string name="statistics_details_dialog">%1$d av %2$d episoder startade.\n\nSpelat %3$s av %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Författare</string>
<string name="language_label">Språk</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Inställningar</string>
<string name="cover_label">Bild</string>
<string name="error_label">Fel</string>
<string name="error_msg_prefix">Ett fel inträffade:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">Ingen extern lagring är tillgänglig. Se till att montera en extern lagringsenhet så att appen kan fungera korrekt.</string>
<string name="chapters_label">Kapitel</string>
<string name="chapter_duration">Längd: %1$s</string>
- <string name="shownotes_label">Shownotes</string>
<string name="description_label">Beskrivning</string>
- <string name="most_recent_prefix">Senaste episoden:\u0020</string>
<string name="episodes_suffix">\u0020episoder</string>
- <string name="length_prefix">Längd:\u0020</string>
- <string name="size_prefix">Storlek:\u0020</string>
<string name="processing_label">Bearbetar</string>
- <string name="loading_label">Laddar…</string>
<string name="save_username_password_label">Spara användarnamn och lösenord</string>
<string name="close_label">Stäng</string>
<string name="retry_label">Försök igen</string>
@@ -108,8 +100,6 @@
<string name="feedurl_label">Flödets URL</string>
<string name="etxtFeedurlHint">URL till flöde eller webbsida</string>
<string name="txtvfeedurl_label">Lägg till podcast via URL</string>
- <string name="podcastdirectories_label">Hitta Podcast i biblioteket</string>
- <string name="podcastdirectories_descr">För att hitta nya podcasts kan du söka i iTunes, fyyd, eller på gpodder.net baserat på namn, kategori eller popularitet.</string>
<string name="browse_gpoddernet_label">Bläddra på gpodder.net</string>
<string name="discover">Upptäck</string>
<string name="discover_more">mer »</string>
@@ -137,7 +127,6 @@
<string name="feed_delete_confirmation_msg">Bekräfta att du vill ta bort podcast \"%1$s\" och ALLA dess episoder (inklusive nedladdade episoder).</string>
<string name="feed_remover_msg">Tar bort podcast</string>
<string name="load_complete_feed">Uppdatera hela podcasten</string>
- <string name="hide_episodes_title">Dölj episoder</string>
<string name="batch_edit">Batchredigering</string>
<string name="select_all_above">Välj alla ovanför</string>
<string name="select_all_below">Välj alla nedanför</string>
@@ -161,9 +150,7 @@
</plurals>
<string name="play_label">Spela</string>
<string name="pause_label">Pausa</string>
- <string name="stop_label">Stopp</string>
<string name="stream_label">Strömma</string>
- <string name="remove_label">Ta bort</string>
<string name="delete_label">Ta bort</string>
<string name="delete_failed">Kunde inte ta bort filen. Testa att starta om enheten.</string>
<string name="delete_episode_label">Radera episod</string>
@@ -207,14 +194,12 @@
<string name="removed_item">Borttagen</string>
<!--Download messages and labels-->
<string name="download_successful">lyckades</string>
- <string name="download_failed">misslyckades</string>
<string name="download_pending">Avvaktar nedladdning</string>
<string name="download_running">Nedladdning pågår</string>
<string name="download_error_details">Detaljer</string>
<string name="download_error_details_message">%1$s \n\nFil-URL:\n%2$s</string>
<string name="download_error_device_not_found">Hittade ingen lagringsenhet</string>
<string name="download_error_insufficient_space">Otillräckligt utrymme</string>
- <string name="download_error_file_error">Filfel</string>
<string name="download_error_http_data_error">HTTP data fel</string>
<string name="download_error_error_unknown">Okänt fel</string>
<string name="download_error_parser_exception">Tolkningsfel</string>
@@ -224,7 +209,6 @@
<string name="download_error_unauthorized">Autentiseringsfel</string>
<string name="download_error_file_type_type">Filtypsfel</string>
<string name="download_error_forbidden">Förbjuden</string>
- <string name="cancel_all_downloads_label">Avbryt alla nedladdningar</string>
<string name="download_canceled_msg">Nedladdning avbruten</string>
<string name="download_canceled_autodownload_enabled_msg">Nedladdning avbruten\nStängde av <i>Automatisk nedladdning</i> för detta föremål</string>
<string name="download_report_title">Nedladdningar avslutades med fel</string>
@@ -243,7 +227,6 @@
<string name="download_log_title_unknown">Okänd titel</string>
<string name="download_type_feed">Flöde</string>
<string name="download_type_media">Mediafil</string>
- <string name="download_type_image">Bild</string>
<string name="download_request_error_dialog_message_prefix">Ett fel uppstod vid försöket att ladda ner filen:\u0020</string>
<string name="null_value_podcast_error">Inga tillhandahållna podcasts som kunde visas.</string>
<string name="authentication_notification_title">Autentisering krävs</string>
@@ -251,6 +234,9 @@
<string name="confirm_mobile_download_dialog_title">Bekräfta mobil nedladdning</string>
<string name="confirm_mobile_download_dialog_message_not_in_queue">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nDu kan välja att antingen bara lägga till episoden i kön eller att tillfälligt tillåta nedladdning.\n\n<small>Ditt val gäller i 10 minuter.</small></string>
<string name="confirm_mobile_download_dialog_message">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nVill du tillfälligt tillåta nedladdning?\n\n<small>Ditt val gäller i 10 minuter.</small></string>
+ <string name="confirm_mobile_streaming_notification_title">Bekräfta mobil strömning</string>
+ <string name="confirm_mobile_streaming_notification_message">Strömning över mobilanslutningar är avaktiverat i inställningarna. Tryck för att strömma ändå.</string>
+ <string name="confirm_mobile_streaming_button_always">Tillåt alltid</string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Köa</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Tillåt tillfälligt</string>
<!--Mediaplayer messages-->
@@ -260,11 +246,12 @@
<string name="player_ready_msg">Beredd</string>
<string name="player_seeking_msg">Söker</string>
<string name="playback_error_server_died">Servern dog</string>
+ <string name="playback_error_unsupported">Mediatypen stöds inte</string>
+ <string name="playback_error_timeout">Åtgärden tog för lång tid</string>
<string name="playback_error_unknown">Okänt fel</string>
<string name="no_media_playing_label">Inget media spelar</string>
<string name="player_buffering_msg">Buffrar</string>
<string name="player_go_to_picture_in_picture">Bild-i-bild läge</string>
- <string name="playbackservice_notification_title">Spelar podcast</string>
<string name="unknown_media_key">AntannaPod - Okänd mediaknapp: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Lås Kön</string>
@@ -273,10 +260,10 @@
<string name="queue_unlocked">Kön upplåst</string>
<string name="clear_queue_label">Rensa kön</string>
<string name="undo">Ångra</string>
- <string name="removed_from_queue">Föremålet avlägsnades</string>
<string name="move_to_top_label">Flytta längst upp</string>
<string name="move_to_bottom_label">Flytta längst ned</string>
<string name="sort">Sortering</string>
+ <string name="keep_sorted">Behåll sorterad</string>
<string name="date">Datum</string>
<string name="duration">Längd</string>
<string name="episode_title">Episodtitel</string>
@@ -291,13 +278,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Ladda ner tillägg</string>
<string name="no_playback_plugin_title">Tillägg ej installerat</string>
- <string name="no_playback_plugin_or_sonic_msg">För att variabel uppspelningshastighet ska fungera rekommenderas att aktivera den inbyggda Sonic mediaspelaren [Android 4.1+].\n\nAlternativt kan du ladda ner tredjepartstillägget <i>Prestissimo</i> från Google Play.\nProblem med Prestissimo är inte AntennaPods ansvar och bör rapporteras till tilläggets ägare.</string>
<string name="set_playback_speed_label">Uppspelningshastigheter</string>
<string name="enable_sonic">Aktivera Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">Inga köade episoder</string>
<string name="no_items_label">Lägg till en episod genom att ladda ner den, eller tryck länge på en episod och väl \"Lägg till i kön\".</string>
- <string name="no_feeds_label">Du har inte prenumererat på några podcasts än.</string>
<string name="no_shownotes_label">Denna episod har inga shownotes.</string>
<string name="no_run_downloads_head_label">Inga pågående nedladdningar</string>
<string name="no_run_downloads_label">Du kan ladda ner episoder på sidan podcastdetaljer.</string>
@@ -318,8 +303,6 @@
<!--Preferences-->
<string name="storage_pref">Lagring</string>
<string name="project_pref">Projekt</string>
- <string name="other_pref">Annat</string>
- <string name="about_pref">Om</string>
<string name="queue_label">Kö</string>
<string name="integrations_label">Integrationer</string>
<string name="automation">Automatisering</string>
@@ -330,6 +313,9 @@
<string name="external_elements">Externa element</string>
<string name="interruptions">Avbrott</string>
<string name="playback_control">Uppspelningskontroll</string>
+ <string name="preference_search_hint">Sök...</string>
+ <string name="preference_search_no_results">Inga resultat</string>
+ <string name="preference_search_clear_history">Resnsa historiken</string>
<string name="media_player">Mediaspelare</string>
<string name="pref_episode_cleanup_title">Episodupprensning</string>
<string name="pref_episode_cleanup_summary">Episoder som inte är i kön och inte är favoriter kan tas bort om Automatisk Nedladdning behöver utrymme för nya episoder</string>
@@ -359,18 +345,19 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Välj tid på dagen</string>
<string name="pref_autoUpdateIntervallOrTime_every">var %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">vid %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Hämta mediefiler endast över WiFi</string>
<string name="pref_followQueue_title">Kontinuerlig uppspelning</string>
- <string name="pref_downloadMediaOnWifiOnly_title">WiFi nedladdning</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar bortkopplas</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar återanslutna</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Blutetooth återansluts</string>
<string name="pref_mobileUpdate_title">Mobila uppdateringar</string>
- <string name="refreshing_label">Uppdaterar</string>
+ <string name="pref_mobileUpdate_sum">Välj vad som ska tillåtas över mobila dataanslutningar</string>
+ <string name="pref_mobileUpdate_refresh">Flödesuppdatering</string>
+ <string name="pref_mobileUpdate_images">Omslagsbilder</string>
+ <string name="pref_mobileUpdate_auto_download">Automatisk nedladdning</string>
+ <string name="pref_mobileUpdate_episode_download">Episodnedladdning</string>
+ <string name="pref_mobileUpdate_streaming">Strömning</string>
<string name="user_interface_label">Användargränssnitt</string>
<string name="pref_set_theme_title">Välj tema</string>
- <string name="pref_nav_drawer_title">Anpassa navigeringsmenyn</string>
- <string name="pref_nav_drawer_sum">Anpassa utseendet på navigeringmenyn.</string>
<string name="pref_nav_drawer_items_title">Välj objekt i navigeringsmenyn</string>
<string name="pref_nav_drawer_items_sum">Ändra vilka saker som visas på navigationsmenyn.</string>
<string name="pref_nav_drawer_feed_order_title">Välj prenumerationsordning</string>
@@ -382,8 +369,6 @@
<string name="pref_automatic_download_sum">Konfigurera automatisk nedladdning av episoder.</string>
<string name="pref_autodl_wifi_filter_title">Aktivera WiFi filtrering</string>
<string name="pref_autodl_wifi_filter_sum">Tillåt automatisk nedladdning endast för utvalda WiFi-nätverk.</string>
- <string name="pref_autodl_allow_on_mobile_title">Ladda ner på mobilanslutning</string>
- <string name="pref_autodl_allow_on_mobile_sum">Tillåt automatiska nedladdningar över en mobil dataanslutning.</string>
<string name="pref_automatic_download_on_battery_title">Nedladdning vid batteridrift</string>
<string name="pref_automatic_download_on_battery_sum">Tillåt automatisk nedladdning när batteriet inte laddas</string>
<string name="pref_parallel_downloads_title">Parallella nedladdningar</string>
@@ -408,7 +393,6 @@
<string name="pref_gpodnet_full_sync_sum">Synkronisera alla prenumerationer och episodstatus med gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Senaste synkroniseringsförsök: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Synkronisering startad</string>
- <string name="pref_gpodnet_full_sync_started">Full synkronisering påbörjad</string>
<string name="pref_gpodnet_login_status"><![CDATA[Inloggad som <i>%1$s</i> med enhet <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Visa notifieringar om synkroniseringsfel</string>
<string name="pref_gpodnet_notifications_sum">Denna inställning påverkar inte autentiseringsfel.</string>
@@ -435,21 +419,14 @@
<string name="pref_showDownloadReport_title">Visa nedladdningsrapport</string>
<string name="pref_showDownloadReport_sum">Visa en rapport med detaljer om felet när nedladdningar misslyckas.</string>
<string name="pref_expand_notify_unsupport_toast">Androidversioner före 4.1 har inte stöd för expanderade aviseringar.</string>
- <string name="pref_queueAddToFront_sum">Lägg till episoder först i kön.</string>
- <string name="pref_queueAddToFront_title">Köa först</string>
<string name="pref_smart_mark_as_played_disabled">Avaktiverad</string>
<string name="pref_image_cache_size_title">Bildcachestorlek</string>
<string name="pref_image_cache_size_sum">Storleken på bildcachen på disken.</string>
- <string name="crash_report_title">Krashrapport</string>
- <string name="crash_report_sum">Sänd den senaste krashrapporten via e-post</string>
- <string name="send_email">Sänd e-post</string>
<string name="experimental_pref">Experimentellt</string>
<string name="pref_media_player_message">Välj vilken mediaspelare som ska spela filer</string>
<string name="pref_current_value">Nuvarande värde: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Använd en nätverksproxy</string>
- <string name="pref_faq">FAQ</string>
- <string name="pref_known_issues">Kända problem</string>
<string name="pref_no_browser_found">Ingen webbläsare hittades.</string>
<string name="pref_cast_title">Chromecast-stöd</string>
<string name="pref_cast_message_play_flavor">Aktivera stöd för fjärruppspelning av media på Cast-enheter (såsom Chromecast, Ljudanläggningar eller Android TV)</string>
@@ -475,6 +452,8 @@
<string name="back_button_go_to_page_title">Välj sida</string>
<string name="pref_delete_removes_from_queue_title">Radering tar bort från kön</string>
<string name="pref_delete_removes_from_queue_sum">Ta automatiskt bort episoder från kön när de raderas.</string>
+ <!--About screen-->
+ <string name="about_pref">Om</string>
<!--Search-->
<string name="search_hint">Sök efter episoder</string>
<string name="found_in_shownotes_label">Hittad i shownotes</string>
@@ -486,22 +465,16 @@
<string name="found_in_title_label">Hittad i titeln</string>
<string name="no_results_for_query">Inga resultat hittades för \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML-filer låter dig flytta dina podcasts från en podcatcher till en annan.</string>
<string name="opml_import_option">Val %1$d</string>
<string name="opml_import_explanation_1">Välj en specifik sökväg från det lokala filsystemet.</string>
- <string name="opml_import_explanation_2">Använd en extern applikation som Dropbox, Google Drive eller ditt favoritval av filhanterare för att öppna en OPML fil.</string>
<string name="opml_import_explanation_3">Flera applikationer som Google Mail, Dropbox, Google Drive och de flesta filhanterare kan <i>öppna</i> OPML filer <i>med</i> AntennaPod.</string>
- <string name="start_import_label">Påbörja importering</string>
<string name="opml_import_label">OPML importering</string>
- <string name="opml_directory_error">FEL! </string>
<string name="reading_opml_label">Läser OPML-fil</string>
<string name="opml_reader_error">Ett fel uppstod vid läsning av OPML dokumentet:</string>
<string name="opml_import_error_no_file">Ingen fil vald!</string>
<string name="select_all_label">Välj alla</string>
<string name="deselect_all_label">Avmarkera alla</string>
- <string name="select_options_label">Välj…</string>
<string name="choose_file_from_filesystem">Från lokalt filsystem</string>
- <string name="choose_file_from_external_application">Använd extern applikation</string>
<string name="opml_export_label">OPML export</string>
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Exporterar…</string>
@@ -594,8 +567,6 @@
<string name="pref_restart_required">AntennaPod behöver startas om för att denna inställning ska gälla.</string>
<!--Online feed view-->
<string name="subscribe_label">Prenumerera</string>
- <string name="subscribed_label">Prenumererar</string>
- <string name="downloading_label">Laddar ner…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Backa</string>
<string name="fast_forward_label">Snabbspola</string>
@@ -703,7 +674,6 @@
<string name="cast_failed_setting_volume">Misslyckades att ändra volymen</string>
<string name="cast_failed_no_connection">Det finns ingen koppling till cast-enheten</string>
<string name="cast_failed_no_connection_trans">Kopplingen till cast-enheten tappades. Applikationen försöker att återansluta, om det är möjligt. Vänta en stund och försök igen.</string>
- <string name="cast_failed_perform_action">Misslyckades att utföra åtgärden</string>
<string name="cast_failed_status_request">Misslyckades att synkronisera med cast-enheten</string>
<string name="cast_failed_seek">Misslyckades att söka till den nya positionen på cast-enheten</string>
<string name="cast_failed_receiver_player_error">Mottagande uppspelaren har stött på ett allvarligt fel</string>
@@ -718,4 +688,5 @@
<string name="notification_channel_error">Fel</string>
<string name="notification_channel_error_description">Visas om något blev fel, exempelvis om nedladdning eller gpodder synkronisering misslyckas.</string>
<string name="import_bad_file">Ogiltig/korrupt fil</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml
index 9ec4df009..16b1980b8 100644
--- a/core/src/main/res/values-tr/strings.xml
+++ b/core/src/main/res/values-tr/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Diğer cihazlarla senkronize et</string>
<string name="gpodnet_auth_label">gpodder.net giriş</string>
- <string name="free_space_label">%1$s ücretsiz</string>
<string name="episode_cache_full_title">Bölüm önbelleği dolu</string>
<string name="episode_cache_full_message">Bölüm önbelleği limitine ulaşıldı. Ayarlardan önbellek limitini arttırabilirsiniz. </string>
- <string name="synchronizing">Senkronize ediliyor... </string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Toplam cep yayını çalma zamanı:</string>
<string name="statistics_mode">İstatistik modu</string>
@@ -59,7 +57,6 @@
<string name="author_label">Yazar(lar)</string>
<string name="language_label">Dil</string>
<string name="url_label">URL</string>
- <string name="podcast_settings_label">Ayarlar</string>
<string name="cover_label">Resim</string>
<string name="error_label">Hata</string>
<string name="error_msg_prefix">Bir hata oluştu:</string>
@@ -68,14 +65,9 @@
<string name="external_storage_error_msg">Harici depolama yok. Lütfen uygulamanın düzgün çalışabilmesi için harici depolama aygıtının bağlandığından emin olun.</string>
<string name="chapters_label">Kısımlar</string>
<string name="chapter_duration">Süre: %1$s</string>
- <string name="shownotes_label">Notlar</string>
<string name="description_label">Tanım</string>
- <string name="most_recent_prefix">Son Bölüm:\u0020 </string>
<string name="episodes_suffix">\u0020bölüm</string>
- <string name="length_prefix">Uzunluk:\u0020</string>
- <string name="size_prefix">Boyut:\u0020</string>
<string name="processing_label">İşleniyor</string>
- <string name="loading_label">Yükleniyor...</string>
<string name="save_username_password_label">Kullanıcı adı ve şifreyi kaydet</string>
<string name="close_label">Kapat</string>
<string name="retry_label">Yeniden dene</string>
@@ -99,8 +91,6 @@
<string name="feedurl_label">Besleme Adresi</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">URL ile cep yayını ekle</string>
- <string name="podcastdirectories_label">Dizinde cep yayını bul</string>
- <string name="podcastdirectories_descr">Yeni podcast\'ler için iTunes, fyyd, veya gpodder.net adresinde isme, kategoriye, veya popülerliğe göre arama yapabilirsiniz.</string>
<string name="browse_gpoddernet_label">gpodder.net\'e gözat</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Hepsini oynatıldı olarak işaretle</string>
@@ -123,7 +113,6 @@
<string name="feed_delete_confirmation_msg">Lütfen bu Cepyayını ve bu Cepyayınına ait indirilmiş BÜTÜN bölümleri silme isteğinizi onaylayın.</string>
<string name="feed_remover_msg">Cepyayını kaldırılıyor</string>
<string name="load_complete_feed">Tüm Cepyayınını yenile</string>
- <string name="hide_episodes_title">Bölümleri gizle</string>
<string name="batch_edit">Toplu düzenleme</string>
<string name="select_all_above">Üsttekilerin tümünü seç</string>
<string name="select_all_below">Alttakilerin tümünü seç</string>
@@ -143,9 +132,7 @@
<string name="download_label">İndir</string>
<string name="play_label">Oynat</string>
<string name="pause_label">Duraklat</string>
- <string name="stop_label">Durdur</string>
<string name="stream_label">Akış</string>
- <string name="remove_label">Kaldır</string>
<string name="delete_label">Sil</string>
<string name="delete_failed">Dosya silinemiyor. Cihazı yeniden başlatmak yardımcı olabilir.</string>
<string name="mark_read_label">Oynatıldı olarak işaretle</string>
@@ -166,13 +153,11 @@
<string name="removed_item">Öge kaldırıldı</string>
<!--Download messages and labels-->
<string name="download_successful">başarılı</string>
- <string name="download_failed">başarısız</string>
<string name="download_pending">İndirme bekleniyor</string>
<string name="download_running">İndiriliyor</string>
<string name="download_error_details">Detaylar</string>
<string name="download_error_device_not_found">Depolama aygıtı bulunamadı</string>
<string name="download_error_insufficient_space">Yetersiz alan</string>
- <string name="download_error_file_error">Dosya hatası</string>
<string name="download_error_http_data_error">HTTP Veri Hatası</string>
<string name="download_error_error_unknown">Bilinmeyen Hata</string>
<string name="download_error_parser_exception">Ayrıştırıcı İstisnası</string>
@@ -182,7 +167,6 @@
<string name="download_error_unauthorized">Yetkilendirme hatası</string>
<string name="download_error_file_type_type">Dosya Tipi Hatası</string>
<string name="download_error_forbidden">Yasak</string>
- <string name="cancel_all_downloads_label">Bütün indirmeleri iptal et</string>
<string name="download_canceled_msg">İndirme iptal edildi</string>
<string name="download_canceled_autodownload_enabled_msg">İndirme iptal edildi\nBu öğe için <i>Otomatik İndirme</i> devre dışı</string>
<string name="download_report_title">İndirme hata(lar) ile tamamlandı</string>
@@ -201,7 +185,6 @@
<string name="download_log_title_unknown">bilinmeyen başlık</string>
<string name="download_type_feed">Besleme</string>
<string name="download_type_media">Medya dosyası</string>
- <string name="download_type_image">Resim</string>
<string name="download_request_error_dialog_message_prefix">Dosyayı indirmeye çalışırken bir hata oluştu:\u0020</string>
<string name="authentication_notification_title">Yetkilendirme gerekiyor</string>
<string name="authentication_notification_msg">İstediğiniz kaynak kullanıcı adı ve şifre istiyor</string>
@@ -221,7 +204,6 @@
<string name="no_media_playing_label">Çalınan medya yok</string>
<string name="player_buffering_msg">Ara belleğe alınıyor</string>
<string name="player_go_to_picture_in_picture">Resim içinde Resim modu</string>
- <string name="playbackservice_notification_title">Cep yayını çalınıyor</string>
<string name="unknown_media_key">AntennaPod - Bilinmeyen medya anahtarı: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Kuyruğu kilitle</string>
@@ -230,7 +212,6 @@
<string name="queue_unlocked">Kuyruk kilidi açıldı</string>
<string name="clear_queue_label">Kuyruğu temizle</string>
<string name="undo">Geri al</string>
- <string name="removed_from_queue">Öge kaldırıldı</string>
<string name="move_to_top_label">En üste taşı</string>
<string name="move_to_bottom_label">En alta taşı</string>
<string name="sort">Sırala</string>
@@ -246,18 +227,14 @@
<!--Variable Speed-->
<string name="download_plugin_label">Eklentiyi İndir</string>
<string name="no_playback_plugin_title">Eklenti Yüklenmedi</string>
- <string name="no_playback_plugin_or_sonic_msg">Değişken hızlı oynatmanın çalışması için, dahili Sonic mediaplayer tavsiye ediyoruz [Android 4.1+].\n\nAlternatif olarak, üçüncü parti <i>Prestissimo</i>eklentisini Play Store\'dan indirebilirsiniz.\nPrestissimo ilgili herhangi bir sorunda AntennaPod sorumlu değildir, sorun eklenti yazarına bildirilmelidir.</string>
<string name="set_playback_speed_label">Çalma hızları</string>
<string name="enable_sonic">Sonic\'i Etkinleştir</string>
<!--Empty list labels-->
- <string name="no_feeds_label">Henüz hiç bir Cepyayınına abone olmadınız.</string>
<string name="no_shownotes_label">Bu dosya herhangi bir bilgi notu içermiyor.</string>
<string name="no_chapters_label">Bu dosya herhangi bir bölüm içermiyor.</string>
<!--Preferences-->
<string name="storage_pref">Depolama</string>
<string name="project_pref">Proje</string>
- <string name="other_pref">Diğer</string>
- <string name="about_pref">Hakkında</string>
<string name="queue_label">Kuyruk</string>
<string name="integrations_label">Entegrasyon</string>
<string name="automation">Otomasyon</string>
@@ -295,18 +272,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Belirli saat ayarla</string>
<string name="pref_autoUpdateIntervallOrTime_every">her%1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">süre%1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Medya dosyalarını sadece kablosuz bağlantı üzerinden indir</string>
<string name="pref_followQueue_title">Devamlı çalma</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Kablosuz medya indirmesi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Kulaklık bağlı değil</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Kulaklıklar yeniden bağlı</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth yeniden bağlandı</string>
<string name="pref_mobileUpdate_title">Mobil güncellemeler</string>
- <string name="refreshing_label">Yenileniyor</string>
<string name="user_interface_label">Kullanıcı Arayüzü</string>
<string name="pref_set_theme_title">Temayı seç</string>
- <string name="pref_nav_drawer_title">Uygulama Çekmecesini Özelleştir</string>
- <string name="pref_nav_drawer_sum">Uygulama çekmecesinin görüntüsünü özelleştir.</string>
<string name="pref_nav_drawer_items_title">Uygulama Çekmecesi öğelerini ayarla</string>
<string name="pref_nav_drawer_items_sum">Hangi öğelerin uygulama çekmecesinde görüneceğini değiştir.</string>
<string name="pref_nav_drawer_feed_order_title">Abonelik sıralamasını ayarla</string>
@@ -342,36 +314,26 @@
<string name="pref_showDownloadReport_title">İndirme Raporunu Göster</string>
<string name="pref_showDownloadReport_sum">Eğer indirme başarısız olursa, hatanın ayrıntılarını gösteren bir rapor oluştur.</string>
<string name="pref_expand_notify_unsupport_toast">Android 4.1 öncesi sürümler genişletilmiş bildirimleri desteklememektedir.</string>
- <string name="pref_queueAddToFront_sum">Yeni bölümleri kuyruğun önüne ekle.</string>
- <string name="pref_queueAddToFront_title">Kuyruğun önüne ekle.</string>
<string name="pref_smart_mark_as_played_disabled">Devre dışı</string>
<string name="pref_image_cache_size_title">Görüntü Önbelleği Boyutu</string>
<string name="pref_image_cache_size_sum">Görüntüler için diskte tutulacak önbelleğin boyutu.</string>
- <string name="crash_report_title">Çökme Raporu</string>
- <string name="crash_report_sum">En son çökme raporunu e-posta ile gönder</string>
- <string name="send_email">E-posta gönder</string>
<string name="experimental_pref">Deneysel</string>
- <string name="pref_faq">SSS</string>
+ <!--About screen-->
+ <string name="about_pref">Hakkında</string>
<!--Search-->
<string name="found_in_chapters_label">Kısımlarda bulundu</string>
<string name="search_status_no_results">Sonuç bulunamadı</string>
<string name="search_label">Ara</string>
<string name="found_in_title_label">Başlıkta bulundu</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML dosyaları cep yayınlarını bir cihazdan diğerine aktarmanıza yarar.</string>
<string name="opml_import_option">Seçenek %1$d</string>
<string name="opml_import_explanation_1">Yerel dosya sisteminden belirli bir yol seçin.</string>
- <string name="opml_import_explanation_2">OPML dosyasını açmak için harici uygulamalardan Dropbox, Google Drive veya kendi favori dosya yöneticinizi kullanın.</string>
<string name="opml_import_explanation_3">Google Mail, Dropbox, Google Drive gibi birçok uygulama ve çoğu dosya yöneticisi OPML dosyalarını AntennaPod <i>ile</i> <i>açabilir.</i></string>
- <string name="start_import_label">İçe aktarmayı başlat</string>
<string name="opml_import_label">OPML içe aktar</string>
- <string name="opml_directory_error">HATA!</string>
<string name="reading_opml_label">OPML dosyası okunuyor</string>
<string name="select_all_label">Hepsini seç</string>
<string name="deselect_all_label">Tüm seçimleri geri al</string>
- <string name="select_options_label">Seç...</string>
<string name="choose_file_from_filesystem">Yerel dosya sisteminden</string>
- <string name="choose_file_from_external_application">Harici uygulama kullan</string>
<string name="opml_export_label">OPML dışa aktar</string>
<string name="export_error_label">Dışa aktarma hatası</string>
<!--Sleep timer-->
@@ -450,8 +412,6 @@
<string name="pref_restart_required">Bu değişikliğin geçerli olması için AntennaPod yeniden başlatılmalıdır.</string>
<!--Online feed view-->
<string name="subscribe_label">Üye ol</string>
- <string name="subscribed_label">Üye olundu</string>
- <string name="downloading_label">İndiriliyor...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Geri sar</string>
<string name="fast_forward_label">İleri sar</string>
@@ -510,4 +470,5 @@
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<string name="cast_failed_seek">Yayın aygıtındaki yeni pozisyona erişilemedi</string>
<!--Notification channels-->
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml
index dce11d4e6..34d2b0ebe 100644
--- a/core/src/main/res/values-uk-rUA/strings.xml
+++ b/core/src/main/res/values-uk-rUA/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">Синхронизація з іншими пристроями</string>
<string name="gpodnet_auth_label">Автентифікуватися на gpodder.net</string>
- <string name="free_space_label">%1$s вільно</string>
<string name="episode_cache_full_title">Кеш епізодів заповнений</string>
<string name="episode_cache_full_message">Досягнута межа розміру кешу епізодів. Розмір кешу можна збільшити в налаштуваннях.</string>
- <string name="synchronizing">Синхронізується…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Загальний час прослуханих подкастів:</string>
<string name="statistics_details_dialog">%1$d з %2$d епізодів почато.\n\nПрослухано %3$s з %4$s.</string>
@@ -63,7 +61,6 @@
<string name="author_label">Автор(и)</string>
<string name="language_label">Мова</string>
<string name="url_label">Посилання</string>
- <string name="podcast_settings_label">Налаштування</string>
<string name="cover_label">Зображення</string>
<string name="error_label">Помилка</string>
<string name="error_msg_prefix">Трапилась помілка:</string>
@@ -72,14 +69,9 @@
<string name="external_storage_error_msg">Немає доступної карти пам\'яті. Зовнішній носій потрібен для коректної роботи додатку</string>
<string name="chapters_label">Глави</string>
<string name="chapter_duration">Тривалість: %1$s</string>
- <string name="shownotes_label">Нотатки до епізода</string>
<string name="description_label">Опис</string>
- <string name="most_recent_prefix">Найновіший епізод:\u0020</string>
<string name="episodes_suffix">\u0020епізодів</string>
- <string name="length_prefix">Довжина:\u0020</string>
- <string name="size_prefix">Розмір:\u0020</string>
<string name="processing_label">Обробка</string>
- <string name="loading_label">Завантажується…</string>
<string name="save_username_password_label">Зберегти ім\'я користувача та пароль</string>
<string name="close_label">Закрити</string>
<string name="retry_label">Повторити знову</string>
@@ -112,8 +104,6 @@
<string name="feedurl_label">Посилання на канал</string>
<string name="etxtFeedurlHint">Посилання на канал чи сторінку</string>
<string name="txtvfeedurl_label">Додати подкаст за URL</string>
- <string name="podcastdirectories_label">Знайти подкаст в каталозі</string>
- <string name="podcastdirectories_descr">Нові подкасти можна шукати в iTunes або fyyd, або переглянути gpodder.net за іменами, категоріями або популярністю.</string>
<string name="browse_gpoddernet_label">Переглянути gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Позначити всі як відтворені</string>
@@ -136,7 +126,6 @@
<string name="feed_delete_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте видалити подкаст \"%1$s\" і ВСІ його епізоди (разом з завантаженими).</string>
<string name="feed_remover_msg">Видалення подкасту</string>
<string name="load_complete_feed">Оновити весь подкаст</string>
- <string name="hide_episodes_title">Приховати епізоди</string>
<string name="batch_edit">Групове редагування</string>
<string name="select_all_above">Вибрати все вище</string>
<string name="select_all_below">Вибрати все нижче</string>
@@ -162,9 +151,7 @@
</plurals>
<string name="play_label">Грати</string>
<string name="pause_label">Пауза</string>
- <string name="stop_label">Стоп</string>
<string name="stream_label">Прослухати без завантаження</string>
- <string name="remove_label">Видалити</string>
<string name="delete_label">Видалити</string>
<string name="delete_failed">Файл не видалено. Можливо, перезавантаження пристрою допоможе.</string>
<string name="delete_episode_label">Видалити епізод</string>
@@ -216,14 +203,12 @@
<string name="removed_item">Видалено</string>
<!--Download messages and labels-->
<string name="download_successful">успішно</string>
- <string name="download_failed">з помилками</string>
<string name="download_pending">Потрібно завантажити</string>
<string name="download_running">Завантаження</string>
<string name="download_error_details">Докладно</string>
<string name="download_error_details_message">%1$s \n\nПосилання на файл:\n%2$s</string>
<string name="download_error_device_not_found">Пристрій зберігання даних не знайдено</string>
<string name="download_error_insufficient_space">Недостатній простір для зберігання</string>
- <string name="download_error_file_error">Помилка файлу</string>
<string name="download_error_http_data_error">Помилка HTTP</string>
<string name="download_error_error_unknown">Щось трапилось</string>
<string name="download_error_parser_exception">Помилка парсера</string>
@@ -233,7 +218,6 @@
<string name="download_error_unauthorized">Помилка автентифікації</string>
<string name="download_error_file_type_type">Помилка типу файлу</string>
<string name="download_error_forbidden">Заборонено</string>
- <string name="cancel_all_downloads_label">Скасувати всі завантаження</string>
<string name="download_canceled_msg">Завантаження скасоване</string>
<string name="download_canceled_autodownload_enabled_msg">Завантаження скасоване\n<i>Автозавантаження</i> для цього елементу вимкнуто</string>
<string name="download_report_title">Завантаження завершені з помилками</string>
@@ -254,7 +238,6 @@
<string name="download_log_title_unknown">Невідомий заголовок</string>
<string name="download_type_feed">Канал</string>
<string name="download_type_media">Файл з медіа</string>
- <string name="download_type_image">Зображення</string>
<string name="download_request_error_dialog_message_prefix">Помилка при завантажені файлу:\u0020</string>
<string name="null_value_podcast_error">Не надано подкаст для перегляду.</string>
<string name="authentication_notification_title">Потрібна автентифікація</string>
@@ -275,7 +258,6 @@
<string name="no_media_playing_label">Немає що грати</string>
<string name="player_buffering_msg">Буферізую</string>
<string name="player_go_to_picture_in_picture">Режим зображення в зображенні</string>
- <string name="playbackservice_notification_title">Грає подкаст</string>
<string name="unknown_media_key">AntennaPod - Невідомий медіа ключ: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">Заблокувати чергу</string>
@@ -284,7 +266,6 @@
<string name="queue_unlocked">Чергу розблоковано</string>
<string name="clear_queue_label">Очистити чергу</string>
<string name="undo">Скасувати</string>
- <string name="removed_from_queue">Видалено</string>
<string name="move_to_top_label">Догори</string>
<string name="move_to_bottom_label">Донизу</string>
<string name="sort">Впорядкувати</string>
@@ -300,13 +281,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">Завантажити додаток</string>
<string name="no_playback_plugin_title">Додаток не встановлено</string>
- <string name="no_playback_plugin_or_sonic_msg">Для програвання зі змінною швидкістю, ми радимо включити вбудований програвач Sonic [Android 4.1+].\n\nАбо ви можете завантажити додаток <i>Prestissimo</i> із Play Store.\nБудь які проблеми з Prestissimo не є відповідальністю розробників AntennaPod та мають вирішуватись авторами додатка.</string>
<string name="set_playback_speed_label">Швидкість програвання</string>
<string name="enable_sonic">Включити Sonic</string>
<!--Empty list labels-->
<string name="no_items_header_label">В черзі немає епізодів.</string>
<string name="no_items_label">Додайте епізод, завантаживши його, або довго натиснувши на нього і вибравши \"Додати до черги\".</string>
- <string name="no_feeds_label">Ви ще не підписалися на жодні подкасти.</string>
<string name="no_shownotes_label">До цього епізода немає нотаток.</string>
<string name="no_run_downloads_head_label">Зараз нічого не завантажується.</string>
<string name="no_run_downloads_label">Завантажувати епізоди можна з детального перегляда подкаста.</string>
@@ -326,8 +305,6 @@
<!--Preferences-->
<string name="storage_pref">Зберігання</string>
<string name="project_pref">Проект</string>
- <string name="other_pref">Інше</string>
- <string name="about_pref">Про програму</string>
<string name="queue_label">Черга</string>
<string name="integrations_label">Інтеграції</string>
<string name="automation">Автоматизація</string>
@@ -366,18 +343,13 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Встановити годину</string>
<string name="pref_autoUpdateIntervallOrTime_every">кожні %1$s</string>
<string name="pref_autoUpdateIntervallOrTime_at">о %1$s</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Завантажувати тільки через Wifi</string>
<string name="pref_followQueue_title">Грати безперервно</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Завантаження через Wifi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Навушники від\'єднані</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Повторне під’єднання навушників</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Повторне під’єднання блютуз</string>
<string name="pref_mobileUpdate_title">Мобільне оновлення</string>
- <string name="refreshing_label">Оновлення</string>
<string name="user_interface_label">Вигляд</string>
<string name="pref_set_theme_title">Обрати тему</string>
- <string name="pref_nav_drawer_title">Налаштувати меню навігації</string>
- <string name="pref_nav_drawer_sum">Налаштувати вигляд меню навігації</string>
<string name="pref_nav_drawer_items_title">Налаштувати зміст меню навігації</string>
<string name="pref_nav_drawer_items_sum">Вибрати елементи які мають бути у меню навігації</string>
<string name="pref_nav_drawer_feed_order_title">Встановити порядок підписок</string>
@@ -389,8 +361,6 @@
<string name="pref_automatic_download_sum">Налаштування автозавантаження епізодів</string>
<string name="pref_autodl_wifi_filter_title">Увімкнути фільтр Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Дозволити автозавантаження тільки в цих Wi-Fi мережах</string>
- <string name="pref_autodl_allow_on_mobile_title">Завантажувати через мобільні мережі</string>
- <string name="pref_autodl_allow_on_mobile_sum">Дозволити автоматичне завантаження через мобільні мережі.</string>
<string name="pref_automatic_download_on_battery_title">Завантаження без зарядного пристрою</string>
<string name="pref_automatic_download_on_battery_sum">Дозволити автозавантаження коли зарядний пристрій не підключений</string>
<string name="pref_parallel_downloads_title">Паралельні завантаження</string>
@@ -414,7 +384,6 @@
<string name="pref_gpodnet_full_sync_sum">Синхронізувати всі підписки та стан епізодів з gpodder.net.</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">Остання спроба синхронізації: %1$s (%2$s)</string>
<string name="pref_gpodnet_sync_started">Cинхронізація почалась</string>
- <string name="pref_gpodnet_full_sync_started">Повну синхронізацію розпочато</string>
<string name="pref_gpodnet_login_status"><![CDATA[Ви увійшли як <i>%1$s</i> з пристрою <i>%2$s</i>]]></string>
<string name="pref_gpodnet_notifications_title">Повідомляти про помилки синхронізації</string>
<string name="pref_gpodnet_notifications_sum">Це налаштування не застосовується до помилок автентифікації.</string>
@@ -439,21 +408,14 @@
<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_queueAddToFront_sum">Додавати нові епізоди до початку черги.</string>
- <string name="pref_queueAddToFront_title">Додавати в початок черги.</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="crash_report_title">Звіт про збій</string>
- <string name="crash_report_sum">Надіслати е-пошту зі звітом про останній збій</string>
- <string name="send_email">Надіслати е-пошту</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_known_issues">Відомі проблеми</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>
@@ -479,6 +441,8 @@
<string name="back_button_go_to_page_title">Вибрати сторінку</string>
<string name="pref_delete_removes_from_queue_title">Видалення з черги видалених епізодів</string>
<string name="pref_delete_removes_from_queue_sum">Автоматично видаляти епізод із черги, коли він буде видалений.</string>
+ <!--About screen-->
+ <string name="about_pref">Про програму</string>
<!--Search-->
<string name="search_hint">Пошук епізодів</string>
<string name="found_in_shownotes_label">Знайдено в нотатках епізода</string>
@@ -490,22 +454,16 @@
<string name="found_in_title_label">Знайдено у назві</string>
<string name="no_results_for_query">Нічого не знайдено за запитом \"%1$s\"</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML файли дозволяют вам перенести подкасти з однієї программи до іншої</string>
<string name="opml_import_option">Варіант %1$d</string>
<string name="opml_import_explanation_1">Виберіть шлях до файлу з локальної файлової системи.</string>
- <string name="opml_import_explanation_2">Вибрати OPML файл за допомогою таких додатків як Dropbox, Google Drive або файловий менеджер.</string>
<string name="opml_import_explanation_3">Багато додатків, таких як Google Mail, Dropbox, Google Drive та більшість файлових менеджерів, здатні <i>відкривати</i> OPML файли <i>за допомогою</i> AntennaPod.</string>
- <string name="start_import_label">Почати імпорт</string>
<string name="opml_import_label">Імпорт OPML</string>
- <string name="opml_directory_error">Помилка!</string>
<string name="reading_opml_label">Читаємо OPML файл</string>
<string name="opml_reader_error">Помилка при читанні документа OPML:</string>
<string name="opml_import_error_no_file">Жодного файлу не обрано!</string>
<string name="select_all_label">Обрати все</string>
<string name="deselect_all_label">Убрати виділення</string>
- <string name="select_options_label">Обрати…</string>
<string name="choose_file_from_filesystem">З локальної файлової системи</string>
- <string name="choose_file_from_external_application">За допомогою додатка</string>
<string name="opml_export_label">OPML экспорт</string>
<string name="html_export_label">Експорт до HTML</string>
<string name="exporting_label">Експортується…</string>
@@ -602,8 +560,6 @@
<string name="pref_restart_required">Для застосування змін потрібно перезапустити AntennaPod</string>
<!--Online feed view-->
<string name="subscribe_label">Підписатися</string>
- <string name="subscribed_label">Підписано</string>
- <string name="downloading_label">Завантажується…</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Перемотка назад</string>
<string name="fast_forward_label">Перемотка вперед</string>
@@ -707,7 +663,6 @@
<string name="cast_failed_setting_volume">Помилка встановлення гучності</string>
<string name="cast_failed_no_connection">Немає з’єднання з пристроєм програвання</string>
<string name="cast_failed_no_connection_trans">Зв’язок з пристроєм програвання втрачено. Програма намагається поновити з\'єднання, якщо це можливо. Будь ласка зачекайте кілька секунд і спробуйте знов.</string>
- <string name="cast_failed_perform_action">Помилка виконання дії</string>
<string name="cast_failed_status_request">Помилка синхронизації з пристроєм програвання</string>
<string name="cast_failed_seek">Помилка перехода на нову позицію програвання на пристрої.</string>
<string name="cast_failed_receiver_player_error">Серйозна помилка відтворення на пристрої</string>
@@ -721,4 +676,5 @@
<string name="notification_channel_playing_description">Дозволяє керувати відтворенням. Це основне сповіщення, яке ви бачите під час відтворення подкасту.</string>
<string name="notification_channel_error">Помилки</string>
<string name="notification_channel_error_description">Відображається, якщо щось пішло не так, наприклад, якщо не вдалося завантажити або синхронізувати з gpodder.</string>
+ <!--Widget settings-->
</resources>
diff --git a/core/src/main/res/values-v19/colors.xml b/core/src/main/res/values-v16/colors.xml
index 4154280e8..4154280e8 100644
--- a/core/src/main/res/values-v19/colors.xml
+++ b/core/src/main/res/values-v16/colors.xml
diff --git a/core/src/main/res/values-v21/styles.xml b/core/src/main/res/values-v21/styles.xml
index c53000c4f..54951b9ec 100644
--- a/core/src/main/res/values-v21/styles.xml
+++ b/core/src/main/res/values-v21/styles.xml
@@ -20,7 +20,8 @@
<item name="android:colorPrimaryDark">@color/black</item>
</style>
- <style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button">
+ <style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button.Colored">
+ <item name="colorButtonNormal">@color/antennapod_blue</item>
<item name="textAllCaps">true</item>
</style>
diff --git a/core/src/main/res/values-zh-rCN/strings.xml b/core/src/main/res/values-zh-rCN/strings.xml
index 5bc57c0c7..123c1cc0d 100644
--- a/core/src/main/res/values-zh-rCN/strings.xml
+++ b/core/src/main/res/values-zh-rCN/strings.xml
@@ -22,10 +22,8 @@
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_summary">与其他设备同步</string>
<string name="gpodnet_auth_label">gpodder.net 登录</string>
- <string name="free_space_label">%1$s 可用</string>
<string name="episode_cache_full_title">曲目缓存已满</string>
<string name="episode_cache_full_message">已达到曲目缓存限制,可以在设置中提高缓存大小。</string>
- <string name="synchronizing">正在同步…</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">总播放时长:</string>
<string name="statistics_details_dialog">听过了总计 %2$d 期播客中的 %1$d 期。\n\n播放了总计 %4$s 中的 %3$s。</string>
@@ -33,6 +31,8 @@
<string name="statistics_mode_normal">计算实际上的播放次数与时长。播放过两次则将被识别为两次,但是被手动标记为已播放状态的不算。</string>
<string name="statistics_mode_count_all">统计播放次数时仅统计所有带有已播放播客标记的数量</string>
<string name="statistics_speed_not_counted">注意:播放速度不在考虑范围之内。</string>
+ <string name="statistics_reset_data">重置统计数据</string>
+ <string name="statistics_reset_data_msg">这将抹去总播放时间的历史。您确定要进行该操作吗?</string>
<!--Main activity-->
<string name="drawer_open">打开菜单</string>
<string name="drawer_close">关闭菜单</string>
@@ -63,7 +63,6 @@
<string name="author_label">作者</string>
<string name="language_label">语言</string>
<string name="url_label">链接地址</string>
- <string name="podcast_settings_label">设置</string>
<string name="cover_label">图片</string>
<string name="error_label">错误</string>
<string name="error_msg_prefix">出错:</string>
@@ -72,14 +71,9 @@
<string name="external_storage_error_msg">没有可用的外部存储. 请确保安装外部存储器, 这样本应用才可以正常工作.</string>
<string name="chapters_label">章节</string>
<string name="chapter_duration">时长:%1$s</string>
- <string name="shownotes_label">笔记</string>
<string name="description_label">描述</string>
- <string name="most_recent_prefix">最近曲目:\u0020</string>
<string name="episodes_suffix">\u0020 曲</string>
- <string name="length_prefix">长度:\u0020</string>
- <string name="size_prefix">大小:\u0020</string>
<string name="processing_label">处理中</string>
- <string name="loading_label">正在加载…</string>
<string name="save_username_password_label">保存用户名密码</string>
<string name="close_label">关闭</string>
<string name="retry_label">重试</string>
@@ -106,8 +100,6 @@
<string name="feedurl_label">订阅 URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">添加播客 URL</string>
- <string name="podcastdirectories_label">从目录中寻找播客</string>
- <string name="podcastdirectories_descr">您可以在 gpodder.net 通过名称、类别或热门来搜索新播客</string>
<string name="browse_gpoddernet_label">浏览 gpodder.net</string>
<string name="discover">发现</string>
<string name="discover_more">更多</string>
@@ -129,13 +121,13 @@
<string name="share_link_label">分享剧集地址</string>
<string name="share_link_with_position_label">分享带播放位置的节目地址</string>
<string name="share_file_label">分享文件</string>
+ <string name="share_website_url_label">分享网站URL</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%1$s” 及其所有节目(包括已经下载的)</string>
<string name="feed_remover_msg">正在移除播客</string>
<string name="load_complete_feed">刷新整档博客</string>
- <string name="hide_episodes_title">隐藏曲目</string>
<string name="batch_edit">批量编辑</string>
<string name="select_all_above">选中上面所有</string>
<string name="select_all_below">选中下面所有</string>
@@ -158,9 +150,7 @@
</plurals>
<string name="play_label">播放</string>
<string name="pause_label">暂停</string>
- <string name="stop_label">停止</string>
<string name="stream_label">流媒体</string>
- <string name="remove_label">删除</string>
<string name="delete_label">删除</string>
<string name="delete_failed">无法删除文件。重启可能解决该问题。</string>
<string name="delete_episode_label">删除节目</string>
@@ -199,13 +189,12 @@
<string name="removed_item">已删除项</string>
<!--Download messages and labels-->
<string name="download_successful">成功</string>
- <string name="download_failed">失败</string>
<string name="download_pending">下载等待</string>
<string name="download_running">下载中</string>
<string name="download_error_details">详细信息</string>
+ <string name="download_error_details_message">%1$s \n\nFile URL:\n%2$s</string>
<string name="download_error_device_not_found">没有找到存储设备</string>
<string name="download_error_insufficient_space">空间不足</string>
- <string name="download_error_file_error">文件错误</string>
<string name="download_error_http_data_error">HTTP 数据错误</string>
<string name="download_error_error_unknown">未知错误</string>
<string name="download_error_parser_exception">解析异常</string>
@@ -215,7 +204,6 @@
<string name="download_error_unauthorized">认证错误</string>
<string name="download_error_file_type_type">文件类型错误</string>
<string name="download_error_forbidden">禁用的</string>
- <string name="cancel_all_downloads_label">取消所有下载</string>
<string name="download_canceled_msg">已取消下载</string>
<string name="download_canceled_autodownload_enabled_msg">已取消下载\n对该曲目禁用<i>自动下载</i></string>
<string name="download_report_title">下载完成</string>
@@ -233,13 +221,16 @@
<string name="download_log_title_unknown">未知标题</string>
<string name="download_type_feed">订阅</string>
<string name="download_type_media">媒体文件</string>
- <string name="download_type_image">图片</string>
<string name="download_request_error_dialog_message_prefix">尝试下载文件:\u0020 时出错</string>
+ <string name="null_value_podcast_error">没有可供显示的播客。</string>
<string name="authentication_notification_title">需要认证</string>
<string name="authentication_notification_msg">您所请求的资源需要用户名和密码</string>
<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您要暂时允许下载吗?。<small>\n\n您的决定将会在10分钟后被遗忘。</small></string>
+ <string name="confirm_mobile_streaming_notification_title">确认使用移动数据播放</string>
+ <string name="confirm_mobile_streaming_notification_message">通过移动连接播放在设置中未启用。点击以确认您无论如何都要用移动数据来播放</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-->
@@ -249,23 +240,26 @@
<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_timeout">操作超时</string>
<string name="playback_error_unknown">未知错误</string>
<string name="no_media_playing_label">没有可播放的媒体</string>
<string name="player_buffering_msg">缓冲中</string>
<string name="player_go_to_picture_in_picture">画中画模式</string>
- <string name="playbackservice_notification_title">播客播放中</string>
<string name="unknown_media_key">AntennaPod - 未知媒体密钥: %1$d</string>
<!--Queue operations-->
<string name="lock_queue">锁定播放列表</string>
<string name="unlock_queue">解锁播放列表</string>
<string name="queue_locked">播放列表已锁定</string>
<string name="queue_unlocked">播放列表已解锁</string>
+ <string name="queue_lock_warning">如果锁定队列,您将再也不能滑动或重新排列音频顺序</string>
+ <string name="checkbox_do_not_show_again">不再显示</string>
<string name="clear_queue_label">清空播放列表</string>
<string name="undo">撤消</string>
- <string name="removed_from_queue">已删除项</string>
<string name="move_to_top_label">移到顶端</string>
<string name="move_to_bottom_label">移到下部</string>
<string name="sort">排序</string>
+ <string name="keep_sorted">保持排序</string>
<string name="date">按日期</string>
<string name="duration">按时长</string>
<string name="episode_title">剧集标题</string>
@@ -280,13 +274,11 @@
<!--Variable Speed-->
<string name="download_plugin_label">插件下载</string>
<string name="no_playback_plugin_title">插件没有安装</string>
- <string name="no_playback_plugin_or_sonic_msg">为使可变速度回放正常工作,我们建议启用内置的Sonic媒体播放器[安卓系统4.1以上]您也可以从谷歌商城下载第三方插件1 Prestissimo1 ,但任何有关该插件的问题均非AntennaPod的责任,应当报告插件所有者</string>
<string name="set_playback_speed_label">播放速度</string>
<string name="enable_sonic">允许声音</string>
<!--Empty list labels-->
<string name="no_items_header_label">没有处在播放列表的曲目</string>
<string name="no_items_label">通过下载或者长按一项剧集再选择“添加到播放列表”以添加剧集</string>
- <string name="no_feeds_label">您尚未订阅任何播客</string>
<string name="no_shownotes_label">本集不包含展示信息</string>
<string name="no_run_downloads_head_label">没有正在运行的下载</string>
<string name="no_run_downloads_label">您可以在播客的详情界面下载曲目</string>
@@ -304,11 +296,11 @@
<string name="no_fav_episodes_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>
- <string name="other_pref">其他</string>
- <string name="about_pref">关于</string>
<string name="queue_label">播放列表</string>
<string name="integrations_label">插件</string>
<string name="automation">自动化</string>
@@ -319,6 +311,9 @@
<string name="external_elements">外部元素</string>
<string name="interruptions">中断</string>
<string name="playback_control">回放控制</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>
@@ -348,18 +343,21 @@
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">设置时间</string>
<string name="pref_autoUpdateIntervallOrTime_every">每%1$s秒</string>
<string name="pref_autoUpdateIntervallOrTime_at">第%1$s秒</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">仅在 WIFI 情况下载媒体文件</string>
<string name="pref_followQueue_title">连续播放</string>
- <string name="pref_downloadMediaOnWifiOnly_title">仅在 WIFI 情况下载</string>
<string name="pref_pauseOnHeadsetDisconnect_title">耳机断开</string>
<string name="pref_unpauseOnHeadsetReconnect_title">耳机重新连接</string>
<string name="pref_unpauseOnBluetoothReconnect_title">蓝牙重新连接</string>
+ <string name="pref_stream_over_download_title">偏好在线播放</string>
+ <string name="pref_stream_over_download_sum">在列表中显示在线播放按钮而非下载按钮</string>
<string name="pref_mobileUpdate_title">数据网络时更新</string>
- <string name="refreshing_label">刷新中</string>
+ <string name="pref_mobileUpdate_sum">选择使用移动数据连接时应当允许的内容</string>
+ <string name="pref_mobileUpdate_refresh">Feed刷新</string>
+ <string name="pref_mobileUpdate_images">封面图片</string>
+ <string name="pref_mobileUpdate_auto_download">自动下载</string>
+ <string name="pref_mobileUpdate_episode_download">曲目下载</string>
+ <string name="pref_mobileUpdate_streaming">播放中</string>
<string name="user_interface_label">界面</string>
<string name="pref_set_theme_title">主题选择</string>
- <string name="pref_nav_drawer_title">侧边栏设置</string>
- <string name="pref_nav_drawer_sum">侧边栏外观设置</string>
<string name="pref_nav_drawer_items_title">设置侧边栏的选项</string>
<string name="pref_nav_drawer_items_sum">改变侧边栏中的选项</string>
<string name="pref_nav_drawer_feed_order_title">设置订阅的排序方式</string>
@@ -371,14 +369,17 @@
<string name="pref_automatic_download_sum">配置自动下载的曲目</string>
<string name="pref_autodl_wifi_filter_title">打开 Wi-Fi 过滤器</string>
<string name="pref_autodl_wifi_filter_sum">只允许在 Wi-Fi 网络下自动下载</string>
- <string name="pref_autodl_allow_on_mobile_title">移动数据连接时下载</string>
- <string name="pref_autodl_allow_on_mobile_sum">允许在数据连接下自动下载</string>
+ <string name="autodl_wifi_filter_permission_title">需要权限</string>
+ <string name="autodl_wifi_filter_permission_message">Wi-Fi过滤器需要位置权限。点击允许授予权限</string>
<string name="pref_automatic_download_on_battery_title">未充电时下载</string>
<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_episode_cache_summary">缓存在设备上的已下载节目总数
若达到此数目,自动下载将被暂停</string>
+ <string name="pref_episode_cover_title">使用音频封面</string>
+ <string name="pref_episode_cover_summary">只要可以就使用某一集的封面。如果未选中,AntennaPod将始终使用播客的封面图像。</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>
@@ -398,12 +399,12 @@
<string name="pref_gpodnet_full_sync_sum">与gpodder.net同步所有订阅和节目状态</string>
<string name="pref_gpodnet_sync_sum_last_sync_line">最后同步尝试于:%1$s(%2$s)</string>
<string name="pref_gpodnet_sync_started">已开始同步</string>
- <string name="pref_gpodnet_full_sync_started">完整同步以开始</string>
<string name="pref_gpodnet_login_status"><![CDATA[在设备 <i>%2$s</i> 上以 <i>%1$s</i> 身份登录]]></string>
<string name="pref_gpodnet_notifications_title">显示同步失败的提示信息</string>
<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_playback_time_respects_speed_title">根据回放速度调整媒体信息</string>
<string name="pref_playback_time_respects_speed_sum">显示的播放位置和持续时间已根据回放速度进行调整</string>
<string name="pref_fast_forward">快进跳过时间</string>
@@ -425,21 +426,25 @@
<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_queueAddToFront_sum">添加新曲目到队列之前。</string>
- <string name="pref_queueAddToFront_title">新曲目从播放列表的前排插入</string>
+ <string name="pref_enqueue_location_title">排队位置</string>
+ <string name="pref_enqueue_location_sum">添加音频至:%1$s</string>
+ <string name="enqueue_location_back">后退</string>
+ <string name="enqueue_location_front">前进</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="crash_report_title">崩溃报告</string>
- <string name="crash_report_sum">通过 E-mail 发送最后崩溃报告</string>
- <string name="send_email">发送 E-mail</string>
+ <string name="view_mailing_list">查看邮件列表</string>
+ <string name="bug_report_title">报告Bug</string>
+ <string name="open_bug_tracker">打开Bug跟踪器</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">FAQ</string>
- <string name="pref_known_issues">已知问题</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">启用在 Cast 设备(例如 Chromecast 、 Audio Speakers 和 Android TV )上对于远端媒体回放的支持</string>
@@ -465,6 +470,16 @@
<string name="back_button_go_to_page_title">选择页面</string>
<string name="pref_delete_removes_from_queue_title">删除队列间距</string>
<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">翻译由AntennPod用户使用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>
@@ -476,22 +491,16 @@
<string name="found_in_title_label">标题中查找</string>
<string name="no_results_for_query">未找到关于 \"%1$s\" 的结果</string>
<!--OPML import and export-->
- <string name="opml_import_txtv_button_lable">OPML 文件可以方便的从别的播客转移数据过来。</string>
<string name="opml_import_option">设置 %1$d</string>
<string name="opml_import_explanation_1">从本地文件系统选择一个特定文件地址。</string>
- <string name="opml_import_explanation_2">使用外部应用程序,例如 Dropbox,Google Drive 或您喜爱的文件管理器打开 OPML 文件。</string>
<string name="opml_import_explanation_3"><i>与</i> AntennaPod 一样,许多应用例如Google Mail,Dropbox,Google Drive和大多数文件管理器均可 <i>打开</i> OPML文件。</string>
- <string name="start_import_label">开始导入</string>
<string name="opml_import_label">OPML 导入</string>
- <string name="opml_directory_error">错误!</string>
<string name="reading_opml_label">OPML 文件读取中</string>
<string name="opml_reader_error">读取 OPML 文档时发生错误</string>
<string name="opml_import_error_no_file">没有选择文件!</string>
<string name="select_all_label">全选</string>
<string name="deselect_all_label">取消所有选择</string>
- <string name="select_options_label">选择</string>
<string name="choose_file_from_filesystem">来自本地文件系统</string>
- <string name="choose_file_from_external_application">使用外部应用</string>
<string name="opml_export_label">OPML 导出</string>
<string name="html_export_label">导出为 HTML 文件</string>
<string name="exporting_label">正在导出</string>
@@ -562,6 +571,7 @@
<string name="choose_data_directory">选择数据文件夹</string>
<string name="choose_data_directory_message">请选择用于 AntennaPod 的数据根目录, AntennaPod 将会创建合适的子目录。</string>
<string name="choose_data_directory_permission_rationale">访问外部存储需要</string>
+ <string name="choose_data_directory_available_space">%2$s中%1$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>
@@ -580,8 +590,7 @@
<string name="pref_restart_required">需要重启 AntennaPod 使改变生效。</string>
<!--Online feed view-->
<string name="subscribe_label">订阅</string>
- <string name="subscribed_label">已订阅</string>
- <string name="downloading_label">正在下载</string>
+ <string name="subscribing_label">订阅中...</string>
<!--Content descriptions for image buttons-->
<string name="rewind_label">回放</string>
<string name="fast_forward_label">快进</string>
@@ -688,7 +697,6 @@
<string name="cast_failed_setting_volume">音量设置失败</string>
<string name="cast_failed_no_connection">与投影设备间无连接</string>
<string name="cast_failed_no_connection_trans">与投影设备间的连接已丢失。程序正尝试重新建立连接。请等待数秒并再次尝试。</string>
- <string name="cast_failed_perform_action">未能执行动作</string>
<string name="cast_failed_status_request">未能与投影设备同步</string>
<string name="cast_failed_seek">未能在投影设备上找到新播放位置</string>
<string name="cast_failed_receiver_player_error">接收播放器遇到一个严重错误</string>
@@ -703,4 +711,7 @@
<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_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 795d06d8d..dc79905cd 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -105,6 +105,19 @@
<item>@string/episode_cleanup_never</item>
</string-array>
+ <string-array name="enqueue_location_options">
+ <item>@string/enqueue_location_back</item>
+ <item>@string/enqueue_location_front</item>
+ <item>@string/enqueue_location_after_current</item>
+ </string-array>
+
+ <string-array name="enqueue_location_values">
+ <!-- MUST be the same as UserPreferences.EnqueueLocation enum -->
+ <item>BACK</item>
+ <item>FRONT</item>
+ <item>AFTER_CURRENTLY_PLAYING</item>
+ </string-array>
+
<string-array name="episode_cleanup_values">
<item>-1</item>
<item>0</item>
@@ -232,16 +245,6 @@
<item>exoplayer</item>
</string-array>
- <string-array name="media_player_options_no_sonic">
- <item>@string/media_player_builtin</item>
- <item>@string/media_player_exoplayer</item>
- </string-array>
-
- <string-array name="media_player_values_no_sonic">
- <item>builtin</item>
- <item>exoplayer</item>
- </string-array>
-
<string-array name="episode_filter_options">
<item>@string/hide_unplayed_episodes_label</item>
<item>@string/hide_paused_episodes_label</item>
@@ -266,6 +269,25 @@
<item>is_favorite</item>
</string-array>
+ <!-- sort for podcast screen, not for queue -->
+ <string-array name="feed_episodes_sort_options">
+ <item>@string/sort_date_new_old</item>
+ <item>@string/sort_date_old_new</item>
+ <item>@string/sort_title_a_z</item>
+ <item>@string/sort_title_z_a</item>
+ <item>@string/sort_duration_short_long</item>
+ <item>@string/sort_duration_long_short</item>
+ </string-array>
+
+ <string-array name="feed_episodes_sort_values">
+ <item>DATE_NEW_OLD</item>
+ <item>DATE_OLD_NEW</item>
+ <item>EPISODE_TITLE_A_Z</item>
+ <item>EPISODE_TITLE_Z_A</item>
+ <item>DURATION_SHORT_LONG</item>
+ <item>DURATION_LONG_SHORT</item>
+ </string-array>
+
<string-array name="image_cache_size_options">
<item>20 MiB</item>
<item>50 MiB</item>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 530b40d46..fe571539a 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -52,6 +52,7 @@
<attr name="ic_select_all" format="reference"/>
<attr name="ic_select_none" format="reference"/>
<attr name="ic_sort" format="reference"/>
+ <attr name="ic_key" format="reference"/>
<attr name="ic_sd_storage" format="reference"/>
<attr name="ic_create_new_folder" format="reference"/>
<attr name="ic_cast_disconnect" format="reference"/>
@@ -63,6 +64,7 @@
<attr name="master_switch_background" format="color"/>
<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"/>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index c8a22eddb..7ba02db3d 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -162,6 +162,7 @@
<string name="filtered_label">Filtered</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Last Refresh failed</string>
<string name="open_podcast">Open Podcast</string>
+ <string name="please_wait_for_data">Please wait until the data is loaded</string>
<!-- actions on feeditems -->
<string name="download_label">Download</string>
@@ -306,7 +307,7 @@
<!-- Variable Speed -->
<string name="download_plugin_label">Download Plugin</string>
<string name="no_playback_plugin_title">Plugin Not Installed</string>
- <string name="no_playback_plugin_or_sonic_msg">For variable speed playback to work, we recommend to enable the built-in Sonic mediaplayer [Android 4.1+].\n\nAlternatively, you can download the third party plugin <i>Prestissimo</i> from the Play Store.\nAny problems with Prestissimo are not the responsibility of AntennaPod and should be reported to the plugin owner.</string>
+ <string name="no_playback_plugin_or_sonic_msg">For variable speed playback to work, we recommend to enable the built-in Sonic mediaplayer.</string>
<string name="set_playback_speed_label">Playback Speeds</string>
<string name="enable_sonic">Enable Sonic</string>
@@ -330,11 +331,12 @@
<string name="no_fav_episodes_label">You can add episodes to the favorites by long-pressing them.</string>
<string name="no_chapters_head_label">No chapters</string>
<string name="no_chapters_label">This episode has no chapters.</string>
+ <string name="no_subscriptions_head_label">No subscriptions</string>
+ <string name="no_subscriptions_label">To subscribe to a podcast, press the plus icon below.</string>
<!-- Preferences -->
<string name="storage_pref">Storage</string>
<string name="project_pref">Project</string>
- <string name="about_pref">About</string>
<string name="queue_label">Queue</string>
<string name="integrations_label">Integrations</string>
<string name="automation">Automation</string>
@@ -381,6 +383,8 @@
<string name="pref_pauseOnHeadsetDisconnect_title">Headphones Disconnect</string>
<string name="pref_unpauseOnHeadsetReconnect_title">Headphones Reconnect</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth Reconnect</string>
+ <string name="pref_stream_over_download_title">Prefer Streaming</string>
+ <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>
@@ -408,6 +412,8 @@
<string name="pref_parallel_downloads_title">Parallel Downloads</string>
<string name="pref_episode_cache_title">Episode Cache</string>
<string name="pref_episode_cache_summary">Total number of downloaded episodes cached on the device. Automatic download will be suspended if this number is reached.</string>
+ <string name="pref_episode_cover_title">Use Episode Cover</string>
+ <string name="pref_episode_cover_summary">Use the episode specific cover whenever available. If unchecked, the app will always use the podcast cover image.</string>
<string name="pref_theme_title_use_system">Use system theme</string>
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
@@ -455,8 +461,11 @@
<string name="pref_showDownloadReport_title">Show Download Report</string>
<string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string>
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
- <string name="pref_queueAddToFront_sum">Add new episodes to the front of the queue.</string>
- <string name="pref_queueAddToFront_title">Enqueue at Front</string>
+ <string name="pref_enqueue_location_title">Enqueue Location</string>
+ <string name="pref_enqueue_location_sum">Add episodes to: %1$s</string>
+ <string name="enqueue_location_back">Back</string>
+ <string name="enqueue_location_front">Front</string>
+ <string name="enqueue_location_after_current">After current episode</string>
<string name="pref_smart_mark_as_played_disabled">Disabled</string>
<string name="pref_image_cache_size_title">Image Cache Size</string>
<string name="pref_image_cache_size_sum">Size of the disk cache for images.</string>
@@ -499,6 +508,17 @@
<string name="pref_delete_removes_from_queue_title">Delete removes from Queue</string>
<string name="pref_delete_removes_from_queue_sum">Automatically remove an episode from the queue when it is deleted.</string>
+ <!-- About screen -->
+ <string name="about_pref">About</string>
+ <string name="antennapod_version">AntennaPod version</string>
+ <string name="developers">Developers</string>
+ <string name="developers_summary">Everyone can help to make AntennaPod better</string>
+ <string name="translators">Translators</string>
+ <string name="translators_summary">Translations are created by users of AntennaPod using Transifex</string>
+ <string name="privacy_policy">Privacy policy</string>
+ <string name="licenses">Licenses</string>
+ <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>
@@ -619,7 +639,7 @@
<!-- Online feed view -->
<string name="subscribe_label">Subscribe</string>
- <string name="downloading_label">Downloading&#8230;</string>
+ <string name="subscribing_label">Subscribing&#8230;</string>
<!-- Content descriptions for image buttons -->
<string name="rewind_label">Rewind</string>
@@ -756,4 +776,9 @@
<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>
+ <string name="widget_create_button">Create widget</string>
+ <string name="widget_opacity">Opacity</string>
</resources>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index d2ba4bb50..a638917f4 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Theme.AntennaPod.Light" parent="Theme.Base.AntennaPod.Light">
<!-- Room for API dependent attributes -->
@@ -18,12 +18,13 @@
<item name="action_about">@drawable/ic_info_grey600_24dp</item>
<item name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_grey600_24dp</item>
<item name="action_search">@drawable/ic_search_grey600_24dp</item>
- <item name="action_stream">@drawable/ic_settings_input_antenna_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_speed">@drawable/ic_playback_speed_dark</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>
@@ -74,6 +75,7 @@
<item name="ic_bug">@drawable/ic_bug_grey600_24dp</item>
<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="master_switch_background">@color/master_switch_background_light</item>
<item name="currently_playing_background">@color/highlight_light</item>
@@ -104,12 +106,13 @@
<item name="action_about">@drawable/ic_info_white_24dp</item>
<item name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_white_24dp</item>
<item name="action_search">@drawable/ic_search_white_24dp</item>
- <item name="action_stream">@drawable/ic_settings_input_antenna_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_speed">@drawable/ic_playback_speed_white</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>
@@ -160,6 +163,7 @@
<item name="ic_bug">@drawable/ic_bug_white_24dp</item>
<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="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>
@@ -224,6 +228,43 @@
<item name="windowActionModeOverlay">true</item>
</style>
+ <style name="Theme.AntennaPod.Light.Translucent" parent="Theme.AntennaPod.Light.NoTitle">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowAnimationStyle">@style/AnimationFade</item>
+ <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
+ <item name="android:fitsSystemWindows">true</item>
+ </style>
+
+ <style name="Theme.AntennaPod.Dark.Translucent" parent="Theme.AntennaPod.Dark.NoTitle">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowAnimationStyle">@style/AnimationFade</item>
+ <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
+ <item name="android:fitsSystemWindows">true</item>
+ </style>
+
+ <style name="Theme.AntennaPod.TrueBlack.Translucent" parent="Theme.Base.AntennaPod.TrueBlack">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowAnimationStyle">@style/AnimationFade</item>
+ <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
+ <item name="android:fitsSystemWindows">true</item>
+ </style>
+
+ <style name="AnimationFade">
+ <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+ </style>
<style name="Theme.AntennaPod.Dark.Splash" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/bg_splash</item>
@@ -267,7 +308,8 @@
<item name="android:textAllCaps">true</item>
</style>
- <style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button">
+ <style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button.Colored">
+ <item name="colorButtonNormal">@color/antennapod_blue</item>
<item name="textAllCaps">false</item>
</style>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
index 800222ada..55d804203 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -10,6 +10,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.exception.RxJavaErrorHandlerSetup;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
* Stores callbacks for core classes like Services, DB classes etc. and other configuration variables.
@@ -58,6 +59,7 @@ public class ClientConfig {
}
SleepTimerPreferences.init(context);
RxJavaErrorHandlerSetup.setupRxJavaErrorHandler();
+ NotificationUtils.createChannels(context);
initialized = true;
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
index 857827219..6bd753561 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
@@ -3,6 +3,9 @@ package de.danoeh.antennapod.core.feed;
import org.junit.Before;
import org.junit.Test;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -40,6 +43,16 @@ public class FeedItemTest {
assertFeedItemImageWasUpdated();
}
+ @Test
+ public void testUpdateFromOther_dateChanged() throws Exception {
+ Date originalDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1952-03-11 00:00:00");
+ Date changedDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1952-03-11 00:42:42");
+ original.setPubDate(originalDate);
+ changedFeedItem.setPubDate(changedDate);
+ original.updateFromOther(changedFeedItem);
+ assertEquals(changedDate.getTime(), original.getPubDate().getTime());
+ }
+
/**
* Test that a played item loses that state after being marked as new.
*/
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
index f46797d28..991495a3f 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.feed;
-class FeedMother {
+public class FeedMother {
public static final String IMAGE_URL = "http://example.com/image";
public static Feed anyFeed() {
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
index 4717041f4..88b342850 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
@@ -3,10 +3,13 @@ package de.danoeh.antennapod.core.feed;
import org.junit.Before;
import org.junit.Test;
+import de.danoeh.antennapod.core.util.SortOrder;
+
import static de.danoeh.antennapod.core.feed.FeedMother.anyFeed;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class FeedTest {
@@ -59,6 +62,27 @@ public class FeedTest {
feedImageWasUpdated();
}
+ @Test
+ public void testSetSortOrder_OnlyIntraFeedSortAllowed() throws Exception {
+ for (SortOrder sortOrder : SortOrder.values()) {
+ if (sortOrder.scope == SortOrder.Scope.INTRA_FEED) {
+ original.setSortOrder(sortOrder); // should be okay
+ } else {
+ try {
+ original.setSortOrder(sortOrder);
+ fail("SortOrder " + sortOrder + " should not be allowed on a feed");
+ } catch (IllegalArgumentException iae) {
+ // expected exception
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testSetSortOrder_NullAllowed() throws Exception {
+ original.setSortOrder(null); // should be okay
+ }
+
private void feedHasNotChanged() {
assertFalse(original.compareWithOther(changedFeed));
}
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
new file mode 100644
index 000000000..17b88bdd2
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
@@ -0,0 +1,293 @@
+package de.danoeh.antennapod.core.storage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedMother;
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
+import de.danoeh.antennapod.core.util.playback.ExternalMedia;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.AFTER_CURRENTLY_PLAYING;
+import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.BACK;
+import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.FRONT;
+import static de.danoeh.antennapod.core.util.CollectionTestUtil.concat;
+import static de.danoeh.antennapod.core.util.CollectionTestUtil.list;
+import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+
+public class ItemEnqueuePositionCalculatorTest {
+
+ @RunWith(Parameterized.class)
+ public static class BasicTest {
+ @Parameters(name = "{index}: case<{0}>, expected:{1}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"case default, i.e., add to the end",
+ concat(QUEUE_DEFAULT_IDS, TFI_ID),
+ BACK, QUEUE_DEFAULT},
+ {"case option enqueue at front",
+ concat(TFI_ID, QUEUE_DEFAULT_IDS),
+ FRONT, QUEUE_DEFAULT},
+ {"case empty queue, option default",
+ list(TFI_ID),
+ BACK, QUEUE_EMPTY},
+ {"case empty queue, option enqueue at front",
+ list(TFI_ID),
+ FRONT, QUEUE_EMPTY},
+ });
+ }
+
+ @Parameter
+ public String message;
+
+ @Parameter(1)
+ public List<Long> idsExpected;
+
+ @Parameter(2)
+ public EnqueueLocation options;
+
+ @Parameter(3)
+ public List<FeedItem> curQueue;
+
+ public static final long TFI_ID = 101;
+
+ /**
+ * Add a FeedItem with ID {@link #TFI_ID} with the setup
+ */
+ @Test
+ public void test() {
+ ItemEnqueuePositionCalculator calculator = new ItemEnqueuePositionCalculator(options);
+
+ // shallow copy to which the test will add items
+ List<FeedItem> queue = new ArrayList<>(curQueue);
+ FeedItem tFI = createFeedItem(TFI_ID);
+ doAddToQueueAndAssertResult(message,
+ calculator, tFI, queue, getCurrentlyPlaying(),
+ idsExpected);
+ }
+
+ Playable getCurrentlyPlaying() { return null; }
+ }
+
+ @RunWith(Parameterized.class)
+ public static class AfterCurrentlyPlayingTest extends BasicTest {
+ @Parameters(name = "{index}: case<{0}>, expected:{1}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"case option after currently playing",
+ list(11L, TFI_ID, 12L, 13L, 14L),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, 11L},
+ {"case option after currently playing, currently playing in the middle of the queue",
+ list(11L, 12L, 13L, TFI_ID, 14L),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, 13L},
+ {"case option after currently playing, currently playing is not in queue",
+ concat(TFI_ID, QUEUE_DEFAULT_IDS),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, 99L},
+ {"case option after currently playing, no currentlyPlaying is null",
+ concat(TFI_ID, QUEUE_DEFAULT_IDS),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NULL},
+ {"case option after currently playing, currentlyPlaying is externalMedia",
+ concat(TFI_ID, QUEUE_DEFAULT_IDS),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA},
+ {"case empty queue, option after currently playing",
+ list(TFI_ID),
+ AFTER_CURRENTLY_PLAYING, QUEUE_EMPTY, ID_CURRENTLY_PLAYING_NULL},
+ });
+ }
+
+ @Parameter(4)
+ public long idCurrentlyPlaying;
+
+ @Override
+ Playable getCurrentlyPlaying() {
+ return ItemEnqueuePositionCalculatorTest.getCurrentlyPlaying(idCurrentlyPlaying);
+ }
+
+ private static Playable externalMedia() {
+ return new ExternalMedia("http://example.com/episode.mp3", MediaType.AUDIO);
+ }
+
+ private static final long ID_CURRENTLY_PLAYING_NULL = -1L;
+ private static final long ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA = -9999L;
+
+ }
+
+ @RunWith(Parameterized.class)
+ public static class ItemEnqueuePositionCalculatorPreserveDownloadOrderTest {
+
+ /**
+ * The test covers the use case that when user initiates multiple downloads in succession,
+ * resulting in multiple addQueueItem() calls in succession.
+ * the items in the queue will be in the same order as the the order user taps to download
+ */
+ @Parameters(name = "{index}: case<{0}>")
+ public static Iterable<Object[]> data() {
+ // Attempts to make test more readable by showing the expected list of ids
+ // (rather than the expected positions)
+ return Arrays.asList(new Object[][] {
+ {"download order test, enqueue default",
+ concat(QUEUE_DEFAULT_IDS, 101L),
+ concat(QUEUE_DEFAULT_IDS, list(101L, 102L)),
+ concat(QUEUE_DEFAULT_IDS, list(101L, 102L, 103L)),
+ BACK, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NULL},
+ {"download order test, enqueue at front (currently playing has no effect)",
+ concat(101L, QUEUE_DEFAULT_IDS),
+ concat(list(101L, 102L), QUEUE_DEFAULT_IDS),
+ concat(list(101L, 103L, 102L), QUEUE_DEFAULT_IDS),
+ // ^ 103 is put ahead of 102, after 102 failed.
+ // It is a limitation as the logic can't tell 102 download has failed
+ // (as opposed to simply being enqueued)
+ FRONT, QUEUE_DEFAULT, 11L}, // 11 is at the front, currently playing
+ {"download order test, enqueue after currently playing",
+ list(11L, 101L, 12L, 13L, 14L),
+ list(11L, 101L, 102L, 12L, 13L, 14L),
+ list(11L, 101L, 103L, 102L, 12L, 13L, 14L),
+ AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, 11L} // 11 is at the front, currently playing
+ });
+ }
+
+ @Parameter
+ public String message;
+
+ @Parameter(1)
+ public List<Long> idsExpectedAfter101;
+
+ @Parameter(2)
+ public List<Long> idsExpectedAfter102;
+
+ @Parameter(3)
+ public List<Long> idsExpectedAfter103;
+
+ @Parameter(4)
+ public EnqueueLocation options;
+
+ @Parameter(5)
+ public List<FeedItem> queueInitial;
+
+ @Parameter(6)
+ public long idCurrentlyPlaying;
+
+ @Test
+ public void testQueueOrderWhenDownloading2Items() {
+
+ // Setup class under test
+ //
+ ItemEnqueuePositionCalculator calculator = new ItemEnqueuePositionCalculator(options);
+ DownloadStateProvider stubDownloadStateProvider = mock(DownloadStateProvider.class);
+ stub(stubDownloadStateProvider.isDownloadingFile(any(FeedMedia.class))).toReturn(false);
+ calculator.downloadStateProvider = stubDownloadStateProvider;
+
+ // Setup initial data
+ // A shallow copy, as the test code will manipulate the queue
+ List<FeedItem> queue = new ArrayList<>(queueInitial);
+
+ // Test body
+ Playable currentlyPlaying = getCurrentlyPlaying(idCurrentlyPlaying);
+ // User clicks download on feed item 101
+ FeedItem tFI101 = setAsDownloading(101, stubDownloadStateProvider, true);
+ doAddToQueueAndAssertResult(message + " (1st download)",
+ calculator, tFI101, queue, currentlyPlaying,
+ idsExpectedAfter101);
+ // Then user clicks download on feed item 102
+ FeedItem tFI102 = setAsDownloading(102, stubDownloadStateProvider, true);
+ doAddToQueueAndAssertResult(message + " (2nd download, it should preserve order of download)",
+ calculator, tFI102, queue, currentlyPlaying,
+ idsExpectedAfter102);
+ // simulate download failure case for 102
+ setAsDownloading(tFI102, stubDownloadStateProvider, false);
+ // Then user clicks download on feed item 103
+ FeedItem tFI103 = setAsDownloading(103, stubDownloadStateProvider, true);
+ doAddToQueueAndAssertResult(message
+ + " (3rd download, with 2nd download failed; "
+ + "it should be behind 1st download (unless enqueueLocation is BACK)",
+ calculator, tFI103, queue, currentlyPlaying,
+ idsExpectedAfter103);
+
+ }
+
+
+ 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");
+ media.setId(item.getId());
+ item.setMedia(media);
+ return setAsDownloading(item, stubDownloadStateProvider, isDownloading);
+ }
+
+ private static FeedItem setAsDownloading(FeedItem item, DownloadStateProvider stubDownloadStateProvider,
+ boolean isDownloading) {
+ stub(stubDownloadStateProvider.isDownloadingFile(item.getMedia())).toReturn(isDownloading);
+ return item;
+ }
+
+ }
+
+
+ static void doAddToQueueAndAssertResult(String message,
+ ItemEnqueuePositionCalculator calculator,
+ FeedItem itemToAdd,
+ List<FeedItem> queue,
+ Playable currentlyPlaying,
+ List<Long> idsExpected) {
+ int posActual = calculator.calcPosition(queue, currentlyPlaying);
+ queue.add(posActual, itemToAdd);
+ assertEquals(message, idsExpected, getIdList(queue));
+ }
+
+ static final List<FeedItem> QUEUE_EMPTY = Collections.unmodifiableList(Arrays.asList());
+
+ static final List<FeedItem> QUEUE_DEFAULT =
+ Collections.unmodifiableList(Arrays.asList(
+ createFeedItem(11), createFeedItem(12), createFeedItem(13), createFeedItem(14)));
+ static final List<Long> QUEUE_DEFAULT_IDS =
+ QUEUE_DEFAULT.stream().map(fi -> fi.getId()).collect(Collectors.toList());
+
+
+ static Playable getCurrentlyPlaying(long idCurrentlyPlaying) {
+ if (ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA == idCurrentlyPlaying) {
+ return externalMedia();
+ }
+ if (ID_CURRENTLY_PLAYING_NULL == idCurrentlyPlaying) {
+ return null;
+ }
+ return createFeedItem(idCurrentlyPlaying).getMedia();
+ }
+
+ static Playable externalMedia() {
+ return new ExternalMedia("http://example.com/episode.mp3", MediaType.AUDIO);
+ }
+
+ static final long ID_CURRENTLY_PLAYING_NULL = -1L;
+ static final long ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA = -9999L;
+
+
+ static FeedItem createFeedItem(long id) {
+ FeedItem item = new FeedItem(id, "Item" + id, "ItemId" + id, "url",
+ new Date(), FeedItem.PLAYED, FeedMother.anyFeed());
+ FeedMedia media = new FeedMedia(item, "download_url", 1234567, "audio/mpeg");
+ media.setId(item.getId());
+ item.setMedia(media);
+ return item;
+ }
+
+}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/CollectionTestUtil.java b/core/src/test/java/de/danoeh/antennapod/core/util/CollectionTestUtil.java
new file mode 100644
index 000000000..21f1ef5d4
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/CollectionTestUtil.java
@@ -0,0 +1,30 @@
+package de.danoeh.antennapod.core.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class CollectionTestUtil {
+
+ public static <T> List<? extends T> concat(T item, List<? extends T> list) {
+ List<T> res = new ArrayList<>(list);
+ res.add(0, item);
+ return res;
+ }
+
+ public static <T> List<? extends T> concat(List<? extends T> list, T item) {
+ List<T> res = new ArrayList<>(list);
+ res.add(item);
+ return res;
+ }
+
+ public static <T> List<? extends T> concat(List<? extends T> list1, List<? extends T> list2) {
+ List<T> res = new ArrayList<>(list1);
+ res.addAll(list2);
+ return res;
+ }
+
+ public static <T> List<T> list(T... a) {
+ return Arrays.asList(a);
+ }
+}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java
index 791b6a75b..ccaa77ae8 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java
@@ -10,22 +10,25 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNull;
/**
- * Test class for QueueSorter.
+ * Test class for FeedItemPermutors.
*/
-public class QueueSorterTest {
+public class FeedItemPermutorsTest {
@Test
- public void testPermutorForRule_null() {
- assertNull(QueueSorter.getPermutor(null));
+ public void testEnsureNonNullPermutors() {
+ for (SortOrder sortOrder : SortOrder.values()) {
+ assertNotNull("The permutor for SortOrder " + sortOrder + " is unexpectedly null",
+ FeedItemPermutors.getPermutor(sortOrder));
+ }
}
@Test
public void testPermutorForRule_EPISODE_TITLE_ASC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.EPISODE_TITLE_A_Z);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_A_Z);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -34,8 +37,21 @@ public class QueueSorterTest {
}
@Test
+ public void testPermutorForRule_EPISODE_TITLE_ASC_NullTitle() {
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_A_Z);
+
+ List<FeedItem> itemList = getTestList();
+ itemList.get(2) // itemId 2
+ .setTitle(null);
+ assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
+ permutor.reorder(itemList);
+ assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
+ }
+
+
+ @Test
public void testPermutorForRule_EPISODE_TITLE_DESC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.EPISODE_TITLE_Z_A);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_Z_A);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -45,7 +61,7 @@ public class QueueSorterTest {
@Test
public void testPermutorForRule_DATE_ASC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DATE_OLD_NEW);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_OLD_NEW);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -54,8 +70,20 @@ public class QueueSorterTest {
}
@Test
+ public void testPermutorForRule_DATE_ASC_NulPubDatel() {
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_OLD_NEW);
+
+ List<FeedItem> itemList = getTestList();
+ itemList.get(2) // itemId 2
+ .setPubDate(null);
+ assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
+ permutor.reorder(itemList);
+ assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
+ }
+
+ @Test
public void testPermutorForRule_DATE_DESC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DATE_NEW_OLD);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_NEW_OLD);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -65,7 +93,7 @@ public class QueueSorterTest {
@Test
public void testPermutorForRule_DURATION_ASC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DURATION_SHORT_LONG);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_SHORT_LONG);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -75,7 +103,7 @@ public class QueueSorterTest {
@Test
public void testPermutorForRule_DURATION_DESC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DURATION_LONG_SHORT);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_LONG_SHORT);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -84,8 +112,20 @@ public class QueueSorterTest {
}
@Test
+ public void testPermutorForRule_DURATION_DESC_NullMedia() {
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_LONG_SHORT);
+
+ List<FeedItem> itemList = getTestList();
+ itemList.get(1) // itemId 3
+ .setMedia(null);
+ assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
+ permutor.reorder(itemList);
+ assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
+ }
+
+ @Test
public void testPermutorForRule_FEED_TITLE_ASC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.FEED_TITLE_A_Z);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_A_Z);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -95,7 +135,7 @@ public class QueueSorterTest {
@Test
public void testPermutorForRule_FEED_TITLE_DESC() {
- Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.FEED_TITLE_Z_A);
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_Z_A);
List<FeedItem> itemList = getTestList();
assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
@@ -103,6 +143,18 @@ public class QueueSorterTest {
assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
}
+ @Test
+ public void testPermutorForRule_FEED_TITLE_DESC_NullTitle() {
+ Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_Z_A);
+
+ List<FeedItem> itemList = getTestList();
+ itemList.get(1) // itemId 3
+ .getFeed().setTitle(null);
+ assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
+ permutor.reorder(itemList);
+ assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
+ }
+
/**
* Generates a list with test data.
*/
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java
new file mode 100644
index 000000000..0b64cb10f
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java
@@ -0,0 +1,73 @@
+package de.danoeh.antennapod.core.util;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+import static org.junit.Assert.assertEquals;
+
+public class FeedItemUtilTest {
+
+
+ @RunWith(Parameterized.class)
+ public static class LinkWithFallbackTest {
+ private static final String FEED_LINK = "http://example.com";
+ private static final String ITEM_LINK = "http://example.com/feedItem1";
+
+ @Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ { "average",
+ FEED_LINK, ITEM_LINK, ITEM_LINK },
+ { "null item link - fallback to feed",
+ FEED_LINK, null, FEED_LINK},
+ { "empty item link - same as null",
+ FEED_LINK, "", FEED_LINK},
+ { "blank item link - same as null",
+ FEED_LINK, " ", FEED_LINK},
+ { "fallback, but feed link is null too",
+ null, null, null },
+ { "fallback - but empty feed link - same as null",
+ "", null, null},
+ { "fallback - but blank feed link - same as null",
+ " ", null, null}
+ });
+ }
+
+ private final String msg;
+ private final String feedLink;
+ private final String itemLink;
+ private final String expected;
+
+ public LinkWithFallbackTest(String msg, String feedLink, String itemLink, String expected) {
+ this.msg = msg;
+ this.feedLink = feedLink;
+ this.itemLink = itemLink;
+ this.expected = expected;
+ }
+
+
+ @Test
+ public void testLinkWithFallback() {
+ String actual = FeedItemUtil.getLinkWithFallback(createFeedItem(feedLink, itemLink));
+ assertEquals(msg, expected, actual);
+ }
+
+ private static FeedItem createFeedItem(String feedLink, String itemLink) {
+ Feed feed = new Feed();
+ feed.setLink(feedLink);
+ FeedItem feedItem = new FeedItem();
+ feedItem.setLink(itemLink);
+ feedItem.setFeed(feed);
+ feed.setItems(Arrays.asList(feedItem));
+ return feedItem;
+ }
+ }
+}
diff --git a/createDevelopersList.py b/createDevelopersList.py
new file mode 100644
index 000000000..df804b979
--- /dev/null
+++ b/createDevelopersList.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+import requests
+
+MAINTAINERS = ['mfietz', 'ByteHamster']
+FORMER_MAINTAINERS = ['TomHennen']
+
+csvFile = open("app/src/main/assets/developers.csv", "w")
+contributorsFile = open("CONTRIBUTORS", "a")
+page = 1
+hasMore = True
+while hasMore:
+ json = requests.get('https://api.github.com/repos/AntennaPod/AntennaPod/contributors'
+ + '?q=contributions&order=desc&per_page=100&page=' + str(page)).json()
+ for contributor in json:
+ role = 'Contributor'
+ if contributor['login'] == 'danieloeh':
+ role = 'Original creator of AntennaPod (retired)'
+ elif contributor['login'] in MAINTAINERS:
+ role = 'Maintainer'
+ elif contributor['login'] in FORMER_MAINTAINERS:
+ role = 'Maintainer (retired)'
+ line = contributor['login'].replace(";", "") + ';' + str(contributor['id']) + ';' + role
+ csvFile.write(line + '\n')
+ print(line)
+ contributorsFile.write(contributor['login'] + '\n')
+ page = page + 1
+ hasMore = len(json) > 0
+csvFile.close()
+contributorsFile.close()
+
diff --git a/createTranslatorsList.py b/createTranslatorsList.py
new file mode 100644
index 000000000..16cbe575a
--- /dev/null
+++ b/createTranslatorsList.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+import pycountry
+import requests
+import configparser
+import os
+
+config = configparser.ConfigParser()
+config.read(os.path.expanduser("~") + '/.transifexrc')
+if 'https://www.transifex.com' in config:
+ TRANSIFEX_USER = config['https://www.transifex.com']['username']
+ TRANSIFEX_PW = config['https://www.transifex.com']['password']
+else:
+ TRANSIFEX_USER = ""
+ TRANSIFEX_PW = ""
+
+csvFile = open("app/src/main/assets/translators.csv", "w")
+contributorsFile = open("CONTRIBUTORS", "a")
+r = requests.get('http://www.transifex.com/api/2/project/antennapod/languages/',
+ auth=(TRANSIFEX_USER, TRANSIFEX_PW))
+for lang in r.json():
+ langContributers = lang['coordinators'] + lang['reviewers'] + lang['translators']
+ langContributers = sorted(langContributers, key=str.lower)
+ langCode = lang['language_code']
+ try:
+ langName = pycountry.languages.lookup(langCode).name
+ except:
+ try:
+ langName = pycountry.languages.lookup(
+ langCode.split('_')[0]).name + ' (' + langCode + ')'
+ except:
+ langName = lang['language_code']
+ print('\033[91mLanguage code not found:' + langCode + '\033[0m')
+
+ joinedTranslators = ', '.join(langContributers).replace(';', '');
+ contributorsFile.write(langName + ": " + joinedTranslators + '\n')
+ csvFile.write(langName + ';' + joinedTranslators + '\n')
+ print(langName + ';' + joinedTranslators)
+csvFile.close()
+contributorsFile.close()
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f4d7b2bf6..f04d6a20a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/makeRelease.sh b/makeRelease.sh
index c3b684ec0..289e7fd42 100755
--- a/makeRelease.sh
+++ b/makeRelease.sh
@@ -9,9 +9,10 @@ set -e
DEST=$2
VERSION_NAME=$1
-# need to get rid of this so that it gets regenerated
-# probably a way to solve this in Gradle too...
-rm -f app/src/main/assets/about.html
+rm CONTRIBUTORS
+./createDevelopersList.py
+./createTranslatorsList.py
+
./gradlew clean assemblePlayDebug assemblePlayRelease
cp app/build/outputs/apk/app-play-debug.apk $DEST/AntennaPod_${VERSION_NAME}_debug.apk
cp app/build/outputs/apk/app-play-release.apk $DEST/AntennaPod_${VERSION_NAME}_release.apk